diff --git a/pkg/services/authn/authn.go b/pkg/services/authn/authn.go index 65d8b31f912..bf2efd2c6f7 100644 --- a/pkg/services/authn/authn.go +++ b/pkg/services/authn/authn.go @@ -45,6 +45,8 @@ type ClientParams struct { AllowSignUp bool // EnableDisabledUsers is a hint to the auth service that it should re-enable disabled users EnableDisabledUsers bool + // FetchSyncedUser ensure that all required information is added to the identity + FetchSyncedUser bool // LookUpParams are the arguments used to look up the entity in the DB. LookUpParams login.UserLookupParams } diff --git a/pkg/services/authn/authnimpl/service.go b/pkg/services/authn/authnimpl/service.go index 52ddf7e43a1..0b41e3bff43 100644 --- a/pkg/services/authn/authnimpl/service.go +++ b/pkg/services/authn/authnimpl/service.go @@ -145,6 +145,8 @@ func ProvideService( s.RegisterPostAuthHook(sync.ProvideOauthTokenSync(oauthTokenService, sessionService).SyncOauthToken, 60) } + s.RegisterPostAuthHook(sync.ProvideFetchUserSync(userService).FetchSyncedUserHook, 100) + return s } diff --git a/pkg/services/authn/authnimpl/sync/fetch_user_sync.go b/pkg/services/authn/authnimpl/sync/fetch_user_sync.go new file mode 100644 index 00000000000..064326f2588 --- /dev/null +++ b/pkg/services/authn/authnimpl/sync/fetch_user_sync.go @@ -0,0 +1,56 @@ +package sync + +import ( + "context" + + "github.com/grafana/grafana/pkg/services/authn" + "github.com/grafana/grafana/pkg/services/org" + "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/util/errutil" +) + +var errFetchingSignedInUser = errutil.NewBase(errutil.StatusInternal, "user.sync.fetch", errutil.WithPublicMessage("Insufficient information to authenticate user")) + +func ProvideFetchUserSync(service user.Service) *FetchUserSync { + return &FetchUserSync{service} +} + +type FetchUserSync struct { + userService user.Service +} + +func (s *FetchUserSync) FetchSyncedUserHook(ctx context.Context, identity *authn.Identity, r *authn.Request) error { + if !identity.ClientParams.FetchSyncedUser { + return nil + } + namespace, id := identity.NamespacedID() + if namespace != authn.NamespaceUser { + return nil + } + + usr, err := s.userService.GetSignedInUserWithCacheCtx(ctx, &user.GetSignedInUserQuery{ + UserID: id, + OrgID: r.OrgID, + }) + if err != nil { + return errFetchingSignedInUser.Errorf("failed to resolve user: %w", err) + } + + syncSignedInUserToIdentity(usr, identity) + return nil +} + +func syncSignedInUserToIdentity(usr *user.SignedInUser, identity *authn.Identity) { + identity.Name = usr.Name + identity.Login = usr.Login + identity.Email = usr.Email + identity.OrgID = usr.OrgID + identity.OrgName = usr.OrgName + identity.OrgCount = usr.OrgCount + identity.OrgRoles = map[int64]org.RoleType{identity.OrgID: usr.OrgRole} + identity.HelpFlags1 = usr.HelpFlags1 + identity.Teams = usr.Teams + identity.LastSeenAt = usr.LastSeenAt + identity.IsDisabled = usr.IsDisabled + identity.IsGrafanaAdmin = &usr.IsGrafanaAdmin +} diff --git a/pkg/services/authn/authnimpl/sync/fetch_user_sync_test.go b/pkg/services/authn/authnimpl/sync/fetch_user_sync_test.go new file mode 100644 index 00000000000..e7f1bac6fd9 --- /dev/null +++ b/pkg/services/authn/authnimpl/sync/fetch_user_sync_test.go @@ -0,0 +1,40 @@ +package sync + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/services/authn" +) + +func TestFetchUserSync_FetchSyncedUserHook(t *testing.T) { + type testCase struct { + desc string + req *authn.Request + identity *authn.Identity + expectedErr error + } + + tests := []testCase{ + { + desc: "should skip hook when flag is not enabled", + req: &authn.Request{}, + identity: &authn.Identity{ClientParams: authn.ClientParams{FetchSyncedUser: false}}, + }, + { + desc: "should skip hook when identity is not a user", + req: &authn.Request{}, + identity: &authn.Identity{ID: "apikey:1", ClientParams: authn.ClientParams{FetchSyncedUser: true}}, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + s := ProvideFetchUserSync(nil) + err := s.FetchSyncedUserHook(context.Background(), tt.identity, tt.req) + require.ErrorIs(t, err, tt.expectedErr) + }) + } +} diff --git a/pkg/services/authn/clients/grafana.go b/pkg/services/authn/clients/grafana.go index ca830973627..ee5c6f85b1d 100644 --- a/pkg/services/authn/clients/grafana.go +++ b/pkg/services/authn/clients/grafana.go @@ -33,6 +33,7 @@ func (c *Grafana) AuthenticateProxy(ctx context.Context, r *authn.Request, usern ClientParams: authn.ClientParams{ SyncUser: true, SyncTeamMembers: true, + FetchSyncedUser: true, AllowSignUp: c.cfg.AuthProxyAutoSignUp, }, } diff --git a/pkg/services/authn/clients/grafana_test.go b/pkg/services/authn/clients/grafana_test.go index 9bf3ce88a13..48f3a16038d 100644 --- a/pkg/services/authn/clients/grafana_test.go +++ b/pkg/services/authn/clients/grafana_test.go @@ -52,6 +52,7 @@ func TestGrafana_AuthenticateProxy(t *testing.T) { SyncUser: true, SyncTeamMembers: true, AllowSignUp: true, + FetchSyncedUser: true, LookUpParams: login.UserLookupParams{ Email: strPtr("email@email.com"), Login: strPtr("test"), diff --git a/pkg/services/authn/clients/jwt.go b/pkg/services/authn/clients/jwt.go index 8ad47e3105e..9b7f6e654c4 100644 --- a/pkg/services/authn/clients/jwt.go +++ b/pkg/services/authn/clients/jwt.go @@ -69,11 +69,9 @@ func (s *JWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identi AuthID: sub, OrgRoles: map[int64]org.RoleType{}, ClientParams: authn.ClientParams{ - SyncUser: true, - // We do not allow team member sync from JWT Authentication - SyncTeamMembers: false, - AllowSignUp: s.cfg.JWTAuthAutoSignUp, - EnableDisabledUsers: false, + SyncUser: true, + FetchSyncedUser: true, + AllowSignUp: s.cfg.JWTAuthAutoSignUp, }} if key := s.cfg.JWTAuthUsernameClaim; key != "" { diff --git a/pkg/services/authn/clients/jwt_test.go b/pkg/services/authn/clients/jwt_test.go index 29035b19145..2fcac9b1b5b 100644 --- a/pkg/services/authn/clients/jwt_test.go +++ b/pkg/services/authn/clients/jwt_test.go @@ -49,9 +49,9 @@ func TestAuthenticateJWT(t *testing.T) { IsDisabled: false, HelpFlags1: 0, ClientParams: authn.ClientParams{ - SyncTeamMembers: false, SyncUser: true, AllowSignUp: true, + FetchSyncedUser: true, LookUpParams: login.UserLookupParams{ UserID: nil, Email: stringPtr("eai.doe@cor.po"), diff --git a/pkg/services/authn/clients/ldap.go b/pkg/services/authn/clients/ldap.go index 943601abf42..2b41d35d74f 100644 --- a/pkg/services/authn/clients/ldap.go +++ b/pkg/services/authn/clients/ldap.go @@ -103,8 +103,9 @@ func identityFromLDAPInfo(orgID int64, info *login.ExternalUserInfo, allowSignup ClientParams: authn.ClientParams{ SyncUser: true, SyncTeamMembers: true, - AllowSignUp: allowSignup, EnableDisabledUsers: true, + FetchSyncedUser: true, + AllowSignUp: allowSignup, LookUpParams: login.UserLookupParams{ Login: &info.Login, Email: &info.Email, diff --git a/pkg/services/authn/clients/ldap_test.go b/pkg/services/authn/clients/ldap_test.go index e5c59207320..cdcaee93192 100644 --- a/pkg/services/authn/clients/ldap_test.go +++ b/pkg/services/authn/clients/ldap_test.go @@ -49,8 +49,8 @@ func TestLDAP_AuthenticateProxy(t *testing.T) { ClientParams: authn.ClientParams{ SyncUser: true, SyncTeamMembers: true, - AllowSignUp: false, EnableDisabledUsers: true, + FetchSyncedUser: true, LookUpParams: login.UserLookupParams{ Email: strPtr("test@test.com"), Login: strPtr("test"), @@ -113,8 +113,8 @@ func TestLDAP_AuthenticatePassword(t *testing.T) { ClientParams: authn.ClientParams{ SyncUser: true, SyncTeamMembers: true, - AllowSignUp: false, EnableDisabledUsers: true, + FetchSyncedUser: true, LookUpParams: login.UserLookupParams{ Email: strPtr("test@test.com"), Login: strPtr("test"), diff --git a/pkg/services/authn/clients/oauth.go b/pkg/services/authn/clients/oauth.go index 0c99385ee6f..9dbbb27d106 100644 --- a/pkg/services/authn/clients/oauth.go +++ b/pkg/services/authn/clients/oauth.go @@ -136,6 +136,7 @@ func (c *OAuth) Authenticate(ctx context.Context, r *authn.Request) (*authn.Iden ClientParams: authn.ClientParams{ SyncUser: true, SyncTeamMembers: true, + FetchSyncedUser: true, AllowSignUp: c.connector.IsSignupAllowed(), LookUpParams: login.UserLookupParams{Email: &userInfo.Email}, }, diff --git a/pkg/services/authn/clients/oauth_test.go b/pkg/services/authn/clients/oauth_test.go index b4f5469d23c..1a3a10988bd 100644 --- a/pkg/services/authn/clients/oauth_test.go +++ b/pkg/services/authn/clients/oauth_test.go @@ -138,6 +138,7 @@ func TestOAuth_Authenticate(t *testing.T) { SyncUser: true, SyncTeamMembers: true, AllowSignUp: true, + FetchSyncedUser: true, LookUpParams: login.UserLookupParams{Email: strPtr("some@email.com")}, }, },