Access Control: Clear user's permission cache after resource creation (#59101)

* refresh user's permission cache after resource creation

* clear the cache instead of reloading the permissions

* don't error if can't clear cache

* fix tests

* fix tests again
This commit is contained in:
Ieva
2022-11-24 14:38:55 +00:00
committed by GitHub
parent a53f57cc43
commit a8bae3f0b0
16 changed files with 104 additions and 32 deletions
+11 -9
View File
@@ -25,6 +25,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
@@ -250,15 +251,16 @@ func (s *fakeRenderService) Init() error {
func setupAccessControlScenarioContext(t *testing.T, cfg *setting.Cfg, url string, permissions []accesscontrol.Permission) (*scenarioContext, *HTTPServer) {
store := sqlstore.InitTestDB(t)
hs := &HTTPServer{
Cfg: cfg,
Live: newTestLive(t, store),
License: &licensing.OSSLicensingService{},
Features: featuremgmt.WithFeatures(),
QuotaService: quotatest.New(false, nil),
RouteRegister: routing.NewRouteRegister(),
AccessControl: accesscontrolmock.New().WithPermissions(permissions),
searchUsersService: searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), usertest.NewUserServiceFake()),
ldapGroups: ldap.ProvideGroupsService(),
Cfg: cfg,
Live: newTestLive(t, store),
License: &licensing.OSSLicensingService{},
Features: featuremgmt.WithFeatures(),
QuotaService: quotatest.New(false, nil),
RouteRegister: routing.NewRouteRegister(),
AccessControl: accesscontrolmock.New().WithPermissions(permissions),
searchUsersService: searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), usertest.NewUserServiceFake()),
ldapGroups: ldap.ProvideGroupsService(),
accesscontrolService: actest.FakeService{},
}
sc := setupScenarioContext(t, url)
+7 -1
View File
@@ -471,7 +471,7 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
}
if liveerr != nil {
hs.log.Warn("unable to broadcast save event", "uid", dashboard.Uid, "error", err)
hs.log.Warn("unable to broadcast save event", "uid", dashboard.Uid, "error", liveerr)
}
}
@@ -479,6 +479,12 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
return apierrors.ToDashboardErrorResponse(ctx, hs.pluginStore, err)
}
// Clear permission cache for the user who's created the dashboard, so that new permissions are fetched for their next call
// Required for cases when caller wants to immediately interact with the newly created object
if newDashboard && !hs.accesscontrolService.IsDisabled() {
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
}
// connect library panels for this dashboard after the dashboard is stored and has an ID
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(ctx, c.SignedInUser, dashboard)
if err != nil {
+3
View File
@@ -22,6 +22,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry/corekind"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
@@ -1093,6 +1094,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
folderService: folderService,
Features: featuremgmt.WithFeatures(),
Kinds: corekind.NewBase(nil),
accesscontrolService: actest.FakeService{},
}
sc := setupScenarioContext(t, url)
@@ -1201,6 +1203,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
Features: featuremgmt.WithFeatures(),
dashboardVersionService: fakeDashboardVersionService,
Kinds: corekind.NewBase(nil),
accesscontrolService: actest.FakeService{},
}
sc := setupScenarioContext(t, url)
+6
View File
@@ -396,6 +396,12 @@ func (hs *HTTPServer) AddDataSource(c *models.ReqContext) response.Response {
return response.Error(500, "Failed to add datasource", err)
}
// Clear permission cache for the user who's created the data source, so that new permissions are fetched for their next call
// Required for cases when caller wants to immediately interact with the newly created object
if !hs.AccessControl.IsDisabled() {
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
}
ds := hs.convertModelToDtos(c.Req.Context(), cmd.Result)
return response.JSON(http.StatusOK, util.DynMap{
"message": "Datasource added",
+8 -2
View File
@@ -19,6 +19,8 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/datasources/permissions"
"github.com/grafana/grafana/pkg/services/org"
@@ -112,7 +114,9 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
DataSourcesService: &dataSourcesServiceMock{
expectedDatasource: &datasources.DataSource{},
},
Cfg: setting.NewCfg(),
Cfg: setting.NewCfg(),
AccessControl: acimpl.ProvideAccessControl(setting.NewCfg()),
accesscontrolService: actest.FakeService{},
}
sc := setupScenarioContext(t, "/api/datasources")
@@ -224,7 +228,9 @@ func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
DataSourcesService: &dataSourcesServiceMock{
expectedDatasource: &datasources.DataSource{},
},
Cfg: setting.NewCfg(),
Cfg: setting.NewCfg(),
AccessControl: acimpl.ProvideAccessControl(setting.NewCfg()),
accesscontrolService: actest.FakeService{},
}
sc := setupScenarioContext(t, "/api/datasources/1234")
+6
View File
@@ -129,6 +129,12 @@ func (hs *HTTPServer) CreateFolder(c *models.ReqContext) response.Response {
return apierrors.ToFolderErrorResponse(err)
}
// Clear permission cache for the user who's created the folder, so that new permissions are fetched for their next call
// Required for cases when caller wants to immediately interact with the newly created object
if !hs.AccessControl.IsDisabled() {
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
}
g := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser)
// TODO set ParentUID if nested folders are enabled
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, folder))
+6 -4
View File
@@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -242,10 +243,11 @@ func createFolderScenario(t *testing.T, desc string, url string, routePattern st
store := mockstore.NewSQLStoreMock()
guardian.InitLegacyGuardian(store, dashSvc, teamSvc)
hs := HTTPServer{
AccessControl: acmock.New(),
folderService: folderService,
Cfg: setting.NewCfg(),
Features: featuremgmt.WithFeatures(),
AccessControl: acmock.New(),
folderService: folderService,
Cfg: setting.NewCfg(),
Features: featuremgmt.WithFeatures(),
accesscontrolService: actest.FakeService{},
}
sc := setupScenarioContext(t, url)
+6
View File
@@ -41,6 +41,12 @@ func (hs *HTTPServer) CreateTeam(c *models.ReqContext) response.Response {
return response.Error(500, "Failed to create Team", err)
}
// Clear permission cache for the user who's created the team, so that new permissions are fetched for their next call
// Required for cases when caller wants to immediately interact with the newly created object
if !hs.AccessControl.IsDisabled() {
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
}
if accessControlEnabled || (c.OrgRole == org.RoleEditor && hs.Cfg.EditorsCanAdmin) {
// if the request is authenticated using API tokens
// the SignedInUser is an empty struct therefore
+4
View File
@@ -15,6 +15,8 @@ import (
"github.com/grafana/grafana/pkg/infra/log/logtest"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
"github.com/grafana/grafana/pkg/services/org"
pref "github.com/grafana/grafana/pkg/services/preference"
"github.com/grafana/grafana/pkg/services/preference/preftest"
@@ -213,6 +215,8 @@ func TestTeamAPIEndpoint_CreateTeam_RBAC(t *testing.T) {
server := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = setting.NewCfg()
hs.teamService = teamtest.NewFakeService()
hs.AccessControl = acimpl.ProvideAccessControl(setting.NewCfg())
hs.accesscontrolService = actest.FakeService{}
})
input := strings.NewReader(fmt.Sprintf(teamCmd, 1))