Auth: Move access control API to SignedInUser interface (#73144)

* move access control api to SignedInUser interface

* remove unused code

* add logic for reading perms from a specific org

* move the specific org logic to org_user.go

* add a comment

---------

Co-authored-by: IevaVasiljeva <ieva.vasiljeva@grafana.com>
This commit is contained in:
Jo 2023-08-18 12:42:18 +02:00 committed by GitHub
parent 4c9469fc9e
commit 26339f978b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 50 additions and 41 deletions

View File

@ -463,25 +463,20 @@ func (hs *HTTPServer) declareFixedRoles() error {
func (hs *HTTPServer) getAccessControlMetadata(c *contextmodel.ReqContext, func (hs *HTTPServer) getAccessControlMetadata(c *contextmodel.ReqContext,
orgID int64, prefix string, resourceID string) ac.Metadata { orgID int64, prefix string, resourceID string) ac.Metadata {
ids := map[string]bool{resourceID: true} ids := map[string]bool{resourceID: true}
return hs.getMultiAccessControlMetadata(c, orgID, prefix, ids)[resourceID] return hs.getMultiAccessControlMetadata(c, prefix, ids)[resourceID]
} }
// getMultiAccessControlMetadata returns the accesscontrol metadata associated with a given set of resources // getMultiAccessControlMetadata returns the accesscontrol metadata associated with a given set of resources
// Context must contain permissions in the given org (see LoadPermissionsMiddleware or AuthorizeInOrgMiddleware) // Context must contain permissions in the given org (see LoadPermissionsMiddleware or AuthorizeInOrgMiddleware)
func (hs *HTTPServer) getMultiAccessControlMetadata(c *contextmodel.ReqContext, func (hs *HTTPServer) getMultiAccessControlMetadata(c *contextmodel.ReqContext,
orgID int64, prefix string, resourceIDs map[string]bool) map[string]ac.Metadata { prefix string, resourceIDs map[string]bool) map[string]ac.Metadata {
if !c.QueryBool("accesscontrol") { if !c.QueryBool("accesscontrol") {
return map[string]ac.Metadata{} return map[string]ac.Metadata{}
} }
if c.SignedInUser.Permissions == nil { if len(c.SignedInUser.GetPermissions()) == 0 {
return map[string]ac.Metadata{} return map[string]ac.Metadata{}
} }
permissions, ok := c.SignedInUser.Permissions[orgID] return ac.GetResourcesMetadata(c.Req.Context(), c.SignedInUser.GetPermissions(), prefix, resourceIDs)
if !ok {
return map[string]ac.Metadata{}
}
return ac.GetResourcesMetadata(c.Req.Context(), permissions, prefix, resourceIDs)
} }

View File

@ -6,9 +6,9 @@ import (
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/stats" "github.com/grafana/grafana/pkg/services/stats"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -67,7 +67,7 @@ func (hs *HTTPServer) AdminGetStats(c *contextmodel.ReqContext) response.Respons
return response.JSON(http.StatusOK, adminStats) return response.JSON(http.StatusOK, adminStats)
} }
func (hs *HTTPServer) getAuthorizedSettings(ctx context.Context, user *user.SignedInUser, bag setting.SettingsBag) (setting.SettingsBag, error) { func (hs *HTTPServer) getAuthorizedSettings(ctx context.Context, user identity.Requester, bag setting.SettingsBag) (setting.SettingsBag, error) {
eval := func(scope string) (bool, error) { eval := func(scope string) (bool, error) {
return hs.AccessControl.Evaluate(ctx, user, ac.EvalPermission(ac.ActionSettingsRead, scope)) return hs.AccessControl.Evaluate(ctx, user, ac.EvalPermission(ac.ActionSettingsRead, scope))
} }
@ -108,7 +108,7 @@ func (hs *HTTPServer) getAuthorizedSettings(ctx context.Context, user *user.Sign
return authorizedBag, nil return authorizedBag, nil
} }
func (hs *HTTPServer) getAuthorizedVerboseSettings(ctx context.Context, user *user.SignedInUser, bag setting.VerboseSettingsBag) (setting.VerboseSettingsBag, error) { func (hs *HTTPServer) getAuthorizedVerboseSettings(ctx context.Context, user identity.Requester, bag setting.VerboseSettingsBag) (setting.VerboseSettingsBag, error) {
eval := func(scope string) (bool, error) { eval := func(scope string) (bool, error) {
return hs.AccessControl.Evaluate(ctx, user, ac.EvalPermission(ac.ActionSettingsRead, scope)) return hs.AccessControl.Evaluate(ctx, user, ac.EvalPermission(ac.ActionSettingsRead, scope))
} }

View File

@ -57,7 +57,7 @@ func (hs *HTTPServer) GetAPIKeys(c *contextmodel.ReqContext) response.Response {
} }
} }
metadata := hs.getMultiAccessControlMetadata(c, c.OrgID, "apikeys:id", ids) metadata := hs.getMultiAccessControlMetadata(c, "apikeys:id", ids)
if len(metadata) > 0 { if len(metadata) > 0 {
for _, key := range result { for _, key := range result {
key.AccessControl = metadata[strconv.FormatInt(key.ID, 10)] key.AccessControl = metadata[strconv.FormatInt(key.ID, 10)]

View File

@ -390,7 +390,7 @@ func (hs *HTTPServer) getFolderACMetadata(c *contextmodel.ReqContext, f *folder.
folderIDs[p.UID] = true folderIDs[p.UID] = true
} }
allMetadata := hs.getMultiAccessControlMetadata(c, c.OrgID, dashboards.ScopeFoldersPrefix, folderIDs) allMetadata := hs.getMultiAccessControlMetadata(c, dashboards.ScopeFoldersPrefix, folderIDs)
metadata := allMetadata[f.UID] metadata := allMetadata[f.UID]
// Flatten metadata - if any parent has a permission, the child folder inherits it // Flatten metadata - if any parent has a permission, the child folder inherits it

View File

@ -310,7 +310,15 @@ func (hs *HTTPServer) searchOrgUsersHelper(c *contextmodel.ReqContext, query *or
} }
// Get accesscontrol metadata and IPD labels for users in the target org // Get accesscontrol metadata and IPD labels for users in the target org
accessControlMetadata := hs.getMultiAccessControlMetadata(c, query.OrgID, "users:id:", userIDs) accessControlMetadata := map[string]accesscontrol.Metadata{}
if c.QueryBool("accesscontrol") && c.SignedInUser.Permissions != nil {
// TODO https://github.com/grafana/grafana-authnz-team/issues/268 - user access control service for fetching permissions from another organization
permissions, ok := c.SignedInUser.Permissions[query.OrgID]
if ok {
accessControlMetadata = accesscontrol.GetResourcesMetadata(c.Req.Context(), permissions, "users:id:", userIDs)
}
}
for i := range filteredUsers { for i := range filteredUsers {
filteredUsers[i].AccessControl = accessControlMetadata[fmt.Sprint(filteredUsers[i].UserID)] filteredUsers[i].AccessControl = accessControlMetadata[fmt.Sprint(filteredUsers[i].UserID)]
if module, ok := modules[filteredUsers[i].UserID]; ok { if module, ok := modules[filteredUsers[i].UserID]; ok {

View File

@ -121,8 +121,7 @@ func (hs *HTTPServer) GetPluginList(c *contextmodel.ReqContext) response.Respons
} }
// Compute metadata // Compute metadata
pluginsMetadata := hs.getMultiAccessControlMetadata(c, c.OrgID, pluginsMetadata := hs.getMultiAccessControlMetadata(c, pluginaccesscontrol.ScopeProvider.GetResourceScope(""), filteredPluginIDs)
pluginaccesscontrol.ScopeProvider.GetResourceScope(""), filteredPluginIDs)
// Prepare DTO // Prepare DTO
result := make(dtos.PluginList, 0) result := make(dtos.PluginList, 0)

View File

@ -167,7 +167,7 @@ func (hs *HTTPServer) SearchTeams(c *contextmodel.ReqContext) response.Response
teamIDs[strconv.FormatInt(team.ID, 10)] = true teamIDs[strconv.FormatInt(team.ID, 10)] = true
} }
metadata := hs.getMultiAccessControlMetadata(c, c.SignedInUser.GetOrgID(), "teams:id:", teamIDs) metadata := hs.getMultiAccessControlMetadata(c, "teams:id:", teamIDs)
if len(metadata) > 0 { if len(metadata) > 0 {
for _, team := range queryResult.Teams { for _, team := range queryResult.Teams {
team.AccessControl = metadata[strconv.FormatInt(team.ID, 10)] team.AccessControl = metadata[strconv.FormatInt(team.ID, 10)]

View File

@ -64,7 +64,7 @@ type SearchOptions struct {
} }
type TeamPermissionsService interface { type TeamPermissionsService interface {
GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]ResourcePermission, error) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]ResourcePermission, error)
SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error) SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error)
} }
@ -86,7 +86,7 @@ type ServiceAccountPermissionsService interface {
type PermissionsService interface { type PermissionsService interface {
// GetPermissions returns all permissions for given resourceID // GetPermissions returns all permissions for given resourceID
GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]ResourcePermission, error) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]ResourcePermission, error)
// SetUserPermission sets permission on resource for a user // SetUserPermission sets permission on resource for a user
SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error) SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error)
// SetTeamPermission sets permission on resource for a team // SetTeamPermission sets permission on resource for a team
@ -151,13 +151,13 @@ var ReqSignedIn = func(c *contextmodel.ReqContext) bool {
} }
var ReqGrafanaAdmin = func(c *contextmodel.ReqContext) bool { var ReqGrafanaAdmin = func(c *contextmodel.ReqContext) bool {
return c.IsGrafanaAdmin return c.SignedInUser.GetIsGrafanaAdmin()
} }
// ReqHasRole generates a fallback to check whether the user has a role // ReqHasRole generates a fallback to check whether the user has a role
// ReqHasRole(org.RoleAdmin) will always return true for Grafana server admins, eg, a Grafana Admin / Viewer role combination // ReqHasRole(org.RoleAdmin) will always return true for Grafana server admins, eg, a Grafana Admin / Viewer role combination
func ReqHasRole(role org.RoleType) func(c *contextmodel.ReqContext) bool { func ReqHasRole(role org.RoleType) func(c *contextmodel.ReqContext) bool {
return func(c *contextmodel.ReqContext) bool { return c.HasRole(role) } return func(c *contextmodel.ReqContext) bool { return c.SignedInUser.HasRole(role) }
} }
func BuildPermissionsMap(permissions []Permission) map[string]bool { func BuildPermissionsMap(permissions []Permission) map[string]bool {

View File

@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity" "github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/user"
) )
var _ accesscontrol.Service = new(FakeService) var _ accesscontrol.Service = new(FakeService)
@ -121,7 +120,7 @@ type FakePermissionsService struct {
ExpectedMappedAction string ExpectedMappedAction string
} }
func (f *FakePermissionsService) GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]accesscontrol.ResourcePermission, error) { func (f *FakePermissionsService) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]accesscontrol.ResourcePermission, error) {
return f.ExpectedPermissions, f.ExpectedErr return f.ExpectedPermissions, f.ExpectedErr
} }

View File

@ -114,7 +114,8 @@ func (api *AccessControlAPI) searchUserPermissions(c *contextmodel.ReqContext) r
return response.JSON(http.StatusBadRequest, "provide one of 'action' or 'actionPrefix'") return response.JSON(http.StatusBadRequest, "provide one of 'action' or 'actionPrefix'")
} }
permissions, err := api.Service.SearchUserPermissions(c.Req.Context(), c.OrgID, searchOptions) permissions, err := api.Service.SearchUserPermissions(c.Req.Context(),
c.SignedInUser.GetOrgID(), searchOptions)
if err != nil { if err != nil {
response.Error(http.StatusInternalServerError, "could not search user permissions", err) response.Error(http.StatusInternalServerError, "could not search user permissions", err)
} }

View File

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/middleware/cookies" "github.com/grafana/grafana/pkg/middleware/cookies"
"github.com/grafana/grafana/pkg/models/usertoken" "github.com/grafana/grafana/pkg/models/usertoken"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/authn"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
@ -30,7 +31,7 @@ func Middleware(ac AccessControl) func(Evaluator) web.Handler {
if c.AllowAnonymous { if c.AllowAnonymous {
forceLogin, _ := strconv.ParseBool(c.Req.URL.Query().Get("forceLogin")) // ignoring error, assuming false for non-true values is ok. forceLogin, _ := strconv.ParseBool(c.Req.URL.Query().Get("forceLogin")) // ignoring error, assuming false for non-true values is ok.
orgID, err := strconv.ParseInt(c.Req.URL.Query().Get("orgId"), 10, 64) orgID, err := strconv.ParseInt(c.Req.URL.Query().Get("orgId"), 10, 64)
if err == nil && orgID > 0 && orgID != c.OrgID { if err == nil && orgID > 0 && orgID != c.SignedInUser.GetOrgID() {
forceLogin = true forceLogin = true
} }
@ -56,9 +57,9 @@ func Middleware(ac AccessControl) func(Evaluator) web.Handler {
} }
} }
func authorize(c *contextmodel.ReqContext, ac AccessControl, user *user.SignedInUser, evaluator Evaluator) { func authorize(c *contextmodel.ReqContext, ac AccessControl, user identity.Requester, evaluator Evaluator) {
injected, err := evaluator.MutateScopes(c.Req.Context(), scopeInjector(scopeParams{ injected, err := evaluator.MutateScopes(c.Req.Context(), scopeInjector(scopeParams{
OrgID: c.OrgID, OrgID: user.GetOrgID(),
URLParams: web.Params(c.Req), URLParams: web.Params(c.Req),
})) }))
if err != nil { if err != nil {
@ -78,9 +79,11 @@ func deny(c *contextmodel.ReqContext, evaluator Evaluator, err error) {
if err != nil { if err != nil {
c.Logger.Error("Error from access control system", "error", err, "accessErrorID", id) c.Logger.Error("Error from access control system", "error", err, "accessErrorID", id)
} else { } else {
namespace, identifier := c.SignedInUser.GetNamespacedID()
c.Logger.Info( c.Logger.Info(
"Access denied", "Access denied",
"userID", c.UserID, "namespace", namespace,
"userID", identifier,
"accessErrorID", id, "accessErrorID", id,
"permissions", evaluator.GoString(), "permissions", evaluator.GoString(),
) )

View File

@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/auth/identity"
) )
var _ accesscontrol.PermissionsService = new(MockPermissionsService) var _ accesscontrol.PermissionsService = new(MockPermissionsService)
@ -19,7 +19,7 @@ type MockPermissionsService struct {
mock.Mock mock.Mock
} }
func (m *MockPermissionsService) GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]accesscontrol.ResourcePermission, error) { func (m *MockPermissionsService) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]accesscontrol.ResourcePermission, error) {
mockedArgs := m.Called(ctx, user, resourceID) mockedArgs := m.Called(ctx, user, resourceID)
return mockedArgs.Get(0).([]accesscontrol.ResourcePermission), mockedArgs.Error(1) return mockedArgs.Get(0).([]accesscontrol.ResourcePermission), mockedArgs.Error(1)
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/folder"
@ -253,7 +254,7 @@ var _ accesscontrol.DatasourcePermissionsService = new(DatasourcePermissionsServ
type DatasourcePermissionsService struct{} type DatasourcePermissionsService struct{}
func (e DatasourcePermissionsService) GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]accesscontrol.ResourcePermission, error) { func (e DatasourcePermissionsService) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]accesscontrol.ResourcePermission, error) {
return nil, nil return nil, nil
} }

View File

@ -155,7 +155,7 @@ func (a *api) setUserPermission(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "bad request data", err) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
_, err = a.service.SetUserPermission(c.Req.Context(), c.OrgID, accesscontrol.User{ID: userID}, resourceID, cmd.Permission) _, err = a.service.SetUserPermission(c.Req.Context(), c.SignedInUser.GetOrgID(), accesscontrol.User{ID: userID}, resourceID, cmd.Permission)
if err != nil { if err != nil {
return response.Error(http.StatusBadRequest, "failed to set user permission", err) return response.Error(http.StatusBadRequest, "failed to set user permission", err)
} }
@ -175,7 +175,7 @@ func (a *api) setTeamPermission(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "bad request data", err) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
_, err = a.service.SetTeamPermission(c.Req.Context(), c.OrgID, teamID, resourceID, cmd.Permission) _, err = a.service.SetTeamPermission(c.Req.Context(), c.SignedInUser.GetOrgID(), teamID, resourceID, cmd.Permission)
if err != nil { if err != nil {
return response.Error(http.StatusBadRequest, "failed to set team permission", err) return response.Error(http.StatusBadRequest, "failed to set team permission", err)
} }
@ -192,7 +192,7 @@ func (a *api) setBuiltinRolePermission(c *contextmodel.ReqContext) response.Resp
return response.Error(http.StatusBadRequest, "bad request data", err) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
_, err := a.service.SetBuiltInRolePermission(c.Req.Context(), c.OrgID, builtInRole, resourceID, cmd.Permission) _, err := a.service.SetBuiltInRolePermission(c.Req.Context(), c.SignedInUser.GetOrgID(), builtInRole, resourceID, cmd.Permission)
if err != nil { if err != nil {
return response.Error(http.StatusBadRequest, "failed to set role permission", err) return response.Error(http.StatusBadRequest, "failed to set role permission", err)
} }
@ -208,7 +208,7 @@ func (a *api) setPermissions(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "bad request data", err) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
_, err := a.service.SetPermissions(c.Req.Context(), c.OrgID, resourceID, cmd.Permissions...) _, err := a.service.SetPermissions(c.Req.Context(), c.SignedInUser.GetOrgID(), resourceID, cmd.Permissions...)
if err != nil { if err != nil {
return response.Error(http.StatusBadRequest, "failed to set permissions", err) return response.Error(http.StatusBadRequest, "failed to set permissions", err)
} }

View File

@ -2,7 +2,7 @@ package resourcepermissions
import ( import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/auth/identity"
) )
type SetResourcePermissionCommand struct { type SetResourcePermissionCommand struct {
@ -29,5 +29,5 @@ type GetResourcePermissionsQuery struct {
OnlyManaged bool OnlyManaged bool
InheritedScopes []string InheritedScopes []string
EnforceAccessControl bool EnforceAccessControl bool
User *user.SignedInUser User identity.Requester
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
@ -115,17 +116,17 @@ type Service struct {
userService user.Service userService user.Service
} }
func (s *Service) GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]accesscontrol.ResourcePermission, error) { func (s *Service) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]accesscontrol.ResourcePermission, error) {
var inheritedScopes []string var inheritedScopes []string
if s.options.InheritedScopesSolver != nil { if s.options.InheritedScopesSolver != nil {
var err error var err error
inheritedScopes, err = s.options.InheritedScopesSolver(ctx, user.OrgID, resourceID) inheritedScopes, err = s.options.InheritedScopesSolver(ctx, user.GetOrgID(), resourceID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
return s.store.GetResourcePermissions(ctx, user.OrgID, GetResourcePermissionsQuery{ return s.store.GetResourcePermissions(ctx, user.GetOrgID(), GetResourcePermissionsQuery{
User: user, User: user,
Actions: s.actions, Actions: s.actions,
Resource: s.options.Resource, Resource: s.options.Resource,

View File

@ -53,6 +53,8 @@ type Requester interface {
// Legacy // Legacy
// HasRole returns true if the active entity has the given role in the active organization.
HasRole(role roletype.RoleType) bool
// GetCacheKey returns a unique key for the entity. // GetCacheKey returns a unique key for the entity.
// Add an extra prefix to avoid collisions with other caches // Add an extra prefix to avoid collisions with other caches
GetCacheKey() (string, error) GetCacheKey() (string, error)