distribution/registry/handlers/context.go
Cory Snider 868faeec67 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 <csnider@mirantis.com>
2023-10-27 10:58:37 -04:00

162 lines
4.2 KiB
Go

package handlers
import (
"context"
"fmt"
"net/http"
"github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/internal/dcontext"
"github.com/distribution/distribution/v3/registry/api/errcode"
v2 "github.com/distribution/distribution/v3/registry/api/v2"
"github.com/distribution/distribution/v3/registry/auth"
"github.com/opencontainers/go-digest"
)
// Context should contain the request specific context for use in across
// handlers. Resources that don't need to be shared across handlers should not
// be on this object.
type Context struct {
// App points to the application structure that created this context.
*App
context.Context
// Repository is the repository for the current request. All requests
// should be scoped to a single repository. This field may be nil.
Repository distribution.Repository
// RepositoryRemover provides method to delete a repository
RepositoryRemover distribution.RepositoryRemover
// Errors is a collection of errors encountered during the request to be
// returned to the client API. If errors are added to the collection, the
// handler *must not* start the response via http.ResponseWriter.
Errors errcode.Errors
urlBuilder *v2.URLBuilder
// TODO(stevvooe): The goal is too completely factor this context and
// dispatching out of the web application. Ideally, we should lean on
// context.Context for injection of these resources.
}
// Value overrides context.Context.Value to ensure that calls are routed to
// correct context.
func (ctx *Context) Value(key interface{}) interface{} {
return ctx.Context.Value(key)
}
func getName(ctx context.Context) (name string) {
return dcontext.GetStringValue(ctx, "vars.name")
}
func getReference(ctx context.Context) (reference string) {
return dcontext.GetStringValue(ctx, "vars.reference")
}
var errDigestNotAvailable = fmt.Errorf("digest not available in context")
func getDigest(ctx context.Context) (dgst digest.Digest, err error) {
dgstStr := dcontext.GetStringValue(ctx, "vars.digest")
if dgstStr == "" {
dcontext.GetLogger(ctx).Errorf("digest not available")
return "", errDigestNotAvailable
}
d, err := digest.Parse(dgstStr)
if err != nil {
dcontext.GetLogger(ctx).Errorf("error parsing digest=%q: %v", dgstStr, err)
return "", err
}
return d, nil
}
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, userNameKey)
// Fallback to request user with basic auth
if username == "" {
var ok bool
uname, _, ok := basicAuth(r)
if ok {
username = uname
}
}
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
}