mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 16:15:42 -06:00
* Picking role registration from OnCall POC branch * Fix test * Remove include actions from this PR * Removing unused permission * Adding test to DeclarePluginRoles * Add testcase to RegisterFixed role * Additional test case * Adding tests to validate plugins roles * Add test to plugin loader * Nit. * Scuemata validation * Changing the design to decouple accesscontrol from plugin management Co-authored-by: Kalle Persson <kalle.persson@grafana.com> * Fixing tests Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Add missing files Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Remove feature toggle check from loader * Remove feature toggleimport * Feedback Co-Authored-By: marefr <marcus.efraimsson@gmail.com> * Fix test' * Make plugins.RoleRegistry interface typed * Remove comment question * No need for json tags anymore * Nit. log * Adding the schema validation * Remove group to take plugin Name instead * Revert sqlstore -> db * Nit. * Nit. on tests Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Update pkg/services/accesscontrol/plugins.go Co-authored-by: Ieva <ieva.vasiljeva@grafana.com> * Log message Co-Authored-By: marefr <marcus.efraimsson@gmail.com> * Log message Co-Authored-By: marefr <marcus.efraimsson@gmail.com> * Remove unecessary method. Update test name. Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Fix linting * Update cue descriptions * Fix test Co-authored-by: Kalle Persson <kalle.persson@grafana.com> Co-authored-by: Jguer <joao.guerreiro@grafana.com> Co-authored-by: marefr <marcus.efraimsson@gmail.com> Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com>
80 lines
2.6 KiB
Go
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", "prefix", 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)
|
|
}
|