grafana/pkg/services/accesscontrol/resolvers.go
Karl Persson 552d3fec8d
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
2022-08-25 12:50:27 +02:00

80 lines
2.6 KiB
Go

package accesscontrol
import (
"context"
"fmt"
"time"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
)
// ScopeAttributeResolver is used to resolve attributes in scopes to one or more scopes that are
// evaluated by logical or. E.g. "dashboards:id:1" -> "dashboards:uid:test-dashboard" or "folder:uid:test-folder"
type ScopeAttributeResolver interface {
Resolve(ctx context.Context, orgID int64, scope string) ([]string, error)
}
// ScopeAttributeResolverFunc is an adapter to allow functions to implement ScopeAttributeResolver interface
type ScopeAttributeResolverFunc func(ctx context.Context, orgID int64, scope string) ([]string, error)
func (f ScopeAttributeResolverFunc) Resolve(ctx context.Context, orgID int64, scope string) ([]string, error) {
return f(ctx, orgID, scope)
}
type ScopeAttributeMutator func(context.Context, string) ([]string, error)
const (
ttl = 30 * time.Second
cleanInterval = 2 * time.Minute
)
func NewResolvers(log log.Logger) Resolvers {
return Resolvers{
log: log,
cache: localcache.New(ttl, cleanInterval),
attributeResolvers: map[string]ScopeAttributeResolver{},
}
}
type Resolvers struct {
log log.Logger
cache *localcache.CacheService
attributeResolvers map[string]ScopeAttributeResolver
}
func (s *Resolvers) AddScopeAttributeResolver(prefix string, resolver ScopeAttributeResolver) {
s.log.Debug("adding scope attribute resolver for '%v'", prefix)
s.attributeResolvers[prefix] = resolver
}
func (s *Resolvers) GetScopeAttributeMutator(orgID int64) ScopeAttributeMutator {
return func(ctx context.Context, scope string) ([]string, error) {
key := getScopeCacheKey(orgID, scope)
// Check cache before computing the scope
if cachedScope, ok := s.cache.Get(key); ok {
scopes := cachedScope.([]string)
s.log.Debug("used cache to resolve scope", "scope", scope, "resolved_scopes", scopes)
return scopes, nil
}
prefix := ScopePrefix(scope)
if resolver, ok := s.attributeResolvers[prefix]; ok {
scopes, err := resolver.Resolve(ctx, orgID, scope)
if err != nil {
return nil, fmt.Errorf("could not resolve %v: %w", scope, err)
}
// Cache result
s.cache.Set(key, scopes, ttl)
s.log.Debug("resolved scope", "scope", scope, "resolved_scopes", scopes)
return scopes, nil
}
return nil, ErrResolverNotFound
}
}
// getScopeCacheKey creates an identifier to fetch and store resolution of scopes in the cache
func getScopeCacheKey(orgID int64, scope string) string {
return fmt.Sprintf("%s-%v", scope, orgID)
}