diff --git a/apps/iam/kinds/v0alpha1/rolespec.cue b/apps/iam/kinds/v0alpha1/rolespec.cue index b7934f0174e..d0aa9361a77 100644 --- a/apps/iam/kinds/v0alpha1/rolespec.cue +++ b/apps/iam/kinds/v0alpha1/rolespec.cue @@ -6,6 +6,8 @@ RoleSpec: { action: string // RBAC scope (e.g: "dashboards:uid:dash1") scope: string + // When true, the permission is disabled and not granted to the role + disabled: bool | *false } // Display name of the role diff --git a/apps/iam/pkg/apis/iam/v0alpha1/corerole_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/corerole_spec_gen.go index ac0b41e1972..21913c8ff10 100644 --- a/apps/iam/pkg/apis/iam/v0alpha1/corerole_spec_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/corerole_spec_gen.go @@ -8,6 +8,8 @@ type CoreRolespecPermission struct { Action string `json:"action"` // RBAC scope (e.g: "dashboards:uid:dash1") Scope string `json:"scope"` + // When true, the permission is disabled and not granted to the role + Disabled bool `json:"disabled"` } // NewCoreRolespecPermission creates a new CoreRolespecPermission object. diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrole_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_spec_gen.go index 1ce8d65646f..1d0a8b9e715 100644 --- a/apps/iam/pkg/apis/iam/v0alpha1/globalrole_spec_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_spec_gen.go @@ -8,6 +8,8 @@ type GlobalRolespecPermission struct { Action string `json:"action"` // RBAC scope (e.g: "dashboards:uid:dash1") Scope string `json:"scope"` + // When true, the permission is disabled and not granted to the role + Disabled bool `json:"disabled"` } // NewGlobalRolespecPermission creates a new GlobalRolespecPermission object. diff --git a/apps/iam/pkg/apis/iam/v0alpha1/role_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/role_spec_gen.go index 5a8185cd25c..e5cf9df6f7f 100644 --- a/apps/iam/pkg/apis/iam/v0alpha1/role_spec_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/role_spec_gen.go @@ -8,6 +8,8 @@ type RolespecPermission struct { Action string `json:"action"` // RBAC scope (e.g: "dashboards:uid:dash1") Scope string `json:"scope"` + // When true, the permission is disabled and not granted to the role + Disabled bool `json:"disabled"` } // NewRolespecPermission creates a new RolespecPermission object. diff --git a/pkg/registry/apis/iam/role_hooks.go b/pkg/registry/apis/iam/role_hooks.go index 831a2993fe5..f52559a732e 100644 --- a/pkg/registry/apis/iam/role_hooks.go +++ b/pkg/registry/apis/iam/role_hooks.go @@ -17,10 +17,16 @@ import ( // convertRolePermissionsToTuples converts role permissions (action/scope) to v1 TupleKey format // using the shared zanzana.ConvertRolePermissionsToTuples utility and common.ToAuthzExtTupleKeys +// Disabled permissions are filtered out and not converted to tuples func convertRolePermissionsToTuples(roleUID string, permissions []iamv0.CoreRolespecPermission) ([]*v1.TupleKey, error) { - // Convert IAM permissions to zanzana.RolePermission format + // Convert IAM permissions to zanzana.RolePermission format, filtering out disabled permissions rolePerms := make([]zanzana.RolePermission, 0, len(permissions)) for _, perm := range permissions { + // Skip disabled permissions - they should not be added to Zanzana + if perm.Disabled { + continue + } + // Split the scope to get kind, attribute, identifier kind, _, identifier := accesscontrol.SplitScope(perm.Scope) rolePerms = append(rolePerms, zanzana.RolePermission{ diff --git a/pkg/services/accesscontrol/database/database.go b/pkg/services/accesscontrol/database/database.go index ce6dd393f77..78f4418a258 100644 --- a/pkg/services/accesscontrol/database/database.go +++ b/pkg/services/accesscontrol/database/database.go @@ -67,7 +67,9 @@ func (s *AccessControlStore) GetUserPermissions(ctx context.Context, query acces permission.scope FROM permission INNER JOIN role ON role.id = permission.role_id - ` + filter + ` + filter + ` + AND (permission.disabled = 0 OR permission.disabled IS NULL) + ` if len(query.RolePrefixes) > 0 { rolePrefixesFilter, filterParams := accesscontrol.RolePrefixesFilter(query.RolePrefixes) @@ -132,6 +134,7 @@ func (s *AccessControlStore) GetTeamsPermissions(ctx context.Context, query acce WHERE tr.team_id IN(?` + strings.Repeat(", ?", len(teams)-1) + `) AND tr.org_id = ? ) as all_role ON role.id = all_role.role_id + WHERE (permission.disabled = 0 OR permission.disabled IS NULL) ` params := make([]any, 0) diff --git a/pkg/services/accesscontrol/models.go b/pkg/services/accesscontrol/models.go index ec67cd03c64..3d9a5581a71 100644 --- a/pkg/services/accesscontrol/models.go +++ b/pkg/services/accesscontrol/models.go @@ -203,10 +203,11 @@ type BuiltinRole struct { // Permission is the model for access control permissions type Permission struct { - ID int64 `json:"-" xorm:"pk autoincr 'id'"` - RoleID int64 `json:"-" xorm:"role_id"` - Action string `json:"action"` - Scope string `json:"scope"` + ID int64 `json:"-" xorm:"pk autoincr 'id'"` + RoleID int64 `json:"-" xorm:"role_id"` + Action string `json:"action"` + Scope string `json:"scope"` + Disabled bool `json:"disabled" xorm:"disabled"` Kind string `json:"-"` Attribute string `json:"-"` diff --git a/pkg/services/sqlstore/migrations/accesscontrol/migrations.go b/pkg/services/sqlstore/migrations/accesscontrol/migrations.go index 2502caf51dc..2bd0ba9b6fd 100644 --- a/pkg/services/sqlstore/migrations/accesscontrol/migrations.go +++ b/pkg/services/sqlstore/migrations/accesscontrol/migrations.go @@ -218,4 +218,9 @@ func AddMigration(mg *migrator.Migrator) { mg.AddMigration("Remove permission role_id index", migrator.NewDropIndexMigration(permissionV1, &migrator.Index{ Cols: []string{"role_id"}, })) + + // Add disabled column to permission table + mg.AddMigration("add column disabled to permission table", migrator.NewAddColumnMigration(permissionV1, &migrator.Column{ + Name: "disabled", Type: migrator.DB_Bool, Nullable: false, Default: "0", + })) }