From fbe341bf5d8c449807a81106d9ab912f341da7b4 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Thu, 28 Apr 2016 18:49:59 +0800 Subject: [PATCH] update --- utils/registry/auth/authorizer.go | 6 +- utils/registry/auth/tokenhandler.go | 106 +++++++++------------------- utils/registry/registry.go | 43 +++++++---- utils/registry/repository.go | 36 ++-------- 4 files changed, 71 insertions(+), 120 deletions(-) diff --git a/utils/registry/auth/authorizer.go b/utils/registry/auth/authorizer.go index e560355ae..af3a604c3 100644 --- a/utils/registry/auth/authorizer.go +++ b/utils/registry/auth/authorizer.go @@ -23,8 +23,8 @@ import ( // Handler authorizes requests according to the schema type Handler interface { - // Schema : basic, bearer - Schema() string + // Scheme : basic, bearer + Scheme() string //AuthorizeRequest adds basic auth or token auth to the header of request AuthorizeRequest(req *http.Request, params map[string]string) error } @@ -47,7 +47,7 @@ func NewRequestAuthorizer(handlers []Handler, challenges []au.Challenge) *Reques func (r *RequestAuthorizer) ModifyRequest(req *http.Request) error { for _, handler := range r.handlers { for _, challenge := range r.challenges { - if handler.Schema() == challenge.Scheme { + if handler.Scheme() == challenge.Scheme { if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil { return err } diff --git a/utils/registry/auth/tokenhandler.go b/utils/registry/auth/tokenhandler.go index 3e0b6c30f..8cdd808a8 100644 --- a/utils/registry/auth/tokenhandler.go +++ b/utils/registry/auth/tokenhandler.go @@ -39,80 +39,59 @@ func (s *scope) string() string { return fmt.Sprintf("%s:%s:%s", s.Type, s.Name, strings.Join(s.Actions, ",")) } -type token struct { - token string - expiresIn time.Time -} - -type tokenGenerator func(realm, service string, scopes []string) (*token, error) +type tokenGenerator func(realm, service string, scopes []string) (token string, expiresIn int, issuedAt *time.Time, err error) type tokenHandler struct { - scope *scope - cache map[string]*token - tg tokenGenerator + scope *scope + tg tokenGenerator + cache string // cached token + expiresIn int // The duration in seconds since the token was issued that it will remain valid + issuedAt *time.Time // The RFC3339-serialized UTC standard time at which a given token was issued } -// Schema returns the schema that the handler can handle -func (t *tokenHandler) Schema() string { +// Scheme returns the scheme that the handler can handle +func (t *tokenHandler) Scheme() string { return "bearer" } // AuthorizeRequest will add authorization header which contains a token before the request is sent func (t *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error { - var token string var scopes []*scope // TODO handle additional scope: xxx.xxx.xxx?from=repo scopes = append(scopes, t.scope) - key := cacheKey(scopes) - value, ok := t.cache[key] - var expired bool + expired := true - if ok { - expired = value.expiresIn.Before(time.Now()) + if t.expiresIn != 0 && t.issuedAt != nil { + expired = t.issuedAt.Add(time.Duration(t.expiresIn) * time.Second).Before(time.Now().UTC()) } - if ok && !expired { - token = value.token - log.Debugf("get token from cache: %s", key) - } else { - if ok && expired { - delete(t.cache, key) - log.Debugf("token is expired, remove from cache: %s", key) - } - + if expired { scopeStrs := []string{} for _, scope := range scopes { scopeStrs = append(scopeStrs, scope.string()) } - tk, err := t.tg(params["realm"], params["service"], scopeStrs) + token, expiresIn, issuedAt, err := t.tg(params["realm"], params["service"], scopeStrs) if err != nil { return err } - token = tk.token - t.cache[key] = tk - log.Debugf("add token to cache: %s", key) + t.cache = token + t.expiresIn = expiresIn + t.issuedAt = issuedAt } - req.Header.Add(http.CanonicalHeaderKey("Authorization"), fmt.Sprintf("Bearer %s", token)) + if !expired { + log.Debug("get token from cache") + } + + req.Header.Add(http.CanonicalHeaderKey("Authorization"), fmt.Sprintf("Bearer %s", t.cache)) log.Debugf("add token to request: %s %s", req.Method, req.URL.String()) return nil } -// cacheKey returns a string which can identify the scope array and can be used as the key in cache map -func cacheKey(scopes []*scope) string { - key := "" - for _, scope := range scopes { - key = key + scope.string() + "|" - } - key = strings.TrimRight(key, "|") - - return key -} - type standardTokenHandler struct { tokenHandler client *http.Client @@ -135,16 +114,15 @@ func NewStandardTokenHandler(credential Credential, scopeType, scopeName string, Name: scopeName, Actions: scopeActions, } - handler.cache = make(map[string]*token, 1) handler.tg = handler.generateToken return handler } -func (s *standardTokenHandler) generateToken(realm, service string, scopes []string) (*token, error) { +func (s *standardTokenHandler) generateToken(realm, service string, scopes []string) (string, int, *time.Time, error) { u, err := url.Parse(realm) if err != nil { - return nil, err + return "", 0, nil, err } q := u.Query() q.Add("service", service) @@ -154,44 +132,39 @@ func (s *standardTokenHandler) generateToken(realm, service string, scopes []str u.RawQuery = q.Encode() r, err := http.NewRequest("GET", u.String(), nil) if err != nil { - return nil, err + return "", 0, nil, err } s.credential.AddAuthorization(r) resp, err := s.client.Do(r) if err != nil { - return nil, err + return "", 0, nil, err } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { - return nil, err + return "", 0, nil, err } if resp.StatusCode != http.StatusOK { - return nil, registry_errors.Error{ + return "", 0, nil, registry_errors.Error{ StatusCode: resp.StatusCode, Message: string(b), } } + // TODO tk := struct { Token string `json:"token"` }{} if err = json.Unmarshal(b, &tk); err != nil { - return nil, err - } - - t := &token{ - token: tk.Token, - // TODO handle the expires time - expiresIn: time.Now().Add(5 * time.Minute), + return "", 0, nil, err } log.Debug("get token from token server") - return t, nil + return tk.Token, 0, nil, nil } type usernameTokenHandler struct { @@ -211,26 +184,15 @@ func NewUsernameTokenHandler(username string, scopeType, scopeName string, scope Name: scopeName, Actions: scopeActions, } - handler.cache = make(map[string]*token, 1) handler.tg = handler.generateToken return handler } -func (u *usernameTokenHandler) generateToken(realm, service string, scopes []string) (*token, error) { - tk, err := token_util.GenTokenForUI(u.username, service, scopes) - if err != nil { - return nil, err - } - - t := &token{ - token: tk, - // TODO handle the expires time - expiresIn: time.Now().Add(5 * time.Minute), - } - +func (u *usernameTokenHandler) generateToken(realm, service string, scopes []string) (token string, expiresIn int, issuedAt *time.Time, err error) { + // TODO + token, err = token_util.GenTokenForUI(u.username, service, scopes) log.Debug("get token by calling GenTokenForUI directly") - - return t, nil + return } diff --git a/utils/registry/registry.go b/utils/registry/registry.go index 201e29f20..62118ecbc 100644 --- a/utils/registry/registry.go +++ b/utils/registry/registry.go @@ -63,25 +63,14 @@ func NewRegistryWithUsername(endpoint, username string) (*Registry, error) { return nil, err } - resp, err := http.Get(buildPingURL(endpoint)) + client, err := newClient(endpoint, username, nil, "registry", "catalog", "*") if err != nil { return nil, err } - var handlers []auth.Handler - handler := auth.NewUsernameTokenHandler(username, "registry", "catalog", "*") - handlers = append(handlers, handler) - - challenges := auth.ParseChallengeFromResponse(resp) - authorizer := auth.NewRequestAuthorizer(handlers, challenges) - - transport := NewTransport(http.DefaultTransport, []RequestModifier{authorizer}) - registry := &Registry{ Endpoint: u, - client: &http.Client{ - Transport: transport, - }, + client: client, } return registry, nil @@ -131,3 +120,31 @@ func (r *Registry) Catalog() ([]string, error) { func buildCatalogURL(endpoint string) string { return fmt.Sprintf("%s/v2/_catalog", endpoint) } + +func newClient(endpoint, username string, credential auth.Credential, + scopeType, scopeName string, scopeActions ...string) (*http.Client, error) { + + endpoint = strings.TrimRight(endpoint, "/") + resp, err := http.Get(buildPingURL(endpoint)) + if err != nil { + return nil, err + } + + var handlers []auth.Handler + var handler auth.Handler + if credential != nil { + handler = auth.NewStandardTokenHandler(credential, scopeType, scopeName, scopeActions...) + } else { + handler = auth.NewUsernameTokenHandler(username, scopeType, scopeName, scopeActions...) + } + + handlers = append(handlers, handler) + + challenges := auth.ParseChallengeFromResponse(resp) + authorizer := auth.NewRequestAuthorizer(handlers, challenges) + + transport := NewTransport(http.DefaultTransport, []RequestModifier{authorizer}) + return &http.Client{ + Transport: transport, + }, nil +} diff --git a/utils/registry/repository.go b/utils/registry/repository.go index 121b26741..04d8f9fdd 100644 --- a/utils/registry/repository.go +++ b/utils/registry/repository.go @@ -71,26 +71,12 @@ func NewRepositoryWithCredential(name, endpoint string, credential auth.Credenti return nil, err } - resp, err := http.Get(buildPingURL(endpoint)) - if err != nil { - return nil, err - } - - var handlers []auth.Handler - handler := auth.NewStandardTokenHandler(credential, "repository", name, "pull", "push") - handlers = append(handlers, handler) - - challenges := auth.ParseChallengeFromResponse(resp) - authorizer := auth.NewRequestAuthorizer(handlers, challenges) - - transport := NewTransport(http.DefaultTransport, []RequestModifier{authorizer}) + client, err := newClient(endpoint, "", credential, "repository", name, "pull", "push") repository := &Repository{ Name: name, Endpoint: u, - client: &http.Client{ - Transport: transport, - }, + client: client, } log.Debugf("initialized a repository client with credential: %s %s", endpoint, name) @@ -109,26 +95,12 @@ func NewRepositoryWithUsername(name, endpoint, username string) (*Repository, er return nil, err } - resp, err := http.Get(buildPingURL(endpoint)) - if err != nil { - return nil, err - } - - var handlers []auth.Handler - handler := auth.NewUsernameTokenHandler(username, "repository", name, "pull", "push") - handlers = append(handlers, handler) - - challenges := auth.ParseChallengeFromResponse(resp) - authorizer := auth.NewRequestAuthorizer(handlers, challenges) - - transport := NewTransport(http.DefaultTransport, []RequestModifier{authorizer}) + client, err := newClient(endpoint, username, nil, "repository", name, "pull", "push") repository := &Repository{ Name: name, Endpoint: u, - client: &http.Client{ - Transport: transport, - }, + client: client, } log.Debugf("initialized a repository client with username: %s %s", endpoint, name)