diff --git a/util/resolver/authorizer.go b/util/resolver/authorizer.go index 2766f2157e07..66f410a6e687 100644 --- a/util/resolver/authorizer.go +++ b/util/resolver/authorizer.go @@ -434,25 +434,28 @@ type scopes map[string]map[string]struct{} func parseScopes(s []string) scopes { // https://docs.docker.com/registry/spec/auth/scope/ m := map[string]map[string]struct{}{} - for _, scope := range s { - parts := strings.SplitN(scope, ":", 3) - names := []string{parts[0]} - if len(parts) > 1 { - names = append(names, parts[1]) - } - var actions []string - if len(parts) == 3 { - actions = append(actions, strings.Split(parts[2], ",")...) - } - name := strings.Join(names, ":") - ma, ok := m[name] - if !ok { - ma = map[string]struct{}{} - m[name] = ma - } + for _, scopeStr := range s { + // The scopeStr may have strings that contain multiple scopes separated by a space. + for _, scope := range strings.Split(scopeStr, " ") { + parts := strings.SplitN(scope, ":", 3) + names := []string{parts[0]} + if len(parts) > 1 { + names = append(names, parts[1]) + } + var actions []string + if len(parts) == 3 { + actions = append(actions, strings.Split(parts[2], ",")...) + } + name := strings.Join(names, ":") + ma, ok := m[name] + if !ok { + ma = map[string]struct{}{} + m[name] = ma + } - for _, a := range actions { - ma[a] = struct{}{} + for _, a := range actions { + ma[a] = struct{}{} + } } } return m diff --git a/util/resolver/authorizer_test.go b/util/resolver/authorizer_test.go new file mode 100644 index 000000000000..7c162a8490cb --- /dev/null +++ b/util/resolver/authorizer_test.go @@ -0,0 +1,51 @@ +package resolver + +import ( + "reflect" + "testing" +) + +func TestParseScopes(t *testing.T) { + for _, tc := range []struct { + name string + input []string + expected scopes + }{ + { + name: "SeparateStrings", + input: []string{ + "repository:foo/bar:pull", + "repository:foo/baz:pull,push", + }, + expected: map[string]map[string]struct{}{ + "repository:foo/bar": { + "pull": struct{}{}, + }, + "repository:foo/baz": { + "pull": struct{}{}, + "push": struct{}{}, + }, + }, + }, + { + name: "CombinedStrings", + input: []string{"repository:foo/bar:pull repository:foo/baz:pull,push"}, + expected: map[string]map[string]struct{}{ + "repository:foo/bar": { + "pull": struct{}{}, + }, + "repository:foo/baz": { + "pull": struct{}{}, + "push": struct{}{}, + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + parsed := parseScopes(tc.input) + if !reflect.DeepEqual(parsed, tc.expected) { + t.Fatalf("expected %v, got %v", tc.expected, parsed) + } + }) + } +}