The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/accesscontrol/permreg/permreg.go

193 lines
6.1 KiB

package permreg
import (
"strings"
"github.com/grafana/grafana/pkg/apimachinery/errutil"
"github.com/grafana/grafana/pkg/infra/log"
)
var (
// ErrInvalidScope is returned when the scope is not valid for the action
ErrInvalidScopeTplt = "invalid scope: {{.Public.Scope}}, for action: {{.Public.Action}}, expected prefixes are {{.Public.ValidScopesFormat}}"
ErrBaseInvalidScope = errutil.BadRequest("permreg.invalid-scope").MustTemplate(ErrInvalidScopeTplt, errutil.WithPublic(ErrInvalidScopeTplt))
ErrUnknownActionTplt = "unknown action: {{.Public.Action}}, was not found in the list of valid actions"
ErrBaseUnknownAction = errutil.BadRequest("permreg.unknown-action").MustTemplate(ErrUnknownActionTplt, errutil.WithPublic(ErrUnknownActionTplt))
ErrBaseUnknownKind = errutil.BadRequest("permreg.unknown-kind").MustTemplate("unknown kind: {{.Public.Kind}}")
)
func ErrInvalidScope(scope string, action string, validScopePrefixes PrefixSet) error {
if len(validScopePrefixes) == 0 {
return ErrBaseInvalidScope.Build(errutil.TemplateData{Public: map[string]any{"Scope": scope, "Action": action, "ValidScopesFormat": "[none]"}})
}
formats := generateValidScopeFormats(validScopePrefixes)
return ErrBaseInvalidScope.Build(errutil.TemplateData{Public: map[string]any{"Scope": scope, "Action": action, "ValidScopesFormat": formats}})
}
func ErrUnknownAction(action string) error {
return ErrBaseUnknownAction.Build(errutil.TemplateData{Public: map[string]any{"Action": action}})
}
func ErrUnknownKind(kind string) error {
return ErrBaseUnknownKind.Build(errutil.TemplateData{Public: map[string]any{"Kind": kind}})
}
func generateValidScopeFormats(acceptedScopePrefixes PrefixSet) []string {
if len(acceptedScopePrefixes) == 0 {
return []string{}
}
acceptedPrefixesList := make([]string, 0, 10)
acceptedPrefixesList = append(acceptedPrefixesList, "*")
for prefix := range acceptedScopePrefixes {
parts := strings.Split(prefix, ":")
// If the prefix has an attribute part add the intermediate format kind:*
if len(parts) > 2 {
acceptedPrefixesList = append(acceptedPrefixesList, parts[0]+":*")
}
// Add the most specific format kind:attribute:*
acceptedPrefixesList = append(acceptedPrefixesList, prefix+"*")
}
return acceptedPrefixesList
}
type PermissionRegistry interface {
RegisterPluginScope(scope string)
RegisterPermission(action, scope string) error
IsPermissionValid(action, scope string) error
GetScopePrefixes(action string) (PrefixSet, bool)
}
type PrefixSet map[string]bool
var _ PermissionRegistry = &permissionRegistry{}
type permissionRegistry struct {
actionScopePrefixes map[string]PrefixSet // TODO use thread safe map
kindScopePrefix map[string]string
logger log.Logger
}
func ProvidePermissionRegistry() PermissionRegistry {
return newPermissionRegistry()
}
func newPermissionRegistry() *permissionRegistry {
// defaultKindScopes maps the most specific accepted scope prefix for a given kind (folders, dashboards, etc)
defaultKindScopes := map[string]string{
"teams": "teams:id:",
"users": "users:id:",
"datasources": "datasources:uid:",
"dashboards": "dashboards:uid:",
"folders": "folders:uid:",
"annotations": "annotations:type:",
"apikeys": "apikeys:id:",
"orgs": "orgs:id:",
"plugins": "plugins:id:",
"provisioners": "provisioners:",
"reports": "reports:id:",
"permissions": "permissions:type:",
"serviceaccounts": "serviceaccounts:id:",
"settings": "settings:",
"global.users": "global.users:id:",
"roles": "roles:uid:",
"services": "services:",
"receivers": "receivers:uid:",
}
return &permissionRegistry{
actionScopePrefixes: make(map[string]PrefixSet, 200),
kindScopePrefix: defaultKindScopes,
logger: log.New("accesscontrol.permreg"),
}
}
func (pr *permissionRegistry) RegisterPluginScope(scope string) {
if scope == "" {
return
}
scopeParts := strings.Split(scope, ":")
kind := scopeParts[0]
// If the scope is already registered, return
if _, found := pr.kindScopePrefix[kind]; found {
return
}
// If the scope contains an attribute part, register the kind and attribute
if len(scopeParts) > 2 {
attr := scopeParts[1]
pr.kindScopePrefix[kind] = kind + ":" + attr + ":"
pr.logger.Debug("registered scope prefix", "kind", kind, "scope_prefix", kind+":"+attr+":")
return
}
pr.logger.Debug("registered scope prefix", "kind", kind, "scope_prefix", kind+":")
pr.kindScopePrefix[kind] = kind + ":"
}
func (pr *permissionRegistry) RegisterPermission(action, scope string) error {
if _, ok := pr.actionScopePrefixes[action]; !ok {
pr.actionScopePrefixes[action] = PrefixSet{}
}
if scope == "" {
// scopeless action
return nil
}
kind := strings.Split(scope, ":")[0]
scopePrefix, ok := pr.kindScopePrefix[kind]
if !ok {
pr.logger.Error("unknown kind: please update `kindScopePrefix` with the correct scope prefix", "kind", kind)
return ErrUnknownKind(kind)
}
// Add a new entry in case the scope is not empty
pr.actionScopePrefixes[action][scopePrefix] = true
return nil
}
func (pr *permissionRegistry) IsPermissionValid(action, scope string) error {
validScopePrefixes, ok := pr.actionScopePrefixes[action]
if !ok {
return ErrUnknownAction(action)
}
if ok && len(validScopePrefixes) == 0 {
// Expecting action without any scope
if scope != "" {
return ErrInvalidScope(scope, action, nil)
}
return nil
}
if !isScopeValid(scope, validScopePrefixes) {
return ErrInvalidScope(scope, action, validScopePrefixes)
}
return nil
}
func isScopeValid(scope string, validScopePrefixes PrefixSet) bool {
// Super wildcard scope
if scope == "*" {
return true
}
for scopePrefix := range validScopePrefixes {
// Correct scope prefix
if strings.HasPrefix(scope, scopePrefix) {
return true
}
// Scope is wildcard of the correct prefix
if strings.HasSuffix(scope, ":*") && strings.HasPrefix(scopePrefix, scope[:len(scope)-2]) {
return true
}
}
return false
}
func (pr *permissionRegistry) GetScopePrefixes(action string) (PrefixSet, bool) {
set, ok := pr.actionScopePrefixes[action]
return set, ok
}