Skip to content

Commit

Permalink
Merge pull request #14 from portward/scope-equality
Browse files Browse the repository at this point in the history
Scope equality
  • Loading branch information
sagikazarmark authored Sep 25, 2023
2 parents 39bf0e0 + a61a41d commit eda3446
Show file tree
Hide file tree
Showing 5 changed files with 432 additions and 19 deletions.
3 changes: 2 additions & 1 deletion auth/authz/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package authz
import (
"context"
"fmt"
"slices"
"strings"

"github.com/portward/registry-auth/auth"
Expand Down Expand Up @@ -88,5 +89,5 @@ func (a DefaultRepositoryAuthorizer) Authorize(_ context.Context, name string, s
return []string{}, nil
}

return requestedActions, nil
return slices.Clone(requestedActions), nil
}
76 changes: 72 additions & 4 deletions auth/scope.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
package auth

import (
"cmp"
"fmt"
"regexp"
"slices"
"strings"

"github.com/portward/registry-auth/pkg/slices"
slicesx "github.com/portward/registry-auth/pkg/slices"
)

// Scopes is a list of [Scope] instances.
type Scopes []Scope

// Compare compares this with another instance of Scopes.
// It compares the values of each Scope
// and returns a value following the mechanics of [cmp.Compare].
//
// Note that the values of Scope.Actions are always cloned and sorted before comparison,
// so this is not a cheap operation.
func (s Scopes) Compare(other Scopes) int {
return slices.CompareFunc(s, other, func(x Scope, y Scope) int {
return x.Compare(y)
})
}

// Equals returns true if the other instance equals to this one, otherwise it returns false.
func (s Scopes) Equals(other Scopes) bool {
return s.Compare(other) == 0
}

func (s Scopes) String() string {
// TODO: create a slices.MapToString??
return strings.Join(slices.Map(s, func(s Scope) string { return s.String() }), " ")
return strings.Join(slicesx.Map(s, func(s Scope) string { return s.String() }), " ")
}

// Scope describes an access request to a specific resource.
Expand All @@ -22,6 +41,35 @@ type Scope struct {
Actions []string `json:"actions"`
}

// Compare compares this with another instance of Scope.
// It compares the values of Resource and Actions (in this order)
// and returns a value following the mechanics of [cmp.Compare].
//
// Note that the values of Actions are always cloned and sorted before comparison,
// so this is not a cheap operation.
func (s Scope) Compare(other Scope) int {
if result := s.Resource.Compare(other.Resource); result != 0 {
return result
}

thisActions := slices.Clone(s.Actions)
slices.Sort(thisActions)

otherActions := slices.Clone(other.Actions)
slices.Sort(otherActions)

if result := slices.Compare(thisActions, otherActions); result != 0 {
return result
}

return 0
}

// Equals returns true if the other instance equals to this one, otherwise it returns false.
func (s Scope) Equals(other Scope) bool {
return s.Compare(other) == 0
}

func (s Scope) String() string {
return fmt.Sprintf("%s:%s", s.Resource.String(), strings.Join(s.Actions, ","))
}
Expand All @@ -32,14 +80,34 @@ type Resource struct {
Name string `json:"name"`
}

// Compare compares this with another instance of Resource.
// It compares the values of Type and Name (in this order)
// and returns a value following the mechanics of [cmp.Compare].
func (r Resource) Compare(other Resource) int {
if result := cmp.Compare(r.Type, other.Type); result != 0 {
return result
}

if result := cmp.Compare(r.Name, other.Name); result != 0 {
return result
}

return 0
}

// Equals returns true if the other instance equals to this one, otherwise it returns false.
func (r Resource) Equals(other Resource) bool {
return r.Compare(other) == 0
}

func (r Resource) String() string {
return fmt.Sprintf("%s:%s", r.Type, r.Name)
}

// ParseScopes calls ParseScope for each scope in the list.
// If any of the scopes is invalid, ParseScopes returns an empty slice and an error.
func ParseScopes(scopes []string) ([]Scope, error) {
return slices.TryMap(scopes, ParseScope)
return slicesx.TryMap(scopes, ParseScope)
}

// ParseScope parses a scope string into a formal structure according to the [Token Scope documentation].
Expand Down Expand Up @@ -72,7 +140,7 @@ func ParseScope(scope string) (Scope, error) {
Type: resourceType,
Name: resourceName,
},
Actions: slices.Map(strings.Split(actions, ","), strings.TrimSpace),
Actions: slicesx.Map(strings.Split(actions, ","), strings.TrimSpace),
}, nil
}

Expand Down
Loading

0 comments on commit eda3446

Please sign in to comment.