Access: Add AfterCreate hooks for Roles/Core Roles (#112666)
As part of migrating Grafana's authorization system to Zanzana (OpenFGA), we need to ensure that role permissions defined in the IAM API are automatically synced to the authorization backend. Without this sync, roles created through the API would not be enforced by Zanzana, creating an inconsistency between defined permissions and actual authorization decisions.
This is a critical piece of the dual-write pattern during the migration to Zanzana, ensuring that:
Role permissions are immediately available for authorization checks
The legacy RBAC system and new Zanzana system remain in sync
Users experience consistent permission enforcement regardless of which backend is queried
safe to revert
This commit is contained in:
@@ -185,7 +185,7 @@ func managedPermissionsCollector(store db.DB, kind string) legacyTupleCollector
|
||||
|
||||
// For resource actions on folders we need to merge the tuples into one with combined subresources.
|
||||
if zanzana.IsFolderResourceTuple(tuple) {
|
||||
key := tupleStringWithoutCondition(tuple)
|
||||
key := zanzana.TupleStringWithoutCondition(tuple)
|
||||
if t, ok := tuples[tuple.Object][key]; ok {
|
||||
zanzana.MergeFolderResourceTuples(t, tuple)
|
||||
} else {
|
||||
@@ -202,14 +202,6 @@ func managedPermissionsCollector(store db.DB, kind string) legacyTupleCollector
|
||||
}
|
||||
}
|
||||
|
||||
func tupleStringWithoutCondition(tuple *openfgav1.TupleKey) string {
|
||||
c := tuple.Condition
|
||||
tuple.Condition = nil
|
||||
s := tuple.String()
|
||||
tuple.Condition = c
|
||||
return s
|
||||
}
|
||||
|
||||
// basicRoleBindingsCollector collects role bindings for basic roles
|
||||
func basicRoleBindingsCollector(store db.DB) legacyTupleCollector {
|
||||
return func(ctx context.Context, orgID int64) (map[string]map[string]*openfgav1.TupleKey, error) {
|
||||
@@ -371,36 +363,41 @@ func rolePermissionsCollector(store db.DB) legacyTupleCollector {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Group permissions by role UID
|
||||
rolePermissionsMap := make(map[string][]zanzana.RolePermission)
|
||||
for _, p := range permissions {
|
||||
rolePermissionsMap[p.RoleUID] = append(rolePermissionsMap[p.RoleUID], zanzana.RolePermission{
|
||||
Action: p.Action,
|
||||
Kind: p.Kind,
|
||||
Identifier: p.Identifier,
|
||||
})
|
||||
}
|
||||
|
||||
tuples := make(map[string]map[string]*openfgav1.TupleKey)
|
||||
|
||||
for _, p := range permissions {
|
||||
tuple, ok := zanzana.TranslateToResourceTuple(
|
||||
zanzana.NewTupleEntry(zanzana.TypeRole, p.RoleUID, zanzana.RelationAssignee),
|
||||
p.Action,
|
||||
p.Kind,
|
||||
p.Identifier,
|
||||
)
|
||||
if !ok {
|
||||
// Convert permissions for each role using the shared utility
|
||||
for roleUID, perms := range rolePermissionsMap {
|
||||
roleTuples, err := zanzana.ConvertRolePermissionsToTuples(roleUID, perms)
|
||||
if err != nil {
|
||||
reconcilerLogger.Warn("Failed to convert role permissions to tuples", "roleUID", roleUID, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if tuples[tuple.Object] == nil {
|
||||
tuples[tuple.Object] = make(map[string]*openfgav1.TupleKey)
|
||||
}
|
||||
|
||||
// For resource actions on folders we need to merge the tuples into one with combined subresources.
|
||||
if zanzana.IsFolderResourceTuple(tuple) {
|
||||
key := tupleStringWithoutCondition(tuple)
|
||||
if t, ok := tuples[tuple.Object][key]; ok {
|
||||
zanzana.MergeFolderResourceTuples(t, tuple)
|
||||
} else {
|
||||
tuples[tuple.Object][key] = tuple
|
||||
// Add tuples to the result map
|
||||
for _, tuple := range roleTuples {
|
||||
if tuples[tuple.Object] == nil {
|
||||
tuples[tuple.Object] = make(map[string]*openfgav1.TupleKey)
|
||||
}
|
||||
|
||||
continue
|
||||
// Use the appropriate key based on whether it's a folder resource tuple
|
||||
var key string
|
||||
if zanzana.IsFolderResourceTuple(tuple) {
|
||||
key = zanzana.TupleStringWithoutCondition(tuple)
|
||||
} else {
|
||||
key = tuple.String()
|
||||
}
|
||||
tuples[tuple.Object][key] = tuple
|
||||
}
|
||||
|
||||
tuples[tuple.Object][tuple.String()] = tuple
|
||||
}
|
||||
|
||||
return tuples, nil
|
||||
@@ -482,7 +479,7 @@ func zanzanaCollector(relations []string) zanzanaTupleCollector {
|
||||
}
|
||||
for _, t := range tuples {
|
||||
if zanzana.IsFolderResourceTuple(t.Key) {
|
||||
out[tupleStringWithoutCondition(t.Key)] = t.Key
|
||||
out[zanzana.TupleStringWithoutCondition(t.Key)] = t.Key
|
||||
} else {
|
||||
out[t.Key.String()] = t.Key
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user