diff --git a/pkg/services/anonymous/anonimpl/client_test.go b/pkg/services/anonymous/anonimpl/client_test.go index ab722a379ff..d05f65f6351 100644 --- a/pkg/services/anonymous/anonimpl/client_test.go +++ b/pkg/services/anonymous/anonimpl/client_test.go @@ -62,7 +62,7 @@ func TestAnonymous_Authenticate(t *testing.T) { assert.Equal(t, true, identity.ID == "") assert.Equal(t, tt.org.ID, identity.OrgID) assert.Equal(t, tt.org.Name, identity.OrgName) - assert.Equal(t, tt.cfg.AnonymousOrgRole, string(identity.Role())) + assert.Equal(t, tt.cfg.AnonymousOrgRole, string(identity.GetOrgRole())) } }) } diff --git a/pkg/services/auth/idimpl/service.go b/pkg/services/auth/idimpl/service.go index 6e9fd5cf7ce..8e41db7a8ee 100644 --- a/pkg/services/auth/idimpl/service.go +++ b/pkg/services/auth/idimpl/service.go @@ -80,9 +80,8 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri } func (s *Service) hook(ctx context.Context, identity *authn.Identity, _ *authn.Request) error { - // FIXME(kalleep): implement identity.Requester for authn.Identity // FIXME(kalleep): we should probably lazy load this - token, err := s.SignIdentity(ctx, identity.SignedInUser()) + token, err := s.SignIdentity(ctx, identity) if err != nil { namespace, id := identity.NamespacedID() s.logger.Error("Failed to sign id token", "err", err, "namespace", namespace, "id", id) diff --git a/pkg/services/authn/authnimpl/sync/permission_sync.go b/pkg/services/authn/authnimpl/sync/permission_sync.go index 33df19d048c..c9c9d67f742 100644 --- a/pkg/services/authn/authnimpl/sync/permission_sync.go +++ b/pkg/services/authn/authnimpl/sync/permission_sync.go @@ -30,8 +30,7 @@ func (s *PermissionsSync) SyncPermissionsHook(ctx context.Context, identity *aut return nil } - permissions, err := s.ac.GetUserPermissions(ctx, identity.SignedInUser(), - accesscontrol.Options{ReloadCache: false}) + permissions, err := s.ac.GetUserPermissions(ctx, identity, accesscontrol.Options{ReloadCache: false}) if err != nil { s.log.FromContext(ctx).Error("Failed to fetch permissions from db", "error", err, "user_id", identity.ID) return errSyncPermissionsForbidden diff --git a/pkg/services/authn/identity.go b/pkg/services/authn/identity.go index 3f79cab5fbd..ae94961ef9c 100644 --- a/pkg/services/authn/identity.go +++ b/pkg/services/authn/identity.go @@ -8,6 +8,7 @@ import ( "golang.org/x/oauth2" + "github.com/grafana/grafana/pkg/models/roletype" "github.com/grafana/grafana/pkg/models/usertoken" "github.com/grafana/grafana/pkg/services/auth/identity" "github.com/grafana/grafana/pkg/services/login" @@ -28,6 +29,8 @@ const ( NamespaceRenderService = identity.NamespaceRenderService ) +var _ identity.Requester = (*Identity)(nil) + type Identity struct { // OrgID is the active organization for the entity. OrgID int64 @@ -81,12 +84,107 @@ type Identity struct { IDToken string } -// Role returns the role of the identity in the active organization. -func (i *Identity) Role() org.RoleType { - return i.OrgRoles[i.OrgID] +func (i *Identity) GetAuthenticatedBy() string { + return i.AuthenticatedBy +} + +func (i *Identity) GetCacheKey() string { + namespace, id := i.GetNamespacedID() + if !i.HasUniqueId() { + // Hack use the org role as id for identities that do not have a unique id + // e.g. anonymous and render key. + id = string(i.GetOrgRole()) + } + + return fmt.Sprintf("%d-%s-%s", i.GetOrgID(), namespace, id) +} + +func (i *Identity) GetDisplayName() string { + return i.Name +} + +func (i *Identity) GetEmail() string { + return i.Email +} + +func (i *Identity) GetIDToken() string { + return i.IDToken +} + +func (i *Identity) GetIsGrafanaAdmin() bool { + return i.IsGrafanaAdmin != nil && *i.IsGrafanaAdmin +} + +func (i *Identity) GetLogin() string { + return i.Login +} + +func (i *Identity) GetNamespacedID() (namespace string, identifier string) { + split := strings.Split(i.ID, ":") + + if len(split) != 2 { + return "", "" + } + + return split[0], split[1] +} + +// GetOrgID implements identity.Requester. +func (i *Identity) GetOrgID() int64 { + return i.OrgID +} + +func (i *Identity) GetOrgName() string { + return i.OrgName +} + +func (i *Identity) GetOrgRole() roletype.RoleType { + if i.OrgRoles == nil { + return roletype.RoleNone + } + + if i.OrgRoles[i.GetOrgID()] == "" { + return roletype.RoleNone + } + + return i.OrgRoles[i.GetOrgID()] +} + +func (i *Identity) GetPermissions() map[string][]string { + if i.Permissions == nil { + return make(map[string][]string) + } + + if i.Permissions[i.GetOrgID()] == nil { + return make(map[string][]string) + } + + return i.Permissions[i.GetOrgID()] +} + +func (i *Identity) GetTeams() []int64 { + return i.Teams +} + +func (i *Identity) HasRole(role roletype.RoleType) bool { + if i.GetIsGrafanaAdmin() { + return true + } + + return i.GetOrgRole().Includes(role) +} + +func (i *Identity) HasUniqueId() bool { + namespace, _ := i.GetNamespacedID() + return namespace == NamespaceUser || namespace == NamespaceServiceAccount || namespace == NamespaceAPIKey +} + +func (i *Identity) IsNil() bool { + return i == nil } // NamespacedID returns the namespace, e.g. "user" and the id for that namespace +// FIXME(kalleep): Replace with GetNamespacedID func (i *Identity) NamespacedID() (string, int64) { split := strings.Split(i.ID, ":") if len(split) != 2 { @@ -104,21 +202,15 @@ func (i *Identity) NamespacedID() (string, int64) { // SignedInUser returns a SignedInUser from the identity. func (i *Identity) SignedInUser() *user.SignedInUser { - var isGrafanaAdmin bool - if i.IsGrafanaAdmin != nil { - isGrafanaAdmin = *i.IsGrafanaAdmin - } - u := &user.SignedInUser{ - UserID: 0, OrgID: i.OrgID, OrgName: i.OrgName, - OrgRole: i.Role(), + OrgRole: i.GetOrgRole(), Login: i.Login, Name: i.Name, Email: i.Email, AuthenticatedBy: i.AuthenticatedBy, - IsGrafanaAdmin: isGrafanaAdmin, + IsGrafanaAdmin: i.GetIsGrafanaAdmin(), IsAnonymous: i.IsAnonymous, IsDisabled: i.IsDisabled, HelpFlags1: i.HelpFlags1, @@ -128,24 +220,34 @@ func (i *Identity) SignedInUser() *user.SignedInUser { IDToken: i.IDToken, } - namespace, id := i.NamespacedID() + namespace, id := i.GetNamespacedID() if namespace == NamespaceAPIKey { - u.ApiKeyID = id + u.ApiKeyID = intIdentifier(id) } else { - u.UserID = id + u.UserID = intIdentifier(id) u.IsServiceAccount = namespace == NamespaceServiceAccount } return u } +func intIdentifier(identifier string) int64 { + id, err := strconv.ParseInt(identifier, 10, 64) + if err != nil { + // FIXME (kalleep): Improve error handling + return -1 + } + + return id +} + func (i *Identity) ExternalUserInfo() login.ExternalUserInfo { - _, id := i.NamespacedID() + _, id := i.GetNamespacedID() return login.ExternalUserInfo{ OAuthToken: i.OAuthToken, AuthModule: i.AuthenticatedBy, AuthId: i.AuthID, - UserId: id, + UserId: intIdentifier(id), Email: i.Email, Login: i.Login, Name: i.Name,