mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
IDForwarding: Add auth hook to generate id token (#75555)
* AuthN: Move identity struct to its own file * IDForwarding: Add IDToken property to usr and identity structs and add GetIDToken to requester interface * Inject IDService into background services * IDForwarding: Register post auth hook when feature toggle is enabled
This commit is contained in:
parent
3a1c3be057
commit
b9b4246432
@ -62,7 +62,7 @@ func ProvideBackgroundServiceRegistry(
|
|||||||
_ serviceaccounts.Service, _ *guardian.Provider,
|
_ serviceaccounts.Service, _ *guardian.Provider,
|
||||||
_ *plugindashboardsservice.DashboardUpdater, _ *sanitizer.Provider,
|
_ *plugindashboardsservice.DashboardUpdater, _ *sanitizer.Provider,
|
||||||
_ *grpcserver.HealthService, _ entity.EntityStoreServer, _ *grpcserver.ReflectionService, _ *ldapapi.Service,
|
_ *grpcserver.HealthService, _ entity.EntityStoreServer, _ *grpcserver.ReflectionService, _ *ldapapi.Service,
|
||||||
_ *apiregistry.Service,
|
_ *apiregistry.Service, _ auth.IDService,
|
||||||
) *BackgroundServiceRegistry {
|
) *BackgroundServiceRegistry {
|
||||||
return NewBackgroundServiceRegistry(
|
return NewBackgroundServiceRegistry(
|
||||||
httpServer,
|
httpServer,
|
||||||
|
@ -62,6 +62,9 @@ type Requester interface {
|
|||||||
HasUniqueId() bool
|
HasUniqueId() bool
|
||||||
// AuthenticatedBy returns the authentication method used to authenticate the entity.
|
// AuthenticatedBy returns the authentication method used to authenticate the entity.
|
||||||
GetAuthenticatedBy() string
|
GetAuthenticatedBy() string
|
||||||
|
// GetIDToken returns a signed token representing the identity that can be forwarded to plugins and external services.
|
||||||
|
// Will only be set when featuremgmt.FlagIdForwarding is enabled.
|
||||||
|
GetIDToken() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntIdentifier converts a string identifier to an int64.
|
// IntIdentifier converts a string identifier to an int64.
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
"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/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,8 +24,17 @@ const (
|
|||||||
|
|
||||||
var _ auth.IDService = (*Service)(nil)
|
var _ auth.IDService = (*Service)(nil)
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, signer auth.IDSigner, cache remotecache.CacheStorage) *Service {
|
func ProvideService(
|
||||||
return &Service{cfg, log.New("id-service"), signer, cache}
|
cfg *setting.Cfg, signer auth.IDSigner, cache remotecache.CacheStorage,
|
||||||
|
features featuremgmt.FeatureToggles, authnService authn.Service,
|
||||||
|
) *Service {
|
||||||
|
s := &Service{cfg, log.New("id-service"), signer, cache}
|
||||||
|
|
||||||
|
if features.IsEnabled(featuremgmt.FlagIdForwarding) {
|
||||||
|
authnService.RegisterPostAuthHook(s.hook, 140)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
@ -46,7 +57,6 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri
|
|||||||
s.logger.Debug("Sign new id token", "namespace", namespace, "id", identifier)
|
s.logger.Debug("Sign new id token", "namespace", namespace, "id", identifier)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
token, err := s.signer.SignIDToken(ctx, &auth.IDClaims{
|
token, err := s.signer.SignIDToken(ctx, &auth.IDClaims{
|
||||||
Claims: jwt.Claims{
|
Claims: jwt.Claims{
|
||||||
ID: identifier,
|
ID: identifier,
|
||||||
@ -69,6 +79,21 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri
|
|||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
if err != nil {
|
||||||
|
namespace, id := identity.NamespacedID()
|
||||||
|
s.logger.Error("Failed to sign id token", "err", err, "namespace", namespace, "id", id)
|
||||||
|
// for now don't return error so we don't break authentication from this hook
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
identity.IDToken = token
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func prefixCacheKey(key string) string {
|
func prefixCacheKey(key string) string {
|
||||||
return fmt.Sprintf("%s-%s", cachePrefix, key)
|
return fmt.Sprintf("%s-%s", cachePrefix, key)
|
||||||
}
|
}
|
||||||
|
41
pkg/services/auth/idimpl/service_test.go
Normal file
41
pkg/services/auth/idimpl/service_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package idimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ProvideService(t *testing.T) {
|
||||||
|
t.Run("should register post auth hook when feature flag is enabled", func(t *testing.T) {
|
||||||
|
features := featuremgmt.WithFeatures(featuremgmt.FlagIdForwarding)
|
||||||
|
|
||||||
|
var hookRegistered bool
|
||||||
|
authnService := &authntest.MockService{
|
||||||
|
RegisterPostAuthHookFunc: func(_ authn.PostAuthHookFn, _ uint) {
|
||||||
|
hookRegistered = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = ProvideService(setting.NewCfg(), nil, nil, features, authnService)
|
||||||
|
assert.True(t, hookRegistered)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not register post auth hook when feature flag is disabled", func(t *testing.T) {
|
||||||
|
features := featuremgmt.WithFeatures()
|
||||||
|
|
||||||
|
var hookRegistered bool
|
||||||
|
authnService := &authntest.MockService{
|
||||||
|
RegisterPostAuthHookFunc: func(_ authn.PostAuthHookFn, _ uint) {
|
||||||
|
hookRegistered = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = ProvideService(setting.NewCfg(), nil, nil, features, authnService)
|
||||||
|
assert.False(t, hookRegistered)
|
||||||
|
})
|
||||||
|
}
|
@ -6,18 +6,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"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/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
@ -176,164 +170,6 @@ type Redirect struct {
|
|||||||
Extra map[string]string
|
Extra map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
NamespaceUser = identity.NamespaceUser
|
|
||||||
NamespaceAPIKey = identity.NamespaceAPIKey
|
|
||||||
NamespaceServiceAccount = identity.NamespaceServiceAccount
|
|
||||||
NamespaceAnonymous = identity.NamespaceAnonymous
|
|
||||||
NamespaceRenderService = identity.NamespaceRenderService
|
|
||||||
)
|
|
||||||
|
|
||||||
type Identity struct {
|
|
||||||
// OrgID is the active organization for the entity.
|
|
||||||
OrgID int64
|
|
||||||
// OrgName is the name of the active organization.
|
|
||||||
OrgName string
|
|
||||||
// OrgRoles is the list of organizations the entity is a member of and their roles.
|
|
||||||
OrgRoles map[int64]org.RoleType
|
|
||||||
// ID is the unique identifier for the entity in the Grafana database.
|
|
||||||
// It is in the format <namespace>:<id> where namespace is one of the
|
|
||||||
// Namespace* constants. For example, "user:1" or "api-key:1".
|
|
||||||
// If the entity is not found in the DB or this entity is non-persistent, this field will be empty.
|
|
||||||
ID string
|
|
||||||
// IsAnonymous
|
|
||||||
IsAnonymous bool
|
|
||||||
// Login is the shorthand identifier of the entity. Should be unique.
|
|
||||||
Login string
|
|
||||||
// Name is the display name of the entity. It is not guaranteed to be unique.
|
|
||||||
Name string
|
|
||||||
// Email is the email address of the entity. Should be unique.
|
|
||||||
Email string
|
|
||||||
// IsGrafanaAdmin is true if the entity is a Grafana admin.
|
|
||||||
IsGrafanaAdmin *bool
|
|
||||||
// AuthenticatedBy is the name of the authentication client that was used to authenticate the current Identity.
|
|
||||||
// For example, "password", "apikey", "auth_ldap" or "auth_azuread".
|
|
||||||
AuthenticatedBy string
|
|
||||||
// AuthId is the unique identifier for the entity in the external system.
|
|
||||||
// Empty if the identity is provided by Grafana.
|
|
||||||
AuthID string
|
|
||||||
// IsDisabled is true if the entity is disabled.
|
|
||||||
IsDisabled bool
|
|
||||||
// HelpFlags1 is the help flags for the entity.
|
|
||||||
HelpFlags1 user.HelpFlags1
|
|
||||||
// LastSeenAt is the time when the entity was last seen.
|
|
||||||
LastSeenAt time.Time
|
|
||||||
// Teams is the list of teams the entity is a member of.
|
|
||||||
Teams []int64
|
|
||||||
// idP Groups that the entity is a member of. This is only populated if the
|
|
||||||
// identity provider supports groups.
|
|
||||||
Groups []string
|
|
||||||
// OAuthToken is the OAuth token used to authenticate the entity.
|
|
||||||
OAuthToken *oauth2.Token
|
|
||||||
// SessionToken is the session token used to authenticate the entity.
|
|
||||||
SessionToken *usertoken.UserToken
|
|
||||||
// ClientParams are hints for the auth service on how to handle the identity.
|
|
||||||
// Set by the authenticating client.
|
|
||||||
ClientParams ClientParams
|
|
||||||
// Permissions is the list of permissions the entity has.
|
|
||||||
Permissions map[int64]map[string][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Role returns the role of the identity in the active organization.
|
|
||||||
func (i *Identity) Role() org.RoleType {
|
|
||||||
return i.OrgRoles[i.OrgID]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamespacedID returns the namespace, e.g. "user" and the id for that namespace
|
|
||||||
func (i *Identity) NamespacedID() (string, int64) {
|
|
||||||
split := strings.Split(i.ID, ":")
|
|
||||||
if len(split) != 2 {
|
|
||||||
return "", -1
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := strconv.ParseInt(split[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
// FIXME (kalleep): Improve error handling
|
|
||||||
return "", -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return split[0], id
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamespacedID builds a namespaced ID from a namespace and an ID.
|
|
||||||
func NamespacedID(namespace string, id int64) string {
|
|
||||||
return fmt.Sprintf("%s:%d", namespace, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(),
|
|
||||||
Login: i.Login,
|
|
||||||
Name: i.Name,
|
|
||||||
Email: i.Email,
|
|
||||||
AuthenticatedBy: i.AuthenticatedBy,
|
|
||||||
IsGrafanaAdmin: isGrafanaAdmin,
|
|
||||||
IsAnonymous: i.IsAnonymous,
|
|
||||||
IsDisabled: i.IsDisabled,
|
|
||||||
HelpFlags1: i.HelpFlags1,
|
|
||||||
LastSeenAt: i.LastSeenAt,
|
|
||||||
Teams: i.Teams,
|
|
||||||
Permissions: i.Permissions,
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace, id := i.NamespacedID()
|
|
||||||
if namespace == NamespaceAPIKey {
|
|
||||||
u.ApiKeyID = id
|
|
||||||
} else {
|
|
||||||
u.UserID = id
|
|
||||||
u.IsServiceAccount = namespace == NamespaceServiceAccount
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Identity) ExternalUserInfo() login.ExternalUserInfo {
|
|
||||||
_, id := i.NamespacedID()
|
|
||||||
return login.ExternalUserInfo{
|
|
||||||
OAuthToken: i.OAuthToken,
|
|
||||||
AuthModule: i.AuthenticatedBy,
|
|
||||||
AuthId: i.AuthID,
|
|
||||||
UserId: id,
|
|
||||||
Email: i.Email,
|
|
||||||
Login: i.Login,
|
|
||||||
Name: i.Name,
|
|
||||||
Groups: i.Groups,
|
|
||||||
OrgRoles: i.OrgRoles,
|
|
||||||
IsGrafanaAdmin: i.IsGrafanaAdmin,
|
|
||||||
IsDisabled: i.IsDisabled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IdentityFromSignedInUser creates an identity from a SignedInUser.
|
|
||||||
func IdentityFromSignedInUser(id string, usr *user.SignedInUser, params ClientParams, authenticatedBy string) *Identity {
|
|
||||||
return &Identity{
|
|
||||||
ID: id,
|
|
||||||
OrgID: usr.OrgID,
|
|
||||||
OrgName: usr.OrgName,
|
|
||||||
OrgRoles: map[int64]org.RoleType{usr.OrgID: usr.OrgRole},
|
|
||||||
Login: usr.Login,
|
|
||||||
Name: usr.Name,
|
|
||||||
Email: usr.Email,
|
|
||||||
AuthenticatedBy: authenticatedBy,
|
|
||||||
IsGrafanaAdmin: &usr.IsGrafanaAdmin,
|
|
||||||
IsDisabled: usr.IsDisabled,
|
|
||||||
HelpFlags1: usr.HelpFlags1,
|
|
||||||
LastSeenAt: usr.LastSeenAt,
|
|
||||||
Teams: usr.Teams,
|
|
||||||
ClientParams: params,
|
|
||||||
Permissions: usr.Permissions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientWithPrefix returns a client name prefixed with "auth.client."
|
// ClientWithPrefix returns a client name prefixed with "auth.client."
|
||||||
func ClientWithPrefix(name string) string {
|
func ClientWithPrefix(name string) string {
|
||||||
return fmt.Sprintf("auth.client.%s", name)
|
return fmt.Sprintf("auth.client.%s", name)
|
||||||
|
@ -10,7 +10,8 @@ var _ authn.Service = new(MockService)
|
|||||||
var _ authn.IdentitySynchronizer = new(MockService)
|
var _ authn.IdentitySynchronizer = new(MockService)
|
||||||
|
|
||||||
type MockService struct {
|
type MockService struct {
|
||||||
SyncIdentityFunc func(ctx context.Context, identity *authn.Identity) error
|
SyncIdentityFunc func(ctx context.Context, identity *authn.Identity) error
|
||||||
|
RegisterPostAuthHookFunc func(hook authn.PostAuthHookFn, priority uint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockService) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) {
|
func (m *MockService) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) {
|
||||||
@ -30,7 +31,9 @@ func (m *MockService) RegisterClient(c authn.Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockService) RegisterPostAuthHook(hook authn.PostAuthHookFn, priority uint) {
|
func (m *MockService) RegisterPostAuthHook(hook authn.PostAuthHookFn, priority uint) {
|
||||||
panic("unimplemented")
|
if m.RegisterPostAuthHookFunc != nil {
|
||||||
|
m.RegisterPostAuthHookFunc(hook, priority)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockService) RegisterPostLoginHook(hook authn.PostLoginHookFn, priority uint) {
|
func (m *MockService) RegisterPostLoginHook(hook authn.PostLoginHookFn, priority uint) {
|
||||||
|
179
pkg/services/authn/identity.go
Normal file
179
pkg/services/authn/identity.go
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
package authn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NamespacedID builds a namespaced ID from a namespace and an ID.
|
||||||
|
func NamespacedID(namespace string, id int64) string {
|
||||||
|
return fmt.Sprintf("%s:%d", namespace, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
NamespaceUser = identity.NamespaceUser
|
||||||
|
NamespaceAPIKey = identity.NamespaceAPIKey
|
||||||
|
NamespaceServiceAccount = identity.NamespaceServiceAccount
|
||||||
|
NamespaceAnonymous = identity.NamespaceAnonymous
|
||||||
|
NamespaceRenderService = identity.NamespaceRenderService
|
||||||
|
)
|
||||||
|
|
||||||
|
type Identity struct {
|
||||||
|
// OrgID is the active organization for the entity.
|
||||||
|
OrgID int64
|
||||||
|
// OrgName is the name of the active organization.
|
||||||
|
OrgName string
|
||||||
|
// OrgRoles is the list of organizations the entity is a member of and their roles.
|
||||||
|
OrgRoles map[int64]org.RoleType
|
||||||
|
// ID is the unique identifier for the entity in the Grafana database.
|
||||||
|
// It is in the format <namespace>:<id> where namespace is one of the
|
||||||
|
// Namespace* constants. For example, "user:1" or "api-key:1".
|
||||||
|
// If the entity is not found in the DB or this entity is non-persistent, this field will be empty.
|
||||||
|
ID string
|
||||||
|
// IsAnonymous
|
||||||
|
IsAnonymous bool
|
||||||
|
// Login is the shorthand identifier of the entity. Should be unique.
|
||||||
|
Login string
|
||||||
|
// Name is the display name of the entity. It is not guaranteed to be unique.
|
||||||
|
Name string
|
||||||
|
// Email is the email address of the entity. Should be unique.
|
||||||
|
Email string
|
||||||
|
// IsGrafanaAdmin is true if the entity is a Grafana admin.
|
||||||
|
IsGrafanaAdmin *bool
|
||||||
|
// AuthenticatedBy is the name of the authentication client that was used to authenticate the current Identity.
|
||||||
|
// For example, "password", "apikey", "auth_ldap" or "auth_azuread".
|
||||||
|
AuthenticatedBy string
|
||||||
|
// AuthId is the unique identifier for the entity in the external system.
|
||||||
|
// Empty if the identity is provided by Grafana.
|
||||||
|
AuthID string
|
||||||
|
// IsDisabled is true if the entity is disabled.
|
||||||
|
IsDisabled bool
|
||||||
|
// HelpFlags1 is the help flags for the entity.
|
||||||
|
HelpFlags1 user.HelpFlags1
|
||||||
|
// LastSeenAt is the time when the entity was last seen.
|
||||||
|
LastSeenAt time.Time
|
||||||
|
// Teams is the list of teams the entity is a member of.
|
||||||
|
Teams []int64
|
||||||
|
// idP Groups that the entity is a member of. This is only populated if the
|
||||||
|
// identity provider supports groups.
|
||||||
|
Groups []string
|
||||||
|
// OAuthToken is the OAuth token used to authenticate the entity.
|
||||||
|
OAuthToken *oauth2.Token
|
||||||
|
// SessionToken is the session token used to authenticate the entity.
|
||||||
|
SessionToken *usertoken.UserToken
|
||||||
|
// ClientParams are hints for the auth service on how to handle the identity.
|
||||||
|
// Set by the authenticating client.
|
||||||
|
ClientParams ClientParams
|
||||||
|
// Permissions is the list of permissions the entity has.
|
||||||
|
Permissions map[int64]map[string][]string
|
||||||
|
// IDToken is a signed token representing the identity that can be forwarded to plugins and external services.
|
||||||
|
// Will only be set when featuremgmt.FlagIdForwarding is enabled.
|
||||||
|
IDToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Role returns the role of the identity in the active organization.
|
||||||
|
func (i *Identity) Role() org.RoleType {
|
||||||
|
return i.OrgRoles[i.OrgID]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamespacedID returns the namespace, e.g. "user" and the id for that namespace
|
||||||
|
func (i *Identity) NamespacedID() (string, int64) {
|
||||||
|
split := strings.Split(i.ID, ":")
|
||||||
|
if len(split) != 2 {
|
||||||
|
return "", -1
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := strconv.ParseInt(split[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
// FIXME (kalleep): Improve error handling
|
||||||
|
return "", -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return split[0], id
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(),
|
||||||
|
Login: i.Login,
|
||||||
|
Name: i.Name,
|
||||||
|
Email: i.Email,
|
||||||
|
AuthenticatedBy: i.AuthenticatedBy,
|
||||||
|
IsGrafanaAdmin: isGrafanaAdmin,
|
||||||
|
IsAnonymous: i.IsAnonymous,
|
||||||
|
IsDisabled: i.IsDisabled,
|
||||||
|
HelpFlags1: i.HelpFlags1,
|
||||||
|
LastSeenAt: i.LastSeenAt,
|
||||||
|
Teams: i.Teams,
|
||||||
|
Permissions: i.Permissions,
|
||||||
|
IDToken: i.IDToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace, id := i.NamespacedID()
|
||||||
|
if namespace == NamespaceAPIKey {
|
||||||
|
u.ApiKeyID = id
|
||||||
|
} else {
|
||||||
|
u.UserID = id
|
||||||
|
u.IsServiceAccount = namespace == NamespaceServiceAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Identity) ExternalUserInfo() login.ExternalUserInfo {
|
||||||
|
_, id := i.NamespacedID()
|
||||||
|
return login.ExternalUserInfo{
|
||||||
|
OAuthToken: i.OAuthToken,
|
||||||
|
AuthModule: i.AuthenticatedBy,
|
||||||
|
AuthId: i.AuthID,
|
||||||
|
UserId: id,
|
||||||
|
Email: i.Email,
|
||||||
|
Login: i.Login,
|
||||||
|
Name: i.Name,
|
||||||
|
Groups: i.Groups,
|
||||||
|
OrgRoles: i.OrgRoles,
|
||||||
|
IsGrafanaAdmin: i.IsGrafanaAdmin,
|
||||||
|
IsDisabled: i.IsDisabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdentityFromSignedInUser creates an identity from a SignedInUser.
|
||||||
|
func IdentityFromSignedInUser(id string, usr *user.SignedInUser, params ClientParams, authenticatedBy string) *Identity {
|
||||||
|
return &Identity{
|
||||||
|
ID: id,
|
||||||
|
OrgID: usr.OrgID,
|
||||||
|
OrgName: usr.OrgName,
|
||||||
|
OrgRoles: map[int64]org.RoleType{usr.OrgID: usr.OrgRole},
|
||||||
|
Login: usr.Login,
|
||||||
|
Name: usr.Name,
|
||||||
|
Email: usr.Email,
|
||||||
|
AuthenticatedBy: authenticatedBy,
|
||||||
|
IsGrafanaAdmin: &usr.IsGrafanaAdmin,
|
||||||
|
IsDisabled: usr.IsDisabled,
|
||||||
|
HelpFlags1: usr.HelpFlags1,
|
||||||
|
LastSeenAt: usr.LastSeenAt,
|
||||||
|
Teams: usr.Teams,
|
||||||
|
ClientParams: params,
|
||||||
|
Permissions: usr.Permissions,
|
||||||
|
IDToken: usr.IDToken,
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,9 @@ type SignedInUser struct {
|
|||||||
Teams []int64
|
Teams []int64
|
||||||
// Permissions grouped by orgID and actions
|
// Permissions grouped by orgID and actions
|
||||||
Permissions map[int64]map[string][]string `json:"-"`
|
Permissions map[int64]map[string][]string `json:"-"`
|
||||||
|
// IDToken is a signed token representing the identity that can be forwarded to plugins and external services.
|
||||||
|
// Will only be set when featuremgmt.FlagIdForwarding is enabled.
|
||||||
|
IDToken string `json:"-" xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SignedInUser) ShouldUpdateLastSeenAt() bool {
|
func (u *SignedInUser) ShouldUpdateLastSeenAt() bool {
|
||||||
@ -210,3 +213,7 @@ func (u *SignedInUser) GetDisplayName() string {
|
|||||||
func (u *SignedInUser) GetAuthenticatedBy() string {
|
func (u *SignedInUser) GetAuthenticatedBy() string {
|
||||||
return u.AuthenticatedBy
|
return u.AuthenticatedBy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) GetIDToken() string {
|
||||||
|
return u.IDToken
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user