AuthZ service: Expand the logic to also evaluate action sets (#112124)

* expand AuthZ service logic to also evaluate action sets

* handle folder creation

* fix test

* simplify mapper code

Co-authored-by: gamab <gabi.mabs@gmail.com>

* more accurate variable name Co-authored-by: gamab <gabi.mabs@gmail.com>

* break alerting import cycle

* Apply suggestion from @gamab

---------

Co-authored-by: gamab <gabi.mabs@gmail.com>
Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
Ieva
2025-10-08 13:37:12 +01:00
committed by GitHub
co-authored by gamab Gabriel MABILLE
parent bb1d7d9070
commit acbbfde256
13 changed files with 359 additions and 214 deletions
+67 -2
View File
@@ -2,8 +2,10 @@ package rbac
import (
"fmt"
"slices"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
)
// Mapping maps a verb to a RBAC action and a resource name to a RBAC scope.
@@ -11,6 +13,9 @@ type Mapping interface {
// action returns the action for the given verb.
// If no action is found, it returns false.
Action(verb string) (string, bool)
// ActionSets returns the action sets for the given verb.
// If no action sets are found, it returns an empty slice. This is expected for resources that do not have action sets (anything apart from dashboards and folders).
ActionSets(verb string) []string
// scope returns the scope for the given resource name.
Scope(name string) string
// prefix returns the scope prefix for the translation.
@@ -27,6 +32,7 @@ type translation struct {
resource string
attribute string
verbMapping map[string]string
actionSetMapping map[string][]string
folderSupport bool
skipScopeOnCreate bool
}
@@ -36,6 +42,11 @@ func (t translation) Action(verb string) (string, bool) {
return action, ok
}
func (t translation) ActionSets(verb string) []string {
actionSets := t.actionSetMapping[verb]
return actionSets
}
func (t translation) Scope(name string) string {
return t.resource + ":" + t.attribute + ":" + name
}
@@ -101,13 +112,67 @@ func newResourceTranslation(resource string, attribute string, folderSupport, sk
}
}
// newDashboardTranslation creates a translation for dashboards and also maps the actions to action sets
func newDashboardTranslation() translation {
dashTranslation := newResourceTranslation("dashboards", "uid", true, false)
actionSetMapping := make(map[string][]string)
for verb, rbacAction := range dashTranslation.verbMapping {
var dashActionSets []string
if slices.Contains(ossaccesscontrol.DashboardViewActions, rbacAction) {
dashActionSets = append(dashActionSets, "dashboards:view")
dashActionSets = append(dashActionSets, "folders:view")
}
if slices.Contains(ossaccesscontrol.DashboardEditActions, rbacAction) {
dashActionSets = append(dashActionSets, "dashboards:edit")
dashActionSets = append(dashActionSets, "folders:edit")
}
if slices.Contains(ossaccesscontrol.DashboardAdminActions, rbacAction) {
dashActionSets = append(dashActionSets, "dashboards:admin")
dashActionSets = append(dashActionSets, "folders:admin")
}
actionSetMapping[verb] = dashActionSets
}
dashTranslation.actionSetMapping = actionSetMapping
return dashTranslation
}
// newFolderTranslation creates a translation for folders and also maps the actions to action sets
func newFolderTranslation() translation {
folderTranslation := newResourceTranslation("folders", "uid", true, false)
actionSetMapping := make(map[string][]string)
for verb, rbacAction := range folderTranslation.verbMapping {
var actionSets []string
// Folder creation has not been added to the FolderEditActions and FolderAdminActions slices (https://github.com/grafana/identity-access-team/issues/794)
// so we handle it as a special case for now
if rbacAction == "folders:create" {
actionSets = append(actionSets, "folders:edit")
actionSets = append(actionSets, "folders:admin")
}
if slices.Contains(ossaccesscontrol.FolderViewActions, rbacAction) {
actionSets = append(actionSets, "folders:view")
}
if slices.Contains(ossaccesscontrol.FolderEditActions, rbacAction) {
actionSets = append(actionSets, "folders:edit")
}
if slices.Contains(ossaccesscontrol.FolderAdminActions, rbacAction) {
actionSets = append(actionSets, "folders:admin")
}
actionSetMapping[verb] = actionSets
}
folderTranslation.actionSetMapping = actionSetMapping
return folderTranslation
}
func NewMapperRegistry() MapperRegistry {
mapper := mapper(map[string]map[string]translation{
"dashboard.grafana.app": {
"dashboards": newResourceTranslation("dashboards", "uid", true, false),
"dashboards": newDashboardTranslation(),
},
"folder.grafana.app": {
"folders": newResourceTranslation("folders", "uid", true, false),
"folders": newFolderTranslation(),
},
"iam.grafana.app": {
// Users is a special case. We translate user permissions from id to uid based.