mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
MT AuthZ: Resolve renderer permissions in MT authZ service (#99362)
* resolve renderer permissions in MT authZ service * also include DS read perms * fix tests and linting
This commit is contained in:
parent
4fb7b47971
commit
723fa7ddf9
@ -41,9 +41,13 @@ func (c *Render) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ide
|
||||
}
|
||||
|
||||
if renderUsr.UserID <= 0 {
|
||||
identityType := claims.TypeAnonymous
|
||||
if org.RoleType(renderUsr.OrgRole) == org.RoleAdmin {
|
||||
identityType = claims.TypeRenderService
|
||||
}
|
||||
return &authn.Identity{
|
||||
ID: "0",
|
||||
Type: claims.TypeRenderService,
|
||||
Type: identityType,
|
||||
OrgID: renderUsr.OrgID,
|
||||
OrgRoles: map[int64]org.RoleType{renderUsr.OrgID: org.RoleType(renderUsr.OrgRole)},
|
||||
ClientParams: authn.ClientParams{SyncPermissions: true},
|
||||
|
@ -29,7 +29,29 @@ func TestRender_Authenticate(t *testing.T) {
|
||||
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "expect valid render key to return render user identity",
|
||||
desc: "expect valid render key to return anonymous user identity for org role Viewer",
|
||||
renderKey: "123",
|
||||
req: &authn.Request{
|
||||
HTTPRequest: &http.Request{
|
||||
Header: map[string][]string{"Cookie": {"renderKey=123"}},
|
||||
},
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "0",
|
||||
Type: claims.TypeAnonymous,
|
||||
OrgID: 1,
|
||||
OrgRoles: map[int64]org.RoleType{1: org.RoleViewer},
|
||||
AuthenticatedBy: login.RenderModule,
|
||||
ClientParams: authn.ClientParams{SyncPermissions: true},
|
||||
},
|
||||
expectedRenderUsr: &rendering.RenderUser{
|
||||
OrgID: 1,
|
||||
UserID: 0,
|
||||
OrgRole: "Viewer",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "expect valid render key to return render user identity for org role Admin",
|
||||
renderKey: "123",
|
||||
req: &authn.Request{
|
||||
HTTPRequest: &http.Request{
|
||||
@ -40,14 +62,14 @@ func TestRender_Authenticate(t *testing.T) {
|
||||
ID: "0",
|
||||
Type: claims.TypeRenderService,
|
||||
OrgID: 1,
|
||||
OrgRoles: map[int64]org.RoleType{1: org.RoleViewer},
|
||||
OrgRoles: map[int64]org.RoleType{1: org.RoleAdmin},
|
||||
AuthenticatedBy: login.RenderModule,
|
||||
ClientParams: authn.ClientParams{SyncPermissions: true},
|
||||
},
|
||||
expectedRenderUsr: &rendering.RenderUser{
|
||||
OrgID: 1,
|
||||
UserID: 0,
|
||||
OrgRole: "Viewer",
|
||||
OrgRole: "Admin",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -98,7 +98,7 @@ func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv
|
||||
}
|
||||
ctx = request.WithNamespace(ctx, req.GetNamespace())
|
||||
|
||||
permissions, err := s.getUserPermissions(ctx, checkReq.Namespace, checkReq.IdentityType, checkReq.UserUID, checkReq.Action)
|
||||
permissions, err := s.getIdentityPermissions(ctx, checkReq.Namespace, checkReq.IdentityType, checkReq.UserUID, checkReq.Action)
|
||||
if err != nil {
|
||||
ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err)
|
||||
return deny, err
|
||||
@ -124,7 +124,7 @@ func (s *Service) List(ctx context.Context, req *authzv1.ListRequest) (*authzv1.
|
||||
}
|
||||
ctx = request.WithNamespace(ctx, req.GetNamespace())
|
||||
|
||||
permissions, err := s.getUserPermissions(ctx, listReq.Namespace, listReq.IdentityType, listReq.UserUID, listReq.Action)
|
||||
permissions, err := s.getIdentityPermissions(ctx, listReq.Namespace, listReq.IdentityType, listReq.UserUID, listReq.Action)
|
||||
if err != nil {
|
||||
ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err)
|
||||
return nil, err
|
||||
@ -226,8 +226,8 @@ func (s *Service) validateSubject(ctx context.Context, subject string) (string,
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// Permission check currently only checks user, anonymous user and service account permissions
|
||||
if !(identityType == claims.TypeUser || identityType == claims.TypeServiceAccount || identityType == claims.TypeAnonymous) {
|
||||
// Permission check currently only checks user, anonymous user, service account and renderer permissions
|
||||
if !(identityType == claims.TypeUser || identityType == claims.TypeServiceAccount || identityType == claims.TypeAnonymous || identityType == claims.TypeRenderService) {
|
||||
ctxLogger.Error("unsupported identity type", "type", identityType)
|
||||
return "", "", status.Error(codes.PermissionDenied, "unsupported identity type")
|
||||
}
|
||||
@ -252,8 +252,8 @@ func (s *Service) validateAction(ctx context.Context, group, resource, verb stri
|
||||
return action, nil
|
||||
}
|
||||
|
||||
func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInfo, idType claims.IdentityType, userID, action string) (map[string]bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getUserPermissions")
|
||||
func (s *Service) getIdentityPermissions(ctx context.Context, ns claims.NamespaceInfo, idType claims.IdentityType, userID, action string) (map[string]bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getIdentityPermissions")
|
||||
defer span.End()
|
||||
|
||||
// When checking folder creation permissions, also check edit and admin action sets for folder, as the scoped folder create actions aren't stored in the DB separately
|
||||
@ -263,9 +263,21 @@ func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInf
|
||||
actionSets = append(actionSets, "folders:admin")
|
||||
}
|
||||
|
||||
if idType == claims.TypeAnonymous {
|
||||
switch idType {
|
||||
case claims.TypeAnonymous:
|
||||
return s.getAnonymousPermissions(ctx, ns, action, actionSets)
|
||||
case claims.TypeRenderService:
|
||||
return s.getRendererPermissions(ctx, action)
|
||||
case claims.TypeUser, claims.TypeServiceAccount:
|
||||
return s.getUserPermissions(ctx, ns, userID, action, actionSets)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported identity type: %s", idType)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInfo, userID, action string, actionSets []string) (map[string]bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getUserPermissions")
|
||||
defer span.End()
|
||||
|
||||
userIdentifiers, err := s.GetUserIdentifiers(ctx, ns, userID)
|
||||
if err != nil {
|
||||
@ -342,6 +354,17 @@ func (s *Service) getAnonymousPermissions(ctx context.Context, ns claims.Namespa
|
||||
return res.(map[string]bool), nil
|
||||
}
|
||||
|
||||
// Renderer is granted permissions to read all dashboards and folders, and no other permissions
|
||||
func (s *Service) getRendererPermissions(ctx context.Context, action string) (map[string]bool, error) {
|
||||
_, span := s.tracer.Start(ctx, "authz_direct_db.service.getRendererPermissions")
|
||||
defer span.End()
|
||||
|
||||
if action == "dashboards:read" || action == "folders:read" || action == "datasources:read" {
|
||||
return map[string]bool{"*": true}, nil
|
||||
}
|
||||
return map[string]bool{}, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetUserIdentifiers(ctx context.Context, ns claims.NamespaceInfo, userUID string) (*store.UserIdentifiers, error) {
|
||||
uidCacheKey := userIdentifierCacheKey(ns.Value, userUID)
|
||||
if cached, ok := s.idCache.Get(uidCacheKey); ok {
|
||||
|
@ -379,7 +379,7 @@ func TestService_getUserPermissions(t *testing.T) {
|
||||
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
||||
}
|
||||
|
||||
perms, err := s.getUserPermissions(ctx, ns, claims.TypeUser, userID.UID, action)
|
||||
perms, err := s.getIdentityPermissions(ctx, ns, claims.TypeUser, userID.UID, action)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, perms, len(tc.expectedPerms))
|
||||
for _, perm := range tc.permissions {
|
||||
|
Loading…
Reference in New Issue
Block a user