RBAC: Search endpoint support wildcards (#80383)
* RBAC: Search endpoint support wildcards * Allow wildcard filter with RAM permissions as well
This commit is contained in:
@@ -57,7 +57,23 @@ type SearchOptions struct {
|
|||||||
ActionPrefix string // Needed for the PoC v1, it's probably going to be removed.
|
ActionPrefix string // Needed for the PoC v1, it's probably going to be removed.
|
||||||
Action string
|
Action string
|
||||||
Scope 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 {
|
type SyncUserRolesCommand struct {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package acimpl
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -249,7 +250,7 @@ func (s *Service) SearchUsersPermissions(ctx context.Context, user identity.Requ
|
|||||||
basicPermissions := map[string][]accesscontrol.Permission{}
|
basicPermissions := map[string][]accesscontrol.Permission{}
|
||||||
for role, basicRole := range s.roles {
|
for role, basicRole := range s.roles {
|
||||||
for i := range basicRole.Permissions {
|
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])
|
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 {
|
for _, builtin := range roles {
|
||||||
if basicRole, ok := s.roles[builtin]; ok {
|
if basicRole, ok := s.roles[builtin]; ok {
|
||||||
for _, permission := range basicRole.Permissions {
|
for _, permission := range basicRole.Permissions {
|
||||||
if PermissionMatchesSearchOptions(permission, searchOptions) {
|
if PermissionMatchesSearchOptions(permission, &searchOptions) {
|
||||||
permissions = append(permissions, permission)
|
permissions = append(permissions, permission)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,7 +385,7 @@ func (s *Service) searchUserPermissionsFromCache(orgID int64, searchOptions acce
|
|||||||
s.log.Debug("Using cached permissions", "key", key)
|
s.log.Debug("Using cached permissions", "key", key)
|
||||||
filteredPermissions := make([]accesscontrol.Permission, 0)
|
filteredPermissions := make([]accesscontrol.Permission, 0)
|
||||||
for _, permission := range permissions.([]accesscontrol.Permission) {
|
for _, permission := range permissions.([]accesscontrol.Permission) {
|
||||||
if PermissionMatchesSearchOptions(permission, searchOptions) {
|
if PermissionMatchesSearchOptions(permission, &searchOptions) {
|
||||||
filteredPermissions = append(filteredPermissions, permission)
|
filteredPermissions = append(filteredPermissions, permission)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,9 +393,13 @@ func (s *Service) searchUserPermissionsFromCache(orgID int64, searchOptions acce
|
|||||||
return filteredPermissions, true
|
return filteredPermissions, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func PermissionMatchesSearchOptions(permission accesscontrol.Permission, searchOptions accesscontrol.SearchOptions) bool {
|
func PermissionMatchesSearchOptions(permission accesscontrol.Permission, searchOptions *accesscontrol.SearchOptions) bool {
|
||||||
if searchOptions.Scope != "" && permission.Scope != searchOptions.Scope {
|
if searchOptions.Scope != "" {
|
||||||
return false
|
// 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 != "" {
|
if searchOptions.Action != "" {
|
||||||
return permission.Action == searchOptions.Action
|
return permission.Action == searchOptions.Action
|
||||||
|
|||||||
@@ -448,6 +448,23 @@ func TestService_SearchUsersPermissions(t *testing.T) {
|
|||||||
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
{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",
|
name: "view permission on subset of users only",
|
||||||
siuPermissions: listSomePerms,
|
siuPermissions: listSomePerms,
|
||||||
|
|||||||
@@ -106,8 +106,12 @@ func (s *AccessControlStore) SearchUsersPermissions(ctx context.Context, orgID i
|
|||||||
params = append(params, options.Action)
|
params = append(params, options.Action)
|
||||||
}
|
}
|
||||||
if options.Scope != "" {
|
if options.Scope != "" {
|
||||||
q += ` AND scope = ?`
|
// Search for scope and wildcard that include the scope
|
||||||
params = append(params, options.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 {
|
if options.UserID != 0 {
|
||||||
|
|||||||
@@ -526,6 +526,20 @@ func TestIntegrationAccessControlStore_SearchUsersPermissions(t *testing.T) {
|
|||||||
{Action: "teams:write", Scope: "teams:id:1"},
|
{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",
|
name: "user assignment by action and scope",
|
||||||
users: []testUser{{orgRole: org.RoleAdmin, isAdmin: false}},
|
users: []testUser{{orgRole: org.RoleAdmin, isAdmin: false}},
|
||||||
|
|||||||
Reference in New Issue
Block a user