2014-12-17 19:35:35 +00:00
|
|
|
// Package auth defines a standard interface for request access controllers.
|
|
|
|
//
|
|
|
|
// An access controller has a simple interface with a single `Authorized`
|
|
|
|
// method which checks that a given request is authorized to perform one or
|
|
|
|
// more actions on one or more resources. This method should return a non-nil
|
2015-04-17 12:39:52 +00:00
|
|
|
// error if the request is not authorized.
|
2014-12-17 19:35:35 +00:00
|
|
|
//
|
|
|
|
// An implementation registers its access controller by name with a constructor
|
|
|
|
// which accepts an options map for configuring the access controller.
|
|
|
|
//
|
2022-11-02 21:05:45 +00:00
|
|
|
// options := map[string]interface{}{"sillySecret": "whysosilly?"}
|
|
|
|
// accessController, _ := auth.GetAccessController("silly", options)
|
2014-12-17 19:35:35 +00:00
|
|
|
//
|
|
|
|
// This `accessController` can then be used in a request handler like so:
|
|
|
|
//
|
2022-11-02 21:05:45 +00:00
|
|
|
// func updateOrder(w http.ResponseWriter, r *http.Request) {
|
|
|
|
// orderNumber := r.FormValue("orderNumber")
|
|
|
|
// resource := auth.Resource{Type: "customerOrder", Name: orderNumber}
|
|
|
|
// access := auth.Access{Resource: resource, Action: "update"}
|
2014-12-17 19:35:35 +00:00
|
|
|
//
|
2023-10-24 18:08:04 +00:00
|
|
|
// if ctx, err := accessController.Authorized(r, access); err != nil {
|
2022-11-02 21:05:45 +00:00
|
|
|
// if challenge, ok := err.(auth.Challenge) {
|
|
|
|
// // Let the challenge write the response.
|
|
|
|
// challenge.SetHeaders(r, w)
|
|
|
|
// w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
// return
|
|
|
|
// } else {
|
|
|
|
// // Some other error.
|
2014-12-17 19:35:35 +00:00
|
|
|
// }
|
2022-11-02 21:05:45 +00:00
|
|
|
// }
|
|
|
|
// }
|
2014-12-17 06:58:39 +00:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2016-02-13 01:15:19 +00:00
|
|
|
"errors"
|
2014-12-17 06:58:39 +00:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2015-02-04 01:59:24 +00:00
|
|
|
)
|
|
|
|
|
2016-02-13 01:15:19 +00:00
|
|
|
var (
|
|
|
|
// ErrInvalidCredential is returned when the auth token does not authenticate correctly.
|
|
|
|
ErrInvalidCredential = errors.New("invalid authorization credential")
|
|
|
|
|
2016-03-04 21:53:06 +00:00
|
|
|
// ErrAuthenticationFailure returned when authentication fails.
|
2016-02-13 01:15:19 +00:00
|
|
|
ErrAuthenticationFailure = errors.New("authentication failure")
|
|
|
|
)
|
|
|
|
|
2023-11-28 06:50:48 +00:00
|
|
|
// InitFunc is the type of an AccessController factory function and is used
|
|
|
|
// to register the constructor for different AccesController backends.
|
|
|
|
type InitFunc func(options map[string]interface{}) (AccessController, error)
|
|
|
|
|
|
|
|
var accessControllers map[string]InitFunc
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
accessControllers = make(map[string]InitFunc)
|
|
|
|
}
|
|
|
|
|
2015-02-04 01:59:24 +00:00
|
|
|
// UserInfo carries information about
|
2024-04-19 09:49:51 +00:00
|
|
|
// an authenticated/authorized client.
|
2015-02-04 01:59:24 +00:00
|
|
|
type UserInfo struct {
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
2014-12-17 06:58:39 +00:00
|
|
|
// Resource describes a resource by type and name.
|
|
|
|
type Resource struct {
|
2016-11-16 01:14:31 +00:00
|
|
|
Type string
|
|
|
|
Class string
|
|
|
|
Name string
|
2014-12-17 06:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Access describes a specific action that is
|
2015-04-17 12:39:52 +00:00
|
|
|
// requested or allowed for a given resource.
|
2014-12-17 06:58:39 +00:00
|
|
|
type Access struct {
|
|
|
|
Resource
|
|
|
|
Action string
|
|
|
|
}
|
|
|
|
|
2023-10-24 20:41:54 +00:00
|
|
|
// Grant describes the permitted level of access for an authorized request.
|
|
|
|
type Grant struct {
|
|
|
|
User UserInfo // The authenticated user for the request.
|
|
|
|
Resources []Resource // The list of resources which have been authorized for the request.
|
|
|
|
}
|
|
|
|
|
2014-12-17 06:58:39 +00:00
|
|
|
// Challenge is a special error type which is used for HTTP 401 Unauthorized
|
|
|
|
// responses and is able to write the response with WWW-Authenticate challenge
|
|
|
|
// header values based on the error.
|
|
|
|
type Challenge interface {
|
|
|
|
error
|
2015-07-24 02:39:56 +00:00
|
|
|
|
|
|
|
// SetHeaders prepares the request to conduct a challenge response by
|
|
|
|
// adding the an HTTP challenge header on the response message. Callers
|
|
|
|
// are expected to set the appropriate HTTP status code (e.g. 401)
|
|
|
|
// themselves.
|
2017-03-13 23:35:15 +00:00
|
|
|
SetHeaders(r *http.Request, w http.ResponseWriter)
|
2014-12-17 06:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AccessController controls access to registry resources based on a request
|
|
|
|
// and required access levels for a request. Implementations can support both
|
|
|
|
// complete denial and http authorization challenges.
|
|
|
|
type AccessController interface {
|
2023-10-24 20:41:54 +00:00
|
|
|
// Authorized determines if the request is granted access. If one or more
|
|
|
|
// Access structs are provided, the requested access will be compared with
|
|
|
|
// what is available to the request.
|
|
|
|
//
|
|
|
|
// Return a Grant to grant the request access. Return an error to deny
|
|
|
|
// access. The error may be of type Challenge, in which case the caller may
|
|
|
|
// have the Challenge handle the request or choose what action to take based
|
|
|
|
// on the Challenge header or response status.
|
|
|
|
Authorized(r *http.Request, access ...Access) (*Grant, error)
|
2014-12-17 06:58:39 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 21:53:06 +00:00
|
|
|
// CredentialAuthenticator is an object which is able to authenticate credentials
|
2016-02-13 01:15:19 +00:00
|
|
|
type CredentialAuthenticator interface {
|
|
|
|
AuthenticateUser(username, password string) error
|
|
|
|
}
|
|
|
|
|
2014-12-17 06:58:39 +00:00
|
|
|
// Register is used to register an InitFunc for
|
|
|
|
// an AccessController backend with the given name.
|
|
|
|
func Register(name string, initFunc InitFunc) error {
|
|
|
|
if _, exists := accessControllers[name]; exists {
|
|
|
|
return fmt.Errorf("name already registered: %s", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
accessControllers[name] = initFunc
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAccessController constructs an AccessController
|
|
|
|
// with the given options using the named backend.
|
|
|
|
func GetAccessController(name string, options map[string]interface{}) (AccessController, error) {
|
|
|
|
if initFunc, exists := accessControllers[name]; exists {
|
|
|
|
return initFunc(options)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("no access controller registered with name: %s", name)
|
|
|
|
}
|