* Use the new authorizer for the User resource * Use accessClient * Update pkg/services/authz/rbac/mapper.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> --------- Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
184 lines
5.4 KiB
Go
184 lines
5.4 KiB
Go
package rbac
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
)
|
|
|
|
// Mapping maps a verb to a RBAC action and a resource name to a RBAC scope.
|
|
type Mapping interface {
|
|
// action returns the action for the given verb.
|
|
// If no action is found, it returns false.
|
|
Action(verb string) (string, bool)
|
|
// scope returns the scope for the given resource name.
|
|
Scope(name string) string
|
|
// prefix returns the scope prefix for the translation.
|
|
Prefix() string
|
|
// AllActions returns all the actions for the translation.
|
|
AllActions() []string
|
|
// HasFolderSupport returns true if the translation supports folders.
|
|
HasFolderSupport() bool
|
|
// SkipScopeOnCreate returns true if the translation does not require a scope on create.
|
|
SkipScopeOnCreate() bool
|
|
}
|
|
|
|
type translation struct {
|
|
resource string
|
|
attribute string
|
|
verbMapping map[string]string
|
|
folderSupport bool
|
|
skipScopeOnCreate bool
|
|
}
|
|
|
|
func (t translation) Action(verb string) (string, bool) {
|
|
action, ok := t.verbMapping[verb]
|
|
return action, ok
|
|
}
|
|
|
|
func (t translation) Scope(name string) string {
|
|
return t.resource + ":" + t.attribute + ":" + name
|
|
}
|
|
|
|
func (t translation) Prefix() string {
|
|
return t.resource + ":" + t.attribute + ":"
|
|
}
|
|
|
|
func (t translation) AllActions() []string {
|
|
actions := make([]string, 0, len(t.verbMapping))
|
|
actionsMap := make(map[string]bool)
|
|
for _, action := range t.verbMapping {
|
|
if actionsMap[action] {
|
|
continue
|
|
}
|
|
actionsMap[action] = true
|
|
actions = append(actions, action)
|
|
}
|
|
return actions
|
|
}
|
|
|
|
func (t translation) HasFolderSupport() bool {
|
|
return t.folderSupport
|
|
}
|
|
|
|
func (t translation) SkipScopeOnCreate() bool {
|
|
return t.skipScopeOnCreate
|
|
}
|
|
|
|
// MapperRegistry is a registry of mappers that maps a group and resource to a translation.
|
|
type MapperRegistry interface {
|
|
// Get returns the permission mapper for the given group and resource.
|
|
// If no translation is found, it returns false.
|
|
Get(group, resource string) (Mapping, bool)
|
|
// GetAll returns all the translations for the given group
|
|
GetAll(group string) []Mapping
|
|
}
|
|
|
|
type mapper map[string]map[string]translation
|
|
|
|
func newResourceTranslation(resource string, attribute string, folderSupport, skipScopeOnCreate bool) translation {
|
|
defaultMapping := func(r string) map[string]string {
|
|
return map[string]string{
|
|
utils.VerbGet: fmt.Sprintf("%s:read", r),
|
|
utils.VerbList: fmt.Sprintf("%s:read", r),
|
|
utils.VerbWatch: fmt.Sprintf("%s:read", r),
|
|
utils.VerbCreate: fmt.Sprintf("%s:create", r),
|
|
utils.VerbUpdate: fmt.Sprintf("%s:write", r),
|
|
utils.VerbPatch: fmt.Sprintf("%s:write", r),
|
|
utils.VerbDelete: fmt.Sprintf("%s:delete", r),
|
|
utils.VerbDeleteCollection: fmt.Sprintf("%s:delete", r),
|
|
utils.VerbGetPermissions: fmt.Sprintf("%s.permissions:read", r),
|
|
utils.VerbSetPermissions: fmt.Sprintf("%s.permissions:write", r),
|
|
}
|
|
}
|
|
|
|
return translation{
|
|
resource: resource,
|
|
attribute: attribute,
|
|
verbMapping: defaultMapping(resource),
|
|
folderSupport: folderSupport,
|
|
skipScopeOnCreate: skipScopeOnCreate,
|
|
}
|
|
}
|
|
|
|
func NewMapperRegistry() MapperRegistry {
|
|
mapper := mapper(map[string]map[string]translation{
|
|
"dashboard.grafana.app": {
|
|
"dashboards": newResourceTranslation("dashboards", "uid", true, false),
|
|
},
|
|
"folder.grafana.app": {
|
|
"folders": newResourceTranslation("folders", "uid", true, false),
|
|
},
|
|
"iam.grafana.app": {
|
|
// Users is a special case. We translate user permissions from id to uid based.
|
|
"users": newResourceTranslation("users", "uid", false, true),
|
|
"serviceaccounts": newResourceTranslation("serviceaccounts", "uid", false, true),
|
|
// Teams is a special case. We translate user permissions from id to uid based.
|
|
"teams": newResourceTranslation("teams", "uid", false, true),
|
|
// No need to skip scope on create for roles because we translate `permissions:type:delegate` to `roles:*``
|
|
"coreroles": newResourceTranslation("roles", "uid", false, false),
|
|
"roles": translation{
|
|
resource: "roles",
|
|
attribute: "uid",
|
|
verbMapping: map[string]string{
|
|
utils.VerbCreate: "roles:write",
|
|
utils.VerbGet: "roles:read",
|
|
utils.VerbUpdate: "roles:write",
|
|
utils.VerbPatch: "roles:write",
|
|
utils.VerbDelete: "roles:delete",
|
|
utils.VerbDeleteCollection: "roles:delete",
|
|
utils.VerbList: "roles:read",
|
|
utils.VerbWatch: "roles:read",
|
|
},
|
|
folderSupport: false,
|
|
skipScopeOnCreate: false,
|
|
},
|
|
},
|
|
"secret.grafana.app": {
|
|
"securevalues": newResourceTranslation("secret.securevalues", "uid", false, false),
|
|
"keepers": newResourceTranslation("secret.keepers", "uid", false, false),
|
|
},
|
|
"query.grafana.app": {
|
|
"query": translation{
|
|
resource: "datasources",
|
|
attribute: "uid",
|
|
verbMapping: map[string]string{
|
|
utils.VerbCreate: "datasources:query",
|
|
},
|
|
folderSupport: false,
|
|
skipScopeOnCreate: false,
|
|
},
|
|
},
|
|
})
|
|
|
|
return mapper
|
|
}
|
|
|
|
func (m mapper) Get(group, resource string) (Mapping, bool) {
|
|
resources, ok := m[group]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
t, ok := resources[resource]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
return &t, true
|
|
}
|
|
|
|
func (m mapper) GetAll(group string) []Mapping {
|
|
resources, ok := m[group]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
translations := make([]Mapping, 0, len(resources))
|
|
for _, t := range resources {
|
|
translations = append(translations, &t)
|
|
}
|
|
|
|
return translations
|
|
}
|