RBAC: Fix resolver issue on wildcard resulting in wrong status code for endpoints (#54208)

* RBAC: Test evaluation before attaching mutator

* RBAC: Return error if no resolver is found for scope

* RBAC: Sync changes to evaluation in mock

* RBAC: Check for resolver not found error and just fail the evaluation in that case
pull/54218/head^2
Karl Persson 3 years ago committed by GitHub
parent 89d264fecc
commit 552d3fec8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      pkg/services/accesscontrol/errors.go
  2. 12
      pkg/services/accesscontrol/mock/mock.go
  3. 10
      pkg/services/accesscontrol/ossaccesscontrol/accesscontrol.go
  4. 2
      pkg/services/accesscontrol/resolvers.go
  5. 10
      pkg/services/accesscontrol/resolvers_test.go

@ -6,4 +6,5 @@ var (
ErrFixedRolePrefixMissing = errors.New("fixed role should be prefixed with '" + FixedRolePrefix + "'") ErrFixedRolePrefixMissing = errors.New("fixed role should be prefixed with '" + FixedRolePrefix + "'")
ErrInvalidBuiltinRole = errors.New("built-in role is not valid") ErrInvalidBuiltinRole = errors.New("built-in role is not valid")
ErrInvalidScope = errors.New("invalid scope") ErrInvalidScope = errors.New("invalid scope")
ErrResolverNotFound = errors.New("no resolver found")
) )

@ -2,6 +2,7 @@ package mock
import ( import (
"context" "context"
"errors"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
@ -105,11 +106,18 @@ func (m *Mock) Evaluate(ctx context.Context, usr *user.SignedInUser, evaluator a
permissions = accesscontrol.GroupScopesByAction(userPermissions) permissions = accesscontrol.GroupScopesByAction(userPermissions)
} }
attributeMutator := m.scopeResolvers.GetScopeAttributeMutator(usr.OrgID) if evaluator.Evaluate(permissions) {
resolvedEvaluator, err := evaluator.MutateScopes(ctx, attributeMutator) return true, nil
}
resolvedEvaluator, err := evaluator.MutateScopes(ctx, m.scopeResolvers.GetScopeAttributeMutator(usr.OrgID))
if err != nil { if err != nil {
if errors.Is(err, accesscontrol.ErrResolverNotFound) {
return false, nil
}
return false, err return false, err
} }
return resolvedEvaluator.Evaluate(permissions), nil return resolvedEvaluator.Evaluate(permissions), nil
} }

@ -2,6 +2,7 @@ package ossaccesscontrol
import ( import (
"context" "context"
"errors"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
@ -45,10 +46,19 @@ func (a *AccessControl) Evaluate(ctx context.Context, user *user.SignedInUser, e
user.Permissions[user.OrgID] = accesscontrol.GroupScopesByAction(permissions) user.Permissions[user.OrgID] = accesscontrol.GroupScopesByAction(permissions)
} }
// Test evaluation without scope resolver first, this will prevent 403 for wildcard scopes when resource does not exist
if evaluator.Evaluate(user.Permissions[user.OrgID]) {
return true, nil
}
resolvedEvaluator, err := evaluator.MutateScopes(ctx, a.resolvers.GetScopeAttributeMutator(user.OrgID)) resolvedEvaluator, err := evaluator.MutateScopes(ctx, a.resolvers.GetScopeAttributeMutator(user.OrgID))
if err != nil { if err != nil {
if errors.Is(err, accesscontrol.ErrResolverNotFound) {
return false, nil
}
return false, err return false, err
} }
return resolvedEvaluator.Evaluate(user.Permissions[user.OrgID]), nil return resolvedEvaluator.Evaluate(user.Permissions[user.OrgID]), nil
} }

@ -69,7 +69,7 @@ func (s *Resolvers) GetScopeAttributeMutator(orgID int64) ScopeAttributeMutator
s.log.Debug("resolved scope", "scope", scope, "resolved_scopes", scopes) s.log.Debug("resolved scope", "scope", scope, "resolved_scopes", scopes)
return scopes, nil return scopes, nil
} }
return []string{scope}, nil return nil, ErrResolverNotFound
} }
} }

@ -89,8 +89,17 @@ func TestResolvers_AttributeScope(t *testing.T) {
wantEvaluator: accesscontrol.EvalPermission("datasources:read", accesscontrol.Scope("datasources", "id", "5")), wantEvaluator: accesscontrol.EvalPermission("datasources:read", accesscontrol.Scope("datasources", "id", "5")),
wantCalls: 1, wantCalls: 1,
}, },
{
name: "should return error if no resolver is found for scope",
orgID: 1,
evaluator: accesscontrol.EvalPermission("dashboards:read", "dashboards:id:1"),
wantEvaluator: nil,
wantCalls: 0,
wantErr: accesscontrol.ErrResolverNotFound,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resolvers := accesscontrol.NewResolvers(log.NewNopLogger()) resolvers := accesscontrol.NewResolvers(log.NewNopLogger())
// Reset calls counter // Reset calls counter
@ -108,5 +117,6 @@ func TestResolvers_AttributeScope(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, tt.wantEvaluator, resolvedEvaluator, "permission did not match expected resolution") assert.EqualValues(t, tt.wantEvaluator, resolvedEvaluator, "permission did not match expected resolution")
assert.Equal(t, tt.wantCalls, calls, "cache has not been used") assert.Equal(t, tt.wantCalls, calls, "cache has not been used")
})
} }
} }

Loading…
Cancel
Save