mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Zanzana: change type name from namespace to group_resource (#97741)
* Change type name from namespace to group_resource * update function names and test descriptions
This commit is contained in:
parent
b4927ff1cd
commit
87ba9c60b2
@ -16,9 +16,12 @@ const (
|
||||
TypeRenderService string = "render"
|
||||
TypeTeam string = "team"
|
||||
TypeRole string = "role"
|
||||
TypeFolder string = "folder"
|
||||
TypeResource string = "resource"
|
||||
TypeNamespace string = "namespace"
|
||||
)
|
||||
|
||||
const (
|
||||
TypeFolder string = "folder"
|
||||
TypeResource string = "resource"
|
||||
TypeGroupResouce string = "group_resource"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -46,8 +49,8 @@ const (
|
||||
RelationFolderResourceDelete string = "resource_" + RelationDelete
|
||||
)
|
||||
|
||||
// RelationsNamespace are relations that can be added on type "namespace".
|
||||
var RelationsNamespace = []string{
|
||||
// RelationsGroupResource are relations that can be added on type "group_resource".
|
||||
var RelationsGroupResource = []string{
|
||||
RelationGet,
|
||||
RelationUpdate,
|
||||
RelationCreate,
|
||||
@ -78,8 +81,8 @@ var RelationsFolder = append(
|
||||
RelationDelete,
|
||||
)
|
||||
|
||||
func IsNamespaceRelation(relation string) bool {
|
||||
return isValidRelation(relation, RelationsNamespace)
|
||||
func IsGroupResourceRelation(relation string) bool {
|
||||
return isValidRelation(relation, RelationsGroupResource)
|
||||
}
|
||||
|
||||
func IsFolderResourceRelation(relation string) bool {
|
||||
@ -115,8 +118,8 @@ func NewFolderIdent(name string) string {
|
||||
return fmt.Sprintf("%s:%s", TypeFolder, name)
|
||||
}
|
||||
|
||||
func NewNamespaceResourceIdent(group, resource string) string {
|
||||
return fmt.Sprintf("%s:%s", TypeNamespace, FormatGroupResource(group, resource))
|
||||
func NewGroupResourceIdent(group, resource string) string {
|
||||
return fmt.Sprintf("%s:%s", TypeGroupResouce, FormatGroupResource(group, resource))
|
||||
}
|
||||
|
||||
func FormatGroupResource(group, resource string) string {
|
||||
@ -169,11 +172,11 @@ func NewFolderResourceTuple(subject, relation, group, resource, folder string) *
|
||||
}
|
||||
}
|
||||
|
||||
func NewNamespaceResourceTuple(subject, relation, group, resource string) *openfgav1.TupleKey {
|
||||
func NewGroupResourceTuple(subject, relation, group, resource string) *openfgav1.TupleKey {
|
||||
return &openfgav1.TupleKey{
|
||||
User: subject,
|
||||
Relation: relation,
|
||||
Object: NewNamespaceResourceIdent(group, resource),
|
||||
Object: NewGroupResourceIdent(group, resource),
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,7 +292,7 @@ func AddRenderContext(req *openfgav1.CheckRequest) {
|
||||
req.ContextualTuples.TupleKeys = append(req.ContextualTuples.TupleKeys, &openfgav1.TupleKey{
|
||||
User: req.TupleKey.User,
|
||||
Relation: RelationSetView,
|
||||
Object: NewNamespaceResourceIdent(
|
||||
Object: NewGroupResourceIdent(
|
||||
dashboardalpha1.DashboardResourceInfo.GroupResource().Group,
|
||||
dashboardalpha1.DashboardResourceInfo.GroupResource().Resource,
|
||||
),
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
Here's some notes about [OpenFGA authorization model](https://openfga.dev/docs/modeling/getting-started) (schema) using to model access control in Grafana.
|
||||
|
||||
## Namespace level permissions
|
||||
## GroupResource level permissions
|
||||
|
||||
A relation to a namespace object grant access to all objects of the GroupResource in the entire namespace.
|
||||
They take the form of `{ “user”: “user:1”, relation: “read”, object:”namespace:dashboard.grafana.app/dashboard” }`. This
|
||||
A relation to a group_resource object grants access to all objects of the GroupResource.
|
||||
They take the form of `{ “user”: “user:1”, relation: “read”, object:”group_resource:dashboard.grafana.app/dashboard” }`. This
|
||||
example would grant `user:1` access to all `dashboard.grafana.app/dashboard` in the namespace.
|
||||
|
||||
## Folder level permissions
|
||||
|
@ -6,24 +6,12 @@ type service-account
|
||||
|
||||
type render
|
||||
|
||||
type namespace
|
||||
relations
|
||||
define view: [user, service-account, render, team#member, role#assignee] or edit
|
||||
define edit: [user, service-account, team#member, role#assignee] or admin
|
||||
define admin: [user, service-account, team#member, role#assignee]
|
||||
|
||||
define get: [user, service-account, render, team#member, role#assignee] or view
|
||||
define create: [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
|
||||
|
||||
type role
|
||||
relations
|
||||
define assignee: [user, service-account, team#member, role#assignee]
|
||||
|
||||
type team
|
||||
relations
|
||||
# Action sets
|
||||
define admin: [user, service-account]
|
||||
define member: [user, service-account] or admin
|
||||
|
||||
|
@ -11,6 +11,17 @@ extend type folder
|
||||
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
|
||||
|
||||
type group_resource
|
||||
relations
|
||||
define view: [user, service-account, render, team#member, role#assignee] or edit
|
||||
define edit: [user, service-account, team#member, role#assignee] or admin
|
||||
define admin: [user, service-account, team#member, role#assignee]
|
||||
|
||||
define get: [user, service-account, render, team#member, role#assignee] or view
|
||||
define create: [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
|
||||
|
||||
type resource
|
||||
relations
|
||||
define view: [user with group_filter, service-account with group_filter, team#member with group_filter, role#assignee with group_filter] or edit
|
||||
|
@ -56,7 +56,7 @@ func (s *Server) batchCheckItem(
|
||||
|
||||
allowed, ok := groupResourceAccess[groupResource]
|
||||
if !ok {
|
||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, item.GetGroup(), item.GetResource(), store)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, item.GetGroup(), item.GetResource(), store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ func testBatchCheck(t *testing.T, server *Server) {
|
||||
assert.False(t, res.Groups[groupResource].Items["2"])
|
||||
})
|
||||
|
||||
t.Run("user:2 should be able to read resource:dashboard.grafana.app/dashboards/{1,2} through namespace", func(t *testing.T) {
|
||||
t.Run("user:2 should be able to read resource:dashboard.grafana.app/dashboards/{1,2} through group_resource", func(t *testing.T) {
|
||||
groupResource := zanzana.FormatGroupResource(dashboardGroup, dashboardResource)
|
||||
res, err := server.BatchCheck(context.Background(), newReq("user:2", utils.VerbGet, dashboardGroup, dashboardResource, []*authzextv1.BatchCheckItem{
|
||||
{Name: "1", Folder: "1"},
|
||||
@ -108,7 +108,7 @@ func testBatchCheck(t *testing.T, server *Server) {
|
||||
assert.False(t, res.Groups[groupResource].Items["2"])
|
||||
})
|
||||
|
||||
t.Run("user:7 should be able to read folder {1,2} through namespace access", func(t *testing.T) {
|
||||
t.Run("user:7 should be able to read folder {1,2} through group_resource access", func(t *testing.T) {
|
||||
groupResource := zanzana.FormatGroupResource(folderGroup, folderResource)
|
||||
res, err := server.BatchCheck(context.Background(), newReq("user:7", utils.VerbGet, folderGroup, folderResource, []*authzextv1.BatchCheckItem{
|
||||
{Name: "1"},
|
||||
|
@ -22,7 +22,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) {
|
||||
out := make([]string, 0, len(common.RelationsResource))
|
||||
for _, relation := range info.Relations {
|
||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -48,7 +48,7 @@ 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) {
|
||||
out := make([]string, 0, len(common.RelationsResource))
|
||||
for _, relation := range common.RelationsResource {
|
||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func testCapabilities(t *testing.T, server *Server) {
|
||||
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 group_resource", func(t *testing.T) {
|
||||
res, err := server.Capabilities(context.Background(), newReq("user:2", dashboardGroup, dashboardResource, "1", "1"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{common.RelationGet, common.RelationUpdate}, res.GetCapabilities())
|
||||
@ -59,7 +59,7 @@ func testCapabilities(t *testing.T, server *Server) {
|
||||
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 group_resource access", func(t *testing.T) {
|
||||
res, err := server.Capabilities(context.Background(), newReq("user:7", folderGroup, folderResource, "", "1"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{common.RelationGet}, res.GetCapabilities())
|
||||
|
@ -21,7 +21,7 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C
|
||||
}
|
||||
|
||||
relation := common.VerbMapping[r.GetVerb()]
|
||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -36,10 +36,10 @@ 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)
|
||||
}
|
||||
|
||||
// checkTyped checks on the root "namespace". If subject has access through the namespace they have access to
|
||||
// every resource for that "GroupResource".
|
||||
func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, resource string, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
if !common.IsNamespaceRelation(relation) {
|
||||
// checkGroupResource check if subject has access to the full "GroupResource", if they do they can access every object
|
||||
// within it.
|
||||
func (s *Server) checkGroupResource(ctx context.Context, subject, relation, group, resource string, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
if !common.IsGroupResourceRelation(relation) {
|
||||
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, r
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: subject,
|
||||
Relation: relation,
|
||||
Object: common.NewNamespaceResourceIdent(group, resource),
|
||||
Object: common.NewGroupResourceIdent(group, resource),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ func testCheck(t *testing.T, server *Server) {
|
||||
assert.False(t, res.GetAllowed())
|
||||
})
|
||||
|
||||
t.Run("user:2 should be able to read resource:dashboard.grafana.app/dashboards/1 through namespace", func(t *testing.T) {
|
||||
t.Run("user:2 should be able to read resource:dashboard.grafana.app/dashboards/1 through group_resource", func(t *testing.T) {
|
||||
res, err := server.Check(context.Background(), newReq("user:2", utils.VerbGet, dashboardGroup, dashboardResource, "1", "1"))
|
||||
require.NoError(t, err)
|
||||
assert.True(t, res.GetAllowed())
|
||||
@ -83,7 +83,7 @@ func testCheck(t *testing.T, server *Server) {
|
||||
assert.True(t, res.GetAllowed())
|
||||
})
|
||||
|
||||
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 group_resource access", func(t *testing.T) {
|
||||
res, err := server.Check(context.Background(), newReq("user:7", utils.VerbGet, folderGroup, folderResource, "", "1"))
|
||||
require.NoError(t, err)
|
||||
assert.True(t, res.GetAllowed())
|
||||
|
@ -22,7 +22,7 @@ func (s *Server) List(ctx context.Context, r *authzextv1.ListRequest) (*authzext
|
||||
|
||||
relation := common.VerbMapping[r.GetVerb()]
|
||||
|
||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -82,14 +82,14 @@ func setup(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
|
||||
TupleKeys: []*openfgav1.TupleKey{
|
||||
common.NewResourceTuple("user:1", common.RelationGet, dashboardGroup, dashboardResource, "1"),
|
||||
common.NewResourceTuple("user:1", common.RelationUpdate, dashboardGroup, dashboardResource, "1"),
|
||||
common.NewNamespaceResourceTuple("user:2", common.RelationGet, dashboardGroup, dashboardResource),
|
||||
common.NewNamespaceResourceTuple("user:2", common.RelationUpdate, dashboardGroup, dashboardResource),
|
||||
common.NewGroupResourceTuple("user:2", common.RelationGet, dashboardGroup, dashboardResource),
|
||||
common.NewGroupResourceTuple("user:2", common.RelationUpdate, dashboardGroup, dashboardResource),
|
||||
common.NewResourceTuple("user:3", common.RelationSetView, dashboardGroup, dashboardResource, "1"),
|
||||
common.NewFolderResourceTuple("user:4", common.RelationGet, dashboardGroup, dashboardResource, "1"),
|
||||
common.NewFolderResourceTuple("user:4", common.RelationGet, dashboardGroup, dashboardResource, "3"),
|
||||
common.NewFolderResourceTuple("user:5", common.RelationSetEdit, dashboardGroup, dashboardResource, "1"),
|
||||
common.NewFolderTuple("user:6", common.RelationGet, "1"),
|
||||
common.NewNamespaceResourceTuple("user:7", common.RelationGet, folderGroup, folderResource),
|
||||
common.NewGroupResourceTuple("user:7", common.RelationGet, folderGroup, folderResource),
|
||||
common.NewFolderParentTuple("5", "4"),
|
||||
common.NewFolderParentTuple("6", "5"),
|
||||
common.NewFolderResourceTuple("user:8", common.RelationSetEdit, dashboardGroup, dashboardResource, "5"),
|
||||
|
@ -18,7 +18,7 @@ const (
|
||||
TypeRole = common.TypeRole
|
||||
TypeFolder = common.TypeFolder
|
||||
TypeResource = common.TypeResource
|
||||
TypeNamespace = common.TypeNamespace
|
||||
TypeNamespace = common.TypeGroupResouce
|
||||
)
|
||||
|
||||
const (
|
||||
@ -95,7 +95,7 @@ func TranslateToResourceTuple(subject string, action, kind, name string) (*openf
|
||||
}
|
||||
|
||||
if name == "*" {
|
||||
return common.NewNamespaceResourceTuple(subject, m.relation, translation.group, translation.resource), true
|
||||
return common.NewGroupResourceTuple(subject, m.relation, translation.group, translation.resource), true
|
||||
}
|
||||
|
||||
if translation.typ == TypeResource {
|
||||
|
Loading…
Reference in New Issue
Block a user