mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Authn: Add function to resolve identity from org and namespace id (#84555)
* Add function to get the namespaced id * Add function to resolve an identity through authn.Service from org and namespace id * Switch to resolve identity for re-authenticate in another org
This commit is contained in:
@@ -20,6 +20,11 @@ var ErrNotIntIdentifier = errors.New("identifier is not an int64")
|
||||
var ErrIdentifierNotInitialized = errors.New("identifier is not initialized")
|
||||
|
||||
type Requester interface {
|
||||
// GetID returns namespaced id for the entity
|
||||
GetID() string
|
||||
// GetNamespacedID returns the namespace and ID of the active entity.
|
||||
// The namespace is one of the constants defined in pkg/services/auth/identity.
|
||||
GetNamespacedID() (namespace string, identifier string)
|
||||
// GetDisplayName returns the display name of the active entity.
|
||||
// The display name is the name if it is set, otherwise the login or email.
|
||||
GetDisplayName() string
|
||||
@@ -31,9 +36,6 @@ type Requester interface {
|
||||
// GetLogin returns the login of the active entity
|
||||
// Can be empty.
|
||||
GetLogin() string
|
||||
// GetNamespacedID returns the namespace and ID of the active entity.
|
||||
// The namespace is one of the constants defined in pkg/services/auth/identity.
|
||||
GetNamespacedID() (namespace string, identifier string)
|
||||
// GetOrgID returns the ID of the active organization
|
||||
GetOrgID() int64
|
||||
// GetOrgRole returns the role of the active entity in the active organization.
|
||||
|
||||
@@ -77,6 +77,10 @@ type Service interface {
|
||||
RedirectURL(ctx context.Context, client string, r *Request) (*Redirect, error)
|
||||
// Logout revokes session token and does additional clean up if client used to authenticate supports it
|
||||
Logout(ctx context.Context, user identity.Requester, sessionToken *usertoken.UserToken) (*Redirect, error)
|
||||
|
||||
// ResolveIdentity resolves an identity from org and namespace id.
|
||||
ResolveIdentity(ctx context.Context, orgID int64, namespaceID string) (*Identity, error)
|
||||
|
||||
// RegisterClient will register a new authn.Client that can be used for authentication
|
||||
RegisterClient(c Client)
|
||||
}
|
||||
|
||||
@@ -391,6 +391,20 @@ Default:
|
||||
return redirect, nil
|
||||
}
|
||||
|
||||
func (s *Service) ResolveIdentity(ctx context.Context, orgID int64, namespaceID string) (*authn.Identity, error) {
|
||||
r := &authn.Request{}
|
||||
r.OrgID = orgID
|
||||
// hack to not update last seen
|
||||
r.SetMeta(authn.MetaKeyIsLogin, "true")
|
||||
|
||||
identity, err := s.authenticate(ctx, clients.ProvideIdentity(namespaceID), r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return identity, nil
|
||||
}
|
||||
|
||||
func (s *Service) RegisterClient(c authn.Client) {
|
||||
s.clients[c.Name()] = c
|
||||
if cac, ok := c.(authn.ContextAwareClient); ok {
|
||||
|
||||
@@ -68,7 +68,11 @@ func (f *FakeService) RedirectURL(ctx context.Context, client string, r *authn.R
|
||||
return f.ExpectedRedirect, f.ExpectedErr
|
||||
}
|
||||
|
||||
func (*FakeService) Logout(_ context.Context, _ identity.Requester, _ *usertoken.UserToken) (*authn.Redirect, error) {
|
||||
func (f *FakeService) Logout(_ context.Context, _ identity.Requester, _ *usertoken.UserToken) (*authn.Redirect, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (f *FakeService) ResolveIdentity(ctx context.Context, orgID int64, namespaceID string) (*authn.Identity, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,10 @@ func (*MockService) Logout(_ context.Context, _ identity.Requester, _ *usertoken
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (m *MockService) ResolveIdentity(ctx context.Context, orgID int64, namespaceID string) (*authn.Identity, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (m *MockService) SyncIdentity(ctx context.Context, identity *authn.Identity) error {
|
||||
if m.SyncIdentityFunc != nil {
|
||||
return m.SyncIdentityFunc(ctx, identity)
|
||||
|
||||
33
pkg/services/authn/clients/identity.go
Normal file
33
pkg/services/authn/clients/identity.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package clients
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
)
|
||||
|
||||
var _ authn.Client = (*IdentityClient)(nil)
|
||||
|
||||
func ProvideIdentity(namespaceID string) *IdentityClient {
|
||||
return &IdentityClient{namespaceID}
|
||||
}
|
||||
|
||||
type IdentityClient struct {
|
||||
namespaceID string
|
||||
}
|
||||
|
||||
func (i *IdentityClient) Name() string {
|
||||
return "identity"
|
||||
}
|
||||
|
||||
// Authenticate implements authn.Client.
|
||||
func (i *IdentityClient) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) {
|
||||
return &authn.Identity{
|
||||
OrgID: r.OrgID,
|
||||
ID: i.namespaceID,
|
||||
ClientParams: authn.ClientParams{
|
||||
FetchSyncedUser: true,
|
||||
SyncPermissions: true,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -87,6 +87,18 @@ type Identity struct {
|
||||
IDToken string
|
||||
}
|
||||
|
||||
func (i *Identity) GetID() string {
|
||||
return i.ID
|
||||
}
|
||||
|
||||
func (i *Identity) GetNamespacedID() (namespace string, identifier string) {
|
||||
split := strings.Split(i.GetID(), ":")
|
||||
if len(split) != 2 {
|
||||
return "", ""
|
||||
}
|
||||
return split[0], split[1]
|
||||
}
|
||||
|
||||
func (i *Identity) GetAuthenticatedBy() string {
|
||||
return i.AuthenticatedBy
|
||||
}
|
||||
@@ -122,16 +134,6 @@ 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
|
||||
|
||||
@@ -2,6 +2,7 @@ package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models/roletype"
|
||||
@@ -189,28 +190,31 @@ func (u *SignedInUser) GetOrgRole() roletype.RoleType {
|
||||
return u.OrgRole
|
||||
}
|
||||
|
||||
// GetNamespacedID returns the namespace and ID of the active entity
|
||||
// The namespace is one of the constants defined in pkg/services/auth/identity
|
||||
func (u *SignedInUser) GetNamespacedID() (string, string) {
|
||||
// GetID returns namespaced id for the entity
|
||||
func (u *SignedInUser) GetID() string {
|
||||
switch {
|
||||
case u.ApiKeyID != 0:
|
||||
return identity.NamespaceAPIKey, fmt.Sprintf("%d", u.ApiKeyID)
|
||||
return namespacedID(identity.NamespaceAPIKey, u.ApiKeyID)
|
||||
case u.IsServiceAccount:
|
||||
return identity.NamespaceServiceAccount, fmt.Sprintf("%d", u.UserID)
|
||||
return namespacedID(identity.NamespaceServiceAccount, u.UserID)
|
||||
case u.UserID > 0:
|
||||
return identity.NamespaceUser, fmt.Sprintf("%d", u.UserID)
|
||||
return namespacedID(identity.NamespaceUser, u.UserID)
|
||||
case u.IsAnonymous:
|
||||
return identity.NamespaceAnonymous, ""
|
||||
case u.AuthenticatedBy == "render": //import cycle render
|
||||
if u.UserID == 0 {
|
||||
return identity.NamespaceRenderService, "0"
|
||||
} else { // this should never happen as u.UserID > 0 already catches this
|
||||
return identity.NamespaceUser, fmt.Sprintf("%d", u.UserID)
|
||||
}
|
||||
return identity.NamespaceAnonymous + ":"
|
||||
case u.AuthenticatedBy == "render" && u.UserID == 0:
|
||||
return namespacedID(identity.NamespaceRenderService, 0)
|
||||
}
|
||||
|
||||
// backwards compatibility
|
||||
return identity.NamespaceUser, fmt.Sprintf("%d", u.UserID)
|
||||
return namespacedID(identity.NamespaceUser, u.UserID)
|
||||
}
|
||||
|
||||
// GetNamespacedID returns the namespace and ID of the active entity
|
||||
// The namespace is one of the constants defined in pkg/services/auth/identity
|
||||
func (u *SignedInUser) GetNamespacedID() (string, string) {
|
||||
parts := strings.Split(u.GetID(), ":")
|
||||
// Safety: GetID always returns a ':' separated string
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
|
||||
// FIXME: remove this method once all services are using an interface
|
||||
@@ -238,3 +242,7 @@ func (u *SignedInUser) GetAuthenticatedBy() string {
|
||||
func (u *SignedInUser) GetIDToken() string {
|
||||
return u.IDToken
|
||||
}
|
||||
|
||||
func namespacedID(namespace string, id int64) string {
|
||||
return fmt.Sprintf("%s:%d", namespace, id)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user