commit
						aa985ba889
					
				|  | @ -245,7 +245,7 @@ func (ts *tokenServer) getToken(ctx context.Context, w http.ResponseWriter, r *h | ||||||
| 		// Get response context.
 | 		// Get response context.
 | ||||||
| 		ctx, w = dcontext.WithResponseWriter(ctx, w) | 		ctx, w = dcontext.WithResponseWriter(ctx, w) | ||||||
| 
 | 
 | ||||||
| 		challenge.SetHeaders(w) | 		challenge.SetHeaders(r, w) | ||||||
| 		handleError(ctx, errcode.ErrorCodeUnauthorized.WithDetail(challenge.Error()), w) | 		handleError(ctx, errcode.ErrorCodeUnauthorized.WithDetail(challenge.Error()), w) | ||||||
| 
 | 
 | ||||||
| 		dcontext.GetResponseLogger(ctx).Info("get token authentication challenge") | 		dcontext.GetResponseLogger(ctx).Info("get token authentication challenge") | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ | ||||||
| // 			if ctx, err := accessController.Authorized(ctx, access); err != nil {
 | // 			if ctx, err := accessController.Authorized(ctx, access); err != nil {
 | ||||||
| //				if challenge, ok := err.(auth.Challenge) {
 | //				if challenge, ok := err.(auth.Challenge) {
 | ||||||
| //					// Let the challenge write the response.
 | //					// Let the challenge write the response.
 | ||||||
| //					challenge.SetHeaders(w)
 | //					challenge.SetHeaders(r, w)
 | ||||||
| //					w.WriteHeader(http.StatusUnauthorized)
 | //					w.WriteHeader(http.StatusUnauthorized)
 | ||||||
| //					return
 | //					return
 | ||||||
| //				} else {
 | //				} else {
 | ||||||
|  | @ -87,7 +87,7 @@ type Challenge interface { | ||||||
| 	// adding the an HTTP challenge header on the response message. Callers
 | 	// adding the an HTTP challenge header on the response message. Callers
 | ||||||
| 	// are expected to set the appropriate HTTP status code (e.g. 401)
 | 	// are expected to set the appropriate HTTP status code (e.g. 401)
 | ||||||
| 	// themselves.
 | 	// themselves.
 | ||||||
| 	SetHeaders(w http.ResponseWriter) | 	SetHeaders(r *http.Request, w http.ResponseWriter) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AccessController controls access to registry resources based on a request
 | // AccessController controls access to registry resources based on a request
 | ||||||
|  |  | ||||||
|  | @ -111,7 +111,7 @@ type challenge struct { | ||||||
| var _ auth.Challenge = challenge{} | var _ auth.Challenge = challenge{} | ||||||
| 
 | 
 | ||||||
| // SetHeaders sets the basic challenge header on the response.
 | // 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)) | 	w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", ch.realm)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -50,7 +50,7 @@ func TestBasicAccessController(t *testing.T) { | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			switch err := err.(type) { | 			switch err := err.(type) { | ||||||
| 			case auth.Challenge: | 			case auth.Challenge: | ||||||
| 				err.SetHeaders(w) | 				err.SetHeaders(r, w) | ||||||
| 				w.WriteHeader(http.StatusUnauthorized) | 				w.WriteHeader(http.StatusUnauthorized) | ||||||
| 				return | 				return | ||||||
| 			default: | 			default: | ||||||
|  |  | ||||||
|  | @ -82,7 +82,7 @@ type challenge struct { | ||||||
| var _ auth.Challenge = challenge{} | var _ auth.Challenge = challenge{} | ||||||
| 
 | 
 | ||||||
| // SetHeaders sets a simple bearer challenge on the response.
 | // 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) | 	header := fmt.Sprintf("Bearer realm=%q,service=%q", ch.realm, ch.service) | ||||||
| 
 | 
 | ||||||
| 	if ch.scope != "" { | 	if ch.scope != "" { | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ func TestSillyAccessController(t *testing.T) { | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			switch err := err.(type) { | 			switch err := err.(type) { | ||||||
| 			case auth.Challenge: | 			case auth.Challenge: | ||||||
| 				err.SetHeaders(w) | 				err.SetHeaders(r, w) | ||||||
| 				w.WriteHeader(http.StatusUnauthorized) | 				w.WriteHeader(http.StatusUnauthorized) | ||||||
| 				return | 				return | ||||||
| 			default: | 			default: | ||||||
|  |  | ||||||
|  | @ -76,10 +76,11 @@ var ( | ||||||
| 
 | 
 | ||||||
| // authChallenge implements the auth.Challenge interface.
 | // authChallenge implements the auth.Challenge interface.
 | ||||||
| type authChallenge struct { | type authChallenge struct { | ||||||
| 	err       error | 	err          error | ||||||
| 	realm     string | 	realm        string | ||||||
| 	service   string | 	autoRedirect bool | ||||||
| 	accessSet accessSet | 	service      string | ||||||
|  | 	accessSet    accessSet | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var _ auth.Challenge = authChallenge{} | var _ auth.Challenge = authChallenge{} | ||||||
|  | @ -97,8 +98,14 @@ func (ac authChallenge) Status() int { | ||||||
| // challengeParams constructs the value to be used in
 | // challengeParams constructs the value to be used in
 | ||||||
| // the WWW-Authenticate response challenge header.
 | // the WWW-Authenticate response challenge header.
 | ||||||
| // See https://tools.ietf.org/html/rfc6750#section-3
 | // See https://tools.ietf.org/html/rfc6750#section-3
 | ||||||
| func (ac authChallenge) challengeParams() string { | func (ac authChallenge) challengeParams(r *http.Request) string { | ||||||
| 	str := fmt.Sprintf("Bearer realm=%q,service=%q", ac.realm, ac.service) | 	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 != "" { | 	if scope := ac.accessSet.scopeParam(); scope != "" { | ||||||
| 		str = fmt.Sprintf("%s,scope=%q", str, 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.
 | // SetChallenge sets the WWW-Authenticate value for the response.
 | ||||||
| func (ac authChallenge) SetHeaders(w http.ResponseWriter) { | func (ac authChallenge) SetHeaders(r *http.Request, w http.ResponseWriter) { | ||||||
| 	w.Header().Add("WWW-Authenticate", ac.challengeParams()) | 	w.Header().Add("WWW-Authenticate", ac.challengeParams(r)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // accessController implements the auth.AccessController interface.
 | // accessController implements the auth.AccessController interface.
 | ||||||
| type accessController struct { | type accessController struct { | ||||||
| 	realm       string | 	realm        string | ||||||
| 	issuer      string | 	autoRedirect bool | ||||||
| 	service     string | 	issuer       string | ||||||
| 	rootCerts   *x509.CertPool | 	service      string | ||||||
| 	trustedKeys map[string]libtrust.PublicKey | 	rootCerts    *x509.CertPool | ||||||
|  | 	trustedKeys  map[string]libtrust.PublicKey | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // tokenAccessOptions is a convenience type for handling
 | // tokenAccessOptions is a convenience type for handling
 | ||||||
| // options to the contstructor of an accessController.
 | // options to the contstructor of an accessController.
 | ||||||
| type tokenAccessOptions struct { | type tokenAccessOptions struct { | ||||||
| 	realm          string | 	realm          string | ||||||
|  | 	autoRedirect   bool | ||||||
| 	issuer         string | 	issuer         string | ||||||
| 	service        string | 	service        string | ||||||
| 	rootCertBundle 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] | 	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 | 	return opts, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -205,11 +220,12 @@ func newAccessController(options map[string]interface{}) (auth.AccessController, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &accessController{ | 	return &accessController{ | ||||||
| 		realm:       config.realm, | 		realm:        config.realm, | ||||||
| 		issuer:      config.issuer, | 		autoRedirect: config.autoRedirect, | ||||||
| 		service:     config.service, | 		issuer:       config.issuer, | ||||||
| 		rootCerts:   rootPool, | 		service:      config.service, | ||||||
| 		trustedKeys: trustedKeys, | 		rootCerts:    rootPool, | ||||||
|  | 		trustedKeys:  trustedKeys, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -217,9 +233,10 @@ func newAccessController(options map[string]interface{}) (auth.AccessController, | ||||||
| // for actions on resources described by the given access items.
 | // for actions on resources described by the given access items.
 | ||||||
| func (ac *accessController) Authorized(ctx context.Context, accessItems ...auth.Access) (context.Context, error) { | func (ac *accessController) Authorized(ctx context.Context, accessItems ...auth.Access) (context.Context, error) { | ||||||
| 	challenge := &authChallenge{ | 	challenge := &authChallenge{ | ||||||
| 		realm:     ac.realm, | 		realm:        ac.realm, | ||||||
| 		service:   ac.service, | 		autoRedirect: ac.autoRedirect, | ||||||
| 		accessSet: newAccessSet(accessItems...), | 		service:      ac.service, | ||||||
|  | 		accessSet:    newAccessSet(accessItems...), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	req, err := dcontext.GetRequest(ctx) | 	req, err := dcontext.GetRequest(ctx) | ||||||
|  |  | ||||||
|  | @ -333,6 +333,7 @@ func TestAccessController(t *testing.T) { | ||||||
| 		"issuer":         issuer, | 		"issuer":         issuer, | ||||||
| 		"service":        service, | 		"service":        service, | ||||||
| 		"rootcertbundle": rootCertBundleFilename, | 		"rootcertbundle": rootCertBundleFilename, | ||||||
|  | 		"autoredirect":   false, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	accessController, err := newAccessController(options) | 	accessController, err := newAccessController(options) | ||||||
|  | @ -518,6 +519,7 @@ func TestNewAccessControllerPemBlock(t *testing.T) { | ||||||
| 		"issuer":         issuer, | 		"issuer":         issuer, | ||||||
| 		"service":        service, | 		"service":        service, | ||||||
| 		"rootcertbundle": rootCertBundleFilename, | 		"rootcertbundle": rootCertBundleFilename, | ||||||
|  | 		"autoredirect":   false, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ac, err := newAccessController(options) | 	ac, err := newAccessController(options) | ||||||
|  |  | ||||||
|  | @ -847,7 +847,7 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont | ||||||
| 		switch err := err.(type) { | 		switch err := err.(type) { | ||||||
| 		case auth.Challenge: | 		case auth.Challenge: | ||||||
| 			// Add the appropriate WWW-Auth header
 | 			// Add the appropriate WWW-Auth header
 | ||||||
| 			err.SetHeaders(w) | 			err.SetHeaders(r, w) | ||||||
| 
 | 
 | ||||||
| 			if err := errcode.ServeJSON(w, errcode.ErrorCodeUnauthorized.WithDetail(accessRecords)); err != nil { | 			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) | 				dcontext.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue