Access control: Display inherited folder permissions in dashboards (#46421)
This commit is contained in:
@@ -13,24 +13,30 @@ import (
|
||||
)
|
||||
|
||||
type flatResourcePermission struct {
|
||||
ID int64 `xorm:"id"`
|
||||
ResourceID string `xorm:"resource_id"`
|
||||
RoleName string
|
||||
Action string
|
||||
Scope string
|
||||
UserId int64
|
||||
UserLogin string
|
||||
UserEmail string
|
||||
TeamId int64
|
||||
TeamEmail string
|
||||
Team string
|
||||
BuiltInRole string
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
ID int64 `xorm:"id"`
|
||||
RoleName string
|
||||
Action string
|
||||
// Scope is what is stored in the database
|
||||
Scope string
|
||||
// ResourceScope is what we ask for
|
||||
ResourceScope string
|
||||
UserId int64
|
||||
UserLogin string
|
||||
UserEmail string
|
||||
TeamId int64
|
||||
TeamEmail string
|
||||
Team string
|
||||
BuiltInRole string
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
func (p *flatResourcePermission) Managed() bool {
|
||||
return strings.HasPrefix(p.RoleName, "managed:")
|
||||
func (p *flatResourcePermission) IsManaged() bool {
|
||||
return strings.HasPrefix(p.RoleName, "managed:") && !p.IsInherited()
|
||||
}
|
||||
|
||||
func (p *flatResourcePermission) IsInherited() bool {
|
||||
return p.Scope != p.ResourceScope
|
||||
}
|
||||
|
||||
func (s *AccessControlStore) SetUserResourcePermission(
|
||||
@@ -236,7 +242,7 @@ func (s *AccessControlStore) setResourcePermission(
|
||||
keep = append(keep, id)
|
||||
}
|
||||
|
||||
permissions, err := s.getResourcePermissions(sess, cmd.ResourceID, keep)
|
||||
permissions, err := s.getResourcePermissionsByIds(sess, cmd.Resource, cmd.ResourceID, keep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -249,12 +255,12 @@ func (s *AccessControlStore) setResourcePermission(
|
||||
return permission, nil
|
||||
}
|
||||
|
||||
func (s *AccessControlStore) GetResourcesPermissions(ctx context.Context, orgID int64, query types.GetResourcesPermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
|
||||
func (s *AccessControlStore) GetResourcePermissions(ctx context.Context, orgID int64, query types.GetResourcePermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
|
||||
var result []accesscontrol.ResourcePermission
|
||||
|
||||
err := s.sql.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
var err error
|
||||
result, err = s.getResourcesPermissions(sess, orgID, query)
|
||||
result, err = s.getResourcePermissions(sess, orgID, query)
|
||||
return err
|
||||
})
|
||||
|
||||
@@ -273,19 +279,16 @@ func (s *AccessControlStore) createResourcePermission(sess *sqlstore.DBSession,
|
||||
return permission.ID, nil
|
||||
}
|
||||
|
||||
func (s *AccessControlStore) getResourcesPermissions(sess *sqlstore.DBSession, orgID int64, query types.GetResourcesPermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
|
||||
func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, orgID int64, query types.GetResourcePermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
|
||||
if len(query.Actions) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if len(query.ResourceIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rawSelect := `
|
||||
SELECT
|
||||
p.*,
|
||||
r.name as role_name,
|
||||
? as resource_scope,
|
||||
`
|
||||
|
||||
userSelect := rawSelect + `
|
||||
@@ -334,25 +337,31 @@ func (s *AccessControlStore) getResourcesPermissions(sess *sqlstore.DBSession, o
|
||||
builtinFrom := rawFrom + `
|
||||
INNER JOIN builtin_role br ON r.id = br.role_id AND (br.org_id = 0 OR br.org_id = ?)
|
||||
`
|
||||
where := `
|
||||
WHERE (r.org_id = ? OR r.org_id = 0)
|
||||
AND (p.scope = '*' OR p.scope = ? OR p.scope = ? OR p.scope IN (?` + strings.Repeat(",?", len(query.ResourceIDs)-1) + `))
|
||||
AND p.action IN (?` + strings.Repeat(",?", len(query.Actions)-1) + `)
|
||||
`
|
||||
|
||||
if query.OnlyManaged {
|
||||
where += `AND r.name LIKE 'managed:%'`
|
||||
}
|
||||
where := `WHERE (r.org_id = ? OR r.org_id = 0) AND (p.scope = '*' OR p.scope = ? OR p.scope = ? OR p.scope = ?`
|
||||
|
||||
scope := accesscontrol.GetResourceScope(query.Resource, query.ResourceID)
|
||||
|
||||
args := []interface{}{
|
||||
scope,
|
||||
orgID,
|
||||
orgID,
|
||||
accesscontrol.GetResourceAllScope(query.Resource),
|
||||
accesscontrol.GetResourceAllIDScope(query.Resource),
|
||||
scope,
|
||||
}
|
||||
|
||||
for _, id := range query.ResourceIDs {
|
||||
args = append(args, accesscontrol.GetResourceScope(query.Resource, id))
|
||||
if len(query.InheritedScopes) > 0 {
|
||||
where += ` OR p.scope IN(?` + strings.Repeat(",?", len(query.InheritedScopes)-1) + `)`
|
||||
for _, scope := range query.InheritedScopes {
|
||||
args = append(args, scope)
|
||||
}
|
||||
}
|
||||
|
||||
where += `) AND p.action IN (?` + strings.Repeat(",?", len(query.Actions)-1) + `)`
|
||||
|
||||
if query.OnlyManaged {
|
||||
where += `AND r.name LIKE 'managed:%'`
|
||||
}
|
||||
|
||||
for _, a := range query.Actions {
|
||||
@@ -386,33 +395,16 @@ func (s *AccessControlStore) getResourcesPermissions(sess *sqlstore.DBSession, o
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scopeAll := accesscontrol.GetResourceAllScope(query.Resource)
|
||||
scopeAllIDs := accesscontrol.GetResourceAllIDScope(query.Resource)
|
||||
|
||||
byResource := make(map[string][]flatResourcePermission)
|
||||
// Add resourceIds and generate permissions for `*`, `resource:*` and `resource:id:*`
|
||||
for _, id := range query.ResourceIDs {
|
||||
scope := accesscontrol.GetResourceScope(query.Resource, id)
|
||||
for _, p := range queryResults {
|
||||
if p.Scope == scope || p.Scope == scopeAll || p.Scope == scopeAllIDs || p.Scope == "*" {
|
||||
p.ResourceID = id
|
||||
byResource[p.ResourceID] = append(byResource[p.ResourceID], p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result []accesscontrol.ResourcePermission
|
||||
for _, permissions := range byResource {
|
||||
users, teams, builtins := groupPermissionsByAssignment(permissions)
|
||||
for _, p := range users {
|
||||
result = append(result, flatPermissionsToResourcePermissions(p)...)
|
||||
}
|
||||
for _, p := range teams {
|
||||
result = append(result, flatPermissionsToResourcePermissions(p)...)
|
||||
}
|
||||
for _, p := range builtins {
|
||||
result = append(result, flatPermissionsToResourcePermissions(p)...)
|
||||
}
|
||||
users, teams, builtins := groupPermissionsByAssignment(queryResults)
|
||||
for _, p := range users {
|
||||
result = append(result, flatPermissionsToResourcePermissions(p)...)
|
||||
}
|
||||
for _, p := range teams {
|
||||
result = append(result, flatPermissionsToResourcePermissions(p)...)
|
||||
}
|
||||
for _, p := range builtins {
|
||||
result = append(result, flatPermissionsToResourcePermissions(p)...)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -439,7 +431,7 @@ func groupPermissionsByAssignment(permissions []flatResourcePermission) (map[int
|
||||
func flatPermissionsToResourcePermissions(permissions []flatResourcePermission) []accesscontrol.ResourcePermission {
|
||||
var managed, provisioned []flatResourcePermission
|
||||
for _, p := range permissions {
|
||||
if p.Managed() {
|
||||
if p.IsManaged() {
|
||||
managed = append(managed, p)
|
||||
} else {
|
||||
provisioned = append(provisioned, p)
|
||||
@@ -470,7 +462,6 @@ func flatPermissionsToResourcePermission(permissions []flatResourcePermission) *
|
||||
first := permissions[0]
|
||||
return &accesscontrol.ResourcePermission{
|
||||
ID: first.ID,
|
||||
ResourceID: first.ResourceID,
|
||||
RoleName: first.RoleName,
|
||||
Actions: actions,
|
||||
Scope: first.Scope,
|
||||
@@ -483,6 +474,7 @@ func flatPermissionsToResourcePermission(permissions []flatResourcePermission) *
|
||||
BuiltInRole: first.BuiltInRole,
|
||||
Created: first.Created,
|
||||
Updated: first.Updated,
|
||||
IsManaged: first.IsManaged(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,7 +574,7 @@ func (s *AccessControlStore) getOrCreateManagedRole(sess *sqlstore.DBSession, or
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, resourceID string, ids []int64) ([]flatResourcePermission, error) {
|
||||
func (s *AccessControlStore) getResourcePermissionsByIds(sess *sqlstore.DBSession, resource, resourceID string, ids []int64) ([]flatResourcePermission, error) {
|
||||
var result []flatResourcePermission
|
||||
if len(ids) == 0 {
|
||||
return result, nil
|
||||
@@ -590,7 +582,7 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, re
|
||||
rawSql := `
|
||||
SELECT
|
||||
p.*,
|
||||
? AS resource_id,
|
||||
? as resource_scope,
|
||||
ur.user_id AS user_id,
|
||||
u.login AS user_login,
|
||||
u.email AS user_email,
|
||||
@@ -610,7 +602,7 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, re
|
||||
`
|
||||
|
||||
args := make([]interface{}, 0, len(ids)+1)
|
||||
args = append(args, resourceID)
|
||||
args = append(args, accesscontrol.GetResourceScope(resource, resourceID))
|
||||
for _, id := range ids {
|
||||
args = append(args, id)
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ func benchmarkDSPermissions(b *testing.B, dsNum, usersNum int) {
|
||||
func getDSPermissions(b *testing.B, store *AccessControlStore, dataSources []int64) {
|
||||
dsId := dataSources[0]
|
||||
|
||||
permissions, err := store.GetResourcesPermissions(context.Background(), accesscontrol.GlobalOrgID, types.GetResourcesPermissionsQuery{
|
||||
Actions: []string{dsAction},
|
||||
Resource: dsResource,
|
||||
ResourceIDs: []string{strconv.Itoa(int(dsId))},
|
||||
permissions, err := store.GetResourcePermissions(context.Background(), accesscontrol.GlobalOrgID, types.GetResourcePermissionsQuery{
|
||||
Actions: []string{dsAction},
|
||||
Resource: dsResource,
|
||||
ResourceID: strconv.Itoa(int(dsId)),
|
||||
})
|
||||
require.NoError(b, err)
|
||||
assert.GreaterOrEqual(b, len(permissions), 2)
|
||||
|
||||
@@ -311,29 +311,29 @@ func TestAccessControlStore_SetResourcePermissions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type getResourcesPermissionsTest struct {
|
||||
type getResourcePermissionsTest struct {
|
||||
desc string
|
||||
user *models.SignedInUser
|
||||
numUsers int
|
||||
actions []string
|
||||
resource string
|
||||
resourceIDs []string
|
||||
resourceID string
|
||||
onlyManaged bool
|
||||
}
|
||||
|
||||
func TestAccessControlStore_GetResourcesPermissions(t *testing.T) {
|
||||
tests := []getResourcesPermissionsTest{
|
||||
func TestAccessControlStore_GetResourcePermissions(t *testing.T) {
|
||||
tests := []getResourcePermissionsTest{
|
||||
{
|
||||
desc: "should return permissions for all resource ids",
|
||||
desc: "should return permissions for resource id",
|
||||
user: &models.SignedInUser{
|
||||
OrgId: 1,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
1: {accesscontrol.ActionOrgUsersRead: {accesscontrol.ScopeUsersAll}},
|
||||
}},
|
||||
numUsers: 3,
|
||||
actions: []string{"datasources:query"},
|
||||
resource: "datasources",
|
||||
resourceIDs: []string{"1", "2"},
|
||||
numUsers: 3,
|
||||
actions: []string{"datasources:query"},
|
||||
resource: "datasources",
|
||||
resourceID: "1",
|
||||
},
|
||||
{
|
||||
desc: "should return manage permissions for all resource ids",
|
||||
@@ -345,7 +345,7 @@ func TestAccessControlStore_GetResourcesPermissions(t *testing.T) {
|
||||
numUsers: 3,
|
||||
actions: []string{"datasources:query"},
|
||||
resource: "datasources",
|
||||
resourceIDs: []string{"1", "2"},
|
||||
resourceID: "1",
|
||||
onlyManaged: true,
|
||||
},
|
||||
}
|
||||
@@ -389,22 +389,20 @@ func TestAccessControlStore_GetResourcesPermissions(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, id := range test.resourceIDs {
|
||||
seedResourcePermissions(t, store, sql, test.actions, test.resource, id, test.numUsers)
|
||||
}
|
||||
seedResourcePermissions(t, store, sql, test.actions, test.resource, test.resourceID, test.numUsers)
|
||||
|
||||
permissions, err := store.GetResourcesPermissions(context.Background(), test.user.OrgId, types.GetResourcesPermissionsQuery{
|
||||
permissions, err := store.GetResourcePermissions(context.Background(), test.user.OrgId, types.GetResourcePermissionsQuery{
|
||||
User: test.user,
|
||||
Actions: test.actions,
|
||||
Resource: test.resource,
|
||||
ResourceIDs: test.resourceIDs,
|
||||
ResourceID: test.resourceID,
|
||||
OnlyManaged: test.onlyManaged,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedLen := test.numUsers * len(test.resourceIDs)
|
||||
expectedLen := test.numUsers
|
||||
if !test.onlyManaged {
|
||||
expectedLen += len(test.resourceIDs)
|
||||
expectedLen += 1
|
||||
}
|
||||
assert.Len(t, permissions, expectedLen)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user