Allows token authentication handler to request additional scopes

When an auth request provides the "from" query parameter, the token
handler will add a "pull" scope for the provided repository, refreshing
the token if the overall scope has increased

Signed-off-by: Brian Bland <brian.bland@docker.com>
This commit is contained in:
Brian Bland 2016-01-05 11:13:27 -08:00
parent 41e30f626b
commit 44d95e5841

View file

@ -108,6 +108,8 @@ type tokenHandler struct {
tokenLock sync.Mutex tokenLock sync.Mutex
tokenCache string tokenCache string
tokenExpiration time.Time tokenExpiration time.Time
additionalScopes map[string]struct{}
} }
// tokenScope represents the scope at which a token will be requested. // tokenScope represents the scope at which a token will be requested.
@ -145,6 +147,7 @@ func newTokenHandler(transport http.RoundTripper, creds CredentialStore, c clock
Scope: scope, Scope: scope,
Actions: actions, Actions: actions,
}, },
additionalScopes: map[string]struct{}{},
} }
} }
@ -160,7 +163,15 @@ func (th *tokenHandler) Scheme() string {
} }
func (th *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error { func (th *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
if err := th.refreshToken(params); err != nil { var additionalScopes []string
if fromParam := req.URL.Query().Get("from"); fromParam != "" {
additionalScopes = append(additionalScopes, tokenScope{
Resource: "repository",
Scope: fromParam,
Actions: []string{"pull"},
}.String())
}
if err := th.refreshToken(params, additionalScopes...); err != nil {
return err return err
} }
@ -169,11 +180,18 @@ func (th *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]st
return nil return nil
} }
func (th *tokenHandler) refreshToken(params map[string]string) error { func (th *tokenHandler) refreshToken(params map[string]string, additionalScopes ...string) error {
th.tokenLock.Lock() th.tokenLock.Lock()
defer th.tokenLock.Unlock() defer th.tokenLock.Unlock()
var addedScopes bool
for _, scope := range additionalScopes {
if _, ok := th.additionalScopes[scope]; !ok {
th.additionalScopes[scope] = struct{}{}
addedScopes = true
}
}
now := th.clock.Now() now := th.clock.Now()
if now.After(th.tokenExpiration) { if now.After(th.tokenExpiration) || addedScopes {
tr, err := th.fetchToken(params) tr, err := th.fetchToken(params)
if err != nil { if err != nil {
return err return err
@ -223,6 +241,10 @@ func (th *tokenHandler) fetchToken(params map[string]string) (token *tokenRespon
reqParams.Add("scope", scopeField) reqParams.Add("scope", scopeField)
} }
for scope := range th.additionalScopes {
reqParams.Add("scope", scope)
}
if th.creds != nil { if th.creds != nil {
username, password := th.creds.Basic(realmURL) username, password := th.creds.Basic(realmURL)
if username != "" && password != "" { if username != "" && password != "" {