X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/8be4b29d8837f8ecff47cfde3bea99d97c7562bc..e8d36edca6d1601a50f3712bc7d1c7258a23fcc7:/lib/ctrlctx/auth.go diff --git a/lib/ctrlctx/auth.go b/lib/ctrlctx/auth.go index 61c6253d41..31746b64cc 100644 --- a/lib/ctrlctx/auth.go +++ b/lib/ctrlctx/auth.go @@ -9,7 +9,6 @@ import ( "crypto/hmac" "crypto/sha256" "database/sql" - "encoding/json" "errors" "fmt" "io" @@ -20,6 +19,7 @@ import ( "git.arvados.org/arvados.git/lib/controller/api" "git.arvados.org/arvados.git/sdk/go/arvados" "git.arvados.org/arvados.git/sdk/go/auth" + "github.com/ghodss/yaml" ) var ( @@ -50,6 +50,27 @@ func WrapCallsWithAuth(cluster *arvados.Cluster) func(api.RoutableFunc) api.Rout } } +// NewWithToken returns a context with the provided auth token. +// +// The incoming context must come from WrapCallsInTransactions or +// NewWithTransaction. +// +// Used for attaching system auth to background threads. +// +// Also useful for tests, where context doesn't necessarily come from +// a router that uses WrapCallsWithAuth. +// +// The returned context comes with its own token lookup cache, so +// NewWithToken is not appropriate to use in a per-request code path. +func NewWithToken(ctx context.Context, cluster *arvados.Cluster, token string) context.Context { + ctx = auth.NewContext(ctx, &auth.Credentials{Tokens: []string{token}}) + return context.WithValue(ctx, contextKeyAuth, &authcontext{ + authcache: &authcache{}, + cluster: cluster, + tokens: []string{token}, + }) +} + // CurrentAuth returns the arvados.User whose privileges should be // used in the given context, and the arvados.APIClientAuthorization // the caller presented in order to authenticate the current request. @@ -135,7 +156,7 @@ func (ac *authcache) lookup(ctx context.Context, cluster *arvados.Cluster, token var args []interface{} if len(token) > 30 && strings.HasPrefix(token, "v2/") && token[30] == '/' { fields := strings.Split(token, "/") - cond = `aca.uuid=$1 and aca.api_token=$2` + cond = `aca.uuid = $1 and aca.api_token = $2` args = []interface{}{fields[1], fields[2]} } else { // Bare token or OIDC access token @@ -145,24 +166,26 @@ func (ac *authcache) lookup(ctx context.Context, cluster *arvados.Cluster, token cond = `aca.api_token in ($1, $2)` args = []interface{}{token, hmac} } - var scopesJSON []byte + var expiresAt sql.NullTime + var scopesYAML []byte err = tx.QueryRowContext(ctx, ` select aca.uuid, aca.expires_at, aca.api_token, aca.scopes, users.uuid, users.is_active, users.is_admin from api_client_authorizations aca left join users on aca.user_id = users.id where `+cond+` and (expires_at is null or expires_at > current_timestamp at time zone 'UTC')`, args...).Scan( - &aca.UUID, &aca.ExpiresAt, &aca.APIToken, &scopesJSON, + &aca.UUID, &expiresAt, &aca.APIToken, &scopesYAML, &user.UUID, &user.IsActive, &user.IsAdmin) if err == sql.ErrNoRows { return nil, nil, nil } else if err != nil { return nil, nil, err } - if len(scopesJSON) > 0 { - err = json.Unmarshal(scopesJSON, &aca.Scopes) + aca.ExpiresAt = expiresAt.Time + if len(scopesYAML) > 0 { + err = yaml.Unmarshal(scopesYAML, &aca.Scopes) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("loading scopes for %s: %w", aca.UUID, err) } } ent = &authcacheent{