9c88801a12
Back in the before time, the best practices surrounding usage of Context weren't quite worked out. We defined our own type to make usage easier. As this packaged was used elsewhere, it make it more and more challenging to integrate with the forked `Context` type. Now that it is available in the standard library, we can just use that one directly. To make usage more consistent, we now use `dcontext` when referring to the distribution context package. Signed-off-by: Stephen J Day <stephen.day@docker.com>
103 lines
2.8 KiB
Go
103 lines
2.8 KiB
Go
// Package silly provides a simple authentication scheme that checks for the
|
|
// existence of an Authorization header and issues access if is present and
|
|
// non-empty.
|
|
//
|
|
// This package is present as an example implementation of a minimal
|
|
// auth.AccessController and for testing. This is not suitable for any kind of
|
|
// production security.
|
|
package silly
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
dcontext "github.com/docker/distribution/context"
|
|
"github.com/docker/distribution/registry/auth"
|
|
)
|
|
|
|
// accessController provides a simple implementation of auth.AccessController
|
|
// that simply checks for a non-empty Authorization header. It is useful for
|
|
// demonstration and testing.
|
|
type accessController struct {
|
|
realm string
|
|
service string
|
|
}
|
|
|
|
var _ auth.AccessController = &accessController{}
|
|
|
|
func newAccessController(options map[string]interface{}) (auth.AccessController, error) {
|
|
realm, present := options["realm"]
|
|
if _, ok := realm.(string); !present || !ok {
|
|
return nil, fmt.Errorf(`"realm" must be set for silly access controller`)
|
|
}
|
|
|
|
service, present := options["service"]
|
|
if _, ok := service.(string); !present || !ok {
|
|
return nil, fmt.Errorf(`"service" must be set for silly access controller`)
|
|
}
|
|
|
|
return &accessController{realm: realm.(string), service: service.(string)}, nil
|
|
}
|
|
|
|
// Authorized simply checks for the existence of the authorization header,
|
|
// responding with a bearer challenge if it doesn't exist.
|
|
func (ac *accessController) Authorized(ctx context.Context, accessRecords ...auth.Access) (context.Context, error) {
|
|
req, err := dcontext.GetRequest(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if req.Header.Get("Authorization") == "" {
|
|
challenge := challenge{
|
|
realm: ac.realm,
|
|
service: ac.service,
|
|
}
|
|
|
|
if len(accessRecords) > 0 {
|
|
var scopes []string
|
|
for _, access := range accessRecords {
|
|
scopes = append(scopes, fmt.Sprintf("%s:%s:%s", access.Type, access.Resource.Name, access.Action))
|
|
}
|
|
challenge.scope = strings.Join(scopes, " ")
|
|
}
|
|
|
|
return nil, &challenge
|
|
}
|
|
|
|
ctx = auth.WithUser(ctx, auth.UserInfo{Name: "silly"})
|
|
ctx = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx, auth.UserNameKey, auth.UserKey))
|
|
|
|
return ctx, nil
|
|
|
|
}
|
|
|
|
type challenge struct {
|
|
realm string
|
|
service string
|
|
scope string
|
|
}
|
|
|
|
var _ auth.Challenge = challenge{}
|
|
|
|
// SetHeaders sets a simple bearer challenge on the response.
|
|
func (ch challenge) SetHeaders(w http.ResponseWriter) {
|
|
header := fmt.Sprintf("Bearer realm=%q,service=%q", ch.realm, ch.service)
|
|
|
|
if ch.scope != "" {
|
|
header = fmt.Sprintf("%s,scope=%q", header, ch.scope)
|
|
}
|
|
|
|
w.Header().Set("WWW-Authenticate", header)
|
|
}
|
|
|
|
func (ch challenge) Error() string {
|
|
return fmt.Sprintf("silly authentication challenge: %#v", ch)
|
|
}
|
|
|
|
// init registers the silly auth backend.
|
|
func init() {
|
|
auth.Register("silly", auth.InitFunc(newAccessController))
|
|
}
|