internal

package
v0.0.0-...-a0a3655 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 28, 2019 License: Apache-2.0 Imports: 31 Imported by: 0

Documentation

Overview

Package internal contains code used internally by auth/integration.

Index

Constants

View Source
const (
	// NoEmail indicates an OAuth2 token is not associated with an email.
	//
	// See Token below. We need this special value to distinguish "an email can not
	// possibly be fetched ever" from "the cached token doesn't have an email yet"
	// cases.
	NoEmail = "-"

	// UnknownEmail indicates an OAuth2 token may potentially be associated with
	// an email, but we haven't tried to fetch the email yet.
	UnknownEmail = ""
)
View Source
const (
	// GCAccessTokenMaxAge defines when to remove unused access tokens from the
	// disk cache.
	//
	// We define "an access token" as an instance of oauth2.Token with
	// RefreshToken set to "".
	//
	// If an access token expired older than GCAccessTokenMaxAge ago, it will be
	// evicted from the cache (it is essentially garbage now anyway).
	GCAccessTokenMaxAge = 2 * time.Hour

	// GCRefreshTokenMaxAge defines when to remove unused refresh tokens from the
	// disk cache.
	//
	// We define "a refresh token" as an instance of oauth2.Token with
	// RefreshToken not set to "".
	//
	// Refresh tokens don't expire, but they get neglected and forgotten by users,
	// staying on their disks forever. We remove such tokens if they haven't been
	// used for more than two weeks.
	//
	// It essentially logs out the user on inactivity. We don't actively revoke
	// evicted tokens though, since it's possible the user has copied the token
	// and uses it elsewhere (it happens). Such token can always be revoked from
	// Google Accounts page manually, if required.
	GCRefreshTokenMaxAge = 14 * 24 * time.Hour

	// CacheFilename is a name of the file with all cached tokens.
	CacheFilename = "creds.json"
)

Variables

View Source
var (
	// ErrInsufficientAccess is returned by MintToken() if token can't be minted
	// for given OAuth scopes. For example, if GCE instance wasn't granted access
	// to requested scopes when it was created.
	ErrInsufficientAccess = errors.New("can't get access token for the given account and scopes")

	// ErrBadRefreshToken is returned by RefreshToken if refresh token was revoked
	// or otherwise invalid. It means MintToken must be used to get a new refresh
	// token.
	ErrBadRefreshToken = errors.New("refresh_token is not valid")

	// ErrBadCredentials is returned by MintToken or RefreshToken if provided
	// offline credentials (like service account key) are invalid.
	ErrBadCredentials = errors.New("invalid service account credentials")
)

Functions

func EqualCacheKeys

func EqualCacheKeys(a, b *CacheKey) bool

EqualCacheKeys returns true if keys are equal.

func EqualTokens

func EqualTokens(a, b *Token) bool

EqualTokens returns true if tokens are equal.

'nil' token corresponds to an empty access token.

func TokenExpiresIn

func TokenExpiresIn(ctx context.Context, t *Token, lifetime time.Duration) bool

TokenExpiresIn returns True if the token is not valid or expires within given duration.

The function returns True in any of the following conditions:

  • The token is not valid.
  • The token expires before now+lifetime.

In all other cases it returns False.

func TokenExpiresInRnd

func TokenExpiresInRnd(ctx context.Context, t *Token, lifetime time.Duration) bool

TokenExpiresInRnd is like TokenExpiresIn, except it slightly randomizes the token expiration time.

If the function returns False, the token expires past now+lifetime. In other words, it is totally safe to use the token until now+lifetime. The inverse of this statement is not correct though: if the function returns True, it doesn't necessarily imply the token will expire before now+lifetime.

The function returns True in any of the following conditions:

  • The token is not valid.
  • The token expires before now+lifetime.
  • The token expiration time is between (now+lifetime, now+lifetime+rnd), where rnd is a uniformly distributed random number between 0 and expiryRandInterval sec (which is set to 30 sec).

This is useful for processes that use multiple service account keys at around the same time. Without randomization, access tokens for such keys expire at the same time (strictly 1h after process startup, where 1h is the default token lifetime). This causes unnecessary contention on the token cache file.

Types

type CacheKey

type CacheKey struct {
	// Key identifies an auth method being used to get the token and its
	// parameters.
	//
	// Its exact form is not important, since it is used only for string matching
	// when searching for a token inside the cache.
	//
	// The following forms are being used currently:
	//  * user/<client_id> when using UserCredentialsMethod with some ClientID.
	//  * service_account/<email>/<key_id> when using ServiceAccountMethod.
	//  * gce/<account> when using GCEMetadataMethod.
	//  * iam/<account> when using actor mode with ActAsServiceAccount != "".
	//  * luci_ctx/<digest> when using LUCIContextMethod.
	Key string `json:"key"`

	// Scopes is the list of requested OAuth scopes.
	Scopes []string `json:"scopes"`
}

CacheKey identifies a slot in the token cache to store the token in.

func (*CacheKey) ToMapKey

func (k *CacheKey) ToMapKey() string

ToMapKey returns a string that can be used as map[string] key.

This string IS NOT PRINTABLE. It's a merely a string-looking []byte.

type DiskTokenCache

type DiskTokenCache struct {
	Context    context.Context // for logging and timing
	SecretsDir string
}

DiskTokenCache implements TokenCache on top of a file.

It uses single file to store all tokens. If multiple processes try to write to it at the same time, only one process wins (so some updates may be lost).

TODO(vadimsh): Either use file locking or split the cache into multiple files to avoid concurrency issues.

TODO(vadimsh): Once this implementation settles and is deployed everywhere, add a cleanup step that removes <cache_dir>/*.tok left from the previous version of this code.

func (*DiskTokenCache) DeleteToken

func (c *DiskTokenCache) DeleteToken(key *CacheKey) error

DeleteToken removes the token from cache.

func (*DiskTokenCache) GetToken

func (c *DiskTokenCache) GetToken(key *CacheKey) (*Token, error)

GetToken reads the token from cache.

func (*DiskTokenCache) PutToken

func (c *DiskTokenCache) PutToken(key *CacheKey, tok *Token) error

PutToken writes the token to cache.

type MemoryTokenCache

type MemoryTokenCache struct {
	// contains filtered or unexported fields
}

MemoryTokenCache implements TokenCache on top of in-process memory.

func (*MemoryTokenCache) DeleteToken

func (c *MemoryTokenCache) DeleteToken(key *CacheKey) error

DeleteToken removes the token from cache.

func (*MemoryTokenCache) GetToken

func (c *MemoryTokenCache) GetToken(key *CacheKey) (*Token, error)

GetToken reads the token from cache.

func (*MemoryTokenCache) PutToken

func (c *MemoryTokenCache) PutToken(key *CacheKey, tok *Token) error

PutToken writes the token to cache.

type Token

type Token struct {
	oauth2.Token

	Email string // an email or NoEmail or empty string (aka UnknownEmail)
}

Token is an oauth2.Token with an email that corresponds to it.

Email may be an empty string, in which case we assume the email hasn't been fetched yet. It can also be a special NoEmail string, which means the token is not associated with an email (happens for tokens without 'userinfo.email' scope).

type TokenCache

type TokenCache interface {
	// GetToken reads the token from cache.
	//
	// Returns (nil, nil) if requested token is not in the cache.
	GetToken(key *CacheKey) (*Token, error)

	// PutToken writes the token to cache.
	PutToken(key *CacheKey, tok *Token) error

	// DeleteToken removes the token from cache.
	DeleteToken(key *CacheKey) error
}

TokenCache stores access and refresh tokens to avoid requesting them all the time.

var (
	// ProcTokenCache is shared in-process cache to use if disk cache is disabled.
	ProcTokenCache TokenCache = &MemoryTokenCache{}
)

type TokenProvider

type TokenProvider interface {
	// RequiresInteraction is true if provider may start user interaction
	// in MintToken.
	RequiresInteraction() bool

	// Lightweight is true if MintToken is very cheap to call.
	//
	// In this case the token is not being cached on disk (only in memory), since
	// it's easy to get a new one each time the process starts.
	//
	// By avoiding the disk cache, we reduce the chance of a leak.
	Lightweight() bool

	// Email is email associated with tokens produced by the provider, if known.
	//
	// May return UnknownEmail, which means the provider doesn't know the email
	// in advance and RefreshToken must be used to get the token and the email.
	// This happens, for example, for interactive providers before user has
	// logged in.
	//
	// It can also be NoEmail which means the email is not available, even if
	// caller is using RefreshToken.
	Email() string

	// CacheKey identifies a slot in the token cache to store the token in.
	//
	// Note: CacheKey MAY change during lifetime of a TokenProvider. It happens,
	// for example, for ServiceAccount token provider if the underlying service
	// account key is replaced while the process is still running.
	CacheKey(ctx context.Context) (*CacheKey, error)

	// MintToken launches authentication flow (possibly interactive) and returns
	// a new refreshable token (or error). It must never return (nil, nil).
	//
	// In actor mode 'base' is an IAM-scoped sufficiently fresh oauth token. It's
	// nil otherwise. Used by IAM-based token provider.
	MintToken(ctx context.Context, base *Token) (*Token, error)

	// RefreshToken takes existing token (probably expired, but not necessarily)
	// and returns a new refreshed token. It should never do any user interaction.
	// If a user interaction is required, a error should be returned instead.
	//
	// In actor mode 'base' is an IAM-scoped sufficiently fresh oauth token. It's
	// nil otherwise. Used by IAM-based token provider.
	RefreshToken(ctx context.Context, prev, base *Token) (*Token, error)
}

TokenProvider knows how to mint new tokens or refresh existing ones.

func NewGCETokenProvider

func NewGCETokenProvider(ctx context.Context, account string, scopes []string) (TokenProvider, error)

NewGCETokenProvider returns TokenProvider that knows how to use GCE metadata server.

func NewIAMTokenProvider

func NewIAMTokenProvider(ctx context.Context, actAs string, scopes []string, transport http.RoundTripper) (TokenProvider, error)

NewIAMTokenProvider returns TokenProvider that uses generateAccessToken IAM API to grab tokens belonging to some service account.

func NewLUCIContextTokenProvider

func NewLUCIContextTokenProvider(ctx context.Context, scopes []string, transport http.RoundTripper) (TokenProvider, error)

NewLUCIContextTokenProvider returns TokenProvider that knows how to use a local auth server to mint tokens.

It requires LUCI_CONTEXT["local_auth"] to be present in the 'ctx'. It's a description of how to locate and contact the local auth server.

See auth/integration/localauth package for the implementation of the server.

func NewServiceAccountTokenProvider

func NewServiceAccountTokenProvider(ctx context.Context, jsonKey []byte, path string, scopes []string) (TokenProvider, error)

NewServiceAccountTokenProvider returns TokenProvider that uses service account private key (on disk or in memory) to make access tokens.

func NewUserAuthTokenProvider

func NewUserAuthTokenProvider(ctx context.Context, clientID, clientSecret string, scopes []string) (TokenProvider, error)

NewUserAuthTokenProvider returns TokenProvider that can perform 3-legged OAuth flow involving interaction with a user.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL