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
@@ -26,6 +26,8 @@ type Service interface {
registry.ProvidesUsageStats
// GetUserPermissions returns user permissions with only action and scope fields set.
GetUserPermissions(ctx context.Context, user *user.SignedInUser, options Options) ([]Permission, error)
// ClearUserPermissionCache removes the permission cache entry for the given user
ClearUserPermissionCache(user *user.SignedInUser)
// DeleteUserPermissions removes all permissions user has in org and all permission to that user
// If orgID is set to 0 remove permissions from all orgs
DeleteUserPermissions(ctx context.Context, orgID, userID int64) error
@@ -147,6 +147,14 @@ func (s *Service) getCachedUserPermissions(ctx context.Context, user *user.Signe
return permissions, nil
}
func (s *Service) ClearUserPermissionCache(user *user.SignedInUser) {
key, err := permissionCacheKey(user)
if err != nil {
return
}
s.cache.Delete(key)
}
func (s *Service) DeleteUserPermissions(ctx context.Context, orgID int64, userID int64) error {
return s.store.DeleteUserPermissions(ctx, orgID, userID)
}
@@ -24,6 +24,8 @@ func (f FakeService) GetUserPermissions(ctx context.Context, user *user.SignedIn
return f.ExpectedPermissions, f.ExpectedErr
}
func (f FakeService) ClearUserPermissionCache(user *user.SignedInUser) {}
func (f FakeService) DeleteUserPermissions(ctx context.Context, orgID, userID int64) error {
return f.ExpectedErr
}
+10
View File
@@ -20,6 +20,7 @@ type fullAccessControl interface {
type Calls struct {
Evaluate []interface{}
GetUserPermissions []interface{}
ClearUserPermissionCache []interface{}
IsDisabled []interface{}
DeclareFixedRoles []interface{}
DeclarePluginRoles []interface{}
@@ -43,6 +44,7 @@ type Mock struct {
// Override functions
EvaluateFunc func(context.Context, *user.SignedInUser, accesscontrol.Evaluator) (bool, error)
GetUserPermissionsFunc func(context.Context, *user.SignedInUser, accesscontrol.Options) ([]accesscontrol.Permission, error)
ClearUserPermissionCacheFunc func(*user.SignedInUser)
IsDisabledFunc func() bool
DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error
DeclarePluginRolesFunc func(context.Context, string, string, []plugins.RoleRegistration) error
@@ -138,6 +140,14 @@ func (m *Mock) GetUserPermissions(ctx context.Context, user *user.SignedInUser,
return m.permissions, nil
}
func (m *Mock) ClearUserPermissionCache(user *user.SignedInUser) {
m.Calls.ClearUserPermissionCache = append(m.Calls.ClearUserPermissionCache, []interface{}{user})
// Use override if provided
if m.ClearUserPermissionCacheFunc != nil {
m.ClearUserPermissionCacheFunc(user)
}
}
// Middleware checks if service disabled or not to switch to fallback authorization.
// This mock return m.disabled unless an override is provided.
func (m *Mock) IsDisabled() bool {
+21 -14
View File
@@ -21,31 +21,34 @@ import (
)
type ServiceAccountsAPI struct {
cfg *setting.Cfg
service serviceaccounts.Service
accesscontrol accesscontrol.AccessControl
RouterRegister routing.RouteRegister
store serviceaccounts.Store
log log.Logger
permissionService accesscontrol.ServiceAccountPermissionsService
cfg *setting.Cfg
service serviceaccounts.Service
accesscontrol accesscontrol.AccessControl
accesscontrolService accesscontrol.Service
RouterRegister routing.RouteRegister
store serviceaccounts.Store
log log.Logger
permissionService accesscontrol.ServiceAccountPermissionsService
}
func NewServiceAccountsAPI(
cfg *setting.Cfg,
service serviceaccounts.Service,
accesscontrol accesscontrol.AccessControl,
accesscontrolService accesscontrol.Service,
routerRegister routing.RouteRegister,
store serviceaccounts.Store,
permissionService accesscontrol.ServiceAccountPermissionsService,
) *ServiceAccountsAPI {
return &ServiceAccountsAPI{
cfg: cfg,
service: service,
accesscontrol: accesscontrol,
RouterRegister: routerRegister,
store: store,
log: log.New("serviceaccounts.api"),
permissionService: permissionService,
cfg: cfg,
service: service,
accesscontrol: accesscontrol,
accesscontrolService: accesscontrolService,
RouterRegister: routerRegister,
store: store,
log: log.New("serviceaccounts.api"),
permissionService: permissionService,
}
}
@@ -127,6 +130,10 @@ func (api *ServiceAccountsAPI) CreateServiceAccount(c *models.ReqContext) respon
return response.Error(http.StatusInternalServerError, "Failed to set permissions for service account creator", err)
}
}
// Clear permission cache for the user who's created the service account, so that new permissions are fetched for their next call
// Required for cases when caller wants to immediately interact with the newly created object
api.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
}
return response.JSON(http.StatusCreated, serviceAccount)
+3 -1
View File
@@ -20,6 +20,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"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/apikey/apikeyimpl"
@@ -296,8 +297,9 @@ func setupTestServer(t *testing.T, svc *tests.ServiceAccountMock,
saPermissionService, err := ossaccesscontrol.ProvideServiceAccountPermissions(
cfg, routing.NewRouteRegister(), sqlStore, acmock, &licensing.OSSLicensingService{}, saStore, acmock, teamSvc, userSvc)
require.NoError(t, err)
acService := actest.FakeService{}
a := NewServiceAccountsAPI(cfg, svc, acmock, routerRegister, saStore, saPermissionService)
a := NewServiceAccountsAPI(cfg, svc, acmock, acService, routerRegister, saStore, saPermissionService)
a.RegisterAPIEndpoints()
a.cfg.ApiKeyMaxSecondsToLive = -1 // disable api key expiration
@@ -51,7 +51,7 @@ func ProvideServiceAccountsService(
usageStats.RegisterMetricsFunc(s.getUsageMetrics)
serviceaccountsAPI := api.NewServiceAccountsAPI(cfg, s, ac, routeRegister, s.store, permissionService)
serviceaccountsAPI := api.NewServiceAccountsAPI(cfg, s, ac, accesscontrolService, routeRegister, s.store, permissionService)
serviceaccountsAPI.RegisterAPIEndpoints()
s.secretScanEnabled = cfg.SectionWithEnvOverrides("secretscan").Key("enabled").MustBool(false)