forked from TrueCloudLab/distribution
868faeec67
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>
162 lines
4.2 KiB
Go
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
|
|
}
|