mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Zanzana: Update relation names (#97638)
* Update relation names to match k8s verbs * Only check namespace if relation is valid * Only list for valid relations
This commit is contained in:
parent
4712883601
commit
718612aabf
@ -55,19 +55,19 @@ func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB,
|
|||||||
newResourceReconciler(
|
newResourceReconciler(
|
||||||
"managed folder permissions",
|
"managed folder permissions",
|
||||||
managedPermissionsCollector(store, zanzana.KindFolders),
|
managedPermissionsCollector(store, zanzana.KindFolders),
|
||||||
zanzanaCollector(zanzana.FolderRelations),
|
zanzanaCollector(zanzana.RelationsFolder),
|
||||||
client,
|
client,
|
||||||
),
|
),
|
||||||
newResourceReconciler(
|
newResourceReconciler(
|
||||||
"managed dashboard permissions",
|
"managed dashboard permissions",
|
||||||
managedPermissionsCollector(store, zanzana.KindDashboards),
|
managedPermissionsCollector(store, zanzana.KindDashboards),
|
||||||
zanzanaCollector(zanzana.ResourceRelations),
|
zanzanaCollector(zanzana.RelationsResouce),
|
||||||
client,
|
client,
|
||||||
),
|
),
|
||||||
newResourceReconciler(
|
newResourceReconciler(
|
||||||
"role permissions",
|
"role permissions",
|
||||||
rolePermissionsCollector(store),
|
rolePermissionsCollector(store),
|
||||||
zanzanaCollector(zanzana.FolderRelations),
|
zanzanaCollector(zanzana.RelationsFolder),
|
||||||
client,
|
client,
|
||||||
),
|
),
|
||||||
newResourceReconciler(
|
newResourceReconciler(
|
||||||
|
@ -11,11 +11,15 @@ type TypeInfo struct {
|
|||||||
Relations []string
|
Relations []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t TypeInfo) IsValidRelation(relation string) bool {
|
||||||
|
return isValidRelation(relation, t.Relations)
|
||||||
|
}
|
||||||
|
|
||||||
var typedResources = map[string]TypeInfo{
|
var typedResources = map[string]TypeInfo{
|
||||||
FormatGroupResource(
|
FormatGroupResource(
|
||||||
folderalpha1.FolderResourceInfo.GroupResource().Group,
|
folderalpha1.FolderResourceInfo.GroupResource().Group,
|
||||||
folderalpha1.FolderResourceInfo.GroupResource().Resource,
|
folderalpha1.FolderResourceInfo.GroupResource().Resource,
|
||||||
): {Type: "folder", Relations: append(ResourceRelations, RelationCreate)},
|
): {Type: "folder", Relations: RelationsFolder},
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTypeInfo(group, resource string) (TypeInfo, bool) {
|
func GetTypeInfo(group, resource string) (TypeInfo, bool) {
|
||||||
@ -24,19 +28,19 @@ func GetTypeInfo(group, resource string) (TypeInfo, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var VerbMapping = map[string]string{
|
var VerbMapping = map[string]string{
|
||||||
utils.VerbGet: RelationRead,
|
utils.VerbGet: RelationGet,
|
||||||
utils.VerbList: RelationRead,
|
utils.VerbList: RelationGet,
|
||||||
utils.VerbWatch: RelationRead,
|
utils.VerbWatch: RelationGet,
|
||||||
utils.VerbCreate: RelationCreate,
|
utils.VerbCreate: RelationCreate,
|
||||||
utils.VerbUpdate: RelationWrite,
|
utils.VerbUpdate: RelationUpdate,
|
||||||
utils.VerbPatch: RelationWrite,
|
utils.VerbPatch: RelationUpdate,
|
||||||
utils.VerbDelete: RelationDelete,
|
utils.VerbDelete: RelationDelete,
|
||||||
utils.VerbDeleteCollection: RelationDelete,
|
utils.VerbDeleteCollection: RelationDelete,
|
||||||
}
|
}
|
||||||
|
|
||||||
var RelationToVerbMapping = map[string]string{
|
var RelationToVerbMapping = map[string]string{
|
||||||
RelationRead: utils.VerbGet,
|
RelationGet: utils.VerbGet,
|
||||||
RelationCreate: utils.VerbCreate,
|
RelationCreate: utils.VerbCreate,
|
||||||
RelationWrite: utils.VerbUpdate,
|
RelationUpdate: utils.VerbUpdate,
|
||||||
RelationDelete: utils.VerbDelete,
|
RelationDelete: utils.VerbDelete,
|
||||||
}
|
}
|
||||||
|
@ -31,44 +31,74 @@ const (
|
|||||||
RelationSetEdit string = "edit"
|
RelationSetEdit string = "edit"
|
||||||
RelationSetAdmin string = "admin"
|
RelationSetAdmin string = "admin"
|
||||||
|
|
||||||
RelationRead string = "read"
|
RelationGet string = "get"
|
||||||
RelationWrite string = "write"
|
RelationUpdate string = "update"
|
||||||
RelationCreate string = "create"
|
RelationCreate string = "create"
|
||||||
RelationDelete string = "delete"
|
RelationDelete string = "delete"
|
||||||
RelationPermissionsRead string = "permissions_read"
|
|
||||||
RelationPermissionsWrite string = "permissions_write"
|
|
||||||
|
|
||||||
RelationFolderResourceSetView string = "resource_" + RelationSetView
|
RelationFolderResourceSetView string = "resource_" + RelationSetView
|
||||||
RelationFolderResourceSetEdit string = "resource_" + RelationSetEdit
|
RelationFolderResourceSetEdit string = "resource_" + RelationSetEdit
|
||||||
RelationFolderResourceSetAdmin string = "resource_" + RelationSetAdmin
|
RelationFolderResourceSetAdmin string = "resource_" + RelationSetAdmin
|
||||||
|
|
||||||
RelationFolderResourceRead string = "resource_" + RelationRead
|
RelationFolderResourceGet string = "resource_" + RelationGet
|
||||||
RelationFolderResourceWrite string = "resource_" + RelationWrite
|
RelationFolderResourceUpdate string = "resource_" + RelationUpdate
|
||||||
RelationFolderResourceCreate string = "resource_" + RelationCreate
|
RelationFolderResourceCreate string = "resource_" + RelationCreate
|
||||||
RelationFolderResourceDelete string = "resource_" + RelationDelete
|
RelationFolderResourceDelete string = "resource_" + RelationDelete
|
||||||
RelationFolderResourcePermissionsRead string = "resource_" + RelationPermissionsRead
|
|
||||||
RelationFolderResourcePermissionsWrite string = "resource_" + RelationPermissionsWrite
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ResourceRelations = []string{
|
// RelationsNamespace are relations that can be added on type "namespace".
|
||||||
RelationRead,
|
var RelationsNamespace = []string{
|
||||||
RelationWrite,
|
RelationGet,
|
||||||
|
RelationUpdate,
|
||||||
|
RelationCreate,
|
||||||
RelationDelete,
|
RelationDelete,
|
||||||
RelationPermissionsRead,
|
|
||||||
RelationPermissionsWrite,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var FolderRelations = append(
|
// RelationsResource are relations that can be added on type "resource".
|
||||||
ResourceRelations,
|
var RelationsResource = []string{
|
||||||
RelationCreate,
|
RelationGet,
|
||||||
RelationFolderResourceRead,
|
RelationUpdate,
|
||||||
RelationFolderResourceWrite,
|
RelationDelete,
|
||||||
|
}
|
||||||
|
|
||||||
|
// RelationsFolderResource are relations that can be added on type "folder" for child resources.
|
||||||
|
var RelationsFolderResource = []string{
|
||||||
|
RelationFolderResourceGet,
|
||||||
|
RelationFolderResourceUpdate,
|
||||||
RelationFolderResourceCreate,
|
RelationFolderResourceCreate,
|
||||||
RelationFolderResourceDelete,
|
RelationFolderResourceDelete,
|
||||||
RelationFolderResourcePermissionsRead,
|
}
|
||||||
RelationFolderResourcePermissionsWrite,
|
|
||||||
|
// RelationsFolder are relations that can be added on type "folder".
|
||||||
|
var RelationsFolder = append(
|
||||||
|
RelationsFolderResource,
|
||||||
|
RelationGet,
|
||||||
|
RelationUpdate,
|
||||||
|
RelationCreate,
|
||||||
|
RelationDelete,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func IsNamespaceRelation(relation string) bool {
|
||||||
|
return isValidRelation(relation, RelationsNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFolderResourceRelation(relation string) bool {
|
||||||
|
return isValidRelation(relation, RelationsFolderResource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsResourceRelation(relation string) bool {
|
||||||
|
return isValidRelation(relation, RelationsResource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidRelation(relation string, valid []string) bool {
|
||||||
|
for _, r := range valid {
|
||||||
|
if r == relation {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func FolderResourceRelation(relation string) string {
|
func FolderResourceRelation(relation string) string {
|
||||||
return fmt.Sprintf("%s_%s", TypeResource, relation)
|
return fmt.Sprintf("%s_%s", TypeResource, relation)
|
||||||
}
|
}
|
||||||
@ -258,10 +288,18 @@ func AddRenderContext(req *openfgav1.CheckRequest) {
|
|||||||
|
|
||||||
req.ContextualTuples.TupleKeys = append(req.ContextualTuples.TupleKeys, &openfgav1.TupleKey{
|
req.ContextualTuples.TupleKeys = append(req.ContextualTuples.TupleKeys, &openfgav1.TupleKey{
|
||||||
User: req.TupleKey.User,
|
User: req.TupleKey.User,
|
||||||
Relation: "view",
|
Relation: RelationSetView,
|
||||||
Object: NewNamespaceResourceIdent(
|
Object: NewNamespaceResourceIdent(
|
||||||
dashboardalpha1.DashboardResourceInfo.GroupResource().Group,
|
dashboardalpha1.DashboardResourceInfo.GroupResource().Group,
|
||||||
dashboardalpha1.DashboardResourceInfo.GroupResource().Resource,
|
dashboardalpha1.DashboardResourceInfo.GroupResource().Resource,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewResourceContext(group, resource string) *structpb.Struct {
|
||||||
|
return &structpb.Struct{
|
||||||
|
Fields: map[string]*structpb.Value{
|
||||||
|
"requested_group": structpb.NewStringValue(FormatGroupResource(group, resource)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,12 +12,10 @@ type namespace
|
|||||||
define edit: [user, service-account, team#member, role#assignee] or admin
|
define edit: [user, service-account, team#member, role#assignee] or admin
|
||||||
define admin: [user, service-account, team#member, role#assignee]
|
define admin: [user, service-account, team#member, role#assignee]
|
||||||
|
|
||||||
define read: [user, service-account, render, team#member, role#assignee] or view
|
define get: [user, service-account, render, team#member, role#assignee] or view
|
||||||
define create: [user, service-account, team#member, role#assignee] or edit
|
define create: [user, service-account, team#member, role#assignee] or edit
|
||||||
define write: [user, service-account, team#member, role#assignee] or edit
|
define update: [user, service-account, team#member, role#assignee] or edit
|
||||||
define delete: [user, service-account, team#member, role#assignee] or edit
|
define delete: [user, service-account, team#member, role#assignee] or edit
|
||||||
define permissions_read: [user, service-account, team#member, role#assignee] or admin
|
|
||||||
define permissions_write: [user, service-account, team#member, role#assignee] or admin
|
|
||||||
|
|
||||||
type role
|
type role
|
||||||
relations
|
relations
|
||||||
@ -29,8 +27,6 @@ type team
|
|||||||
define admin: [user, service-account]
|
define admin: [user, service-account]
|
||||||
define member: [user, service-account] or admin
|
define member: [user, service-account] or admin
|
||||||
|
|
||||||
define read: [role#assignee] or member
|
define get: [role#assignee] or member
|
||||||
define write: [role#assignee] or admin
|
define update: [role#assignee] or admin
|
||||||
define delete: [role#assignee] or admin
|
define delete: [role#assignee] or admin
|
||||||
define permissions_read: [role#assignee] or admin
|
|
||||||
define permissions_write: [role#assignee] or admin
|
|
||||||
|
@ -9,9 +9,7 @@ type folder
|
|||||||
define edit: [user, service-account, team#member, role#assignee] or admin or edit from parent
|
define edit: [user, service-account, team#member, role#assignee] or admin or edit from parent
|
||||||
define admin: [user, service-account, team#member, role#assignee] or admin from parent
|
define admin: [user, service-account, team#member, role#assignee] or admin from parent
|
||||||
|
|
||||||
define read: [user, service-account, team#member, role#assignee] or view or read from parent
|
define get: [user, service-account, team#member, role#assignee] or view or get from parent
|
||||||
define create: [user, service-account, team#member, role#assignee] or edit or create from parent
|
define create: [user, service-account, team#member, role#assignee] or edit or create from parent
|
||||||
define write: [user, service-account, team#member, role#assignee] or edit or write from parent
|
define update: [user, service-account, team#member, role#assignee] or edit or update from parent
|
||||||
define delete: [user, service-account, team#member, role#assignee] or edit or delete from parent
|
define delete: [user, service-account, team#member, role#assignee] or edit or delete from parent
|
||||||
define permissions_read: [user, service-account, team#member, role#assignee] or admin or permissions_read from parent
|
|
||||||
define permissions_write: [user, service-account, team#member, role#assignee] or admin or permissions_write from parent
|
|
||||||
|
@ -6,12 +6,10 @@ extend type folder
|
|||||||
define resource_edit: [user, service-account, team#member, role#assignee] or resource_admin or resource_edit from parent
|
define resource_edit: [user, service-account, team#member, role#assignee] or resource_admin or resource_edit from parent
|
||||||
define resource_admin: [user, service-account, team#member, role#assignee] or resource_admin from parent
|
define resource_admin: [user, service-account, team#member, role#assignee] or resource_admin from parent
|
||||||
|
|
||||||
define resource_read: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_view or resource_read from parent
|
define resource_get: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_view or resource_get from parent
|
||||||
define resource_create: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_edit or resource_create from parent
|
define resource_create: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_edit or resource_create from parent
|
||||||
define resource_write: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_edit or resource_write from parent
|
define resource_update: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_edit or resource_update from parent
|
||||||
define resource_delete: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_edit or resource_delete from parent
|
define resource_delete: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_edit or resource_delete from parent
|
||||||
define resource_permissions_read: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_admin or resource_permissions_read from parent
|
|
||||||
define resource_permissions_write: [user with folder_group_filter, service-account with folder_group_filter, team#member with folder_group_filter, role#assignee with folder_group_filter] or resource_admin or resource_permissions_write from parent
|
|
||||||
|
|
||||||
type resource
|
type resource
|
||||||
relations
|
relations
|
||||||
@ -19,11 +17,9 @@ type resource
|
|||||||
define edit: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or admin
|
define edit: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or admin
|
||||||
define admin: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter]
|
define admin: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter]
|
||||||
|
|
||||||
define read: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or view
|
define get: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or view
|
||||||
define write: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or edit
|
define update: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or edit
|
||||||
define delete: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or edit
|
define delete: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or edit
|
||||||
define permissions_read: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or admin
|
|
||||||
define permissions_write: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or admin
|
|
||||||
|
|
||||||
condition group_filter(requested_group: string, group_resource: string) {
|
condition group_filter(requested_group: string, group_resource: string) {
|
||||||
requested_group == group_resource
|
requested_group == group_resource
|
||||||
|
@ -20,7 +20,7 @@ func (s *Server) Capabilities(ctx context.Context, r *authzextv1.CapabilitiesReq
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) capabilitiesTyped(ctx context.Context, r *authzextv1.CapabilitiesRequest, info common.TypeInfo, store *storeInfo) (*authzextv1.CapabilitiesResponse, error) {
|
func (s *Server) capabilitiesTyped(ctx context.Context, r *authzextv1.CapabilitiesRequest, info common.TypeInfo, store *storeInfo) (*authzextv1.CapabilitiesResponse, error) {
|
||||||
out := make([]string, 0, len(common.ResourceRelations))
|
out := make([]string, 0, len(common.RelationsResource))
|
||||||
for _, relation := range info.Relations {
|
for _, relation := range info.Relations {
|
||||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -46,8 +46,8 @@ func (s *Server) capabilitiesTyped(ctx context.Context, r *authzextv1.Capabiliti
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) capabilitiesGeneric(ctx context.Context, r *authzextv1.CapabilitiesRequest, store *storeInfo) (*authzextv1.CapabilitiesResponse, error) {
|
func (s *Server) capabilitiesGeneric(ctx context.Context, r *authzextv1.CapabilitiesRequest, store *storeInfo) (*authzextv1.CapabilitiesResponse, error) {
|
||||||
out := make([]string, 0, len(common.ResourceRelations))
|
out := make([]string, 0, len(common.RelationsResource))
|
||||||
for _, relation := range common.ResourceRelations {
|
for _, relation := range common.RelationsResource {
|
||||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -26,42 +26,42 @@ func testCapabilities(t *testing.T, server *Server) {
|
|||||||
t.Run("user:1 should only be able to read and write resource:dashboards.grafana.app/dashboards/1", func(t *testing.T) {
|
t.Run("user:1 should only be able to read and write resource:dashboards.grafana.app/dashboards/1", func(t *testing.T) {
|
||||||
res, err := server.Capabilities(context.Background(), newReq("user:1", dashboardGroup, dashboardResource, "1", "1"))
|
res, err := server.Capabilities(context.Background(), newReq("user:1", dashboardGroup, dashboardResource, "1", "1"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []string{common.RelationRead, common.RelationWrite}, res.GetCapabilities())
|
assert.Equal(t, []string{common.RelationGet, common.RelationUpdate}, res.GetCapabilities())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user:2 should be able to read and write resource:dashboards.grafana.app/dashboards/1 through namespace", func(t *testing.T) {
|
t.Run("user:2 should be able to read and write resource:dashboards.grafana.app/dashboards/1 through namespace", func(t *testing.T) {
|
||||||
res, err := server.Capabilities(context.Background(), newReq("user:2", dashboardGroup, dashboardResource, "1", "1"))
|
res, err := server.Capabilities(context.Background(), newReq("user:2", dashboardGroup, dashboardResource, "1", "1"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []string{common.RelationRead, common.RelationWrite}, res.GetCapabilities())
|
assert.Equal(t, []string{common.RelationGet, common.RelationUpdate}, res.GetCapabilities())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user:3 should be able to read resource:dashboards.grafana.app/dashboards/1 with set relation", func(t *testing.T) {
|
t.Run("user:3 should be able to read resource:dashboards.grafana.app/dashboards/1 with set relation", func(t *testing.T) {
|
||||||
res, err := server.Capabilities(context.Background(), newReq("user:3", dashboardGroup, dashboardResource, "1", "1"))
|
res, err := server.Capabilities(context.Background(), newReq("user:3", dashboardGroup, dashboardResource, "1", "1"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []string{common.RelationRead}, res.GetCapabilities())
|
assert.Equal(t, []string{common.RelationGet}, res.GetCapabilities())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user:4 should be able to read dashboards.grafana.app/dashboards in folder 1", func(t *testing.T) {
|
t.Run("user:4 should be able to read dashboards.grafana.app/dashboards in folder 1", func(t *testing.T) {
|
||||||
res, err := server.Capabilities(context.Background(), newReq("user:4", dashboardGroup, dashboardResource, "1", "1"))
|
res, err := server.Capabilities(context.Background(), newReq("user:4", dashboardGroup, dashboardResource, "1", "1"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []string{common.RelationRead}, res.GetCapabilities())
|
assert.Equal(t, []string{common.RelationGet}, res.GetCapabilities())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user:5 should be able to read, write, create and delete resource:dashboards.grafana.app/dashboards/1 through folder with set relation", func(t *testing.T) {
|
t.Run("user:5 should be able to read, write, create and delete resource:dashboards.grafana.app/dashboards/1 through folder with set relation", func(t *testing.T) {
|
||||||
res, err := server.Capabilities(context.Background(), newReq("user:5", dashboardGroup, dashboardResource, "1", "1"))
|
res, err := server.Capabilities(context.Background(), newReq("user:5", dashboardGroup, dashboardResource, "1", "1"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []string{common.RelationRead, common.RelationWrite, common.RelationDelete}, res.GetCapabilities())
|
assert.Equal(t, []string{common.RelationGet, common.RelationUpdate, common.RelationDelete}, res.GetCapabilities())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user:6 should be able to read folder 1 ", func(t *testing.T) {
|
t.Run("user:6 should be able to read folder 1", func(t *testing.T) {
|
||||||
res, err := server.Capabilities(context.Background(), newReq("user:6", folderGroup, folderResource, "", "1"))
|
res, err := server.Capabilities(context.Background(), newReq("user:6", folderGroup, folderResource, "", "1"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []string{common.RelationRead}, res.GetCapabilities())
|
assert.Equal(t, []string{common.RelationGet}, res.GetCapabilities())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user:7 should be able to read folder one through namespace access", func(t *testing.T) {
|
t.Run("user:7 should be able to read folder one through namespace access", func(t *testing.T) {
|
||||||
res, err := server.Capabilities(context.Background(), newReq("user:7", folderGroup, folderResource, "", "1"))
|
res, err := server.Capabilities(context.Background(), newReq("user:7", folderGroup, folderResource, "", "1"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []string{common.RelationRead}, res.GetCapabilities())
|
assert.Equal(t, []string{common.RelationGet}, res.GetCapabilities())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
||||||
)
|
)
|
||||||
@ -22,8 +21,6 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
relation := common.VerbMapping[r.GetVerb()]
|
relation := common.VerbMapping[r.GetVerb()]
|
||||||
|
|
||||||
// Check if subject has access through namespace
|
|
||||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -39,9 +36,13 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C
|
|||||||
return s.checkGeneric(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), r.GetName(), r.GetFolder(), store)
|
return s.checkGeneric(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), r.GetName(), r.GetFolder(), store)
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkTyped performes check on the root "namespace". If subject has access through the namespace they have access to
|
// checkTyped checks on the root "namespace". If subject has access through the namespace they have access to
|
||||||
// every resource for that "GroupResource".
|
// every resource for that "GroupResource".
|
||||||
func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, resource string, store *storeInfo) (*authzv1.CheckResponse, error) {
|
func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, resource string, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||||
|
if !common.IsNamespaceRelation(relation) {
|
||||||
|
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||||
|
}
|
||||||
|
|
||||||
req := &openfgav1.CheckRequest{
|
req := &openfgav1.CheckRequest{
|
||||||
StoreId: store.ID,
|
StoreId: store.ID,
|
||||||
AuthorizationModelId: store.ModelID,
|
AuthorizationModelId: store.ModelID,
|
||||||
@ -51,6 +52,7 @@ func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, r
|
|||||||
Object: common.NewNamespaceResourceIdent(group, resource),
|
Object: common.NewNamespaceResourceIdent(group, resource),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(subject, fmt.Sprintf("%s:", common.TypeRenderService)) {
|
if strings.HasPrefix(subject, fmt.Sprintf("%s:", common.TypeRenderService)) {
|
||||||
common.AddRenderContext(req)
|
common.AddRenderContext(req)
|
||||||
}
|
}
|
||||||
@ -63,8 +65,12 @@ func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, r
|
|||||||
return &authzv1.CheckResponse{Allowed: res.GetAllowed()}, nil
|
return &authzv1.CheckResponse{Allowed: res.GetAllowed()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkTyped performes checks on our typed resources e.g. folder.
|
// checkTyped checks on our typed resources e.g. folder.
|
||||||
func (s *Server) checkTyped(ctx context.Context, subject, relation, name string, info common.TypeInfo, store *storeInfo) (*authzv1.CheckResponse, error) {
|
func (s *Server) checkTyped(ctx context.Context, subject, relation, name string, info common.TypeInfo, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||||
|
if !info.IsValidRelation(relation) {
|
||||||
|
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check if subject has direct access to resource
|
// Check if subject has direct access to resource
|
||||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||||
StoreId: store.ID,
|
StoreId: store.ID,
|
||||||
@ -86,27 +92,26 @@ func (s *Server) checkTyped(ctx context.Context, subject, relation, name string,
|
|||||||
return &authzv1.CheckResponse{Allowed: false}, nil
|
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkGeneric check our generic "resource" type.
|
// checkGeneric check our generic "resource" type. It checks:
|
||||||
|
// 1. If subject has access as a sub resource for a folder.
|
||||||
|
// 2. If subject has direct access to resource.
|
||||||
func (s *Server) checkGeneric(ctx context.Context, subject, relation, group, resource, name, folder string, store *storeInfo) (*authzv1.CheckResponse, error) {
|
func (s *Server) checkGeneric(ctx context.Context, subject, relation, group, resource, name, folder string, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||||
groupResource := structpb.NewStringValue(common.FormatGroupResource(group, resource))
|
var (
|
||||||
|
resourceCtx = common.NewResourceContext(group, resource)
|
||||||
|
folderRelation = common.FolderResourceRelation(relation)
|
||||||
|
)
|
||||||
|
|
||||||
// Create relation can only exist on namespace or folder level.
|
if folder != "" && common.IsFolderResourceRelation(folderRelation) {
|
||||||
// So we skip direct resource access check.
|
// Check if subject has access as a sub resource for the folder
|
||||||
if relation != common.RelationCreate {
|
|
||||||
// Check if subject has direct access to resource
|
|
||||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||||
StoreId: store.ID,
|
StoreId: store.ID,
|
||||||
AuthorizationModelId: store.ModelID,
|
AuthorizationModelId: store.ModelID,
|
||||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||||
User: subject,
|
User: subject,
|
||||||
Relation: relation,
|
Relation: common.FolderResourceRelation(relation),
|
||||||
Object: common.NewResourceIdent(group, resource, name),
|
Object: common.NewFolderIdent(folder),
|
||||||
},
|
|
||||||
Context: &structpb.Struct{
|
|
||||||
Fields: map[string]*structpb.Value{
|
|
||||||
"requested_group": groupResource,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
Context: resourceCtx,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -114,28 +119,24 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation, group, res
|
|||||||
}
|
}
|
||||||
|
|
||||||
if res.GetAllowed() {
|
if res.GetAllowed() {
|
||||||
return &authzv1.CheckResponse{Allowed: true}, nil
|
return &authzv1.CheckResponse{Allowed: res.GetAllowed()}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if folder == "" {
|
if !common.IsResourceRelation(relation) {
|
||||||
return &authzv1.CheckResponse{Allowed: false}, nil
|
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if subject has access as a sub resource for the folder
|
// Check if subject has direct access to resource
|
||||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||||
StoreId: store.ID,
|
StoreId: store.ID,
|
||||||
AuthorizationModelId: store.ModelID,
|
AuthorizationModelId: store.ModelID,
|
||||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||||
User: subject,
|
User: subject,
|
||||||
Relation: common.FolderResourceRelation(relation),
|
Relation: relation,
|
||||||
Object: common.NewFolderIdent(folder),
|
Object: common.NewResourceIdent(group, resource, name),
|
||||||
},
|
|
||||||
Context: &structpb.Struct{
|
|
||||||
Fields: map[string]*structpb.Value{
|
|
||||||
"requested_group": groupResource,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
Context: resourceCtx,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
|
||||||
|
|
||||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||||
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
||||||
@ -23,15 +22,7 @@ func (s *Server) List(ctx context.Context, r *authzextv1.ListRequest) (*authzext
|
|||||||
|
|
||||||
relation := common.VerbMapping[r.GetVerb()]
|
relation := common.VerbMapping[r.GetVerb()]
|
||||||
|
|
||||||
res, err := s.checkNamespace(
|
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||||
ctx,
|
|
||||||
r.GetSubject(),
|
|
||||||
relation,
|
|
||||||
r.GetGroup(),
|
|
||||||
r.GetResource(),
|
|
||||||
store,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -55,8 +46,12 @@ func (s *Server) listObjects(ctx context.Context, req *openfgav1.ListObjectsRequ
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) listTyped(ctx context.Context, subject, relation string, info common.TypeInfo, store *storeInfo) (*authzextv1.ListResponse, error) {
|
func (s *Server) listTyped(ctx context.Context, subject, relation string, info common.TypeInfo, store *storeInfo) (*authzextv1.ListResponse, error) {
|
||||||
|
if !info.IsValidRelation(relation) {
|
||||||
|
return &authzextv1.ListResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// List all resources user has access too
|
// List all resources user has access too
|
||||||
listRes, err := s.listObjects(ctx, &openfgav1.ListObjectsRequest{
|
res, err := s.listObjects(ctx, &openfgav1.ListObjectsRequest{
|
||||||
StoreId: store.ID,
|
StoreId: store.ID,
|
||||||
AuthorizationModelId: store.ModelID,
|
AuthorizationModelId: store.ModelID,
|
||||||
Type: info.Type,
|
Type: info.Type,
|
||||||
@ -68,50 +63,56 @@ func (s *Server) listTyped(ctx context.Context, subject, relation string, info c
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &authzextv1.ListResponse{
|
return &authzextv1.ListResponse{
|
||||||
Items: typedObjects(info.Type, listRes.GetObjects()),
|
Items: typedObjects(info.Type, res.GetObjects()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) listGeneric(ctx context.Context, subject, relation, group, resource string, store *storeInfo) (*authzextv1.ListResponse, error) {
|
func (s *Server) listGeneric(ctx context.Context, subject, relation, group, resource string, store *storeInfo) (*authzextv1.ListResponse, error) {
|
||||||
groupResource := structpb.NewStringValue(common.FormatGroupResource(group, resource))
|
var (
|
||||||
|
resourceCtx = common.NewResourceContext(group, resource)
|
||||||
|
folderRelation = common.FolderResourceRelation(relation)
|
||||||
|
)
|
||||||
|
|
||||||
// 1. List all folders subject has access to resource type in
|
// 1. List all folders subject has access to resource type in
|
||||||
folders, err := s.listObjects(ctx, &openfgav1.ListObjectsRequest{
|
var folders []string
|
||||||
StoreId: store.ID,
|
if common.IsFolderResourceRelation(folderRelation) {
|
||||||
AuthorizationModelId: store.ModelID,
|
res, err := s.listObjects(ctx, &openfgav1.ListObjectsRequest{
|
||||||
Type: common.TypeFolder,
|
StoreId: store.ID,
|
||||||
Relation: common.FolderResourceRelation(relation),
|
AuthorizationModelId: store.ModelID,
|
||||||
User: subject,
|
Type: common.TypeFolder,
|
||||||
Context: &structpb.Struct{
|
Relation: folderRelation,
|
||||||
Fields: map[string]*structpb.Value{
|
User: subject,
|
||||||
"requested_group": groupResource,
|
Context: resourceCtx,
|
||||||
},
|
})
|
||||||
},
|
|
||||||
})
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
|
|
||||||
|
folders = res.GetObjects()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. List all resource directly assigned to subject
|
// 2. List all resource directly assigned to subject
|
||||||
direct, err := s.listObjects(ctx, &openfgav1.ListObjectsRequest{
|
var resources []string
|
||||||
StoreId: store.ID,
|
if common.IsResourceRelation(relation) {
|
||||||
AuthorizationModelId: store.ModelID,
|
res, err := s.listObjects(ctx, &openfgav1.ListObjectsRequest{
|
||||||
Type: common.TypeResource,
|
StoreId: store.ID,
|
||||||
Relation: relation,
|
AuthorizationModelId: store.ModelID,
|
||||||
User: subject,
|
Type: common.TypeResource,
|
||||||
Context: &structpb.Struct{
|
Relation: relation,
|
||||||
Fields: map[string]*structpb.Value{
|
User: subject,
|
||||||
"requested_group": groupResource,
|
Context: resourceCtx,
|
||||||
},
|
})
|
||||||
},
|
if err != nil {
|
||||||
})
|
return nil, err
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
|
resources = res.GetObjects()
|
||||||
}
|
}
|
||||||
|
|
||||||
return &authzextv1.ListResponse{
|
return &authzextv1.ListResponse{
|
||||||
Folders: folderObject(folders.GetObjects()),
|
Folders: folderObject(folders),
|
||||||
Items: directObjects(group, resource, direct.GetObjects()),
|
Items: directObjects(group, resource, resources),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,19 +80,19 @@ func setup(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
|
|||||||
AuthorizationModelId: storeInf.ModelID,
|
AuthorizationModelId: storeInf.ModelID,
|
||||||
Writes: &openfgav1.WriteRequestWrites{
|
Writes: &openfgav1.WriteRequestWrites{
|
||||||
TupleKeys: []*openfgav1.TupleKey{
|
TupleKeys: []*openfgav1.TupleKey{
|
||||||
common.NewResourceTuple("user:1", "read", dashboardGroup, dashboardResource, "1"),
|
common.NewResourceTuple("user:1", common.RelationGet, dashboardGroup, dashboardResource, "1"),
|
||||||
common.NewResourceTuple("user:1", "write", dashboardGroup, dashboardResource, "1"),
|
common.NewResourceTuple("user:1", common.RelationUpdate, dashboardGroup, dashboardResource, "1"),
|
||||||
common.NewNamespaceResourceTuple("user:2", "read", dashboardGroup, dashboardResource),
|
common.NewNamespaceResourceTuple("user:2", common.RelationGet, dashboardGroup, dashboardResource),
|
||||||
common.NewNamespaceResourceTuple("user:2", "write", dashboardGroup, dashboardResource),
|
common.NewNamespaceResourceTuple("user:2", common.RelationUpdate, dashboardGroup, dashboardResource),
|
||||||
common.NewResourceTuple("user:3", "view", dashboardGroup, dashboardResource, "1"),
|
common.NewResourceTuple("user:3", common.RelationSetView, dashboardGroup, dashboardResource, "1"),
|
||||||
common.NewFolderResourceTuple("user:4", "read", dashboardGroup, dashboardResource, "1"),
|
common.NewFolderResourceTuple("user:4", common.RelationGet, dashboardGroup, dashboardResource, "1"),
|
||||||
common.NewFolderResourceTuple("user:4", "read", dashboardGroup, dashboardResource, "3"),
|
common.NewFolderResourceTuple("user:4", common.RelationGet, dashboardGroup, dashboardResource, "3"),
|
||||||
common.NewFolderResourceTuple("user:5", "edit", dashboardGroup, dashboardResource, "1"),
|
common.NewFolderResourceTuple("user:5", common.RelationSetEdit, dashboardGroup, dashboardResource, "1"),
|
||||||
common.NewFolderTuple("user:6", "read", "1"),
|
common.NewFolderTuple("user:6", common.RelationGet, "1"),
|
||||||
common.NewNamespaceResourceTuple("user:7", "read", folderGroup, folderResource),
|
common.NewNamespaceResourceTuple("user:7", common.RelationGet, folderGroup, folderResource),
|
||||||
common.NewFolderParentTuple("5", "4"),
|
common.NewFolderParentTuple("5", "4"),
|
||||||
common.NewFolderParentTuple("6", "5"),
|
common.NewFolderParentTuple("6", "5"),
|
||||||
common.NewFolderResourceTuple("user:8", "edit", dashboardGroup, dashboardResource, "5"),
|
common.NewFolderResourceTuple("user:8", common.RelationSetEdit, dashboardGroup, dashboardResource, "5"),
|
||||||
common.NewFolderResourceTuple("user:9", "create", dashboardGroup, dashboardResource, "5"),
|
common.NewFolderResourceTuple("user:9", "create", dashboardGroup, dashboardResource, "5"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -56,18 +56,14 @@ var resourceTranslations = map[string]resourceTranslation{
|
|||||||
group: folderGroup,
|
group: folderGroup,
|
||||||
resource: folderResource,
|
resource: folderResource,
|
||||||
mapping: map[string]actionMappig{
|
mapping: map[string]actionMappig{
|
||||||
"folders:read": newMapping(RelationRead),
|
"folders:read": newMapping(RelationGet),
|
||||||
"folders:write": newMapping(RelationWrite),
|
"folders:write": newMapping(RelationUpdate),
|
||||||
"folders:create": newMapping(RelationCreate),
|
"folders:create": newMapping(RelationCreate),
|
||||||
"folders:delete": newMapping(RelationDelete),
|
"folders:delete": newMapping(RelationDelete),
|
||||||
"folders.permissions:read": newMapping(RelationPermissionsRead),
|
"dashboards:read": newScopedMapping(RelationGet, dashboardGroup, dashboardResource),
|
||||||
"folders.permissions:write": newMapping(RelationPermissionsWrite),
|
"dashboards:write": newScopedMapping(RelationUpdate, dashboardGroup, dashboardResource),
|
||||||
"dashboards:read": newScopedMapping(RelationRead, dashboardGroup, dashboardResource),
|
"dashboards:create": newScopedMapping(RelationCreate, dashboardGroup, dashboardResource),
|
||||||
"dashboards:write": newScopedMapping(RelationWrite, dashboardGroup, dashboardResource),
|
"dashboards:delete": newScopedMapping(RelationDelete, dashboardGroup, dashboardResource),
|
||||||
"dashboards:create": newScopedMapping(RelationCreate, dashboardGroup, dashboardResource),
|
|
||||||
"dashboards:delete": newScopedMapping(RelationDelete, dashboardGroup, dashboardResource),
|
|
||||||
"dashboards.permissions:read": newScopedMapping(RelationPermissionsRead, dashboardGroup, dashboardResource),
|
|
||||||
"dashboards.permissions:write": newScopedMapping(RelationPermissionsWrite, dashboardGroup, dashboardResource),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
KindDashboards: {
|
KindDashboards: {
|
||||||
@ -75,12 +71,10 @@ var resourceTranslations = map[string]resourceTranslation{
|
|||||||
group: dashboardGroup,
|
group: dashboardGroup,
|
||||||
resource: dashboardResource,
|
resource: dashboardResource,
|
||||||
mapping: map[string]actionMappig{
|
mapping: map[string]actionMappig{
|
||||||
"dashboards:read": newMapping(RelationRead),
|
"dashboards:read": newMapping(RelationGet),
|
||||||
"dashboards:write": newMapping(RelationWrite),
|
"dashboards:write": newMapping(RelationUpdate),
|
||||||
"dashboards:create": newMapping(RelationCreate),
|
"dashboards:create": newMapping(RelationCreate),
|
||||||
"dashboards:delete": newMapping(RelationDelete),
|
"dashboards:delete": newMapping(RelationDelete),
|
||||||
"dashboards.permissions:read": newMapping(RelationPermissionsRead),
|
|
||||||
"dashboards.permissions:write": newMapping(RelationPermissionsWrite),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -31,28 +31,25 @@ const (
|
|||||||
RelationSetEdit = common.RelationSetEdit
|
RelationSetEdit = common.RelationSetEdit
|
||||||
RelationSetAdmin = common.RelationSetAdmin
|
RelationSetAdmin = common.RelationSetAdmin
|
||||||
|
|
||||||
RelationRead = common.RelationRead
|
RelationGet = common.RelationGet
|
||||||
RelationWrite = common.RelationWrite
|
RelationUpdate = common.RelationUpdate
|
||||||
RelationCreate = common.RelationCreate
|
RelationCreate = common.RelationCreate
|
||||||
RelationDelete = common.RelationDelete
|
RelationDelete = common.RelationDelete
|
||||||
RelationPermissionsRead = common.RelationPermissionsRead
|
|
||||||
RelationPermissionsWrite = common.RelationPermissionsWrite
|
|
||||||
|
|
||||||
RelationFolderResourceSetView = common.RelationFolderResourceSetView
|
RelationFolderResourceSetView = common.RelationFolderResourceSetView
|
||||||
RelationFolderResourceSetEdit = common.RelationFolderResourceSetEdit
|
RelationFolderResourceSetEdit = common.RelationFolderResourceSetEdit
|
||||||
RelationFolderResourceSetAdmin = common.RelationFolderResourceSetAdmin
|
RelationFolderResourceSetAdmin = common.RelationFolderResourceSetAdmin
|
||||||
|
|
||||||
RelationFolderResourceRead = common.RelationFolderResourceRead
|
RelationFolderResourceRead = common.RelationFolderResourceGet
|
||||||
RelationFolderResourceWrite = common.RelationFolderResourceWrite
|
RelationFolderResourceWrite = common.RelationFolderResourceUpdate
|
||||||
RelationFolderResourceCreate = common.RelationFolderResourceCreate
|
RelationFolderResourceCreate = common.RelationFolderResourceCreate
|
||||||
RelationFolderResourceDelete = common.RelationFolderResourceDelete
|
RelationFolderResourceDelete = common.RelationFolderResourceDelete
|
||||||
RelationFolderResourcePermissionsRead = common.RelationFolderResourcePermissionsRead
|
|
||||||
RelationFolderResourcePermissionsWrite = common.RelationFolderResourcePermissionsWrite
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
FolderRelations = common.FolderRelations
|
RelationsFolder = common.RelationsFolder
|
||||||
ResourceRelations = common.ResourceRelations
|
RelationsFolderResource = common.RelationsFolder
|
||||||
|
RelationsResouce = common.RelationsResource
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
Loading…
Reference in New Issue
Block a user