diff --git a/registry/remote/auth/client.go b/registry/remote/auth/client.go index 0303b8b9..27bf182d 100644 --- a/registry/remote/auth/client.go +++ b/registry/remote/auth/client.go @@ -187,12 +187,7 @@ func (c *Client) Do(originalReq *http.Request) (*http.Response, error) { req.Header.Set("Authorization", "Basic "+token) } case SchemeBearer: - // merge per-host scopes with global scopes - scopes := GetScopesForHost(ctx, host) - if moreScopes := GetScopes(ctx); len(moreScopes) > 0 { - scopes = append(scopes, moreScopes...) - scopes = CleanScopes(scopes) - } + scopes := getAllScopesForHost(ctx, host) attemptedKey = strings.Join(scopes, " ") token, err := cache.GetToken(ctx, host, SchemeBearer, attemptedKey) if err == nil { @@ -228,18 +223,10 @@ func (c *Client) Do(originalReq *http.Request) (*http.Response, error) { case SchemeBearer: resp.Body.Close() - scopes := GetScopesForHost(ctx, host) - cleanScopeLen := len(scopes) - if moreScopes := GetScopes(ctx); len(moreScopes) > 0 { - // merge per-host scopes with global scopes - scopes = append(scopes, moreScopes...) - } + scopes := getAllScopesForHost(ctx, host) if paramScope := params["scope"]; paramScope != "" { // merge hinted scopes with challenged scopes scopes = append(scopes, strings.Split(paramScope, " ")...) - } - if len(scopes) > cleanScopeLen { - // re-clean the scopes scopes = CleanScopes(scopes) } key := strings.Join(scopes, " ") diff --git a/registry/remote/auth/scope.go b/registry/remote/auth/scope.go index bd98b6c6..0c042c4a 100644 --- a/registry/remote/auth/scope.go +++ b/registry/remote/auth/scope.go @@ -163,7 +163,7 @@ func AppendScopesForHost(ctx context.Context, host string, scopes ...string) con } // GetScopesForHost returns the scopes in the context for the given host, -// excluding the global scopes added by [WithScopes] and [AppendScopes]. +// excluding global scopes added by [WithScopes] and [AppendScopes]. func GetScopesForHost(ctx context.Context, host string) []string { if scopes, ok := ctx.Value(scopesForHostContextKey(host)).([]string); ok { return slices.Clone(scopes) @@ -171,6 +171,24 @@ func GetScopesForHost(ctx context.Context, host string) []string { return nil } +// getAllScopesForHost returns the scopes in the context for the given host, +// including global scopes added by [WithScopes] and [AppendScopes]. +func getAllScopesForHost(ctx context.Context, host string) []string { + scopes := GetScopesForHost(ctx, host) + globalScopes := GetScopes(ctx) + + switch { + case len(scopes) == 0: + return globalScopes + case len(globalScopes) == 0: + return scopes + default: + // re-clean the scopes + allScopes := append(scopes, globalScopes...) + return CleanScopes(allScopes) + } +} + // CleanScopes merges and sort the actions in ascending order if the scopes have // the same resource type and name. The final scopes are sorted in ascending // order. In other words, the scopes passed in are de-duplicated and sorted.