RBAC: handle partially resolved scopes (#85323)

* RBAC: handle partially resolved scopes

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
Karl Persson 2024-03-28 10:08:07 +01:00 committed by GitHub
parent c6a489cfd8
commit 5dd98a0fd5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 79 additions and 0 deletions

View File

@ -2,6 +2,7 @@ package accesscontrol
import (
"context"
"errors"
"fmt"
"strings"
@ -84,14 +85,25 @@ func (p permissionEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttri
return EvalPermission(p.Action), nil
}
resolved := false
scopes := make([]string, 0, len(p.Scopes))
for _, scope := range p.Scopes {
mutated, err := mutate(ctx, scope)
if err != nil {
if errors.Is(err, ErrResolverNotFound) {
scopes = append(scopes, mutated...)
continue
}
return nil, err
}
resolved = true
scopes = append(scopes, mutated...)
}
if !resolved {
return nil, ErrResolverNotFound
}
return EvalPermission(p.Action, scopes...), nil
}
@ -124,14 +136,24 @@ func (a allEvaluator) Evaluate(permissions map[string][]string) bool {
}
func (a allEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error) {
resolved := false
modified := make([]Evaluator, 0, len(a.allOf))
for _, e := range a.allOf {
i, err := e.MutateScopes(ctx, mutate)
if err != nil {
if errors.Is(err, ErrResolverNotFound) {
modified = append(modified, e)
continue
}
return nil, err
}
resolved = true
modified = append(modified, i)
}
if !resolved {
return nil, ErrResolverNotFound
}
return EvalAll(modified...), nil
}
@ -174,14 +196,25 @@ func (a anyEvaluator) Evaluate(permissions map[string][]string) bool {
}
func (a anyEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error) {
resolved := false
modified := make([]Evaluator, 0, len(a.anyOf))
for _, e := range a.anyOf {
i, err := e.MutateScopes(ctx, mutate)
if err != nil {
if errors.Is(err, ErrResolverNotFound) {
modified = append(modified, e)
continue
}
return nil, err
}
resolved = true
modified = append(modified, i)
}
if !resolved {
return nil, ErrResolverNotFound
}
return EvalAny(modified...), nil
}

View File

@ -410,3 +410,49 @@ func TestEval(t *testing.T) {
})
}
}
func TestEval_MutateScopes(t *testing.T) {
t.Run("should return error if none of the scopes was a resolved", func(t *testing.T) {
eval := EvalAll(
EvalPermission("action:1", "scope:uid:1"),
EvalPermission("action:2", "scope:id:1"),
)
calls := 0
_, err := eval.MutateScopes(context.Background(), func(ctx context.Context, s string) ([]string, error) {
calls += 1
return nil, ErrResolverNotFound
})
assert.Equal(t, 2, calls)
assert.ErrorIs(t, err, ErrResolverNotFound)
})
t.Run("should return if at least one scope was resolved", func(t *testing.T) {
eval := EvalAll(
EvalPermission("action:1", "scope:uid:1"),
EvalPermission("action:2", "scope:id:1"),
)
calls := 0
resolved := 0
eval, err := eval.MutateScopes(context.Background(), func(ctx context.Context, s string) ([]string, error) {
calls += 1
if s == "scope:id:1" {
resolved += 1
return []string{"scope:uid:2"}, nil
}
return nil, ErrResolverNotFound
})
assert.NoError(t, err)
assert.Equal(t, 2, calls)
assert.Equal(t, 1, resolved)
hasAccess := eval.Evaluate(map[string][]string{
"action:1": {"scope:uid:1"},
"action:2": {"scope:uid:2"},
})
assert.True(t, hasAccess)
})
}