mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Zanzana: reconcile basic roles and bindings (#96473)
* Add reconciler for basic roles * Add reconciler for basic role bindings
This commit is contained in:
parent
0f4517df98
commit
1f34096fdf
@ -17,7 +17,7 @@ func teamMembershipCollector(store db.DB) legacyTupleCollector {
|
||||
FROM team_member tm
|
||||
INNER JOIN team t ON tm.team_id = t.id
|
||||
INNER JOIN ` + store.GetDialect().Quote("user") + ` u ON tm.user_id = u.id
|
||||
WHERE org_id = ?
|
||||
WHERE t.org_id = ?
|
||||
`
|
||||
|
||||
type membership struct {
|
||||
@ -109,7 +109,7 @@ func folderTreeCollector(store db.DB) legacyTupleCollector {
|
||||
}
|
||||
}
|
||||
|
||||
// managedPermissionsCollector collects managed permissions into provided tuple map.
|
||||
// managedPermissionsCollector collects managed permissions.
|
||||
// It will only store actions that are supported by our schema. Managed permissions can
|
||||
// be directly mapped to user/team/role without having to write an intermediate role.
|
||||
func managedPermissionsCollector(store db.DB, kind string) legacyTupleCollector {
|
||||
@ -195,6 +195,99 @@ 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
|
||||
func basicRoleBindingsCollector(store db.DB) legacyTupleCollector {
|
||||
return func(ctx context.Context, orgID int64) (map[string]map[string]*openfgav1.TupleKey, error) {
|
||||
query := `
|
||||
SELECT ou.org_id, u.uid as user_uid, ou.role as org_role
|
||||
FROM org_user ou
|
||||
LEFT JOIN ` + store.GetDialect().Quote("user") + ` u ON u.id = ou.user_id
|
||||
WHERE ou.org_id = ?
|
||||
AND NOT u.is_service_account
|
||||
`
|
||||
// FIXME: handle service admin role
|
||||
type Binding struct {
|
||||
UserUID string `xorm:"user_uid"`
|
||||
OrgRole string `xorm:"org_role"`
|
||||
}
|
||||
|
||||
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 {
|
||||
subject := zanzana.NewTupleEntry(zanzana.TypeUser, b.UserUID, "")
|
||||
|
||||
tuple := &openfgav1.TupleKey{
|
||||
User: subject,
|
||||
Relation: zanzana.RelationAssignee,
|
||||
Object: zanzana.NewTupleEntry(zanzana.TypeRole, zanzana.TranslateBasicRole(b.OrgRole), ""),
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -64,6 +64,18 @@ func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB,
|
||||
zanzanaCollector(zanzana.ResourceRelations),
|
||||
client,
|
||||
),
|
||||
newResourceReconciler(
|
||||
"basic role permissions",
|
||||
basicRolePermissionsCollector(store),
|
||||
zanzanaCollector(zanzana.FolderRelations),
|
||||
client,
|
||||
),
|
||||
newResourceReconciler(
|
||||
"basic role bindings",
|
||||
basicRoleBindingsCollector(store),
|
||||
zanzanaCollector([]string{zanzana.RelationAssignee}),
|
||||
client,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,22 @@ import (
|
||||
folderalpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
roleGrafanaAdmin = "Grafana Admin"
|
||||
roleAdmin = "Admin"
|
||||
roleEditor = "Editor"
|
||||
roleViewer = "Viewer"
|
||||
roleNone = "None"
|
||||
)
|
||||
|
||||
var basicRolesTranslations = map[string]string{
|
||||
roleGrafanaAdmin: "basic_grafana_admin",
|
||||
roleAdmin: "basic_admin",
|
||||
roleEditor: "basic_editor",
|
||||
roleViewer: "basic_viewer",
|
||||
roleNone: "basic_none",
|
||||
}
|
||||
|
||||
type resourceTranslation struct {
|
||||
typ string
|
||||
group string
|
||||
|
@ -73,19 +73,6 @@ const (
|
||||
KindFolders string = "folders"
|
||||
)
|
||||
|
||||
const (
|
||||
RoleGrafanaAdmin = "Grafana Admin"
|
||||
RoleAdmin = "Admin"
|
||||
RoleEditor = "Editor"
|
||||
RoleViewer = "Viewer"
|
||||
RoleNone = "None"
|
||||
|
||||
BasicRolePrefix = "basic:"
|
||||
BasicRoleUIDPrefix = "basic_"
|
||||
|
||||
GlobalOrgID = 0
|
||||
)
|
||||
|
||||
var (
|
||||
ToAuthzExtTupleKey = common.ToAuthzExtTupleKey
|
||||
ToAuthzExtTupleKeys = common.ToAuthzExtTupleKeys
|
||||
@ -98,11 +85,11 @@ var (
|
||||
ToOpenFGATupleKeyWithoutCondition = common.ToOpenFGATupleKeyWithoutCondition
|
||||
)
|
||||
|
||||
// NewTupleEntry constructs new openfga entry type:id[#relation].
|
||||
// Relation allows to specify group of users (subjects) related to type:id
|
||||
// NewTupleEntry constructs new openfga entry type:name[#relation].
|
||||
// Relation allows to specify group of users (subjects) related to type:name
|
||||
// (for example, team:devs#member refers to users which are members of team devs)
|
||||
func NewTupleEntry(objectType, id, relation string) string {
|
||||
obj := fmt.Sprintf("%s:%s", objectType, id)
|
||||
func NewTupleEntry(objectType, name, relation string) string {
|
||||
obj := fmt.Sprintf("%s:%s", objectType, name)
|
||||
if relation != "" {
|
||||
obj = fmt.Sprintf("%s#%s", obj, relation)
|
||||
}
|
||||
@ -121,6 +108,10 @@ func TranslateToResourceTuple(subject string, action, kind, name string) (*openf
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if name == "*" {
|
||||
return common.NewNamespaceResourceTuple(subject, m.relation, translation.group, translation.resource), true
|
||||
}
|
||||
|
||||
if translation.typ == TypeResource {
|
||||
return common.NewResourceTuple(subject, m.relation, translation.group, translation.resource, name), true
|
||||
}
|
||||
@ -174,3 +165,7 @@ func TranslateToCheckRequest(namespace, action, kind, folder, name string) (*aut
|
||||
|
||||
return req, true
|
||||
}
|
||||
|
||||
func TranslateBasicRole(name string) string {
|
||||
return basicRolesTranslations[name]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user