Access control: Display inherited folder permissions in dashboards (#46421)

This commit is contained in:
Karl Persson
2022-03-17 17:08:51 +01:00
committed by GitHub
parent fb17b9f545
commit 4df7bf5ab2
13 changed files with 142 additions and 129 deletions
@@ -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)
})