From 868faeec6761a3b20c3d62cd0be57087ce04d528 Mon Sep 17 00:00:00 2001 From: Cory Snider Date: Tue, 24 Oct 2023 17:04:16 -0400 Subject: [PATCH] registry: unexport auth-related context utilities The specifics of how the authorization for a request is propagated through the registry app are private implementation details. Hide those details from outsiders so they can be changed as needed without fear of breaking third-party code. Move the utilities for attaching a request's authorization status to its context and retrieving it from the context into the registry/handlers package as unexported symbols. Signed-off-by: Cory Snider --- registry/auth/auth.go | 68 --------------------------------- registry/handlers/app.go | 8 ++-- registry/handlers/context.go | 69 +++++++++++++++++++++++++++++++++- registry/handlers/manifests.go | 3 +- 4 files changed, 73 insertions(+), 75 deletions(-) diff --git a/registry/auth/auth.go b/registry/auth/auth.go index 0bda67f6..6266d1e5 100644 --- a/registry/auth/auth.go +++ b/registry/auth/auth.go @@ -32,22 +32,11 @@ package auth import ( - "context" "errors" "fmt" "net/http" ) -const ( - // UserKey is used to get the user object from - // a user context - UserKey = "auth.user" - - // UserNameKey is used to get the user name from - // a user context - UserNameKey = "auth.user.name" -) - var ( // ErrInvalidCredential is returned when the auth token does not authenticate correctly. ErrInvalidCredential = errors.New("invalid authorization credential") @@ -115,63 +104,6 @@ type CredentialAuthenticator interface { AuthenticateUser(username, password string) error } -// WithUser returns a context with the authorized user info. -func WithUser(ctx context.Context, user UserInfo) context.Context { - return userInfoContext{ - Context: ctx, - user: user, - } -} - -type userInfoContext struct { - context.Context - user UserInfo -} - -func (uic userInfoContext) Value(key interface{}) interface{} { - switch key { - case UserKey: - return uic.user - case UserNameKey: - return uic.user.Name - } - - return uic.Context.Value(key) -} - -// WithResources returns a context with the authorized resources. -func WithResources(ctx context.Context, resources []Resource) context.Context { - return resourceContext{ - Context: ctx, - resources: resources, - } -} - -type resourceContext struct { - context.Context - resources []Resource -} - -type resourceKey struct{} - -func (rc resourceContext) Value(key interface{}) interface{} { - if key == (resourceKey{}) { - return rc.resources - } - - return rc.Context.Value(key) -} - -// AuthorizedResources returns the list of resources which have -// been authorized for this request. -func AuthorizedResources(ctx context.Context) []Resource { - if resources, ok := ctx.Value(resourceKey{}).([]Resource); ok { - return resources - } - - return nil -} - // InitFunc is the type of an AccessController factory function and is used // to register the constructor for different AccesController backends. type InitFunc func(options map[string]interface{}) (AccessController, error) diff --git a/registry/handlers/app.go b/registry/handlers/app.go index 471f7c16..2254a0ac 100644 --- a/registry/handlers/app.go +++ b/registry/handlers/app.go @@ -635,7 +635,7 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler { } // Add username to request logging - context.Context = dcontext.WithLogger(context.Context, dcontext.GetLogger(context.Context, auth.UserNameKey)) + context.Context = dcontext.WithLogger(context.Context, dcontext.GetLogger(context.Context, userNameKey)) // sync up context on the request. r = r.WithContext(context) @@ -822,10 +822,10 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont return fmt.Errorf("access controller returned neither an access grant nor an error") } - ctx := auth.WithUser(context.Context, grant.User) - ctx = auth.WithResources(ctx, grant.Resources) + ctx := withUser(context.Context, grant.User) + ctx = withResources(ctx, grant.Resources) - dcontext.GetLogger(ctx, auth.UserNameKey).Info("authorized request") + dcontext.GetLogger(ctx, userNameKey).Info("authorized request") // TODO(stevvooe): This pattern needs to be cleaned up a bit. One context // should be replaced by another, rather than replacing the context on a // mutable object. diff --git a/registry/handlers/context.go b/registry/handlers/context.go index cb354023..c272095c 100644 --- a/registry/handlers/context.go +++ b/registry/handlers/context.go @@ -77,10 +77,20 @@ func getUploadUUID(ctx context.Context) (uuid string) { return dcontext.GetStringValue(ctx, "vars.uuid") } +const ( + // userKey is used to get the user object from + // a user context + userKey = "auth.user" + + // userNameKey is used to get the user name from + // a user context + userNameKey = "auth.user.name" +) + // getUserName attempts to resolve a username from the context and request. If // a username cannot be resolved, the empty string is returned. func getUserName(ctx context.Context, r *http.Request) string { - username := dcontext.GetStringValue(ctx, auth.UserNameKey) + username := dcontext.GetStringValue(ctx, userNameKey) // Fallback to request user with basic auth if username == "" { @@ -93,3 +103,60 @@ func getUserName(ctx context.Context, r *http.Request) string { return username } + +// withUser returns a context with the authorized user info. +func withUser(ctx context.Context, user auth.UserInfo) context.Context { + return userInfoContext{ + Context: ctx, + user: user, + } +} + +type userInfoContext struct { + context.Context + user auth.UserInfo +} + +func (uic userInfoContext) Value(key interface{}) interface{} { + switch key { + case userKey: + return uic.user + case userNameKey: + return uic.user.Name + } + + return uic.Context.Value(key) +} + +// withResources returns a context with the authorized resources. +func withResources(ctx context.Context, resources []auth.Resource) context.Context { + return resourceContext{ + Context: ctx, + resources: resources, + } +} + +type resourceContext struct { + context.Context + resources []auth.Resource +} + +type resourceKey struct{} + +func (rc resourceContext) Value(key interface{}) interface{} { + if key == (resourceKey{}) { + return rc.resources + } + + return rc.Context.Value(key) +} + +// authorizedResources returns the list of resources which have +// been authorized for this request. +func authorizedResources(ctx context.Context) []auth.Resource { + if resources, ok := ctx.Value(resourceKey{}).([]auth.Resource); ok { + return resources + } + + return nil +} diff --git a/registry/handlers/manifests.go b/registry/handlers/manifests.go index 06b7c0c7..4c6dbd0a 100644 --- a/registry/handlers/manifests.go +++ b/registry/handlers/manifests.go @@ -13,7 +13,6 @@ import ( "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/manifest/schema2" "github.com/distribution/distribution/v3/registry/api/errcode" - "github.com/distribution/distribution/v3/registry/auth" "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/reference" "github.com/gorilla/handlers" @@ -394,7 +393,7 @@ func (imh *manifestHandler) applyResourcePolicy(manifest distribution.Manifest) return errcode.ErrorCodeDenied.WithMessage(fmt.Sprintf("registry does not allow %s manifest", class)) } - resources := auth.AuthorizedResources(imh) + resources := authorizedResources(imh) n := imh.Repository.Named().Name() var foundResource bool