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