...

Source file src/github.com/docker/distribution/registry/auth/auth.go

Documentation: github.com/docker/distribution/registry/auth

     1  // Package auth defines a standard interface for request access controllers.
     2  //
     3  // An access controller has a simple interface with a single `Authorized`
     4  // method which checks that a given request is authorized to perform one or
     5  // more actions on one or more resources. This method should return a non-nil
     6  // error if the request is not authorized.
     7  //
     8  // An implementation registers its access controller by name with a constructor
     9  // which accepts an options map for configuring the access controller.
    10  //
    11  //	options := map[string]interface{}{"sillySecret": "whysosilly?"}
    12  //	accessController, _ := auth.GetAccessController("silly", options)
    13  //
    14  // This `accessController` can then be used in a request handler like so:
    15  //
    16  //	func updateOrder(w http.ResponseWriter, r *http.Request) {
    17  //		orderNumber := r.FormValue("orderNumber")
    18  //		resource := auth.Resource{Type: "customerOrder", Name: orderNumber}
    19  //		access := auth.Access{Resource: resource, Action: "update"}
    20  //
    21  //		if ctx, err := accessController.Authorized(ctx, access); err != nil {
    22  //			if challenge, ok := err.(auth.Challenge) {
    23  //				// Let the challenge write the response.
    24  //				challenge.SetHeaders(r, w)
    25  //				w.WriteHeader(http.StatusUnauthorized)
    26  //				return
    27  //			} else {
    28  //				// Some other error.
    29  //			}
    30  //		}
    31  //	}
    32  package auth
    33  
    34  import (
    35  	"context"
    36  	"errors"
    37  	"fmt"
    38  	"net/http"
    39  )
    40  
    41  const (
    42  	// UserKey is used to get the user object from
    43  	// a user context
    44  	UserKey = "auth.user"
    45  
    46  	// UserNameKey is used to get the user name from
    47  	// a user context
    48  	UserNameKey = "auth.user.name"
    49  )
    50  
    51  var (
    52  	// ErrInvalidCredential is returned when the auth token does not authenticate correctly.
    53  	ErrInvalidCredential = errors.New("invalid authorization credential")
    54  
    55  	// ErrAuthenticationFailure returned when authentication fails.
    56  	ErrAuthenticationFailure = errors.New("authentication failure")
    57  )
    58  
    59  // UserInfo carries information about
    60  // an autenticated/authorized client.
    61  type UserInfo struct {
    62  	Name string
    63  }
    64  
    65  // Resource describes a resource by type and name.
    66  type Resource struct {
    67  	Type  string
    68  	Class string
    69  	Name  string
    70  }
    71  
    72  // Access describes a specific action that is
    73  // requested or allowed for a given resource.
    74  type Access struct {
    75  	Resource
    76  	Action string
    77  }
    78  
    79  // Challenge is a special error type which is used for HTTP 401 Unauthorized
    80  // responses and is able to write the response with WWW-Authenticate challenge
    81  // header values based on the error.
    82  type Challenge interface {
    83  	error
    84  
    85  	// SetHeaders prepares the request to conduct a challenge response by
    86  	// adding the an HTTP challenge header on the response message. Callers
    87  	// are expected to set the appropriate HTTP status code (e.g. 401)
    88  	// themselves.
    89  	SetHeaders(r *http.Request, w http.ResponseWriter)
    90  }
    91  
    92  // AccessController controls access to registry resources based on a request
    93  // and required access levels for a request. Implementations can support both
    94  // complete denial and http authorization challenges.
    95  type AccessController interface {
    96  	// Authorized returns a non-nil error if the context is granted access and
    97  	// returns a new authorized context. If one or more Access structs are
    98  	// provided, the requested access will be compared with what is available
    99  	// to the context. The given context will contain a "http.request" key with
   100  	// a `*http.Request` value. If the error is non-nil, access should always
   101  	// be denied. The error may be of type Challenge, in which case the caller
   102  	// may have the Challenge handle the request or choose what action to take
   103  	// based on the Challenge header or response status. The returned context
   104  	// object should have a "auth.user" value set to a UserInfo struct.
   105  	Authorized(ctx context.Context, access ...Access) (context.Context, error)
   106  }
   107  
   108  // CredentialAuthenticator is an object which is able to authenticate credentials
   109  type CredentialAuthenticator interface {
   110  	AuthenticateUser(username, password string) error
   111  }
   112  
   113  // WithUser returns a context with the authorized user info.
   114  func WithUser(ctx context.Context, user UserInfo) context.Context {
   115  	return userInfoContext{
   116  		Context: ctx,
   117  		user:    user,
   118  	}
   119  }
   120  
   121  type userInfoContext struct {
   122  	context.Context
   123  	user UserInfo
   124  }
   125  
   126  func (uic userInfoContext) Value(key interface{}) interface{} {
   127  	switch key {
   128  	case UserKey:
   129  		return uic.user
   130  	case UserNameKey:
   131  		return uic.user.Name
   132  	}
   133  
   134  	return uic.Context.Value(key)
   135  }
   136  
   137  // WithResources returns a context with the authorized resources.
   138  func WithResources(ctx context.Context, resources []Resource) context.Context {
   139  	return resourceContext{
   140  		Context:   ctx,
   141  		resources: resources,
   142  	}
   143  }
   144  
   145  type resourceContext struct {
   146  	context.Context
   147  	resources []Resource
   148  }
   149  
   150  type resourceKey struct{}
   151  
   152  func (rc resourceContext) Value(key interface{}) interface{} {
   153  	if key == (resourceKey{}) {
   154  		return rc.resources
   155  	}
   156  
   157  	return rc.Context.Value(key)
   158  }
   159  
   160  // AuthorizedResources returns the list of resources which have
   161  // been authorized for this request.
   162  func AuthorizedResources(ctx context.Context) []Resource {
   163  	if resources, ok := ctx.Value(resourceKey{}).([]Resource); ok {
   164  		return resources
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  // InitFunc is the type of an AccessController factory function and is used
   171  // to register the constructor for different AccesController backends.
   172  type InitFunc func(options map[string]interface{}) (AccessController, error)
   173  
   174  var accessControllers map[string]InitFunc
   175  
   176  func init() {
   177  	accessControllers = make(map[string]InitFunc)
   178  }
   179  
   180  // Register is used to register an InitFunc for
   181  // an AccessController backend with the given name.
   182  func Register(name string, initFunc InitFunc) error {
   183  	if _, exists := accessControllers[name]; exists {
   184  		return fmt.Errorf("name already registered: %s", name)
   185  	}
   186  
   187  	accessControllers[name] = initFunc
   188  
   189  	return nil
   190  }
   191  
   192  // GetAccessController constructs an AccessController
   193  // with the given options using the named backend.
   194  func GetAccessController(name string, options map[string]interface{}) (AccessController, error) {
   195  	if initFunc, exists := accessControllers[name]; exists {
   196  		return initFunc(options)
   197  	}
   198  
   199  	return nil, fmt.Errorf("no access controller registered with name: %s", name)
   200  }
   201  

View as plain text