mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 20:22:32 +08:00
196 lines
6.3 KiB
Go
196 lines
6.3 KiB
Go
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:",
|
|
"secret.securevalues": "secret.securevalues:uid:",
|
|
"secret.keepers": "secret.keepers: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
|
|
}
|