fix: idp usage (#4571)

* fix: send email verification instead of init code for idp users

* fix: select single idp of external only users

* fix: use single idp on login
This commit is contained in:
Livio Spring
2022-10-18 16:48:26 +02:00
committed by GitHub
parent e7bc887a47
commit 3270a94291
11 changed files with 172 additions and 35 deletions

View File

@@ -48,6 +48,7 @@ type AuthRequestRepo struct {
LockoutPolicyViewProvider lockoutPolicyViewProvider
PrivacyPolicyProvider privacyPolicyProvider
IDPProviderViewProvider idpProviderViewProvider
IDPUserLinksProvider idpUserLinksProvider
UserGrantProvider userGrantProvider
ProjectProvider projectProvider
ApplicationProvider applicationProvider
@@ -83,6 +84,10 @@ type idpProviderViewProvider interface {
IDPProvidersByAggregateIDAndState(string, string, iam_model.IDPConfigState) ([]*iam_view_model.IDPProviderView, error)
}
type idpUserLinksProvider interface {
IDPUserLinks(ctx context.Context, queries *query.IDPUserLinksSearchQuery) (*query.IDPUserLinks, error)
}
type userEventProvider interface {
UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error)
}
@@ -469,11 +474,15 @@ func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, regis
if err != nil {
return err
}
emailCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, repo.UserCodeAlg)
if err != nil {
return err
}
phoneCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, repo.UserCodeAlg)
if err != nil {
return err
}
human, err := repo.Command.RegisterHuman(ctx, resourceOwner, registerUser, externalIDP, orgMemberRoles, initCodeGenerator, phoneCodeGenerator)
human, err := repo.Command.RegisterHuman(ctx, resourceOwner, registerUser, externalIDP, orgMemberRoles, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
if err != nil {
return err
}
@@ -909,11 +918,18 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
}
isInternalLogin := request.SelectedIDPConfigID == "" && userSession.SelectedIDPConfigID == ""
if !isInternalLogin && len(request.LinkingUsers) == 0 && !checkVerificationTimeMaxAge(userSession.ExternalLoginVerification, request.LoginPolicy.ExternalLoginCheckLifetime, request) {
idps, err := checkExternalIDPsOfUser(ctx, repo.IDPUserLinksProvider, user.ID)
if err != nil {
return nil, err
}
if (!isInternalLogin || len(idps.Links) > 0) && len(request.LinkingUsers) == 0 && !checkVerificationTimeMaxAge(userSession.ExternalLoginVerification, request.LoginPolicy.ExternalLoginCheckLifetime, request) {
selectedIDPConfigID := request.SelectedIDPConfigID
if selectedIDPConfigID == "" {
selectedIDPConfigID = userSession.SelectedIDPConfigID
}
if selectedIDPConfigID == "" {
selectedIDPConfigID = idps.Links[0].IDPID
}
return append(steps, &domain.ExternalLoginStep{SelectedIDPConfigID: selectedIDPConfigID}), nil
}
if isInternalLogin || (!isInternalLogin && len(request.LinkingUsers) > 0) {
@@ -976,6 +992,14 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
return append(steps, &domain.RedirectToCallbackStep{}), nil
}
func checkExternalIDPsOfUser(ctx context.Context, idpUserLinksProvider idpUserLinksProvider, userID string) (*query.IDPUserLinks, error) {
userIDQuery, err := query.NewIDPUserLinksUserIDSearchQuery(userID)
if err != nil {
return nil, err
}
return idpUserLinksProvider.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: []query.SearchQuery{userIDQuery}})
}
func (repo *AuthRequestRepo) usersForUserSelection(request *domain.AuthRequest) ([]domain.UserSelection, error) {
userSessions, err := userSessionsByUserAgentID(repo.UserSessionViewProvider, request.AgentID, request.InstanceID)
if err != nil {

View File

@@ -234,6 +234,14 @@ func (m *mockApp) AppByOIDCClientID(ctx context.Context, id string) (*query.App,
return nil, errors.ThrowNotFound(nil, "ERROR", "error")
}
type mockIDPUserLinks struct {
idps []*query.IDPUserLink
}
func (m *mockIDPUserLinks) IDPUserLinks(ctx context.Context, queries *query.IDPUserLinksSearchQuery) (*query.IDPUserLinks, error) {
return &query.IDPUserLinks{Links: m.idps}, nil
}
func TestAuthRequestRepo_nextSteps(t *testing.T) {
type fields struct {
AuthRequests *cache.AuthRequestCache
@@ -247,6 +255,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
applicationProvider applicationProvider
loginPolicyProvider loginPolicyViewProvider
lockoutPolicyProvider lockoutPolicyViewProvider
idpUserLinksProvider idpUserLinksProvider
}
type args struct {
request *domain.AuthRequest
@@ -498,6 +507,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.PasswordStep{}},
@@ -515,6 +525,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
nil,
@@ -535,6 +546,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.InitUserStep{
@@ -561,6 +573,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MultiFactorCheckLifetime: 10 * time.Hour,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
[]domain.NextStep{&domain.PasswordlessRegistrationPromptStep{}},
@@ -585,6 +598,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MultiFactorCheckLifetime: 10 * time.Hour,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
[]domain.NextStep{&domain.PasswordlessStep{}},
@@ -610,6 +624,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MultiFactorCheckLifetime: 10 * time.Hour,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
[]domain.NextStep{&domain.PasswordlessStep{PasswordSet: true}},
@@ -635,7 +650,8 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -661,14 +677,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.InitPasswordStep{}},
nil,
},
{
"external user (no external verification), external login step",
"external user (idp selected, no external verification), external login step",
fields{
userSessionViewProvider: &mockViewUserSession{
SecondFactorVerification: testNow.Add(-5 * time.Minute),
@@ -689,6 +706,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
SecondFactorCheckLifetime: 18 * time.Hour,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -699,6 +717,40 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
[]domain.NextStep{&domain.ExternalLoginStep{SelectedIDPConfigID: "IDPConfigID"}},
nil,
},
{
"external user (only idp available, no external verification), external login step",
fields{
userSessionViewProvider: &mockViewUserSession{
SecondFactorVerification: testNow.Add(-5 * time.Minute),
},
userViewProvider: &mockViewUser{
IsEmailVerified: true,
MFAMaxSetUp: int32(domain.MFALevelSecondFactor),
},
userEventProvider: &mockEventUser{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &query.LockoutPolicy{
ShowFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
loginPolicyProvider: &mockLoginPolicy{
policy: &query.LoginPolicy{
SecondFactorCheckLifetime: 18 * time.Hour,
},
},
idpUserLinksProvider: &mockIDPUserLinks{
idps: []*query.IDPUserLink{{IDPID: "IDPConfigID"}},
},
},
args{&domain.AuthRequest{
UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{
SecondFactorCheckLifetime: 18 * time.Hour,
}}, false},
[]domain.NextStep{&domain.ExternalLoginStep{SelectedIDPConfigID: "IDPConfigID"}},
nil,
},
{
"external user (external verification set), callback",
fields{
@@ -720,6 +772,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{
&domain.AuthRequest{
@@ -754,6 +807,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordCheckLifetime: 10 * 24 * time.Hour,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.PasswordStep{}},
@@ -781,6 +835,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{
&domain.AuthRequest{
@@ -814,6 +869,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{
&domain.AuthRequest{
@@ -847,6 +903,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{
&domain.AuthRequest{
@@ -881,6 +938,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{
&domain.AuthRequest{
@@ -918,6 +976,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{
&domain.AuthRequest{
@@ -949,6 +1008,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -980,6 +1040,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -1014,6 +1075,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -1049,6 +1111,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -1085,6 +1148,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -1123,6 +1187,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -1162,6 +1227,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -1200,6 +1266,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -1239,6 +1306,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{&domain.AuthRequest{
UserID: "UserID",
@@ -1274,8 +1342,9 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
SecondFactorCheckLifetime: 18 * time.Hour,
},
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{
&domain.AuthRequest{
@@ -1306,6 +1375,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowFailures: true,
},
},
idpUserLinksProvider: &mockIDPUserLinks{},
},
args{
&domain.AuthRequest{
@@ -1336,6 +1406,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ApplicationProvider: tt.fields.applicationProvider,
LoginPolicyViewProvider: tt.fields.loginPolicyProvider,
LockoutPolicyViewProvider: tt.fields.lockoutPolicyProvider,
IDPUserLinksProvider: tt.fields.idpUserLinksProvider,
}
got, err := repo.nextSteps(context.Background(), tt.args.request, tt.args.checkLoggedIn)
if (err != nil && tt.wantErr == nil) || (tt.wantErr != nil && !tt.wantErr(err)) {

View File

@@ -80,6 +80,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Comma
UserCommandProvider: command,
UserEventProvider: &userRepo,
IDPProviderViewProvider: view,
IDPUserLinksProvider: queries,
LockoutPolicyViewProvider: queries,
LoginPolicyViewProvider: queries,
UserGrantProvider: queryView,