mirror of
https://github.com/goharbor/harbor.git
synced 2024-10-06 17:27:35 +02:00
update
This commit is contained in:
parent
65fe14d2d3
commit
fbe341bf5d
@ -23,8 +23,8 @@ import (
|
|||||||
|
|
||||||
// Handler authorizes requests according to the schema
|
// Handler authorizes requests according to the schema
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
// Schema : basic, bearer
|
// Scheme : basic, bearer
|
||||||
Schema() string
|
Scheme() string
|
||||||
//AuthorizeRequest adds basic auth or token auth to the header of request
|
//AuthorizeRequest adds basic auth or token auth to the header of request
|
||||||
AuthorizeRequest(req *http.Request, params map[string]string) error
|
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 {
|
func (r *RequestAuthorizer) ModifyRequest(req *http.Request) error {
|
||||||
for _, handler := range r.handlers {
|
for _, handler := range r.handlers {
|
||||||
for _, challenge := range r.challenges {
|
for _, challenge := range r.challenges {
|
||||||
if handler.Schema() == challenge.Scheme {
|
if handler.Scheme() == challenge.Scheme {
|
||||||
if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil {
|
if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -39,80 +39,59 @@ func (s *scope) string() string {
|
|||||||
return fmt.Sprintf("%s:%s:%s", s.Type, s.Name, strings.Join(s.Actions, ","))
|
return fmt.Sprintf("%s:%s:%s", s.Type, s.Name, strings.Join(s.Actions, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
type token struct {
|
type tokenGenerator func(realm, service string, scopes []string) (token string, expiresIn int, issuedAt *time.Time, err error)
|
||||||
token string
|
|
||||||
expiresIn time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type tokenGenerator func(realm, service string, scopes []string) (*token, error)
|
|
||||||
|
|
||||||
type tokenHandler struct {
|
type tokenHandler struct {
|
||||||
scope *scope
|
scope *scope
|
||||||
cache map[string]*token
|
tg tokenGenerator
|
||||||
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
|
// Scheme returns the scheme that the handler can handle
|
||||||
func (t *tokenHandler) Schema() string {
|
func (t *tokenHandler) Scheme() string {
|
||||||
return "bearer"
|
return "bearer"
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizeRequest will add authorization header which contains a token before the request is sent
|
// 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 {
|
func (t *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
||||||
var token string
|
|
||||||
var scopes []*scope
|
var scopes []*scope
|
||||||
|
|
||||||
// TODO handle additional scope: xxx.xxx.xxx?from=repo
|
// TODO handle additional scope: xxx.xxx.xxx?from=repo
|
||||||
|
|
||||||
scopes = append(scopes, t.scope)
|
scopes = append(scopes, t.scope)
|
||||||
key := cacheKey(scopes)
|
|
||||||
|
|
||||||
value, ok := t.cache[key]
|
expired := true
|
||||||
var expired bool
|
|
||||||
|
|
||||||
if ok {
|
if t.expiresIn != 0 && t.issuedAt != nil {
|
||||||
expired = value.expiresIn.Before(time.Now())
|
expired = t.issuedAt.Add(time.Duration(t.expiresIn) * time.Second).Before(time.Now().UTC())
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok && !expired {
|
if 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
scopeStrs := []string{}
|
scopeStrs := []string{}
|
||||||
for _, scope := range scopes {
|
for _, scope := range scopes {
|
||||||
scopeStrs = append(scopeStrs, scope.string())
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
token = tk.token
|
t.cache = token
|
||||||
t.cache[key] = tk
|
t.expiresIn = expiresIn
|
||||||
log.Debugf("add token to cache: %s", key)
|
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())
|
log.Debugf("add token to request: %s %s", req.Method, req.URL.String())
|
||||||
|
|
||||||
return nil
|
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 {
|
type standardTokenHandler struct {
|
||||||
tokenHandler
|
tokenHandler
|
||||||
client *http.Client
|
client *http.Client
|
||||||
@ -135,16 +114,15 @@ func NewStandardTokenHandler(credential Credential, scopeType, scopeName string,
|
|||||||
Name: scopeName,
|
Name: scopeName,
|
||||||
Actions: scopeActions,
|
Actions: scopeActions,
|
||||||
}
|
}
|
||||||
handler.cache = make(map[string]*token, 1)
|
|
||||||
handler.tg = handler.generateToken
|
handler.tg = handler.generateToken
|
||||||
|
|
||||||
return handler
|
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)
|
u, err := url.Parse(realm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", 0, nil, err
|
||||||
}
|
}
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
q.Add("service", service)
|
q.Add("service", service)
|
||||||
@ -154,44 +132,39 @@ func (s *standardTokenHandler) generateToken(realm, service string, scopes []str
|
|||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
r, err := http.NewRequest("GET", u.String(), nil)
|
r, err := http.NewRequest("GET", u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.credential.AddAuthorization(r)
|
s.credential.AddAuthorization(r)
|
||||||
|
|
||||||
resp, err := s.client.Do(r)
|
resp, err := s.client.Do(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", 0, nil, err
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, registry_errors.Error{
|
return "", 0, nil, registry_errors.Error{
|
||||||
StatusCode: resp.StatusCode,
|
StatusCode: resp.StatusCode,
|
||||||
Message: string(b),
|
Message: string(b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
tk := struct {
|
tk := struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}{}
|
}{}
|
||||||
if err = json.Unmarshal(b, &tk); err != nil {
|
if err = json.Unmarshal(b, &tk); err != nil {
|
||||||
return nil, err
|
return "", 0, nil, err
|
||||||
}
|
|
||||||
|
|
||||||
t := &token{
|
|
||||||
token: tk.Token,
|
|
||||||
// TODO handle the expires time
|
|
||||||
expiresIn: time.Now().Add(5 * time.Minute),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("get token from token server")
|
log.Debug("get token from token server")
|
||||||
|
|
||||||
return t, nil
|
return tk.Token, 0, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type usernameTokenHandler struct {
|
type usernameTokenHandler struct {
|
||||||
@ -211,26 +184,15 @@ func NewUsernameTokenHandler(username string, scopeType, scopeName string, scope
|
|||||||
Name: scopeName,
|
Name: scopeName,
|
||||||
Actions: scopeActions,
|
Actions: scopeActions,
|
||||||
}
|
}
|
||||||
handler.cache = make(map[string]*token, 1)
|
|
||||||
|
|
||||||
handler.tg = handler.generateToken
|
handler.tg = handler.generateToken
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *usernameTokenHandler) generateToken(realm, service string, scopes []string) (*token, error) {
|
func (u *usernameTokenHandler) generateToken(realm, service string, scopes []string) (token string, expiresIn int, issuedAt *time.Time, err error) {
|
||||||
tk, err := token_util.GenTokenForUI(u.username, service, scopes)
|
// TODO
|
||||||
if err != nil {
|
token, err = token_util.GenTokenForUI(u.username, service, scopes)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
t := &token{
|
|
||||||
token: tk,
|
|
||||||
// TODO handle the expires time
|
|
||||||
expiresIn: time.Now().Add(5 * time.Minute),
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("get token by calling GenTokenForUI directly")
|
log.Debug("get token by calling GenTokenForUI directly")
|
||||||
|
return
|
||||||
return t, nil
|
|
||||||
}
|
}
|
||||||
|
@ -63,25 +63,14 @@ func NewRegistryWithUsername(endpoint, username string) (*Registry, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.Get(buildPingURL(endpoint))
|
client, err := newClient(endpoint, username, nil, "registry", "catalog", "*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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{
|
registry := &Registry{
|
||||||
Endpoint: u,
|
Endpoint: u,
|
||||||
client: &http.Client{
|
client: client,
|
||||||
Transport: transport,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return registry, nil
|
return registry, nil
|
||||||
@ -131,3 +120,31 @@ func (r *Registry) Catalog() ([]string, error) {
|
|||||||
func buildCatalogURL(endpoint string) string {
|
func buildCatalogURL(endpoint string) string {
|
||||||
return fmt.Sprintf("%s/v2/_catalog", endpoint)
|
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
|
||||||
|
}
|
||||||
|
@ -71,26 +71,12 @@ func NewRepositoryWithCredential(name, endpoint string, credential auth.Credenti
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.Get(buildPingURL(endpoint))
|
client, err := newClient(endpoint, "", credential, "repository", name, "pull", "push")
|
||||||
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})
|
|
||||||
|
|
||||||
repository := &Repository{
|
repository := &Repository{
|
||||||
Name: name,
|
Name: name,
|
||||||
Endpoint: u,
|
Endpoint: u,
|
||||||
client: &http.Client{
|
client: client,
|
||||||
Transport: transport,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("initialized a repository client with credential: %s %s", endpoint, name)
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.Get(buildPingURL(endpoint))
|
client, err := newClient(endpoint, username, nil, "repository", name, "pull", "push")
|
||||||
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})
|
|
||||||
|
|
||||||
repository := &Repository{
|
repository := &Repository{
|
||||||
Name: name,
|
Name: name,
|
||||||
Endpoint: u,
|
Endpoint: u,
|
||||||
client: &http.Client{
|
client: client,
|
||||||
Transport: transport,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("initialized a repository client with username: %s %s", endpoint, name)
|
log.Debugf("initialized a repository client with username: %s %s", endpoint, name)
|
||||||
|
Loading…
Reference in New Issue
Block a user