add autoredirect auth config

It redirects the user to to the Host header's domain whenever they try to use
token auth.

Signed-off-by: David Wu <david.wu@docker.com>
This commit is contained in:
Viktor Stanchev 2017-03-13 16:35:15 -07:00 committed by David Wu
parent 16128bbac4
commit f730f3ab77
5 changed files with 43 additions and 26 deletions

View file

@ -21,7 +21,7 @@
// if ctx, err := accessController.Authorized(ctx, access); err != nil {
// if challenge, ok := err.(auth.Challenge) {
// // Let the challenge write the response.
// challenge.SetHeaders(w)
// challenge.SetHeaders(r, w)
// w.WriteHeader(http.StatusUnauthorized)
// return
// } else {
@ -87,7 +87,7 @@ type Challenge interface {
// adding the an HTTP challenge header on the response message. Callers
// are expected to set the appropriate HTTP status code (e.g. 401)
// themselves.
SetHeaders(w http.ResponseWriter)
SetHeaders(r *http.Request, w http.ResponseWriter)
}
// AccessController controls access to registry resources based on a request

View file

@ -111,7 +111,7 @@ type challenge struct {
var _ auth.Challenge = challenge{}
// SetHeaders sets the basic challenge header on the response.
func (ch challenge) SetHeaders(w http.ResponseWriter) {
func (ch challenge) SetHeaders(r *http.Request, w http.ResponseWriter) {
w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", ch.realm))
}

View file

@ -82,7 +82,7 @@ type challenge struct {
var _ auth.Challenge = challenge{}
// SetHeaders sets a simple bearer challenge on the response.
func (ch challenge) SetHeaders(w http.ResponseWriter) {
func (ch challenge) SetHeaders(r *http.Request, w http.ResponseWriter) {
header := fmt.Sprintf("Bearer realm=%q,service=%q", ch.realm, ch.service)
if ch.scope != "" {

View file

@ -76,10 +76,11 @@ var (
// authChallenge implements the auth.Challenge interface.
type authChallenge struct {
err error
realm string
service string
accessSet accessSet
err error
realm string
autoRedirect bool
service string
accessSet accessSet
}
var _ auth.Challenge = authChallenge{}
@ -97,8 +98,14 @@ func (ac authChallenge) Status() int {
// challengeParams constructs the value to be used in
// the WWW-Authenticate response challenge header.
// See https://tools.ietf.org/html/rfc6750#section-3
func (ac authChallenge) challengeParams() string {
str := fmt.Sprintf("Bearer realm=%q,service=%q", ac.realm, ac.service)
func (ac authChallenge) challengeParams(r *http.Request) string {
var realm string
if ac.autoRedirect {
realm = fmt.Sprintf("https://%s/auth/token", r.Host)
} else {
realm = ac.realm
}
str := fmt.Sprintf("Bearer realm=%q,service=%q", realm, ac.service)
if scope := ac.accessSet.scopeParam(); scope != "" {
str = fmt.Sprintf("%s,scope=%q", str, scope)
@ -114,23 +121,25 @@ func (ac authChallenge) challengeParams() string {
}
// SetChallenge sets the WWW-Authenticate value for the response.
func (ac authChallenge) SetHeaders(w http.ResponseWriter) {
w.Header().Add("WWW-Authenticate", ac.challengeParams())
func (ac authChallenge) SetHeaders(r *http.Request, w http.ResponseWriter) {
w.Header().Add("WWW-Authenticate", ac.challengeParams(r))
}
// accessController implements the auth.AccessController interface.
type accessController struct {
realm string
issuer string
service string
rootCerts *x509.CertPool
trustedKeys map[string]libtrust.PublicKey
realm string
autoRedirect bool
issuer string
service string
rootCerts *x509.CertPool
trustedKeys map[string]libtrust.PublicKey
}
// tokenAccessOptions is a convenience type for handling
// options to the contstructor of an accessController.
type tokenAccessOptions struct {
realm string
autoRedirect bool
issuer string
service string
rootCertBundle string
@ -153,6 +162,12 @@ func checkOptions(options map[string]interface{}) (tokenAccessOptions, error) {
opts.realm, opts.issuer, opts.service, opts.rootCertBundle = vals[0], vals[1], vals[2], vals[3]
autoRedirect, ok := options["autoredirect"].(bool)
if !ok {
return opts, fmt.Errorf("token auth requires a valid option bool: autoredirect")
}
opts.autoRedirect = autoRedirect
return opts, nil
}
@ -205,11 +220,12 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
}
return &accessController{
realm: config.realm,
issuer: config.issuer,
service: config.service,
rootCerts: rootPool,
trustedKeys: trustedKeys,
realm: config.realm,
autoRedirect: config.autoRedirect,
issuer: config.issuer,
service: config.service,
rootCerts: rootPool,
trustedKeys: trustedKeys,
}, nil
}
@ -217,9 +233,10 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
// for actions on resources described by the given access items.
func (ac *accessController) Authorized(ctx context.Context, accessItems ...auth.Access) (context.Context, error) {
challenge := &authChallenge{
realm: ac.realm,
service: ac.service,
accessSet: newAccessSet(accessItems...),
realm: ac.realm,
autoRedirect: ac.autoRedirect,
service: ac.service,
accessSet: newAccessSet(accessItems...),
}
req, err := dcontext.GetRequest(ctx)

View file

@ -847,7 +847,7 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont
switch err := err.(type) {
case auth.Challenge:
// Add the appropriate WWW-Auth header
err.SetHeaders(w)
err.SetHeaders(r, w)
if err := errcode.ServeJSON(w, errcode.ErrorCodeUnauthorized.WithDetail(accessRecords)); err != nil {
dcontext.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)