From 6fbf34674775b2ddb8bdf176405c33daf6058755 Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Fri, 11 Feb 2022 17:40:43 +0100 Subject: [PATCH] AccessControl: Add endpoint to get user permissions (#45309) * AccessControl: Add endpoint to get user permissions Co-authored-by: ievaVasiljeva Co-authored-by: Kalle Persson Co-authored-by: Eric Leijonmarck Co-authored-by: Alexander Zobnin * Fix SA tests * Linter is wrong :p * Wait I was wrong * Adding the route for teams:creator too Co-authored-by: ievaVasiljeva Co-authored-by: Kalle Persson Co-authored-by: Eric Leijonmarck Co-authored-by: Alexander Zobnin --- pkg/api/common_test.go | 14 ++++---- pkg/api/datasources.go | 3 +- pkg/api/index.go | 2 +- pkg/api/org_users.go | 2 +- pkg/api/team.go | 4 +-- pkg/api/user.go | 2 +- pkg/services/accesscontrol/accesscontrol.go | 6 +++- pkg/services/accesscontrol/api/api.go | 34 +++++++++++++++++++ .../accesscontrol/middleware/middleware.go | 3 +- pkg/services/accesscontrol/mock/mock.go | 11 +++--- .../ossaccesscontrol/ossaccesscontrol.go | 16 +++++++-- .../ossaccesscontrol/ossaccesscontrol_test.go | 4 ++- pkg/services/serviceaccounts/api/api.go | 2 +- pkg/services/serviceaccounts/api/api_test.go | 10 +++--- .../serviceaccounts/api/token_test.go | 14 ++++---- pkg/services/serviceaccounts/tests/common.go | 4 ++- public/app/core/services/context_srv.ts | 14 +++++++- public/app/features/teams/CreateTeam.tsx | 2 ++ public/app/routes/routes.tsx | 2 +- 19 files changed, 110 insertions(+), 39 deletions(-) create mode 100644 pkg/services/accesscontrol/api/api.go diff --git a/pkg/api/common_test.go b/pkg/api/common_test.go index 2d8884e403f..ce94ba3e77e 100644 --- a/pkg/api/common_test.go +++ b/pkg/api/common_test.go @@ -279,12 +279,13 @@ type accessControlScenarioContext struct { } func setAccessControlPermissions(acmock *accesscontrolmock.Mock, perms []*accesscontrol.Permission, org int64) { - acmock.GetUserPermissionsFunc = func(_ context.Context, u *models.SignedInUser) ([]*accesscontrol.Permission, error) { - if u.OrgId == org { - return perms, nil + acmock.GetUserPermissionsFunc = + func(_ context.Context, u *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { + if u.OrgId == org { + return perms, nil + } + return nil, nil } - return nil, nil - } } // setInitCtxSignedInUser sets a copy of the user in initCtx @@ -370,7 +371,8 @@ func setupHTTPServerWithCfg(t *testing.T, useFakeAccessControl, enableAccessCont require.NoError(t, err) hs.TeamPermissionsService = teamPermissionService } else { - ac := ossaccesscontrol.ProvideService(hs.Features, &usagestats.UsageStatsMock{T: t}, database.ProvideService(db)) + ac := ossaccesscontrol.ProvideService(hs.Features, &usagestats.UsageStatsMock{T: t}, + database.ProvideService(db), routing.NewRouteRegister()) hs.AccessControl = ac // Perform role registration err := hs.declareFixedRoles() diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 5e03b0b37ab..138635024c6 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -76,7 +76,8 @@ func (hs *HTTPServer) getDataSourceAccessControlMetadata(c *models.ReqContext, d return nil, nil } - userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser) + userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser, + accesscontrol.Options{ReloadCache: false}) if err != nil || len(userPermissions) == 0 { return nil, err } diff --git a/pkg/api/index.go b/pkg/api/index.go index a800635e161..3f7a17e5764 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -625,7 +625,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat } if hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) { - userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser) + userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser, ac.Options{ReloadCache: false}) if err != nil { return nil, err } diff --git a/pkg/api/org_users.go b/pkg/api/org_users.go index 8bbd2a1eba4..dfc70933943 100644 --- a/pkg/api/org_users.go +++ b/pkg/api/org_users.go @@ -118,7 +118,7 @@ func (hs *HTTPServer) getUserAccessControlMetadata(c *models.ReqContext, resourc return nil, nil } - userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser) + userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser, accesscontrol.Options{ReloadCache: false}) if err != nil || len(userPermissions) == 0 { return nil, err } diff --git a/pkg/api/team.go b/pkg/api/team.go index d22cf601943..74a66238af6 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -110,7 +110,7 @@ func (hs *HTTPServer) getTeamsAccessControlMetadata(c *models.ReqContext, teamID return nil, nil } - userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser) + userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser, accesscontrol.Options{ReloadCache: false}) if err != nil || len(userPermissions) == 0 { hs.log.Warn("could not fetch accesscontrol metadata for teams", "error", err) return nil, err @@ -175,7 +175,7 @@ func (hs *HTTPServer) getTeamAccessControlMetadata(c *models.ReqContext, teamID return nil, nil } - userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser) + userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser, accesscontrol.Options{ReloadCache: false}) if err != nil || len(userPermissions) == 0 { hs.log.Warn("could not fetch accesscontrol metadata", "team", teamID, "error", err) return nil, err diff --git a/pkg/api/user.go b/pkg/api/user.go index fac5aa8acbf..64f609fff43 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -64,7 +64,7 @@ func (hs *HTTPServer) getGlobalUserAccessControlMetadata(c *models.ReqContext, u return nil, nil } - userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser) + userPermissions, err := hs.AccessControl.GetUserPermissions(c.Req.Context(), c.SignedInUser, accesscontrol.Options{ReloadCache: false}) if err != nil || len(userPermissions) == 0 { return nil, err } diff --git a/pkg/services/accesscontrol/accesscontrol.go b/pkg/services/accesscontrol/accesscontrol.go index 2d29e8347cb..c05280f5693 100644 --- a/pkg/services/accesscontrol/accesscontrol.go +++ b/pkg/services/accesscontrol/accesscontrol.go @@ -7,12 +7,16 @@ import ( "github.com/grafana/grafana/pkg/models" ) +type Options struct { + ReloadCache bool +} + type AccessControl interface { // Evaluate evaluates access to the given resources. Evaluate(ctx context.Context, user *models.SignedInUser, evaluator Evaluator) (bool, error) // GetUserPermissions returns user permissions. - GetUserPermissions(ctx context.Context, user *models.SignedInUser) ([]*Permission, error) + GetUserPermissions(ctx context.Context, user *models.SignedInUser, options Options) ([]*Permission, error) // GetUserRoles returns user roles. GetUserRoles(ctx context.Context, user *models.SignedInUser) ([]*RoleDTO, error) diff --git a/pkg/services/accesscontrol/api/api.go b/pkg/services/accesscontrol/api/api.go new file mode 100644 index 00000000000..4477dfe9eca --- /dev/null +++ b/pkg/services/accesscontrol/api/api.go @@ -0,0 +1,34 @@ +package api + +import ( + "net/http" + + "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/api/routing" + "github.com/grafana/grafana/pkg/middleware" + "github.com/grafana/grafana/pkg/models" + ac "github.com/grafana/grafana/pkg/services/accesscontrol" +) + +type AccessControlAPI struct { + RouteRegister routing.RouteRegister + AccessControl ac.AccessControl +} + +func (api *AccessControlAPI) RegisterAPIEndpoints() { + // Users + api.RouteRegister.Get("/api/access-control/user/permissions", + middleware.ReqSignedIn, routing.Wrap(api.getUsersPermissions)) +} + +// GET /api/access-control/user/permissions +func (api *AccessControlAPI) getUsersPermissions(c *models.ReqContext) response.Response { + reloadCache := c.QueryBool("reloadcache") + permissions, err := api.AccessControl.GetUserPermissions(c.Req.Context(), + c.SignedInUser, ac.Options{ReloadCache: reloadCache}) + if err != nil { + response.JSON(http.StatusInternalServerError, err) + } + + return response.JSON(http.StatusOK, ac.BuildPermissionsMap(permissions)) +} diff --git a/pkg/services/accesscontrol/middleware/middleware.go b/pkg/services/accesscontrol/middleware/middleware.go index 333cae520af..3242949d0dd 100644 --- a/pkg/services/accesscontrol/middleware/middleware.go +++ b/pkg/services/accesscontrol/middleware/middleware.go @@ -156,7 +156,8 @@ func LoadPermissionsMiddleware(ac accesscontrol.AccessControl) web.Handler { return } - permissions, err := ac.GetUserPermissions(c.Req.Context(), c.SignedInUser) + permissions, err := ac.GetUserPermissions(c.Req.Context(), c.SignedInUser, + accesscontrol.Options{ReloadCache: false}) if err != nil { c.JsonApiErr(http.StatusForbidden, "", err) return diff --git a/pkg/services/accesscontrol/mock/mock.go b/pkg/services/accesscontrol/mock/mock.go index 5460749f9b9..26125e2f532 100644 --- a/pkg/services/accesscontrol/mock/mock.go +++ b/pkg/services/accesscontrol/mock/mock.go @@ -39,7 +39,7 @@ type Mock struct { // Override functions EvaluateFunc func(context.Context, *models.SignedInUser, accesscontrol.Evaluator) (bool, error) - GetUserPermissionsFunc func(context.Context, *models.SignedInUser) ([]*accesscontrol.Permission, error) + GetUserPermissionsFunc func(context.Context, *models.SignedInUser, accesscontrol.Options) ([]*accesscontrol.Permission, error) GetUserRolesFunc func(context.Context, *models.SignedInUser) ([]*accesscontrol.RoleDTO, error) IsDisabledFunc func() bool DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error @@ -86,7 +86,7 @@ func (m *Mock) Evaluate(ctx context.Context, user *models.SignedInUser, evaluato return m.EvaluateFunc(ctx, user, evaluator) } // Otherwise perform an actual evaluation of the permissions - permissions, err := m.GetUserPermissions(ctx, user) + permissions, err := m.GetUserPermissions(ctx, user, accesscontrol.Options{ReloadCache: false}) if err != nil { return false, err } @@ -95,11 +95,12 @@ func (m *Mock) Evaluate(ctx context.Context, user *models.SignedInUser, evaluato // GetUserPermissions returns user permissions. // This mock return m.permissions unless an override is provided. -func (m *Mock) GetUserPermissions(ctx context.Context, user *models.SignedInUser) ([]*accesscontrol.Permission, error) { - m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user}) +func (m *Mock) GetUserPermissions(ctx context.Context, user *models.SignedInUser, + opts accesscontrol.Options) ([]*accesscontrol.Permission, error) { + m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user, opts}) // Use override if provided if m.GetUserPermissionsFunc != nil { - return m.GetUserPermissionsFunc(ctx, user) + return m.GetUserPermissionsFunc(ctx, user, opts) } // Otherwise return the Permissions list return m.permissions, nil diff --git a/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol.go b/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol.go index 755c2f9e005..efe6dc98118 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol.go @@ -4,19 +4,29 @@ import ( "context" "errors" + "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/accesscontrol/api" "github.com/grafana/grafana/pkg/services/accesscontrol/resourceservices" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/prometheus/client_golang/prometheus" ) -func ProvideService(features featuremgmt.FeatureToggles, usageStats usagestats.Service, provider accesscontrol.PermissionsProvider) *OSSAccessControlService { +func ProvideService(features featuremgmt.FeatureToggles, usageStats usagestats.Service, + provider accesscontrol.PermissionsProvider, routeRegister routing.RouteRegister) *OSSAccessControlService { s := ProvideOSSAccessControl(features, usageStats, provider) s.registerUsageMetrics() + if !s.IsDisabled() { + api := api.AccessControlAPI{ + RouteRegister: routeRegister, + AccessControl: s, + } + api.RegisterAPIEndpoints() + } return s } @@ -75,7 +85,7 @@ func (ac *OSSAccessControlService) Evaluate(ctx context.Context, user *models.Si } if _, ok := user.Permissions[user.OrgId]; !ok { - permissions, err := ac.GetUserPermissions(ctx, user) + permissions, err := ac.GetUserPermissions(ctx, user, accesscontrol.Options{ReloadCache: true}) if err != nil { return false, err } @@ -96,7 +106,7 @@ func (ac *OSSAccessControlService) GetUserRoles(ctx context.Context, user *model } // GetUserPermissions returns user permissions based on built-in roles -func (ac *OSSAccessControlService) GetUserPermissions(ctx context.Context, user *models.SignedInUser) ([]*accesscontrol.Permission, error) { +func (ac *OSSAccessControlService) GetUserPermissions(ctx context.Context, user *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary) defer timer.ObserveDuration() diff --git a/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol_test.go b/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol_test.go index 103e86d498a..7ff45b1e87c 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol_test.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/models" @@ -152,6 +153,7 @@ func TestUsageMetrics(t *testing.T) { featuremgmt.WithFeatures("accesscontrol", tt.enabled), &usagestats.UsageStatsMock{T: t}, database.ProvideService(sqlstore.InitTestDB(t)), + routing.NewRouteRegister(), ) report, err := s.usageStats.GetUsageReport(context.Background()) assert.Nil(t, err) @@ -543,7 +545,7 @@ func TestOSSAccessControlService_GetUserPermissions(t *testing.T) { require.NoError(t, err) // Test - userPerms, err := ac.GetUserPermissions(context.Background(), &tt.user) + userPerms, err := ac.GetUserPermissions(context.Background(), &tt.user, accesscontrol.Options{}) if tt.wantErr { assert.Error(t, err, "Expected an error with GetUserPermissions.") return diff --git a/pkg/services/serviceaccounts/api/api.go b/pkg/services/serviceaccounts/api/api.go index 4498a8d0d54..3c5ade4d439 100644 --- a/pkg/services/serviceaccounts/api/api.go +++ b/pkg/services/serviceaccounts/api/api.go @@ -163,7 +163,7 @@ func (api *ServiceAccountsAPI) getAccessControlMetadata(c *models.ReqContext, sa return nil, nil } - userPermissions, err := api.accesscontrol.GetUserPermissions(c.Req.Context(), c.SignedInUser) + userPermissions, err := api.accesscontrol.GetUserPermissions(c.Req.Context(), c.SignedInUser, accesscontrol.Options{ReloadCache: false}) if err != nil || len(userPermissions) == 0 { api.log.Warn("could not fetch accesscontrol metadata for teams", "error", err) return nil, err diff --git a/pkg/services/serviceaccounts/api/api_test.go b/pkg/services/serviceaccounts/api/api_test.go index a8ce037b38f..84172674f3e 100644 --- a/pkg/services/serviceaccounts/api/api_test.go +++ b/pkg/services/serviceaccounts/api/api_test.go @@ -51,7 +51,7 @@ func TestServiceAccountsAPI_DeleteServiceAccount(t *testing.T) { user: tests.TestUser{Login: "servicetest1@admin", IsServiceAccount: true}, acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionDelete, Scope: serviceaccounts.ScopeAll}}, nil }, false, @@ -75,7 +75,7 @@ func TestServiceAccountsAPI_DeleteServiceAccount(t *testing.T) { user: tests.TestUser{Login: "servicetest2@admin", IsServiceAccount: true}, acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{}, nil }, false, @@ -137,7 +137,7 @@ func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) { user: &tests.TestUser{Login: "servicetest1@admin", IsServiceAccount: true}, acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}}, nil }, false, @@ -149,7 +149,7 @@ func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) { user: &tests.TestUser{Login: "servicetest2@admin", IsServiceAccount: true}, acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{}, nil }, false, @@ -162,7 +162,7 @@ func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) { Id: 12, acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}}, nil }, false, diff --git a/pkg/services/serviceaccounts/api/token_test.go b/pkg/services/serviceaccounts/api/token_test.go index 8f59ad0c875..b404b08549f 100644 --- a/pkg/services/serviceaccounts/api/token_test.go +++ b/pkg/services/serviceaccounts/api/token_test.go @@ -64,7 +64,7 @@ func TestServiceAccountsAPI_CreateToken(t *testing.T) { desc: "should be ok to create serviceaccount token with scope all permissions", acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: serviceaccounts.ScopeAll}}, nil }, false, @@ -76,7 +76,7 @@ func TestServiceAccountsAPI_CreateToken(t *testing.T) { desc: "serviceaccount token should match SA orgID and SA provided in parameters even if specified in body", acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: serviceaccounts.ScopeAll}}, nil }, false, @@ -88,7 +88,7 @@ func TestServiceAccountsAPI_CreateToken(t *testing.T) { desc: "should be ok to create serviceaccount token with scope id permissions", acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: "serviceaccounts:id:1"}}, nil }, false, @@ -100,7 +100,7 @@ func TestServiceAccountsAPI_CreateToken(t *testing.T) { desc: "should be forbidden to create serviceaccount token if wrong scoped", acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: "serviceaccounts:id:2"}}, nil }, false, @@ -171,7 +171,7 @@ func TestServiceAccountsAPI_DeleteToken(t *testing.T) { keyName: "Test1", acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: "serviceaccounts:id:1"}}, nil }, false, @@ -183,7 +183,7 @@ func TestServiceAccountsAPI_DeleteToken(t *testing.T) { keyName: "Test2", acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: serviceaccounts.ScopeAll}}, nil }, false, @@ -195,7 +195,7 @@ func TestServiceAccountsAPI_DeleteToken(t *testing.T) { keyName: "Test3", acmock: tests.SetupMockAccesscontrol( t, - func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) { + func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: "serviceaccounts:id:10"}}, nil }, false, diff --git a/pkg/services/serviceaccounts/tests/common.go b/pkg/services/serviceaccounts/tests/common.go index ed5473ea56f..2262438e022 100644 --- a/pkg/services/serviceaccounts/tests/common.go +++ b/pkg/services/serviceaccounts/tests/common.go @@ -41,7 +41,9 @@ func (s *ServiceAccountMock) Migrated(ctx context.Context, orgID int64) bool { return false } -func SetupMockAccesscontrol(t *testing.T, userpermissionsfunc func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error), disableAccessControl bool) *accesscontrolmock.Mock { +func SetupMockAccesscontrol(t *testing.T, + userpermissionsfunc func(c context.Context, siu *models.SignedInUser, opt accesscontrol.Options) ([]*accesscontrol.Permission, error), + disableAccessControl bool) *accesscontrolmock.Mock { t.Helper() acmock := accesscontrolmock.New() if disableAccessControl { diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index bf719821487..b9dfcaca92a 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -2,7 +2,7 @@ import config from '../../core/config'; import { extend } from 'lodash'; import { rangeUtil, WithAccessControlMetadata } from '@grafana/data'; import { AccessControlAction, UserPermission } from 'app/types'; -import { featureEnabled } from '@grafana/runtime'; +import { featureEnabled, getBackendSrv } from '@grafana/runtime'; export class User { id: number; @@ -66,6 +66,18 @@ export class ContextSrv { this.minRefreshInterval = config.minRefreshInterval; } + async fetchUserPermissions() { + try { + if (this.accessControlEnabled()) { + this.user.permissions = await getBackendSrv().get('/api/access-control/user/permissions', { + reloadcache: true, + }); + } + } catch (e) { + console.error(e); + } + } + /** * Indicate the user has been logged out */ diff --git a/public/app/features/teams/CreateTeam.tsx b/public/app/features/teams/CreateTeam.tsx index 77505494cce..47373c38d76 100644 --- a/public/app/features/teams/CreateTeam.tsx +++ b/public/app/features/teams/CreateTeam.tsx @@ -6,6 +6,7 @@ import { getBackendSrv, locationService } from '@grafana/runtime'; import { connect } from 'react-redux'; import { getNavModel } from 'app/core/selectors/navModel'; import { StoreState } from 'app/types'; +import { contextSrv } from 'app/core/core'; export interface Props { navModel: NavModel; @@ -20,6 +21,7 @@ export class CreateTeam extends PureComponent { create = async (formModel: TeamDTO) => { const result = await getBackendSrv().post('/api/teams', formModel); if (result.teamId) { + await contextSrv.fetchUserPermissions(); locationService.push(`/org/teams/edit/${result.teamId}`); } }; diff --git a/public/app/routes/routes.tsx b/public/app/routes/routes.tsx index 4f81935e387..e9893268a36 100644 --- a/public/app/routes/routes.tsx +++ b/public/app/routes/routes.tsx @@ -237,7 +237,7 @@ export function getAppRoutes(): RouteDescriptor[] { roles: () => contextSrv.evaluatePermission( () => (config.editorsCanAdmin ? ['Editor', 'Admin'] : ['Admin']), - [AccessControlAction.ActionTeamsRead] + [AccessControlAction.ActionTeamsRead, AccessControlAction.ActionTeamsCreate] ), component: SafeDynamicImport(() => import(/* webpackChunkName: "TeamPages" */ 'app/features/teams/TeamPages')), },