github.com/lusis/distribution@v2.0.1+incompatible/registry/auth/auth.go (about)

     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.ServeHTTP(w, r)
    25  //				} else {
    26  //					// Some other error.
    27  //				}
    28  //			}
    29  // 		}
    30  //
    31  package auth
    32  
    33  import (
    34  	"fmt"
    35  	"net/http"
    36  
    37  	"golang.org/x/net/context"
    38  )
    39  
    40  // UserInfo carries information about
    41  // an autenticated/authorized client.
    42  type UserInfo struct {
    43  	Name string
    44  }
    45  
    46  // Resource describes a resource by type and name.
    47  type Resource struct {
    48  	Type string
    49  	Name string
    50  }
    51  
    52  // Access describes a specific action that is
    53  // requested or allowed for a given resource.
    54  type Access struct {
    55  	Resource
    56  	Action string
    57  }
    58  
    59  // Challenge is a special error type which is used for HTTP 401 Unauthorized
    60  // responses and is able to write the response with WWW-Authenticate challenge
    61  // header values based on the error.
    62  type Challenge interface {
    63  	error
    64  	// ServeHTTP prepares the request to conduct the appropriate challenge
    65  	// response. For most implementations, simply calling ServeHTTP should be
    66  	// sufficient. Because no body is written, users may write a custom body after
    67  	// calling ServeHTTP, but any headers must be written before the call and may
    68  	// be overwritten.
    69  	ServeHTTP(w http.ResponseWriter, r *http.Request)
    70  }
    71  
    72  // AccessController controls access to registry resources based on a request
    73  // and required access levels for a request. Implementations can support both
    74  // complete denial and http authorization challenges.
    75  type AccessController interface {
    76  	// Authorized returns a non-nil error if the context is granted access and
    77  	// returns a new authorized context. If one or more Access structs are
    78  	// provided, the requested access will be compared with what is available
    79  	// to the context. The given context will contain a "http.request" key with
    80  	// a `*http.Request` value. If the error is non-nil, access should always
    81  	// be denied. The error may be of type Challenge, in which case the caller
    82  	// may have the Challenge handle the request or choose what action to take
    83  	// based on the Challenge header or response status. The returned context
    84  	// object should have a "auth.user" value set to a UserInfo struct.
    85  	Authorized(ctx context.Context, access ...Access) (context.Context, error)
    86  }
    87  
    88  // WithUser returns a context with the authorized user info.
    89  func WithUser(ctx context.Context, user UserInfo) context.Context {
    90  	return userInfoContext{
    91  		Context: ctx,
    92  		user:    user,
    93  	}
    94  }
    95  
    96  type userInfoContext struct {
    97  	context.Context
    98  	user UserInfo
    99  }
   100  
   101  func (uic userInfoContext) Value(key interface{}) interface{} {
   102  	switch key {
   103  	case "auth.user":
   104  		return uic.user
   105  	case "auth.user.name":
   106  		return uic.user.Name
   107  	}
   108  
   109  	return uic.Context.Value(key)
   110  }
   111  
   112  // InitFunc is the type of an AccessController factory function and is used
   113  // to register the constructor for different AccesController backends.
   114  type InitFunc func(options map[string]interface{}) (AccessController, error)
   115  
   116  var accessControllers map[string]InitFunc
   117  
   118  func init() {
   119  	accessControllers = make(map[string]InitFunc)
   120  }
   121  
   122  // Register is used to register an InitFunc for
   123  // an AccessController backend with the given name.
   124  func Register(name string, initFunc InitFunc) error {
   125  	if _, exists := accessControllers[name]; exists {
   126  		return fmt.Errorf("name already registered: %s", name)
   127  	}
   128  
   129  	accessControllers[name] = initFunc
   130  
   131  	return nil
   132  }
   133  
   134  // GetAccessController constructs an AccessController
   135  // with the given options using the named backend.
   136  func GetAccessController(name string, options map[string]interface{}) (AccessController, error) {
   137  	if initFunc, exists := accessControllers[name]; exists {
   138  		return initFunc(options)
   139  	}
   140  
   141  	return nil, fmt.Errorf("no access controller registered with name: %s", name)
   142  }