mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
RBAC: Search endpoint support wildcards (#80383)
* RBAC: Search endpoint support wildcards * Allow wildcard filter with RAM permissions as well
This commit is contained in:
parent
c27bee567f
commit
dce9d1e87c
@ -57,7 +57,23 @@ type SearchOptions struct {
|
||||
ActionPrefix string // Needed for the PoC v1, it's probably going to be removed.
|
||||
Action string
|
||||
Scope string
|
||||
UserID int64 // ID for the user for which to return information, if none is specified information is returned for all users.
|
||||
UserID int64 // ID for the user for which to return information, if none is specified information is returned for all users.
|
||||
wildcards Wildcards // private field computed based on the Scope
|
||||
}
|
||||
|
||||
// Wildcards computes the wildcard scopes that include the scope
|
||||
func (s *SearchOptions) Wildcards() []string {
|
||||
if s.wildcards != nil {
|
||||
return s.wildcards
|
||||
}
|
||||
|
||||
if s.Scope == "" {
|
||||
s.wildcards = []string{}
|
||||
return s.wildcards
|
||||
}
|
||||
|
||||
s.wildcards = WildcardsFromPrefix(ScopePrefix(s.Scope))
|
||||
return s.wildcards
|
||||
}
|
||||
|
||||
type SyncUserRolesCommand struct {
|
||||
|
@ -3,6 +3,7 @@ package acimpl
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -249,7 +250,7 @@ func (s *Service) SearchUsersPermissions(ctx context.Context, user identity.Requ
|
||||
basicPermissions := map[string][]accesscontrol.Permission{}
|
||||
for role, basicRole := range s.roles {
|
||||
for i := range basicRole.Permissions {
|
||||
if PermissionMatchesSearchOptions(basicRole.Permissions[i], options) {
|
||||
if PermissionMatchesSearchOptions(basicRole.Permissions[i], &options) {
|
||||
basicPermissions[role] = append(basicPermissions[role], basicRole.Permissions[i])
|
||||
}
|
||||
}
|
||||
@ -351,7 +352,7 @@ func (s *Service) searchUserPermissions(ctx context.Context, orgID int64, search
|
||||
for _, builtin := range roles {
|
||||
if basicRole, ok := s.roles[builtin]; ok {
|
||||
for _, permission := range basicRole.Permissions {
|
||||
if PermissionMatchesSearchOptions(permission, searchOptions) {
|
||||
if PermissionMatchesSearchOptions(permission, &searchOptions) {
|
||||
permissions = append(permissions, permission)
|
||||
}
|
||||
}
|
||||
@ -384,7 +385,7 @@ func (s *Service) searchUserPermissionsFromCache(orgID int64, searchOptions acce
|
||||
s.log.Debug("Using cached permissions", "key", key)
|
||||
filteredPermissions := make([]accesscontrol.Permission, 0)
|
||||
for _, permission := range permissions.([]accesscontrol.Permission) {
|
||||
if PermissionMatchesSearchOptions(permission, searchOptions) {
|
||||
if PermissionMatchesSearchOptions(permission, &searchOptions) {
|
||||
filteredPermissions = append(filteredPermissions, permission)
|
||||
}
|
||||
}
|
||||
@ -392,9 +393,13 @@ func (s *Service) searchUserPermissionsFromCache(orgID int64, searchOptions acce
|
||||
return filteredPermissions, true
|
||||
}
|
||||
|
||||
func PermissionMatchesSearchOptions(permission accesscontrol.Permission, searchOptions accesscontrol.SearchOptions) bool {
|
||||
if searchOptions.Scope != "" && permission.Scope != searchOptions.Scope {
|
||||
return false
|
||||
func PermissionMatchesSearchOptions(permission accesscontrol.Permission, searchOptions *accesscontrol.SearchOptions) bool {
|
||||
if searchOptions.Scope != "" {
|
||||
// Permissions including the scope should also match
|
||||
scopes := append(searchOptions.Wildcards(), searchOptions.Scope)
|
||||
if !slices.Contains[[]string, string](scopes, permission.Scope) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if searchOptions.Action != "" {
|
||||
return permission.Action == searchOptions.Action
|
||||
|
@ -448,6 +448,23 @@ func TestService_SearchUsersPermissions(t *testing.T) {
|
||||
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ram only search on scope",
|
||||
siuPermissions: listAllPerms,
|
||||
searchOption: accesscontrol.SearchOptions{Scope: "teams:id:2"},
|
||||
ramRoles: map[string]*accesscontrol.RoleDTO{
|
||||
string(roletype.RoleAdmin): {Permissions: []accesscontrol.Permission{
|
||||
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
||||
}},
|
||||
},
|
||||
storedRoles: map[int64][]string{
|
||||
1: {string(roletype.RoleEditor)},
|
||||
2: {string(roletype.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
||||
},
|
||||
want: map[int64][]accesscontrol.Permission{
|
||||
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "view permission on subset of users only",
|
||||
siuPermissions: listSomePerms,
|
||||
|
@ -106,8 +106,12 @@ func (s *AccessControlStore) SearchUsersPermissions(ctx context.Context, orgID i
|
||||
params = append(params, options.Action)
|
||||
}
|
||||
if options.Scope != "" {
|
||||
q += ` AND scope = ?`
|
||||
params = append(params, options.Scope)
|
||||
// Search for scope and wildcard that include the scope
|
||||
scopes := append(options.Wildcards(), options.Scope)
|
||||
q += ` AND scope IN ( ? ` + strings.Repeat(", ?", len(scopes)-1) + ")"
|
||||
for i := range scopes {
|
||||
params = append(params, scopes[i])
|
||||
}
|
||||
}
|
||||
|
||||
if options.UserID != 0 {
|
||||
|
@ -526,6 +526,20 @@ func TestIntegrationAccessControlStore_SearchUsersPermissions(t *testing.T) {
|
||||
{Action: "teams:write", Scope: "teams:id:1"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "user assignment by scope",
|
||||
users: []testUser{{orgRole: org.RoleAdmin, isAdmin: false}},
|
||||
permCmds: []rs.SetResourcePermissionsCommand{
|
||||
{User: accesscontrol.User{ID: 1, IsExternal: false}, SetResourcePermissionCommand: readTeamPerm("*")}, // hack to have a global permission
|
||||
{User: accesscontrol.User{ID: 1, IsExternal: false}, SetResourcePermissionCommand: writeTeamPerm("1")},
|
||||
},
|
||||
options: accesscontrol.SearchOptions{Scope: "teams:id:1"},
|
||||
wantPerm: map[int64][]accesscontrol.Permission{1: {
|
||||
{Action: "teams:read", Scope: "teams:id:*"},
|
||||
{Action: "teams:read", Scope: "teams:id:1"},
|
||||
{Action: "teams:write", Scope: "teams:id:1"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "user assignment by action and scope",
|
||||
users: []testUser{{orgRole: org.RoleAdmin, isAdmin: false}},
|
||||
|
Loading…
Reference in New Issue
Block a user