Zanzana: sync roles and bindings (#96661)

* Sync fix and custom roles and team bindings.

* Add collector for user role bindings
This commit is contained in:
Karl Persson 2024-11-19 13:18:44 +01:00 committed by GitHub
parent 3c182a37fa
commit 7e442efa9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 163 additions and 62 deletions

View File

@ -115,7 +115,7 @@ func folderTreeCollector(store db.DB) legacyTupleCollector {
func managedPermissionsCollector(store db.DB, kind string) legacyTupleCollector {
return func(ctx context.Context, orgID int64) (map[string]map[string]*openfgav1.TupleKey, error) {
query := `
SELECT u.uid as user_uid, t.uid as team_uid, p.action, p.kind, p.identifier, r.org_id
SELECT u.uid as user_uid, t.uid as team_uid, p.action, p.kind, p.identifier, r.org_id, br.role as basic_role_name
FROM permission p
INNER JOIN role r ON p.role_id = r.id
LEFT JOIN user_role ur ON r.id = ur.role_id
@ -128,12 +128,12 @@ func managedPermissionsCollector(store db.DB, kind string) legacyTupleCollector
AND p.kind = ?
`
type Permission struct {
RoleName string `xorm:"role_name"`
Action string `xorm:"action"`
Kind string
Identifier string
UserUID string `xorm:"user_uid"`
TeamUID string `xorm:"team_uid"`
Action string `xorm:"action"`
Kind string
Identifier string
UserUID string `xorm:"user_uid"`
TeamUID string `xorm:"team_uid"`
BasicRoleName string `xorm:"basic_role_name"`
}
var permissions []Permission
@ -152,10 +152,9 @@ func managedPermissionsCollector(store db.DB, kind string) legacyTupleCollector
if len(p.UserUID) > 0 {
subject = zanzana.NewTupleEntry(zanzana.TypeUser, p.UserUID, "")
} else if len(p.TeamUID) > 0 {
subject = zanzana.NewTupleEntry(zanzana.TypeTeam, p.TeamUID, "member")
subject = zanzana.NewTupleEntry(zanzana.TypeTeam, p.TeamUID, zanzana.RelationTeamMember)
} else {
// FIXME(kalleep): Unsuported role binding (org role). We need to have basic roles in place
continue
subject = zanzana.NewTupleEntry(zanzana.TypeRole, zanzana.TranslateBasicRole(p.BasicRoleName), zanzana.RelationAssignee)
}
tuple, ok := zanzana.TranslateToResourceTuple(subject, p.Action, p.Kind, p.Identifier)
@ -195,53 +194,7 @@ func tupleStringWithoutCondition(tuple *openfgav1.TupleKey) string {
return s
}
// basicRolePermissionsCollector collects permissions for basic roles
func basicRolePermissionsCollector(store db.DB) legacyTupleCollector {
return func(ctx context.Context, _ int64) (map[string]map[string]*openfgav1.TupleKey, error) {
const query = `
SELECT r.uid as role_uid, p.action, p.kind, p.identifier
FROM permission p
INNER JOIN role r ON p.role_id = r.id
LEFT JOIN builtin_role br ON r.id = br.role_id
WHERE r.name LIKE 'basic:%'
`
type Permission struct {
Action string `xorm:"action"`
Kind string
Identifier string
RoleUID string `xorm:"role_uid"`
}
var permissions []Permission
err := store.WithDbSession(ctx, func(sess *db.Session) error {
return sess.SQL(query).Find(&permissions)
})
if err != nil {
return nil, err
}
tuples := make(map[string]map[string]*openfgav1.TupleKey)
for _, p := range permissions {
subject := zanzana.NewTupleEntry(zanzana.TypeRole, p.RoleUID, "assignee")
tuple, ok := zanzana.TranslateToResourceTuple(subject, p.Action, p.Kind, p.Identifier)
if !ok {
continue
}
if tuples[tuple.Object] == nil {
tuples[tuple.Object] = make(map[string]*openfgav1.TupleKey)
}
tuples[tuple.Object][tuple.String()] = tuple
}
return tuples, nil
}
}
// basicRoleBindingsCollects collects role bindings for basic roles
// 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) {
query := `
@ -269,10 +222,8 @@ func basicRoleBindingsCollector(store db.DB) legacyTupleCollector {
tuples := make(map[string]map[string]*openfgav1.TupleKey)
for _, b := range bindings {
subject := zanzana.NewTupleEntry(zanzana.TypeUser, b.UserUID, "")
tuple := &openfgav1.TupleKey{
User: subject,
User: zanzana.NewTupleEntry(zanzana.TypeUser, b.UserUID, ""),
Relation: zanzana.RelationAssignee,
Object: zanzana.NewTupleEntry(zanzana.TypeRole, zanzana.TranslateBasicRole(b.OrgRole), ""),
}
@ -288,6 +239,144 @@ func basicRoleBindingsCollector(store db.DB) legacyTupleCollector {
}
}
func teamRoleBindingsCollector(store db.DB) legacyTupleCollector {
return func(ctx context.Context, orgID int64) (map[string]map[string]*openfgav1.TupleKey, error) {
query := `
SELECT t.uid AS team_uid, r.uid AS role_uid
FROM team_role tr
INNER JOIN team t ON tr.team_id = t.id
INNER JOIN role r ON tr.role_id = r.id
WHERE t.org_id = ?
AND r.name NOT LIKE 'managed:%'
`
type Binding struct {
TeamUID string `xorm:"team_uid"`
RoleUID string `xorm:"role_uid"`
}
var bindings []Binding
err := store.WithDbSession(ctx, func(sess *db.Session) error {
return sess.SQL(query, orgID).Find(&bindings)
})
if err != nil {
return nil, err
}
tuples := make(map[string]map[string]*openfgav1.TupleKey)
for _, b := range bindings {
tuple := &openfgav1.TupleKey{
User: zanzana.NewTupleEntry(zanzana.TypeTeam, b.TeamUID, zanzana.RelationTeamMember),
Relation: zanzana.RelationAssignee,
Object: zanzana.NewTupleEntry(zanzana.TypeRole, b.RoleUID, ""),
}
if tuples[tuple.Object] == nil {
tuples[tuple.Object] = make(map[string]*openfgav1.TupleKey)
}
tuples[tuple.Object][tuple.String()] = tuple
}
return tuples, nil
}
}
func userRoleBindingsCollector(store db.DB) legacyTupleCollector {
return func(ctx context.Context, orgID int64) (map[string]map[string]*openfgav1.TupleKey, error) {
query := `
SELECT u.uid AS user_uid, r.uid AS role_uid
FROM user_role ur
INNER JOIN ` + store.GetDialect().Quote("user") + ` u ON ur.user_id = u.id
INNER JOIN role r ON ur.role_id = r.id
WHERE (ur.org_id = 0 OR ur.org_id = ?)
AND r.name NOT LIKE 'managed:%'
`
type Binding struct {
UserUID string `xorm:"user_uid"`
RoleUID string `xorm:"role_uid"`
}
var bindings []Binding
err := store.WithDbSession(ctx, func(sess *db.Session) error {
return sess.SQL(query, orgID).Find(&bindings)
})
if err != nil {
return nil, err
}
tuples := make(map[string]map[string]*openfgav1.TupleKey)
for _, b := range bindings {
tuple := &openfgav1.TupleKey{
User: zanzana.NewTupleEntry(zanzana.TypeUser, b.UserUID, ""),
Relation: zanzana.RelationAssignee,
Object: zanzana.NewTupleEntry(zanzana.TypeRole, b.RoleUID, ""),
}
if tuples[tuple.Object] == nil {
tuples[tuple.Object] = make(map[string]*openfgav1.TupleKey)
}
tuples[tuple.Object][tuple.String()] = tuple
}
return tuples, nil
}
}
func rolePermissionsCollector(store db.DB) legacyTupleCollector {
return func(ctx context.Context, orgID int64) (map[string]map[string]*openfgav1.TupleKey, error) {
var query = `
SELECT r.uid as role_uid, p.action, p.kind, p.identifier
FROM permission p
INNER JOIN role r ON p.role_id = r.id
LEFT JOIN builtin_role br ON r.id = br.role_id
WHERE (r.org_id = 0 OR r.org_id = ?)
AND r.name NOT LIKE 'managed:%'
`
type Permission struct {
Action string `xorm:"action"`
Kind string
Identifier string
RoleUID string `xorm:"role_uid"`
}
var permissions []Permission
err := store.WithDbSession(ctx, func(sess *db.Session) error {
return sess.SQL(query, orgID).Find(&permissions)
})
if err != nil {
return nil, err
}
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 {
continue
}
if tuples[tuple.Object] == nil {
tuples[tuple.Object] = make(map[string]*openfgav1.TupleKey)
}
tuples[tuple.Object][tuple.String()] = tuple
}
return tuples, nil
}
}
func zanzanaCollector(relations []string) zanzanaTupleCollector {
return func(ctx context.Context, client zanzana.Client, object string, namespace string) (map[string]*openfgav1.TupleKey, error) {
// list will use continuation token to collect all tuples for object and relation

View File

@ -65,8 +65,8 @@ func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB,
client,
),
newResourceReconciler(
"basic role permissions",
basicRolePermissionsCollector(store),
"role permissions",
rolePermissionsCollector(store),
zanzanaCollector(zanzana.FolderRelations),
client,
),
@ -76,6 +76,18 @@ func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB,
zanzanaCollector([]string{zanzana.RelationAssignee}),
client,
),
newResourceReconciler(
"team role bindings",
teamRoleBindingsCollector(store),
zanzanaCollector([]string{zanzana.RelationAssignee}),
client,
),
newResourceReconciler(
"user role bindings",
userRoleBindingsCollector(store),
zanzanaCollector([]string{zanzana.RelationAssignee}),
client,
),
},
}
}