Merge pull request #2711 from davidswu/autoredirect
add autoredirect auth config
This commit is contained in:
commit
aa985ba889
9 changed files with 48 additions and 29 deletions
|
@ -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:
|
||||||
|
|
|
@ -78,6 +78,7 @@ var (
|
||||||
type authChallenge struct {
|
type authChallenge struct {
|
||||||
err error
|
err error
|
||||||
realm string
|
realm string
|
||||||
|
autoRedirect bool
|
||||||
service string
|
service string
|
||||||
accessSet accessSet
|
accessSet accessSet
|
||||||
}
|
}
|
||||||
|
@ -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,13 +121,14 @@ 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
|
||||||
|
autoRedirect bool
|
||||||
issuer string
|
issuer string
|
||||||
service string
|
service string
|
||||||
rootCerts *x509.CertPool
|
rootCerts *x509.CertPool
|
||||||
|
@ -131,6 +139,7 @@ type accessController struct {
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +221,7 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
|
||||||
|
|
||||||
return &accessController{
|
return &accessController{
|
||||||
realm: config.realm,
|
realm: config.realm,
|
||||||
|
autoRedirect: config.autoRedirect,
|
||||||
issuer: config.issuer,
|
issuer: config.issuer,
|
||||||
service: config.service,
|
service: config.service,
|
||||||
rootCerts: rootPool,
|
rootCerts: rootPool,
|
||||||
|
@ -218,6 +234,7 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
|
||||||
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,
|
||||||
|
autoRedirect: ac.autoRedirect,
|
||||||
service: ac.service,
|
service: ac.service,
|
||||||
accessSet: newAccessSet(accessItems...),
|
accessSet: newAccessSet(accessItems...),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 a new issue