diff --git a/internal/query/policy_login.go b/internal/query/login_policy.go similarity index 90% rename from internal/query/policy_login.go rename to internal/query/login_policy.go index d2ef259d5b..75d78dc580 100644 --- a/internal/query/policy_login.go +++ b/internal/query/login_policy.go @@ -44,43 +44,56 @@ var ( name: projection.LoginPolicyTable, } LoginPolicyColumnOrgID = Column{ - name: projection.LoginPolicyIDCol, + name: projection.LoginPolicyIDCol, + table: loginPolicyTable, } LoginPolicyColumnCreationDate = Column{ - name: projection.LoginPolicyCreationDateCol, + name: projection.LoginPolicyCreationDateCol, + table: loginPolicyTable, } LoginPolicyColumnChangeDate = Column{ - name: projection.LoginPolicyChangeDateCol, + name: projection.LoginPolicyChangeDateCol, + table: loginPolicyTable, } LoginPolicyColumnSequence = Column{ - name: projection.LoginPolicySequenceCol, + name: projection.LoginPolicySequenceCol, + table: loginPolicyTable, } LoginPolicyColumnAllowRegister = Column{ - name: projection.LoginPolicyAllowRegisterCol, + name: projection.LoginPolicyAllowRegisterCol, + table: loginPolicyTable, } LoginPolicyColumnAllowUsernamePassword = Column{ - name: projection.LoginPolicyAllowUsernamePasswordCol, + name: projection.LoginPolicyAllowUsernamePasswordCol, + table: loginPolicyTable, } LoginPolicyColumnAllowExternalIDPs = Column{ - name: projection.LoginPolicyAllowExternalIDPsCol, + name: projection.LoginPolicyAllowExternalIDPsCol, + table: loginPolicyTable, } LoginPolicyColumnForceMFA = Column{ - name: projection.LoginPolicyForceMFACol, + name: projection.LoginPolicyForceMFACol, + table: loginPolicyTable, } LoginPolicyColumnSecondFactors = Column{ - name: projection.LoginPolicy2FAsCol, + name: projection.LoginPolicy2FAsCol, + table: loginPolicyTable, } LoginPolicyColumnMultiFactors = Column{ - name: projection.LoginPolicyMFAsCol, + name: projection.LoginPolicyMFAsCol, + table: loginPolicyTable, } LoginPolicyColumnPasswordlessType = Column{ - name: projection.LoginPolicyPasswordlessTypeCol, + name: projection.LoginPolicyPasswordlessTypeCol, + table: loginPolicyTable, } LoginPolicyColumnIsDefault = Column{ - name: projection.LoginPolicyIsDefaultCol, + name: projection.LoginPolicyIsDefaultCol, + table: loginPolicyTable, } LoginPolicyColumnHidePasswordReset = Column{ - name: projection.LoginPolicyHidePWResetCol, + name: projection.LoginPolicyHidePWResetCol, + table: loginPolicyTable, } ) diff --git a/internal/query/login_policy_test.go b/internal/query/login_policy_test.go new file mode 100644 index 0000000000..24c76abfb0 --- /dev/null +++ b/internal/query/login_policy_test.go @@ -0,0 +1,318 @@ +package query + +import ( + "database/sql" + "database/sql/driver" + "errors" + "fmt" + "regexp" + "testing" + + "github.com/caos/zitadel/internal/domain" + errs "github.com/caos/zitadel/internal/errors" + "github.com/lib/pq" +) + +func Test_LoginPolicyPrepares(t *testing.T) { + type want struct { + sqlExpectations sqlExpectation + err checkErr + } + tests := []struct { + name string + prepare interface{} + want want + object interface{} + }{ + { + name: "prepareLoginPolicyQuery no result", + prepare: prepareLoginPolicyQuery, + want: want{ + sqlExpectations: mockQueries( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.aggregate_id,`+ + ` zitadel.projections.login_policies.creation_date,`+ + ` zitadel.projections.login_policies.change_date,`+ + ` zitadel.projections.login_policies.sequence,`+ + ` zitadel.projections.login_policies.allow_register,`+ + ` zitadel.projections.login_policies.allow_username_password,`+ + ` zitadel.projections.login_policies.allow_external_idps,`+ + ` zitadel.projections.login_policies.force_mfa,`+ + ` zitadel.projections.login_policies.second_factors,`+ + ` zitadel.projections.login_policies.multi_factors,`+ + ` zitadel.projections.login_policies.passwordless_type,`+ + ` zitadel.projections.login_policies.is_default,`+ + ` zitadel.projections.login_policies.hide_password_reset`+ + ` FROM zitadel.projections.login_policies`), + nil, + nil, + ), + err: func(err error) (error, bool) { + if !errs.IsNotFound(err) { + return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false + } + return nil, true + }, + }, + object: (*LoginPolicy)(nil), + }, + { + name: "prepareLoginPolicyQuery found", + prepare: prepareLoginPolicyQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.aggregate_id,`+ + ` zitadel.projections.login_policies.creation_date,`+ + ` zitadel.projections.login_policies.change_date,`+ + ` zitadel.projections.login_policies.sequence,`+ + ` zitadel.projections.login_policies.allow_register,`+ + ` zitadel.projections.login_policies.allow_username_password,`+ + ` zitadel.projections.login_policies.allow_external_idps,`+ + ` zitadel.projections.login_policies.force_mfa,`+ + ` zitadel.projections.login_policies.second_factors,`+ + ` zitadel.projections.login_policies.multi_factors,`+ + ` zitadel.projections.login_policies.passwordless_type,`+ + ` zitadel.projections.login_policies.is_default,`+ + ` zitadel.projections.login_policies.hide_password_reset`+ + ` FROM zitadel.projections.login_policies`), + []string{ + "aggregate_id", + "creation_date", + "change_date", + "sequence", + "allow_register", + "allow_username_password", + "allow_external_idps", + "force_mfa", + "second_factors", + "multi_factors", + "passwordless_type", + "is_default", + "hide_password_reset", + }, + []driver.Value{ + "ro", + testNow, + testNow, + uint64(20211109), + true, + true, + true, + true, + pq.Int32Array{int32(domain.SecondFactorTypeOTP)}, + pq.Int32Array{int32(domain.MultiFactorTypeU2FWithPIN)}, + domain.PasswordlessTypeAllowed, + true, + true, + }, + ), + }, + object: &LoginPolicy{ + OrgID: "ro", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211109, + AllowRegister: true, + AllowUsernamePassword: true, + AllowExternalIDPs: true, + ForceMFA: true, + SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, + MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN}, + PasswordlessType: domain.PasswordlessTypeAllowed, + IsDefault: true, + HidePasswordReset: true, + }, + }, + { + name: "prepareLoginPolicyQuery sql err", + prepare: prepareLoginPolicyQuery, + want: want{ + sqlExpectations: mockQueryErr( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.aggregate_id,`+ + ` zitadel.projections.login_policies.creation_date,`+ + ` zitadel.projections.login_policies.change_date,`+ + ` zitadel.projections.login_policies.sequence,`+ + ` zitadel.projections.login_policies.allow_register,`+ + ` zitadel.projections.login_policies.allow_username_password,`+ + ` zitadel.projections.login_policies.allow_external_idps,`+ + ` zitadel.projections.login_policies.force_mfa,`+ + ` zitadel.projections.login_policies.second_factors,`+ + ` zitadel.projections.login_policies.multi_factors,`+ + ` zitadel.projections.login_policies.passwordless_type,`+ + ` zitadel.projections.login_policies.is_default,`+ + ` zitadel.projections.login_policies.hide_password_reset`+ + ` FROM zitadel.projections.login_policies`), + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: nil, + }, + { + name: "prepareLoginPolicy2FAsQuery no result", + prepare: prepareLoginPolicy2FAsQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.second_factors`+ + ` FROM zitadel.projections.login_policies`), + []string{ + "second_factors", + }, + nil, + ), + err: func(err error) (error, bool) { + if !errs.IsNotFound(err) { + return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false + } + return nil, true + }, + }, + object: (*SecondFactors)(nil), + }, + { + name: "prepareLoginPolicy2FAsQuery found", + prepare: prepareLoginPolicy2FAsQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.second_factors`+ + ` FROM zitadel.projections.login_policies`), + []string{ + "second_factors", + }, + []driver.Value{ + pq.Int32Array{int32(domain.SecondFactorTypeOTP)}, + }, + ), + }, + object: &SecondFactors{ + SearchResponse: SearchResponse{ + Count: 1, + }, + Factors: []domain.SecondFactorType{domain.SecondFactorTypeOTP}, + }, + }, + { + name: "prepareLoginPolicy2FAsQuery found no factors", + prepare: prepareLoginPolicy2FAsQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.second_factors`+ + ` FROM zitadel.projections.login_policies`), + []string{ + "second_factors", + }, + []driver.Value{ + pq.Int32Array{}, + }, + ), + }, + object: &SecondFactors{Factors: []domain.SecondFactorType{}}, + }, + { + name: "prepareLoginPolicy2FAsQuery sql err", + prepare: prepareLoginPolicy2FAsQuery, + want: want{ + sqlExpectations: mockQueryErr( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.second_factors`+ + ` FROM zitadel.projections.login_policies`), + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: nil, + }, + + { + name: "prepareLoginPolicyMFAsQuery no result", + prepare: prepareLoginPolicyMFAsQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.multi_factors`+ + ` FROM zitadel.projections.login_policies`), + []string{ + "multi_factors", + }, + nil, + ), + err: func(err error) (error, bool) { + if !errs.IsNotFound(err) { + return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false + } + return nil, true + }, + }, + object: (*MultiFactors)(nil), + }, + { + name: "prepareLoginPolicyMFAsQuery found", + prepare: prepareLoginPolicyMFAsQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.multi_factors`+ + ` FROM zitadel.projections.login_policies`), + []string{ + "multi_factors", + }, + []driver.Value{ + pq.Int32Array{int32(domain.MultiFactorTypeU2FWithPIN)}, + }, + ), + }, + object: &MultiFactors{ + SearchResponse: SearchResponse{ + Count: 1, + }, + Factors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN}, + }, + }, + { + name: "prepareLoginPolicyMFAsQuery found no factors", + prepare: prepareLoginPolicyMFAsQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.multi_factors`+ + ` FROM zitadel.projections.login_policies`), + []string{ + "multi_factors", + }, + []driver.Value{ + pq.Int32Array{}, + }, + ), + }, + object: &MultiFactors{Factors: []domain.MultiFactorType{}}, + }, + { + name: "prepareLoginPolicyMFAsQuery sql err", + prepare: prepareLoginPolicyMFAsQuery, + want: want{ + sqlExpectations: mockQueryErr( + regexp.QuoteMeta(`SELECT zitadel.projections.login_policies.multi_factors`+ + ` FROM zitadel.projections.login_policies`), + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err) + }) + } +} diff --git a/internal/query/prepare_test.go b/internal/query/prepare_test.go index bcb0bf6e09..5665409220 100644 --- a/internal/query/prepare_test.go +++ b/internal/query/prepare_test.go @@ -79,7 +79,9 @@ func mockQuery(stmt string, cols []string, row []driver.Value) func(m sqlmock.Sq return func(m sqlmock.Sqlmock) sqlmock.Sqlmock { q := m.ExpectQuery(stmt) result := sqlmock.NewRows(cols) - result.AddRow(row...) + if len(row) > 0 { + result.AddRow(row...) + } q.WillReturnRows(result) return m }