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 }