From bd1a856d3392000178a063f29181ac584b9583cf Mon Sep 17 00:00:00 2001 From: Jo Date: Wed, 9 Aug 2023 09:35:50 +0200 Subject: [PATCH] Auth: Add SignedIn user interface NamespacedID (#72944) * wip * scope active user to 1 org * remove TODOs * add render auth namespace * import cycle fix * make condition more readable * convert Evaluate to user Requester * only use active OrgID for SearchUserPermissions * add cache key to interface definition * change final SignedInUsers to interface * fix api key managed roles fetch * fix anon auth id parsing * Update pkg/services/accesscontrol/acimpl/accesscontrol.go Co-authored-by: Ieva --------- Co-authored-by: Ieva --- pkg/services/accesscontrol/accesscontrol.go | 15 +++---- .../accesscontrol/acimpl/accesscontrol.go | 25 +++++++----- pkg/services/accesscontrol/acimpl/service.go | 40 +++++++++++++------ .../acimpl/service_bench_test.go | 4 +- .../accesscontrol/acimpl/service_test.go | 2 +- pkg/services/accesscontrol/actest/fake.go | 9 +++-- pkg/services/accesscontrol/api/api.go | 2 +- pkg/services/accesscontrol/filter.go | 2 +- pkg/services/accesscontrol/mock/mock.go | 17 +++++--- pkg/services/auth/identity/requester.go | 19 ++++++++- pkg/services/authn/authn.go | 7 ++-- pkg/services/org/orgimpl/store_test.go | 6 +-- pkg/services/user/identity.go | 35 +++++++++++++++- 13 files changed, 128 insertions(+), 55 deletions(-) diff --git a/pkg/services/accesscontrol/accesscontrol.go b/pkg/services/accesscontrol/accesscontrol.go index 04f7766a590..084beb0515c 100644 --- a/pkg/services/accesscontrol/accesscontrol.go +++ b/pkg/services/accesscontrol/accesscontrol.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/grafana/grafana/pkg/registry" + "github.com/grafana/grafana/pkg/services/auth/identity" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/user" @@ -14,7 +15,7 @@ import ( type AccessControl interface { // Evaluate evaluates access to the given resources. - Evaluate(ctx context.Context, user *user.SignedInUser, evaluator Evaluator) (bool, error) + Evaluate(ctx context.Context, user identity.Requester, evaluator Evaluator) (bool, error) // RegisterScopeAttributeResolver allows the caller to register a scope resolver for a // specific scope prefix (ex: datasources:name:) RegisterScopeAttributeResolver(prefix string, resolver ScopeAttributeResolver) @@ -25,11 +26,11 @@ type AccessControl interface { 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) + GetUserPermissions(ctx context.Context, user identity.Requester, options Options) ([]Permission, error) // SearchUsersPermissions returns all users' permissions filtered by an action prefix - SearchUsersPermissions(ctx context.Context, user *user.SignedInUser, orgID int64, options SearchOptions) (map[int64][]Permission, error) + SearchUsersPermissions(ctx context.Context, user identity.Requester, options SearchOptions) (map[int64][]Permission, error) // ClearUserPermissionCache removes the permission cache entry for the given user - ClearUserPermissionCache(user *user.SignedInUser) + ClearUserPermissionCache(user identity.Requester) // SearchUserPermissions returns single user's permissions filtered by an action prefix or an action SearchUserPermissions(ctx context.Context, orgID int64, filterOptions SearchOptions) ([]Permission, error) // DeleteUserPermissions removes all permissions user has in org and all permission to that user @@ -375,10 +376,10 @@ func IsDisabled(cfg *setting.Cfg) bool { } // GetOrgRoles returns legacy org roles for a user -func GetOrgRoles(user *user.SignedInUser) []string { - roles := []string{string(user.OrgRole)} +func GetOrgRoles(user identity.Requester) []string { + roles := []string{string(user.GetOrgRole())} - if user.IsGrafanaAdmin { + if user.GetIsGrafanaAdmin() { roles = append(roles, RoleGrafanaAdmin) } diff --git a/pkg/services/accesscontrol/acimpl/accesscontrol.go b/pkg/services/accesscontrol/acimpl/accesscontrol.go index caac5b873c6..2c3b90feddc 100644 --- a/pkg/services/accesscontrol/acimpl/accesscontrol.go +++ b/pkg/services/accesscontrol/acimpl/accesscontrol.go @@ -9,7 +9,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/services/auth/identity" "github.com/grafana/grafana/pkg/setting" ) @@ -28,21 +28,28 @@ type AccessControl struct { resolvers accesscontrol.Resolvers } -func (a *AccessControl) Evaluate(ctx context.Context, user *user.SignedInUser, evaluator accesscontrol.Evaluator) (bool, error) { +func (a *AccessControl) Evaluate(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) { timer := prometheus.NewTimer(metrics.MAccessEvaluationsSummary) defer timer.ObserveDuration() metrics.MAccessEvaluationCount.Inc() - if !verifyPermissions(user) { - a.log.Warn("no permissions set for user", "userID", user.UserID, "orgID", user.OrgID, "login", user.Login) + if user == nil || user.IsNil() { + a.log.Warn("no entity set for access control evaluation") return false, nil } + + namespace, identifier := user.GetNamespacedID() + if len(user.GetPermissions()) == 0 { + a.log.Warn("no permissions set for entity", "namespace", namespace, "id", identifier, "orgID", user.GetOrgID(), "login", user.GetLogin()) + return false, nil + } + // Test evaluation without scope resolver first, this will prevent 403 for wildcard scopes when resource does not exist - if evaluator.Evaluate(user.Permissions[user.OrgID]) { + if evaluator.Evaluate(user.GetPermissions()) { return true, nil } - resolvedEvaluator, err := evaluator.MutateScopes(ctx, a.resolvers.GetScopeAttributeMutator(user.OrgID)) + resolvedEvaluator, err := evaluator.MutateScopes(ctx, a.resolvers.GetScopeAttributeMutator(user.GetOrgID())) if err != nil { if errors.Is(err, accesscontrol.ErrResolverNotFound) { return false, nil @@ -50,7 +57,7 @@ func (a *AccessControl) Evaluate(ctx context.Context, user *user.SignedInUser, e return false, err } - return resolvedEvaluator.Evaluate(user.Permissions[user.OrgID]), nil + return resolvedEvaluator.Evaluate(user.GetPermissions()), nil } func (a *AccessControl) RegisterScopeAttributeResolver(prefix string, resolver accesscontrol.ScopeAttributeResolver) { @@ -60,7 +67,3 @@ func (a *AccessControl) RegisterScopeAttributeResolver(prefix string, resolver a func (a *AccessControl) IsDisabled() bool { return accesscontrol.IsDisabled(a.cfg) } - -func verifyPermissions(u *user.SignedInUser) bool { - return u.Permissions != nil || u.Permissions[u.OrgID] != nil -} diff --git a/pkg/services/accesscontrol/acimpl/service.go b/pkg/services/accesscontrol/acimpl/service.go index e0cad1791db..739d6546400 100644 --- a/pkg/services/accesscontrol/acimpl/service.go +++ b/pkg/services/accesscontrol/acimpl/service.go @@ -21,6 +21,8 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol/database" "github.com/grafana/grafana/pkg/services/accesscontrol/migrator" "github.com/grafana/grafana/pkg/services/accesscontrol/pluginutils" + "github.com/grafana/grafana/pkg/services/auth/identity" + "github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" @@ -95,7 +97,7 @@ func (s *Service) GetUsageStats(_ context.Context) map[string]interface{} { } // GetUserPermissions returns user permissions based on built-in roles -func (s *Service) GetUserPermissions(ctx context.Context, user *user.SignedInUser, options accesscontrol.Options) ([]accesscontrol.Permission, error) { +func (s *Service) GetUserPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options) ([]accesscontrol.Permission, error) { timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary) defer timer.ObserveDuration() @@ -106,7 +108,7 @@ func (s *Service) GetUserPermissions(ctx context.Context, user *user.SignedInUse return s.getCachedUserPermissions(ctx, user, options) } -func (s *Service) getUserPermissions(ctx context.Context, user *user.SignedInUser, options accesscontrol.Options) ([]accesscontrol.Permission, error) { +func (s *Service) getUserPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options) ([]accesscontrol.Permission, error) { permissions := make([]accesscontrol.Permission, 0) for _, builtin := range accesscontrol.GetOrgRoles(user) { if basicRole, ok := s.roles[builtin]; ok { @@ -114,11 +116,23 @@ func (s *Service) getUserPermissions(ctx context.Context, user *user.SignedInUse } } + namespace, identifier := user.GetNamespacedID() + + var userID int64 + switch namespace { + case authn.NamespaceUser, authn.NamespaceServiceAccount, identity.NamespaceRenderService: + var err error + userID, err = strconv.ParseInt(identifier, 10, 64) + if err != nil { + return nil, err + } + } + dbPermissions, err := s.store.GetUserPermissions(ctx, accesscontrol.GetUserPermissionsQuery{ - OrgID: user.OrgID, - UserID: user.UserID, + OrgID: user.GetOrgID(), + UserID: userID, Roles: accesscontrol.GetOrgRoles(user), - TeamIDs: user.Teams, + TeamIDs: user.GetTeams(), RolePrefixes: []string{accesscontrol.ManagedRolePrefix, accesscontrol.ExternalServiceRolePrefix}, }) if err != nil { @@ -128,7 +142,7 @@ func (s *Service) getUserPermissions(ctx context.Context, user *user.SignedInUse return append(permissions, dbPermissions...), nil } -func (s *Service) getCachedUserPermissions(ctx context.Context, user *user.SignedInUser, options accesscontrol.Options) ([]accesscontrol.Permission, error) { +func (s *Service) getCachedUserPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options) ([]accesscontrol.Permission, error) { key, err := permissionCacheKey(user) if err != nil { return nil, err @@ -154,7 +168,7 @@ func (s *Service) getCachedUserPermissions(ctx context.Context, user *user.Signe return permissions, nil } -func (s *Service) ClearUserPermissionCache(user *user.SignedInUser) { +func (s *Service) ClearUserPermissionCache(user identity.Requester) { key, err := permissionCacheKey(user) if err != nil { return @@ -209,7 +223,7 @@ func (s *Service) IsDisabled() bool { return accesscontrol.IsDisabled(s.cfg) } -func permissionCacheKey(user *user.SignedInUser) (string, error) { +func permissionCacheKey(user identity.Requester) (string, error) { key, err := user.GetCacheKey() if err != nil { return "", err @@ -248,7 +262,7 @@ func (s *Service) DeclarePluginRoles(_ context.Context, ID, name string, regs [] } // SearchUsersPermissions returns all users' permissions filtered by action prefixes -func (s *Service) SearchUsersPermissions(ctx context.Context, user *user.SignedInUser, orgID int64, +func (s *Service) SearchUsersPermissions(ctx context.Context, user identity.Requester, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) { // Filter ram permissions basicPermissions := map[string][]accesscontrol.Permission{} @@ -260,21 +274,21 @@ func (s *Service) SearchUsersPermissions(ctx context.Context, user *user.SignedI } } - usersRoles, err := s.store.GetUsersBasicRoles(ctx, nil, orgID) + usersRoles, err := s.store.GetUsersBasicRoles(ctx, nil, user.GetOrgID()) if err != nil { return nil, err } // Get managed permissions (DB) - usersPermissions, err := s.store.SearchUsersPermissions(ctx, orgID, options) + usersPermissions, err := s.store.SearchUsersPermissions(ctx, user.GetOrgID(), options) if err != nil { return nil, err } // helper to filter out permissions the signed in users cannot see canView := func() func(userID int64) bool { - siuPermissions, ok := user.Permissions[orgID] - if !ok { + siuPermissions := user.GetPermissions() + if len(siuPermissions) == 0 { return func(_ int64) bool { return false } } scopes, ok := siuPermissions[accesscontrol.ActionUsersPermissionsRead] diff --git a/pkg/services/accesscontrol/acimpl/service_bench_test.go b/pkg/services/accesscontrol/acimpl/service_bench_test.go index 81e6d0e858f..37e83cfb2e9 100644 --- a/pkg/services/accesscontrol/acimpl/service_bench_test.go +++ b/pkg/services/accesscontrol/acimpl/service_bench_test.go @@ -141,7 +141,7 @@ func benchSearchUsersPermissions(b *testing.B, usersCount, resourceCount int) { b.ResetTimer() for n := 0; n < b.N; n++ { - usersPermissions, err := acService.SearchUsersPermissions(context.Background(), siu, 1, accesscontrol.SearchOptions{ActionPrefix: "resources:"}) + usersPermissions, err := acService.SearchUsersPermissions(context.Background(), siu, accesscontrol.SearchOptions{ActionPrefix: "resources:"}) require.NoError(b, err) require.Len(b, usersPermissions, usersCount) for _, permissions := range usersPermissions { @@ -206,7 +206,7 @@ func benchSearchUsersWithPerm(b *testing.B, usersCount, resourceCount int) { b.ResetTimer() for n := 0; n < b.N; n++ { - usersPermissions, err := acService.SearchUsersPermissions(context.Background(), siu, 1, + usersPermissions, err := acService.SearchUsersPermissions(context.Background(), siu, accesscontrol.SearchOptions{Action: "resources:action2", Scope: "resources:id:1"}) require.NoError(b, err) require.Len(b, usersPermissions, usersCount) diff --git a/pkg/services/accesscontrol/acimpl/service_test.go b/pkg/services/accesscontrol/acimpl/service_test.go index ff0ea62508d..d8e365e7db0 100644 --- a/pkg/services/accesscontrol/acimpl/service_test.go +++ b/pkg/services/accesscontrol/acimpl/service_test.go @@ -523,7 +523,7 @@ func TestService_SearchUsersPermissions(t *testing.T) { } siu := &user.SignedInUser{OrgID: 2, Permissions: map[int64]map[string][]string{2: tt.siuPermissions}} - got, err := ac.SearchUsersPermissions(ctx, siu, 2, tt.searchOption) + got, err := ac.SearchUsersPermissions(ctx, siu, tt.searchOption) if tt.wantErr { require.NotNil(t, err) return diff --git a/pkg/services/accesscontrol/actest/fake.go b/pkg/services/accesscontrol/actest/fake.go index 85a039b1bd8..e6540b59eb4 100644 --- a/pkg/services/accesscontrol/actest/fake.go +++ b/pkg/services/accesscontrol/actest/fake.go @@ -4,6 +4,7 @@ import ( "context" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/auth/identity" "github.com/grafana/grafana/pkg/services/user" ) @@ -23,11 +24,11 @@ func (f FakeService) GetUsageStats(ctx context.Context) map[string]interface{} { return map[string]interface{}{} } -func (f FakeService) GetUserPermissions(ctx context.Context, user *user.SignedInUser, options accesscontrol.Options) ([]accesscontrol.Permission, error) { +func (f FakeService) GetUserPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options) ([]accesscontrol.Permission, error) { return f.ExpectedPermissions, f.ExpectedErr } -func (f FakeService) SearchUsersPermissions(ctx context.Context, user *user.SignedInUser, orgID int64, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) { +func (f FakeService) SearchUsersPermissions(ctx context.Context, user identity.Requester, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) { return f.ExpectedUsersPermissions, f.ExpectedErr } @@ -35,7 +36,7 @@ func (f FakeService) SearchUserPermissions(ctx context.Context, orgID int64, sea return f.ExpectedFilteredUserPermissions, f.ExpectedErr } -func (f FakeService) ClearUserPermissionCache(user *user.SignedInUser) {} +func (f FakeService) ClearUserPermissionCache(user identity.Requester) {} func (f FakeService) DeleteUserPermissions(ctx context.Context, orgID, userID int64) error { return f.ExpectedErr @@ -69,7 +70,7 @@ type FakeAccessControl struct { ExpectedEvaluate bool } -func (f FakeAccessControl) Evaluate(ctx context.Context, user *user.SignedInUser, evaluator accesscontrol.Evaluator) (bool, error) { +func (f FakeAccessControl) Evaluate(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) { return f.ExpectedEvaluate, f.ExpectedErr } diff --git a/pkg/services/accesscontrol/api/api.go b/pkg/services/accesscontrol/api/api.go index 02ee172cd32..9c5f41b8fbf 100644 --- a/pkg/services/accesscontrol/api/api.go +++ b/pkg/services/accesscontrol/api/api.go @@ -82,7 +82,7 @@ func (api *AccessControlAPI) searchUsersPermissions(c *contextmodel.ReqContext) } // Compute metadata - permissions, err := api.Service.SearchUsersPermissions(c.Req.Context(), c.SignedInUser, c.OrgID, searchOptions) + permissions, err := api.Service.SearchUsersPermissions(c.Req.Context(), c.SignedInUser, searchOptions) if err != nil { return response.Error(http.StatusInternalServerError, "could not get org user permissions", err) } diff --git a/pkg/services/accesscontrol/filter.go b/pkg/services/accesscontrol/filter.go index 828393d05eb..2b2cabc1f1f 100644 --- a/pkg/services/accesscontrol/filter.go +++ b/pkg/services/accesscontrol/filter.go @@ -45,7 +45,7 @@ func Filter(user identity.Requester, sqlID, prefix string, actions ...string) (S wildcards := 0 result := make(map[interface{}]int) for _, a := range actions { - ids, hasWildcard := ParseScopes(prefix, user.GetPermissions(user.GetOrgID())[a]) + ids, hasWildcard := ParseScopes(prefix, user.GetPermissions()[a]) if hasWildcard { wildcards += 1 continue diff --git a/pkg/services/accesscontrol/mock/mock.go b/pkg/services/accesscontrol/mock/mock.go index 7e9a9546d2a..6e1b4ad5fb0 100644 --- a/pkg/services/accesscontrol/mock/mock.go +++ b/pkg/services/accesscontrol/mock/mock.go @@ -7,6 +7,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/auth/identity" "github.com/grafana/grafana/pkg/services/user" ) @@ -101,7 +102,8 @@ func (m *Mock) WithBuiltInRoles(builtInRoles []string) *Mock { // Evaluate evaluates access to the given resource. // This mock uses GetUserPermissions to then call the evaluator Evaluate function. -func (m *Mock) Evaluate(ctx context.Context, usr *user.SignedInUser, evaluator accesscontrol.Evaluator) (bool, error) { +func (m *Mock) Evaluate(ctx context.Context, us identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) { + usr := us.(*user.SignedInUser) m.Calls.Evaluate = append(m.Calls.Evaluate, []interface{}{ctx, usr, evaluator}) // Use override if provided if m.EvaluateFunc != nil { @@ -138,7 +140,8 @@ func (m *Mock) Evaluate(ctx context.Context, usr *user.SignedInUser, evaluator a // GetUserPermissions returns user permissions. // This mock return m.permissions unless an override is provided. -func (m *Mock) GetUserPermissions(ctx context.Context, user *user.SignedInUser, opts accesscontrol.Options) ([]accesscontrol.Permission, error) { +func (m *Mock) GetUserPermissions(ctx context.Context, usr identity.Requester, opts accesscontrol.Options) ([]accesscontrol.Permission, error) { + user := usr.(*user.SignedInUser) m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user, opts}) // Use override if provided if m.GetUserPermissionsFunc != nil { @@ -148,7 +151,8 @@ func (m *Mock) GetUserPermissions(ctx context.Context, user *user.SignedInUser, return m.permissions, nil } -func (m *Mock) ClearUserPermissionCache(user *user.SignedInUser) { +func (m *Mock) ClearUserPermissionCache(usr identity.Requester) { + user := usr.(*user.SignedInUser) m.Calls.ClearUserPermissionCache = append(m.Calls.ClearUserPermissionCache, []interface{}{user}) // Use override if provided if m.ClearUserPermissionCacheFunc != nil { @@ -222,11 +226,12 @@ func (m *Mock) DeleteUserPermissions(ctx context.Context, orgID, userID int64) e } // SearchUsersPermissions returns all users' permissions filtered by an action prefix -func (m *Mock) SearchUsersPermissions(ctx context.Context, user *user.SignedInUser, orgID int64, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) { - m.Calls.SearchUsersPermissions = append(m.Calls.SearchUsersPermissions, []interface{}{ctx, user, orgID, options}) +func (m *Mock) SearchUsersPermissions(ctx context.Context, usr identity.Requester, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) { + user := usr.(*user.SignedInUser) + m.Calls.SearchUsersPermissions = append(m.Calls.SearchUsersPermissions, []interface{}{ctx, user, options}) // Use override if provided if m.SearchUsersPermissionsFunc != nil { - return m.SearchUsersPermissionsFunc(ctx, user, orgID, options) + return m.SearchUsersPermissionsFunc(ctx, user, usr.GetOrgID(), options) } return nil, nil } diff --git a/pkg/services/auth/identity/requester.go b/pkg/services/auth/identity/requester.go index 353b26b2573..f8031e7ab89 100644 --- a/pkg/services/auth/identity/requester.go +++ b/pkg/services/auth/identity/requester.go @@ -1,9 +1,26 @@ package identity +import "github.com/grafana/grafana/pkg/models/roletype" + +const ( + NamespaceUser = "user" + NamespaceAPIKey = "api-key" + NamespaceServiceAccount = "service-account" + NamespaceAnonymous = "anonymous" + NamespaceRenderService = "render" +) + type Requester interface { GetIsGrafanaAdmin() bool GetLogin() string GetOrgID() int64 - GetPermissions(orgID int64) map[string][]string + GetPermissions() map[string][]string + GetTeams() []int64 + GetOrgRole() roletype.RoleType + GetNamespacedID() (string, string) IsNil() bool + + // Legacy + GetCacheKey() (string, error) + HasUniqueId() bool } diff --git a/pkg/services/authn/authn.go b/pkg/services/authn/authn.go index 0b60e1450e8..7b7889ea381 100644 --- a/pkg/services/authn/authn.go +++ b/pkg/services/authn/authn.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/middleware/cookies" "github.com/grafana/grafana/pkg/models/usertoken" + "github.com/grafana/grafana/pkg/services/auth/identity" "github.com/grafana/grafana/pkg/services/login" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/user" @@ -172,9 +173,9 @@ type Redirect struct { } const ( - NamespaceUser = "user" - NamespaceAPIKey = "api-key" - NamespaceServiceAccount = "service-account" + NamespaceUser = identity.NamespaceUser + NamespaceAPIKey = identity.NamespaceAPIKey + NamespaceServiceAccount = identity.NamespaceServiceAccount ) type Identity struct { diff --git a/pkg/services/org/orgimpl/store_test.go b/pkg/services/org/orgimpl/store_test.go index 5cb9f5e4aeb..d24c28388ba 100644 --- a/pkg/services/org/orgimpl/store_test.go +++ b/pkg/services/org/orgimpl/store_test.go @@ -615,7 +615,7 @@ func TestIntegration_SQLStore_GetOrgUsers(t *testing.T) { if !hasWildcardScope(tt.query.User, accesscontrol.ActionOrgUsersRead) { for _, u := range result.OrgUsers { - assert.Contains(t, tt.query.User.GetPermissions(tt.query.User.GetOrgID())[accesscontrol.ActionOrgUsersRead], fmt.Sprintf("users:id:%d", u.UserID)) + assert.Contains(t, tt.query.User.GetPermissions()[accesscontrol.ActionOrgUsersRead], fmt.Sprintf("users:id:%d", u.UserID)) } } }) @@ -647,7 +647,7 @@ func seedOrgUsers(t *testing.T, orgUserStore store, store *sqlstore.SQLStore, nu } func hasWildcardScope(user identity.Requester, action string) bool { - for _, scope := range user.GetPermissions(user.GetOrgID())[action] { + for _, scope := range user.GetPermissions()[action] { if strings.HasSuffix(scope, ":*") { return true } @@ -792,7 +792,7 @@ func TestIntegration_SQLStore_SearchOrgUsers(t *testing.T) { if !hasWildcardScope(tt.query.User, accesscontrol.ActionOrgUsersRead) { for _, u := range result.OrgUsers { - assert.Contains(t, tt.query.User.GetPermissions(tt.query.User.GetOrgID())[accesscontrol.ActionOrgUsersRead], fmt.Sprintf("users:id:%d", u.UserID)) + assert.Contains(t, tt.query.User.GetPermissions()[accesscontrol.ActionOrgUsersRead], fmt.Sprintf("users:id:%d", u.UserID)) } } }) diff --git a/pkg/services/user/identity.go b/pkg/services/user/identity.go index 75a1a2b1b07..a92fbe083f8 100644 --- a/pkg/services/user/identity.go +++ b/pkg/services/user/identity.go @@ -5,6 +5,7 @@ import ( "time" "github.com/grafana/grafana/pkg/models/roletype" + "github.com/grafana/grafana/pkg/services/auth/identity" ) type SignedInUser struct { @@ -105,12 +106,42 @@ func (u *SignedInUser) GetOrgID() int64 { return u.OrgID } -func (u *SignedInUser) GetPermissions(orgID int64) map[string][]string { +func (u *SignedInUser) GetPermissions() map[string][]string { if u.Permissions == nil { return make(map[string][]string) } - return u.Permissions[orgID] + if u.Permissions[u.GetOrgID()] == nil { + return make(map[string][]string) + } + + return u.Permissions[u.GetOrgID()] +} + +func (u *SignedInUser) GetTeams() []int64 { + return u.Teams +} + +func (u *SignedInUser) GetOrgRole() roletype.RoleType { + return u.OrgRole +} + +func (u *SignedInUser) GetNamespacedID() (string, string) { + switch { + case u.ApiKeyID != 0: + return identity.NamespaceAPIKey, fmt.Sprintf("%d", u.ApiKeyID) + case u.IsServiceAccount: + return identity.NamespaceServiceAccount, fmt.Sprintf("%d", u.UserID) + case u.UserID != 0: + return identity.NamespaceUser, fmt.Sprintf("%d", u.UserID) + case u.IsAnonymous: + return identity.NamespaceAnonymous, "" + case u.AuthenticatedBy == "render": //import cycle render + return identity.NamespaceRenderService, fmt.Sprintf("%d", u.UserID) + } + + // backwards compatibility + return identity.NamespaceUser, fmt.Sprintf("%d", u.UserID) } // FIXME: remove this method once all services are using an interface