mirror of
https://github.com/grafana/grafana.git
synced 2026-02-20 00:11:35 -05:00
623 lines
18 KiB
Go
623 lines
18 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/seeding"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
|
"github.com/grafana/grafana/pkg/util/xorm/core"
|
|
)
|
|
|
|
const basicRolePermBatchSize = 500
|
|
|
|
// LoadRoles returns all fixed and plugin roles (global org) with permissions, indexed by role name.
|
|
func (s *AccessControlStore) LoadRoles(ctx context.Context) (map[string]*accesscontrol.RoleDTO, error) {
|
|
out := map[string]*accesscontrol.RoleDTO{}
|
|
|
|
err := s.sql.WithDbSession(ctx, func(sess *db.Session) error {
|
|
type roleRow struct {
|
|
ID int64 `xorm:"id"`
|
|
OrgID int64 `xorm:"org_id"`
|
|
Version int64 `xorm:"version"`
|
|
UID string `xorm:"uid"`
|
|
Name string `xorm:"name"`
|
|
DisplayName string `xorm:"display_name"`
|
|
Description string `xorm:"description"`
|
|
Group string `xorm:"group_name"`
|
|
Hidden bool `xorm:"hidden"`
|
|
Updated time.Time `xorm:"updated"`
|
|
Created time.Time `xorm:"created"`
|
|
}
|
|
|
|
roles := []roleRow{}
|
|
if err := sess.Table("role").
|
|
Where("org_id = ?", accesscontrol.GlobalOrgID).
|
|
Where("(name LIKE ? OR name LIKE ?)", accesscontrol.FixedRolePrefix+"%", accesscontrol.PluginRolePrefix+"%").
|
|
Find(&roles); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(roles) == 0 {
|
|
return nil
|
|
}
|
|
|
|
roleIDs := make([]any, 0, len(roles))
|
|
roleByID := make(map[int64]*accesscontrol.RoleDTO, len(roles))
|
|
for _, r := range roles {
|
|
dto := &accesscontrol.RoleDTO{
|
|
ID: r.ID,
|
|
OrgID: r.OrgID,
|
|
Version: r.Version,
|
|
UID: r.UID,
|
|
Name: r.Name,
|
|
DisplayName: r.DisplayName,
|
|
Description: r.Description,
|
|
Group: r.Group,
|
|
Hidden: r.Hidden,
|
|
Updated: r.Updated,
|
|
Created: r.Created,
|
|
}
|
|
out[dto.Name] = dto
|
|
roleByID[dto.ID] = dto
|
|
roleIDs = append(roleIDs, dto.ID)
|
|
}
|
|
|
|
type permRow struct {
|
|
RoleID int64 `xorm:"role_id"`
|
|
Action string `xorm:"action"`
|
|
Scope string `xorm:"scope"`
|
|
}
|
|
perms := []permRow{}
|
|
if err := sess.Table("permission").In("role_id", roleIDs...).Find(&perms); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, p := range perms {
|
|
dto := roleByID[p.RoleID]
|
|
if dto == nil {
|
|
continue
|
|
}
|
|
dto.Permissions = append(dto.Permissions, accesscontrol.Permission{
|
|
RoleID: p.RoleID,
|
|
Action: p.Action,
|
|
Scope: p.Scope,
|
|
})
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return out, err
|
|
}
|
|
|
|
func (s *AccessControlStore) SetRole(ctx context.Context, existingRole *accesscontrol.RoleDTO, wantedRole accesscontrol.RoleDTO) error {
|
|
if existingRole == nil {
|
|
return nil
|
|
}
|
|
|
|
return s.sql.WithDbSession(ctx, func(sess *db.Session) error {
|
|
_, err := sess.Table("role").
|
|
Where("id = ? AND org_id = ?", existingRole.ID, accesscontrol.GlobalOrgID).
|
|
Update(map[string]any{
|
|
"display_name": wantedRole.DisplayName,
|
|
"description": wantedRole.Description,
|
|
"group_name": wantedRole.Group,
|
|
"hidden": wantedRole.Hidden,
|
|
"updated": time.Now(),
|
|
})
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (s *AccessControlStore) SetPermissions(ctx context.Context, existingRole *accesscontrol.RoleDTO, wantedRole accesscontrol.RoleDTO) error {
|
|
if existingRole == nil {
|
|
return nil
|
|
}
|
|
|
|
type key struct{ Action, Scope string }
|
|
existing := map[key]struct{}{}
|
|
for _, p := range existingRole.Permissions {
|
|
existing[key{p.Action, p.Scope}] = struct{}{}
|
|
}
|
|
desired := map[key]struct{}{}
|
|
for _, p := range wantedRole.Permissions {
|
|
desired[key{p.Action, p.Scope}] = struct{}{}
|
|
}
|
|
|
|
toAdd := make([]accesscontrol.Permission, 0)
|
|
toRemove := make([]accesscontrol.SeedPermission, 0)
|
|
|
|
now := time.Now()
|
|
for k := range desired {
|
|
if _, ok := existing[k]; ok {
|
|
continue
|
|
}
|
|
perm := accesscontrol.Permission{
|
|
RoleID: existingRole.ID,
|
|
Action: k.Action,
|
|
Scope: k.Scope,
|
|
Created: now,
|
|
Updated: now,
|
|
}
|
|
perm.Kind, perm.Attribute, perm.Identifier = accesscontrol.SplitScope(perm.Scope)
|
|
toAdd = append(toAdd, perm)
|
|
}
|
|
|
|
for k := range existing {
|
|
if _, ok := desired[k]; ok {
|
|
continue
|
|
}
|
|
toRemove = append(toRemove, accesscontrol.SeedPermission{Action: k.Action, Scope: k.Scope})
|
|
}
|
|
|
|
if len(toAdd) == 0 && len(toRemove) == 0 {
|
|
return nil
|
|
}
|
|
|
|
return s.sql.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
if len(toRemove) > 0 {
|
|
if err := DeleteRolePermissionTuples(sess, s.sql.GetDBType(), existingRole.ID, toRemove); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(toAdd) > 0 {
|
|
_, err := sess.InsertMulti(toAdd)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (s *AccessControlStore) CreateRole(ctx context.Context, role accesscontrol.RoleDTO) error {
|
|
now := time.Now()
|
|
uid := role.UID
|
|
if uid == "" && (strings.HasPrefix(role.Name, accesscontrol.FixedRolePrefix) || strings.HasPrefix(role.Name, accesscontrol.PluginRolePrefix)) {
|
|
uid = accesscontrol.PrefixedRoleUID(role.Name)
|
|
}
|
|
r := accesscontrol.Role{
|
|
OrgID: accesscontrol.GlobalOrgID,
|
|
Version: role.Version,
|
|
UID: uid,
|
|
Name: role.Name,
|
|
DisplayName: role.DisplayName,
|
|
Description: role.Description,
|
|
Group: role.Group,
|
|
Hidden: role.Hidden,
|
|
Created: now,
|
|
Updated: now,
|
|
}
|
|
if r.Version == 0 {
|
|
r.Version = 1
|
|
}
|
|
|
|
return s.sql.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
if _, err := sess.Insert(&r); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(role.Permissions) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// De-duplicate permissions on (action, scope) to avoid unique constraint violations.
|
|
// Some role definitions may accidentally include duplicates.
|
|
type permKey struct{ Action, Scope string }
|
|
seen := make(map[permKey]struct{}, len(role.Permissions))
|
|
|
|
perms := make([]accesscontrol.Permission, 0, len(role.Permissions))
|
|
for _, p := range role.Permissions {
|
|
k := permKey{Action: p.Action, Scope: p.Scope}
|
|
if _, ok := seen[k]; ok {
|
|
continue
|
|
}
|
|
seen[k] = struct{}{}
|
|
|
|
perm := accesscontrol.Permission{
|
|
RoleID: r.ID,
|
|
Action: p.Action,
|
|
Scope: p.Scope,
|
|
Created: now,
|
|
Updated: now,
|
|
}
|
|
perm.Kind, perm.Attribute, perm.Identifier = accesscontrol.SplitScope(perm.Scope)
|
|
perms = append(perms, perm)
|
|
}
|
|
_, err := sess.InsertMulti(perms)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (s *AccessControlStore) DeleteRoles(ctx context.Context, roleUIDs []string) error {
|
|
if len(roleUIDs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
uids := make([]any, 0, len(roleUIDs))
|
|
for _, uid := range roleUIDs {
|
|
uids = append(uids, uid)
|
|
}
|
|
|
|
return s.sql.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
type row struct {
|
|
ID int64 `xorm:"id"`
|
|
UID string `xorm:"uid"`
|
|
}
|
|
rows := []row{}
|
|
if err := sess.Table("role").
|
|
Where("org_id = ?", accesscontrol.GlobalOrgID).
|
|
In("uid", uids...).
|
|
Find(&rows); err != nil {
|
|
return err
|
|
}
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
|
|
roleIDs := make([]any, 0, len(rows))
|
|
for _, r := range rows {
|
|
roleIDs = append(roleIDs, r.ID)
|
|
}
|
|
|
|
// Remove permissions and assignments first to avoid FK issues (if enabled).
|
|
{
|
|
args := append([]any{"DELETE FROM permission WHERE role_id IN (?" + strings.Repeat(",?", len(roleIDs)-1) + ")"}, roleIDs...)
|
|
if _, err := sess.Exec(args...); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
{
|
|
args := append([]any{"DELETE FROM user_role WHERE role_id IN (?" + strings.Repeat(",?", len(roleIDs)-1) + ")"}, roleIDs...)
|
|
if _, err := sess.Exec(args...); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
{
|
|
args := append([]any{"DELETE FROM team_role WHERE role_id IN (?" + strings.Repeat(",?", len(roleIDs)-1) + ")"}, roleIDs...)
|
|
if _, err := sess.Exec(args...); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
{
|
|
args := append([]any{"DELETE FROM builtin_role WHERE role_id IN (?" + strings.Repeat(",?", len(roleIDs)-1) + ")"}, roleIDs...)
|
|
if _, err := sess.Exec(args...); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
args := append([]any{"DELETE FROM role WHERE org_id = ? AND uid IN (?" + strings.Repeat(",?", len(uids)-1) + ")", accesscontrol.GlobalOrgID}, uids...)
|
|
_, err := sess.Exec(args...)
|
|
return err
|
|
})
|
|
}
|
|
|
|
// OSS basic-role permission refresh uses seeding.Seeder.Seed() with a desired set computed in memory.
|
|
// These methods implement the permission seeding part of seeding.SeedingBackend against the current permission table.
|
|
func (s *AccessControlStore) LoadPrevious(ctx context.Context) (map[accesscontrol.SeedPermission]struct{}, error) {
|
|
var out map[accesscontrol.SeedPermission]struct{}
|
|
err := s.sql.WithDbSession(ctx, func(sess *db.Session) error {
|
|
rows, err := LoadBasicRoleSeedPermissions(sess)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
out = make(map[accesscontrol.SeedPermission]struct{}, len(rows))
|
|
for _, r := range rows {
|
|
r.Origin = ""
|
|
out[r] = struct{}{}
|
|
}
|
|
return nil
|
|
})
|
|
return out, err
|
|
}
|
|
|
|
func (s *AccessControlStore) Apply(ctx context.Context, added, removed []accesscontrol.SeedPermission, updated map[accesscontrol.SeedPermission]accesscontrol.SeedPermission) error {
|
|
rolesToUpgrade := seeding.RolesToUpgrade(added, removed)
|
|
|
|
// Run the same OSS apply logic as ossBasicRoleSeedBackend.Apply inside a single transaction.
|
|
return s.sql.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
defs := accesscontrol.BuildBasicRoleDefinitions()
|
|
builtinToRoleID, err := EnsureBasicRolesExist(sess, defs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
backend := &ossBasicRoleSeedBackend{
|
|
sess: sess,
|
|
now: time.Now(),
|
|
builtinToRoleID: builtinToRoleID,
|
|
desired: nil,
|
|
dbType: s.sql.GetDBType(),
|
|
}
|
|
if err := backend.Apply(ctx, added, removed, updated); err != nil {
|
|
return err
|
|
}
|
|
|
|
return BumpBasicRoleVersions(sess, rolesToUpgrade)
|
|
})
|
|
}
|
|
|
|
// EnsureBasicRolesExist ensures the built-in basic roles exist in the role table and are bound in builtin_role.
|
|
// It returns a mapping from builtin role name (for example "Admin") to role ID.
|
|
func EnsureBasicRolesExist(sess *db.Session, defs map[string]*accesscontrol.RoleDTO) (map[string]int64, error) {
|
|
uidToBuiltin := make(map[string]string, len(defs))
|
|
uids := make([]any, 0, len(defs))
|
|
for builtin, def := range defs {
|
|
uidToBuiltin[def.UID] = builtin
|
|
uids = append(uids, def.UID)
|
|
}
|
|
|
|
type roleRow struct {
|
|
ID int64 `xorm:"id"`
|
|
UID string `xorm:"uid"`
|
|
}
|
|
|
|
rows := []roleRow{}
|
|
if err := sess.Table("role").
|
|
Where("org_id = ?", accesscontrol.GlobalOrgID).
|
|
In("uid", uids...).
|
|
Find(&rows); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ts := time.Now()
|
|
|
|
builtinToRoleID := make(map[string]int64, len(defs))
|
|
for _, r := range rows {
|
|
br, ok := uidToBuiltin[r.UID]
|
|
if !ok {
|
|
continue
|
|
}
|
|
builtinToRoleID[br] = r.ID
|
|
}
|
|
|
|
for builtin, def := range defs {
|
|
roleID, ok := builtinToRoleID[builtin]
|
|
if !ok {
|
|
role := accesscontrol.Role{
|
|
OrgID: def.OrgID,
|
|
Version: def.Version,
|
|
UID: def.UID,
|
|
Name: def.Name,
|
|
DisplayName: def.DisplayName,
|
|
Description: def.Description,
|
|
Group: def.Group,
|
|
Hidden: def.Hidden,
|
|
Created: ts,
|
|
Updated: ts,
|
|
}
|
|
if _, err := sess.Insert(&role); err != nil {
|
|
return nil, err
|
|
}
|
|
roleID = role.ID
|
|
builtinToRoleID[builtin] = roleID
|
|
}
|
|
|
|
has, err := sess.Table("builtin_role").
|
|
Where("role_id = ? AND role = ? AND org_id = ?", roleID, builtin, accesscontrol.GlobalOrgID).
|
|
Exist()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !has {
|
|
br := accesscontrol.BuiltinRole{
|
|
RoleID: roleID,
|
|
OrgID: accesscontrol.GlobalOrgID,
|
|
Role: builtin,
|
|
Created: ts,
|
|
Updated: ts,
|
|
}
|
|
if _, err := sess.Table("builtin_role").Insert(&br); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
return builtinToRoleID, nil
|
|
}
|
|
|
|
// DeleteRolePermissionTuples deletes permissions for a single role by (action, scope) pairs.
|
|
//
|
|
// It uses a row-constructor IN clause where supported (MySQL, Postgres, SQLite) and falls back
|
|
// to a WHERE ... OR ... form for MSSQL.
|
|
func DeleteRolePermissionTuples(sess *db.Session, dbType core.DbType, roleID int64, perms []accesscontrol.SeedPermission) error {
|
|
if len(perms) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if dbType == migrator.MSSQL {
|
|
// MSSQL doesn't support (action, scope) IN ((?,?),(?,?)) row constructors.
|
|
where := make([]string, 0, len(perms))
|
|
args := make([]any, 0, 1+len(perms)*2)
|
|
args = append(args, roleID)
|
|
for _, p := range perms {
|
|
where = append(where, "(action = ? AND scope = ?)")
|
|
args = append(args, p.Action, p.Scope)
|
|
}
|
|
_, err := sess.Exec(
|
|
append([]any{
|
|
"DELETE FROM permission WHERE role_id = ? AND (" + strings.Join(where, " OR ") + ")",
|
|
}, args...)...,
|
|
)
|
|
return err
|
|
}
|
|
|
|
args := make([]any, 0, 1+len(perms)*2)
|
|
args = append(args, roleID)
|
|
for _, p := range perms {
|
|
args = append(args, p.Action, p.Scope)
|
|
}
|
|
sql := "DELETE FROM permission WHERE role_id = ? AND (action, scope) IN (" +
|
|
strings.Repeat("(?, ?),", len(perms)-1) + "(?, ?))"
|
|
_, err := sess.Exec(append([]any{sql}, args...)...)
|
|
return err
|
|
}
|
|
|
|
type ossBasicRoleSeedBackend struct {
|
|
sess *db.Session
|
|
now time.Time
|
|
builtinToRoleID map[string]int64
|
|
desired map[accesscontrol.SeedPermission]struct{}
|
|
dbType core.DbType
|
|
}
|
|
|
|
func (b *ossBasicRoleSeedBackend) LoadPrevious(_ context.Context) (map[accesscontrol.SeedPermission]struct{}, error) {
|
|
rows, err := LoadBasicRoleSeedPermissions(b.sess)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out := make(map[accesscontrol.SeedPermission]struct{}, len(rows))
|
|
for _, r := range rows {
|
|
// Ensure the key matches what OSS seeding uses (Origin is always empty for basic role refresh).
|
|
r.Origin = ""
|
|
out[r] = struct{}{}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (b *ossBasicRoleSeedBackend) LoadDesired(_ context.Context) (map[accesscontrol.SeedPermission]struct{}, error) {
|
|
return b.desired, nil
|
|
}
|
|
|
|
func (b *ossBasicRoleSeedBackend) Apply(_ context.Context, added, removed []accesscontrol.SeedPermission, updated map[accesscontrol.SeedPermission]accesscontrol.SeedPermission) error {
|
|
// Delete removed permissions (this includes user-defined permissions that aren't in desired).
|
|
if len(removed) > 0 {
|
|
permsByRoleID := map[int64][]accesscontrol.SeedPermission{}
|
|
for _, p := range removed {
|
|
roleID, ok := b.builtinToRoleID[p.BuiltInRole]
|
|
if !ok {
|
|
continue
|
|
}
|
|
permsByRoleID[roleID] = append(permsByRoleID[roleID], p)
|
|
}
|
|
|
|
for roleID, perms := range permsByRoleID {
|
|
// Chunk to keep statement sizes and parameter counts bounded.
|
|
if err := batch(len(perms), basicRolePermBatchSize, func(start, end int) error {
|
|
return DeleteRolePermissionTuples(b.sess, b.dbType, roleID, perms[start:end])
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// Insert added permissions and updated-target permissions.
|
|
toInsertSeed := make([]accesscontrol.SeedPermission, 0, len(added)+len(updated))
|
|
toInsertSeed = append(toInsertSeed, added...)
|
|
for _, v := range updated {
|
|
toInsertSeed = append(toInsertSeed, v)
|
|
}
|
|
if len(toInsertSeed) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// De-duplicate on (role_id, action, scope). This avoids unique constraint violations when:
|
|
// - the same permission appears in both added and updated
|
|
// - multiple plugin origins grant the same permission (Origin is not persisted in permission table)
|
|
type permKey struct {
|
|
RoleID int64
|
|
Action string
|
|
Scope string
|
|
}
|
|
seen := make(map[permKey]struct{}, len(toInsertSeed))
|
|
|
|
toInsert := make([]accesscontrol.Permission, 0, len(toInsertSeed))
|
|
for _, p := range toInsertSeed {
|
|
roleID, ok := b.builtinToRoleID[p.BuiltInRole]
|
|
if !ok {
|
|
continue
|
|
}
|
|
k := permKey{RoleID: roleID, Action: p.Action, Scope: p.Scope}
|
|
if _, ok := seen[k]; ok {
|
|
continue
|
|
}
|
|
seen[k] = struct{}{}
|
|
|
|
perm := accesscontrol.Permission{
|
|
RoleID: roleID,
|
|
Action: p.Action,
|
|
Scope: p.Scope,
|
|
Created: b.now,
|
|
Updated: b.now,
|
|
}
|
|
perm.Kind, perm.Attribute, perm.Identifier = accesscontrol.SplitScope(perm.Scope)
|
|
toInsert = append(toInsert, perm)
|
|
}
|
|
|
|
return batch(len(toInsert), basicRolePermBatchSize, func(start, end int) error {
|
|
// MySQL: ignore conflicts to make seeding idempotent under retries/concurrency.
|
|
// Conflicts can happen if the same permission already exists (unique on role_id, action, scope).
|
|
if b.dbType == migrator.MySQL {
|
|
args := make([]any, 0, (end-start)*8)
|
|
for i := start; i < end; i++ {
|
|
p := toInsert[i]
|
|
args = append(args, p.RoleID, p.Action, p.Scope, p.Kind, p.Attribute, p.Identifier, p.Updated, p.Created)
|
|
}
|
|
sql := append([]any{`INSERT IGNORE INTO permission (role_id, action, scope, kind, attribute, identifier, updated, created) VALUES ` +
|
|
strings.Repeat("(?, ?, ?, ?, ?, ?, ?, ?),", end-start-1) + "(?, ?, ?, ?, ?, ?, ?, ?)"}, args...)
|
|
_, err := b.sess.Exec(sql...)
|
|
return err
|
|
}
|
|
|
|
_, err := b.sess.InsertMulti(toInsert[start:end])
|
|
return err
|
|
})
|
|
}
|
|
|
|
func batch(count, size int, eachFn func(start, end int) error) error {
|
|
for i := 0; i < count; {
|
|
end := i + size
|
|
if end > count {
|
|
end = count
|
|
}
|
|
if err := eachFn(i, end); err != nil {
|
|
return err
|
|
}
|
|
i = end
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// BumpBasicRoleVersions increments the role version for the given builtin basic roles (Viewer/Editor/Admin/Grafana Admin).
|
|
// Unknown role names are ignored.
|
|
func BumpBasicRoleVersions(sess *db.Session, basicRoles []string) error {
|
|
if len(basicRoles) == 0 {
|
|
return nil
|
|
}
|
|
|
|
defs := accesscontrol.BuildBasicRoleDefinitions()
|
|
uids := make([]any, 0, len(basicRoles))
|
|
for _, br := range basicRoles {
|
|
def, ok := defs[br]
|
|
if !ok {
|
|
continue
|
|
}
|
|
uids = append(uids, def.UID)
|
|
}
|
|
if len(uids) == 0 {
|
|
return nil
|
|
}
|
|
|
|
sql := "UPDATE role SET version = version + 1 WHERE org_id = ? AND uid IN (?" + strings.Repeat(",?", len(uids)-1) + ")"
|
|
_, err := sess.Exec(append([]any{sql, accesscontrol.GlobalOrgID}, uids...)...)
|
|
return err
|
|
}
|
|
|
|
// LoadBasicRoleSeedPermissions returns the current (builtin_role, action, scope) permissions granted to basic roles.
|
|
// It sets Origin to empty.
|
|
func LoadBasicRoleSeedPermissions(sess *db.Session) ([]accesscontrol.SeedPermission, error) {
|
|
rows := []accesscontrol.SeedPermission{}
|
|
err := sess.SQL(
|
|
`SELECT role.display_name AS builtin_role, p.action, p.scope, '' AS origin
|
|
FROM role INNER JOIN permission AS p ON p.role_id = role.id
|
|
WHERE role.org_id = ? AND role.name LIKE 'basic:%'`,
|
|
accesscontrol.GlobalOrgID,
|
|
).Find(&rows)
|
|
return rows, err
|
|
}
|