mirror of https://github.com/grafana/grafana
parent
5533b30135
commit
95379dcd23
@ -0,0 +1,24 @@ |
|||||||
|
package common |
||||||
|
|
||||||
|
// Common relation for each resource
|
||||||
|
const ( |
||||||
|
RelationView string = "view" |
||||||
|
RelationEdit string = "edit" |
||||||
|
RelationAdmin string = "admin" |
||||||
|
|
||||||
|
RelationRead string = "read" |
||||||
|
RelationWrite string = "write" |
||||||
|
RelationCreate string = "create" |
||||||
|
RelationDelete string = "delete" |
||||||
|
RelationPermissionsRead string = "permissions_read" |
||||||
|
RelationPermissionsWrite string = "permissions_write" |
||||||
|
) |
||||||
|
|
||||||
|
var ResourceRelations = [...]string{ |
||||||
|
RelationRead, |
||||||
|
RelationWrite, |
||||||
|
RelationCreate, |
||||||
|
RelationDelete, |
||||||
|
RelationPermissionsRead, |
||||||
|
RelationPermissionsWrite, |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package server |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
authzv1 "github.com/grafana/authlib/authz/proto/v1" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/authz/zanzana/common" |
||||||
|
authzextv1 "github.com/grafana/grafana/pkg/services/authz/zanzana/proto/v1" |
||||||
|
) |
||||||
|
|
||||||
|
func (s *Server) Capabilities(ctx context.Context, r *authzextv1.CapabilitiesRequest) (*authzextv1.CapabilitiesResponse, error) { |
||||||
|
if info, ok := common.GetTypeInfo(r.Group, r.Resource); ok { |
||||||
|
return s.capabilitiesTyped(ctx, r, info) |
||||||
|
} |
||||||
|
return s.capabilitiesGeneric(ctx, r) |
||||||
|
} |
||||||
|
|
||||||
|
func (s *Server) capabilitiesTyped(ctx context.Context, r *authzextv1.CapabilitiesRequest, info common.TypeInfo) (*authzextv1.CapabilitiesResponse, error) { |
||||||
|
out := make([]string, 0, len(common.ResourceRelations)) |
||||||
|
for _, relation := range common.ResourceRelations { |
||||||
|
res, err := s.checkTyped(ctx, &authzv1.CheckRequest{ |
||||||
|
Subject: r.Subject, |
||||||
|
Group: r.Group, |
||||||
|
Resource: r.Resource, |
||||||
|
Namespace: r.Namespace, |
||||||
|
Name: r.Name, |
||||||
|
Folder: r.Folder, |
||||||
|
Subresource: r.Subresource, |
||||||
|
Path: r.Path, |
||||||
|
}, info, relation) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
if res.GetAllowed() { |
||||||
|
out = append(out, relation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return &authzextv1.CapabilitiesResponse{Capabilities: out}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s *Server) capabilitiesGeneric(ctx context.Context, r *authzextv1.CapabilitiesRequest) (*authzextv1.CapabilitiesResponse, error) { |
||||||
|
out := make([]string, 0, len(common.ResourceRelations)) |
||||||
|
for _, relation := range common.ResourceRelations { |
||||||
|
res, err := s.checkGeneric(ctx, &authzv1.CheckRequest{ |
||||||
|
Subject: r.Subject, |
||||||
|
Group: r.Group, |
||||||
|
Resource: r.Resource, |
||||||
|
Namespace: r.Namespace, |
||||||
|
Name: r.Name, |
||||||
|
Folder: r.Folder, |
||||||
|
Subresource: r.Subresource, |
||||||
|
Path: r.Path, |
||||||
|
}, relation) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
if res.GetAllowed() { |
||||||
|
out = append(out, relation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return &authzextv1.CapabilitiesResponse{Capabilities: out}, nil |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
package server |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/authz/zanzana/common" |
||||||
|
authzextv1 "github.com/grafana/grafana/pkg/services/authz/zanzana/proto/v1" |
||||||
|
) |
||||||
|
|
||||||
|
func testCapabilities(t *testing.T, server *Server) { |
||||||
|
newReq := func(subject, group, resource, folder, name string) *authzextv1.CapabilitiesRequest { |
||||||
|
return &authzextv1.CapabilitiesRequest{ |
||||||
|
// FIXME: namespace should map to store
|
||||||
|
// Namespace: storeID,
|
||||||
|
Subject: subject, |
||||||
|
Group: group, |
||||||
|
Resource: resource, |
||||||
|
Name: name, |
||||||
|
Folder: folder, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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")) |
||||||
|
require.NoError(t, err) |
||||||
|
assert.Equal(t, res.GetCapabilities(), []string{common.RelationRead, common.RelationWrite}) |
||||||
|
}) |
||||||
|
|
||||||
|
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")) |
||||||
|
require.NoError(t, err) |
||||||
|
assert.Equal(t, res.GetCapabilities(), []string{common.RelationRead, common.RelationWrite}) |
||||||
|
}) |
||||||
|
|
||||||
|
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")) |
||||||
|
require.NoError(t, err) |
||||||
|
assert.Equal(t, res.GetCapabilities(), []string{common.RelationRead}) |
||||||
|
}) |
||||||
|
|
||||||
|
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")) |
||||||
|
require.NoError(t, err) |
||||||
|
assert.Equal(t, res.GetCapabilities(), []string{common.RelationRead}) |
||||||
|
}) |
||||||
|
|
||||||
|
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")) |
||||||
|
require.NoError(t, err) |
||||||
|
assert.Equal(t, res.GetCapabilities(), []string{common.RelationRead, common.RelationWrite, common.RelationCreate, common.RelationDelete}) |
||||||
|
}) |
||||||
|
|
||||||
|
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")) |
||||||
|
require.NoError(t, err) |
||||||
|
assert.Equal(t, res.GetCapabilities(), []string{common.RelationRead}) |
||||||
|
}) |
||||||
|
|
||||||
|
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")) |
||||||
|
require.NoError(t, err) |
||||||
|
assert.Equal(t, res.GetCapabilities(), []string{common.RelationRead}) |
||||||
|
}) |
||||||
|
} |
Loading…
Reference in new issue