AuthN: fetch final state of signed in user (#62854)

* AuthN: add a hook we can use to fetch final state of user
This commit is contained in:
Karl Persson
2023-02-03 14:14:38 +01:00
committed by GitHub
parent 29212c9a3f
commit 180a587f70
12 changed files with 112 additions and 9 deletions

View File

@@ -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
}

View File

@@ -145,6 +145,8 @@ func ProvideService(
s.RegisterPostAuthHook(sync.ProvideOauthTokenSync(oauthTokenService, sessionService).SyncOauthToken, 60)
}
s.RegisterPostAuthHook(sync.ProvideFetchUserSync(userService).FetchSyncedUserHook, 100)
return s
}

View File

@@ -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
}

View File

@@ -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)
})
}
}

View File

@@ -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,
},
}

View File

@@ -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"),

View File

@@ -70,10 +70,8 @@ func (s *JWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identi
OrgRoles: map[int64]org.RoleType{},
ClientParams: authn.ClientParams{
SyncUser: true,
// We do not allow team member sync from JWT Authentication
SyncTeamMembers: false,
FetchSyncedUser: true,
AllowSignUp: s.cfg.JWTAuthAutoSignUp,
EnableDisabledUsers: false,
}}
if key := s.cfg.JWTAuthUsernameClaim; key != "" {

View File

@@ -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"),

View File

@@ -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,

View File

@@ -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"),

View File

@@ -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},
},

View File

@@ -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")},
},
},