mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:37:31 +00:00
feat: allow to force MFA local only (#6234)
This PR adds an option to the LoginPolicy to "Force MFA for local users", so that users authenticated through an IDP must not configure (and verify) an MFA.
This commit is contained in:
@@ -22,7 +22,7 @@ var (
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.idp_login_policy_links5` +
|
||||
` LEFT JOIN projections.idp_templates5 ON projections.idp_login_policy_links5.idp_id = projections.idp_templates5.id AND projections.idp_login_policy_links5.instance_id = projections.idp_templates5.instance_id` +
|
||||
` RIGHT JOIN (SELECT login_policy_owner.aggregate_id, login_policy_owner.instance_id, login_policy_owner.owner_removed FROM projections.login_policies4 AS login_policy_owner` +
|
||||
` RIGHT JOIN (SELECT login_policy_owner.aggregate_id, login_policy_owner.instance_id, login_policy_owner.owner_removed FROM projections.login_policies5 AS login_policy_owner` +
|
||||
` WHERE (login_policy_owner.instance_id = $1 AND (login_policy_owner.aggregate_id = $2 OR login_policy_owner.aggregate_id = $3)) ORDER BY login_policy_owner.is_default LIMIT 1) AS login_policy_owner` +
|
||||
` ON login_policy_owner.aggregate_id = projections.idp_login_policy_links5.resource_owner AND login_policy_owner.instance_id = projections.idp_login_policy_links5.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
|
@@ -26,6 +26,7 @@ type LoginPolicy struct {
|
||||
AllowUsernamePassword bool
|
||||
AllowExternalIDPs bool
|
||||
ForceMFA bool
|
||||
ForceMFALocalOnly bool
|
||||
SecondFactors database.EnumArray[domain.SecondFactorType]
|
||||
MultiFactors database.EnumArray[domain.MultiFactorType]
|
||||
PasswordlessType domain.PasswordlessType
|
||||
@@ -95,6 +96,10 @@ var (
|
||||
name: projection.LoginPolicyForceMFACol,
|
||||
table: loginPolicyTable,
|
||||
}
|
||||
LoginPolicyColumnForceMFALocalOnly = Column{
|
||||
name: projection.LoginPolicyForceMFALocalOnlyCol,
|
||||
table: loginPolicyTable,
|
||||
}
|
||||
LoginPolicyColumnSecondFactors = Column{
|
||||
name: projection.LoginPolicy2FAsCol,
|
||||
table: loginPolicyTable,
|
||||
@@ -351,6 +356,7 @@ func prepareLoginPolicyQuery(ctx context.Context, db prepareDatabase) (sq.Select
|
||||
LoginPolicyColumnAllowUsernamePassword.identifier(),
|
||||
LoginPolicyColumnAllowExternalIDPs.identifier(),
|
||||
LoginPolicyColumnForceMFA.identifier(),
|
||||
LoginPolicyColumnForceMFALocalOnly.identifier(),
|
||||
LoginPolicyColumnSecondFactors.identifier(),
|
||||
LoginPolicyColumnMultiFactors.identifier(),
|
||||
LoginPolicyColumnPasswordlessType.identifier(),
|
||||
@@ -381,6 +387,7 @@ func prepareLoginPolicyQuery(ctx context.Context, db prepareDatabase) (sq.Select
|
||||
&p.AllowUsernamePassword,
|
||||
&p.AllowExternalIDPs,
|
||||
&p.ForceMFA,
|
||||
&p.ForceMFALocalOnly,
|
||||
&p.SecondFactors,
|
||||
&p.MultiFactors,
|
||||
&p.PasswordlessType,
|
||||
|
@@ -15,30 +15,31 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
loginPolicyQuery = `SELECT projections.login_policies4.aggregate_id,` +
|
||||
` projections.login_policies4.creation_date,` +
|
||||
` projections.login_policies4.change_date,` +
|
||||
` projections.login_policies4.sequence,` +
|
||||
` projections.login_policies4.allow_register,` +
|
||||
` projections.login_policies4.allow_username_password,` +
|
||||
` projections.login_policies4.allow_external_idps,` +
|
||||
` projections.login_policies4.force_mfa,` +
|
||||
` projections.login_policies4.second_factors,` +
|
||||
` projections.login_policies4.multi_factors,` +
|
||||
` projections.login_policies4.passwordless_type,` +
|
||||
` projections.login_policies4.is_default,` +
|
||||
` projections.login_policies4.hide_password_reset,` +
|
||||
` projections.login_policies4.ignore_unknown_usernames,` +
|
||||
` projections.login_policies4.allow_domain_discovery,` +
|
||||
` projections.login_policies4.disable_login_with_email,` +
|
||||
` projections.login_policies4.disable_login_with_phone,` +
|
||||
` projections.login_policies4.default_redirect_uri,` +
|
||||
` projections.login_policies4.password_check_lifetime,` +
|
||||
` projections.login_policies4.external_login_check_lifetime,` +
|
||||
` projections.login_policies4.mfa_init_skip_lifetime,` +
|
||||
` projections.login_policies4.second_factor_check_lifetime,` +
|
||||
` projections.login_policies4.multi_factor_check_lifetime` +
|
||||
` FROM projections.login_policies4` +
|
||||
loginPolicyQuery = `SELECT projections.login_policies5.aggregate_id,` +
|
||||
` projections.login_policies5.creation_date,` +
|
||||
` projections.login_policies5.change_date,` +
|
||||
` projections.login_policies5.sequence,` +
|
||||
` projections.login_policies5.allow_register,` +
|
||||
` projections.login_policies5.allow_username_password,` +
|
||||
` projections.login_policies5.allow_external_idps,` +
|
||||
` projections.login_policies5.force_mfa,` +
|
||||
` projections.login_policies5.force_mfa_local_only,` +
|
||||
` projections.login_policies5.second_factors,` +
|
||||
` projections.login_policies5.multi_factors,` +
|
||||
` projections.login_policies5.passwordless_type,` +
|
||||
` projections.login_policies5.is_default,` +
|
||||
` projections.login_policies5.hide_password_reset,` +
|
||||
` projections.login_policies5.ignore_unknown_usernames,` +
|
||||
` projections.login_policies5.allow_domain_discovery,` +
|
||||
` projections.login_policies5.disable_login_with_email,` +
|
||||
` projections.login_policies5.disable_login_with_phone,` +
|
||||
` projections.login_policies5.default_redirect_uri,` +
|
||||
` projections.login_policies5.password_check_lifetime,` +
|
||||
` projections.login_policies5.external_login_check_lifetime,` +
|
||||
` projections.login_policies5.mfa_init_skip_lifetime,` +
|
||||
` projections.login_policies5.second_factor_check_lifetime,` +
|
||||
` projections.login_policies5.multi_factor_check_lifetime` +
|
||||
` FROM projections.login_policies5` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
loginPolicyCols = []string{
|
||||
"aggregate_id",
|
||||
@@ -49,6 +50,7 @@ var (
|
||||
"allow_username_password",
|
||||
"allow_external_idps",
|
||||
"force_mfa",
|
||||
"force_mfa_local_only",
|
||||
"second_factors",
|
||||
"multi_factors",
|
||||
"passwordless_type",
|
||||
@@ -66,15 +68,15 @@ var (
|
||||
"multi_factor_check_lifetime",
|
||||
}
|
||||
|
||||
prepareLoginPolicy2FAsStmt = `SELECT projections.login_policies4.second_factors` +
|
||||
` FROM projections.login_policies4` +
|
||||
prepareLoginPolicy2FAsStmt = `SELECT projections.login_policies5.second_factors` +
|
||||
` FROM projections.login_policies5` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
prepareLoginPolicy2FAsCols = []string{
|
||||
"second_factors",
|
||||
}
|
||||
|
||||
prepareLoginPolicyMFAsStmt = `SELECT projections.login_policies4.multi_factors` +
|
||||
` FROM projections.login_policies4` +
|
||||
prepareLoginPolicyMFAsStmt = `SELECT projections.login_policies5.multi_factors` +
|
||||
` FROM projections.login_policies5` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
prepareLoginPolicyMFAsCols = []string{
|
||||
"multi_factors",
|
||||
@@ -126,6 +128,7 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeOTP},
|
||||
database.EnumArray[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN},
|
||||
domain.PasswordlessTypeAllowed,
|
||||
@@ -153,6 +156,7 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDPs: true,
|
||||
ForceMFA: true,
|
||||
ForceMFALocalOnly: true,
|
||||
SecondFactors: database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeOTP},
|
||||
MultiFactors: database.EnumArray[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN},
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
|
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
LoginPolicyTable = "projections.login_policies4"
|
||||
LoginPolicyTable = "projections.login_policies5"
|
||||
|
||||
LoginPolicyIDCol = "aggregate_id"
|
||||
LoginPolicyInstanceIDCol = "instance_id"
|
||||
@@ -25,6 +25,7 @@ const (
|
||||
LoginPolicyAllowUsernamePasswordCol = "allow_username_password"
|
||||
LoginPolicyAllowExternalIDPsCol = "allow_external_idps"
|
||||
LoginPolicyForceMFACol = "force_mfa"
|
||||
LoginPolicyForceMFALocalOnlyCol = "force_mfa_local_only"
|
||||
LoginPolicy2FAsCol = "second_factors"
|
||||
LoginPolicyMFAsCol = "multi_factors"
|
||||
LoginPolicyPasswordlessTypeCol = "passwordless_type"
|
||||
@@ -62,6 +63,7 @@ func newLoginPolicyProjection(ctx context.Context, config crdb.StatementHandlerC
|
||||
crdb.NewColumn(LoginPolicyAllowUsernamePasswordCol, crdb.ColumnTypeBool),
|
||||
crdb.NewColumn(LoginPolicyAllowExternalIDPsCol, crdb.ColumnTypeBool),
|
||||
crdb.NewColumn(LoginPolicyForceMFACol, crdb.ColumnTypeBool),
|
||||
crdb.NewColumn(LoginPolicyForceMFALocalOnlyCol, crdb.ColumnTypeBool, crdb.Default(false)),
|
||||
crdb.NewColumn(LoginPolicy2FAsCol, crdb.ColumnTypeEnumArray, crdb.Nullable()),
|
||||
crdb.NewColumn(LoginPolicyMFAsCol, crdb.ColumnTypeEnumArray, crdb.Nullable()),
|
||||
crdb.NewColumn(LoginPolicyPasswordlessTypeCol, crdb.ColumnTypeEnum),
|
||||
@@ -185,6 +187,7 @@ func (p *loginPolicyProjection) reduceLoginPolicyAdded(event eventstore.Event) (
|
||||
handler.NewCol(LoginPolicyAllowUsernamePasswordCol, policyEvent.AllowUserNamePassword),
|
||||
handler.NewCol(LoginPolicyAllowExternalIDPsCol, policyEvent.AllowExternalIDP),
|
||||
handler.NewCol(LoginPolicyForceMFACol, policyEvent.ForceMFA),
|
||||
handler.NewCol(LoginPolicyForceMFALocalOnlyCol, policyEvent.ForceMFALocalOnly),
|
||||
handler.NewCol(LoginPolicyPasswordlessTypeCol, policyEvent.PasswordlessType),
|
||||
handler.NewCol(LoginPolicyIsDefaultCol, isDefault),
|
||||
handler.NewCol(LoginPolicyHidePWResetCol, policyEvent.HidePasswordReset),
|
||||
@@ -228,6 +231,9 @@ func (p *loginPolicyProjection) reduceLoginPolicyChanged(event eventstore.Event)
|
||||
if policyEvent.ForceMFA != nil {
|
||||
cols = append(cols, handler.NewCol(LoginPolicyForceMFACol, *policyEvent.ForceMFA))
|
||||
}
|
||||
if policyEvent.ForceMFALocalOnly != nil {
|
||||
cols = append(cols, handler.NewCol(LoginPolicyForceMFALocalOnlyCol, *policyEvent.ForceMFALocalOnly))
|
||||
}
|
||||
if policyEvent.PasswordlessType != nil {
|
||||
cols = append(cols, handler.NewCol(LoginPolicyPasswordlessTypeCol, *policyEvent.PasswordlessType))
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
want wantReduce
|
||||
}{
|
||||
{
|
||||
name: "org reduceLoginPolicyAdded",
|
||||
name: "org reduceLoginPolicyAdded without forceMFALocalOnly",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(org.LoginPolicyAddedEventType),
|
||||
@@ -57,7 +57,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.login_policies4 (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)",
|
||||
expectedStmt: "INSERT INTO projections.login_policies5 (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, force_mfa_local_only, passwordless_type, is_default, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@@ -68,6 +68,73 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
"https://example.com/redirect",
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org reduceLoginPolicyAdded",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(org.LoginPolicyAddedEventType),
|
||||
org.AggregateType,
|
||||
[]byte(`{
|
||||
"allowUsernamePassword": true,
|
||||
"allowRegister": true,
|
||||
"allowExternalIdp": true,
|
||||
"forceMFA": true,
|
||||
"forceMFALocalOnly": true,
|
||||
"hidePasswordReset": true,
|
||||
"ignoreUnknownUsernames": true,
|
||||
"allowDomainDiscovery": true,
|
||||
"disableLoginWithEmail": true,
|
||||
"disableLoginWithPhone": true,
|
||||
"passwordlessType": 1,
|
||||
"defaultRedirectURI": "https://example.com/redirect",
|
||||
"passwordCheckLifetime": 10000000,
|
||||
"externalLoginCheckLifetime": 10000000,
|
||||
"mfaInitSkipLifetime": 10000000,
|
||||
"secondFactorCheckLifetime": 10000000,
|
||||
"multiFactorCheckLifetime": 10000000
|
||||
}`),
|
||||
), org.LoginPolicyAddedEventMapper),
|
||||
},
|
||||
reduce: (&loginPolicyProjection{}).reduceLoginPolicyAdded,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("org"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.login_policies5 (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, force_mfa_local_only, passwordless_type, is_default, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
false,
|
||||
true,
|
||||
@@ -99,6 +166,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
"allowRegister": true,
|
||||
"allowExternalIdp": true,
|
||||
"forceMFA": true,
|
||||
"forceMFALocalOnly": true,
|
||||
"hidePasswordReset": true,
|
||||
"ignoreUnknownUsernames": true,
|
||||
"allowDomainDiscovery": true,
|
||||
@@ -121,7 +189,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) WHERE (aggregate_id = $19) AND (instance_id = $20)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, force_mfa_local_only, passwordless_type, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19) WHERE (aggregate_id = $20) AND (instance_id = $21)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -129,6 +197,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
true,
|
||||
true,
|
||||
@@ -168,7 +237,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, multi_factors) = ($1, $2, array_append(multi_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, multi_factors) = ($1, $2, array_append(multi_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -200,7 +269,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, multi_factors) = ($1, $2, array_remove(multi_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, multi_factors) = ($1, $2, array_remove(multi_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -230,7 +299,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.login_policies4 WHERE (aggregate_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.login_policies5 WHERE (aggregate_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@@ -259,7 +328,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, second_factors) = ($1, $2, array_append(second_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, second_factors) = ($1, $2, array_append(second_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -291,7 +360,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, second_factors) = ($1, $2, array_remove(second_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, second_factors) = ($1, $2, array_remove(second_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -314,8 +383,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
[]byte(`{
|
||||
"allowUsernamePassword": true,
|
||||
"allowRegister": true,
|
||||
"allowExternalIdp": false,
|
||||
"forceMFA": false,
|
||||
"allowExternalIdp": true,
|
||||
"forceMFA": true,
|
||||
"forceMFALocalOnly": true,
|
||||
"hidePasswordReset": true,
|
||||
"ignoreUnknownUsernames": true,
|
||||
"allowDomainDiscovery": true,
|
||||
@@ -338,7 +408,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.login_policies4 (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)",
|
||||
expectedStmt: "INSERT INTO projections.login_policies5 (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, force_mfa_local_only, passwordless_type, is_default, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@@ -347,8 +417,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
uint64(15),
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
true,
|
||||
true,
|
||||
@@ -380,6 +451,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
"allowRegister": true,
|
||||
"allowExternalIdp": true,
|
||||
"forceMFA": true,
|
||||
"forceMFALocalOnly": true,
|
||||
"hidePasswordReset": true,
|
||||
"ignoreUnknownUsernames": true,
|
||||
"allowDomainDiscovery": true,
|
||||
@@ -397,7 +469,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) WHERE (aggregate_id = $14) AND (instance_id = $15)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, force_mfa_local_only, passwordless_type, hide_password_reset, ignore_unknown_usernames, allow_domain_discovery, disable_login_with_email, disable_login_with_phone, default_redirect_uri) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) WHERE (aggregate_id = $15) AND (instance_id = $16)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -405,6 +477,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
true,
|
||||
true,
|
||||
@@ -439,7 +512,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, multi_factors) = ($1, $2, array_append(multi_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, multi_factors) = ($1, $2, array_append(multi_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -471,7 +544,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, multi_factors) = ($1, $2, array_remove(multi_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, multi_factors) = ($1, $2, array_remove(multi_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -503,7 +576,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, second_factors) = ($1, $2, array_append(second_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, second_factors) = ($1, $2, array_append(second_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -535,7 +608,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, second_factors) = ($1, $2, array_remove(second_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, second_factors) = ($1, $2, array_remove(second_factors, $3)) WHERE (aggregate_id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -565,7 +638,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.login_policies4 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (aggregate_id = $5)",
|
||||
expectedStmt: "UPDATE projections.login_policies5 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (aggregate_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@@ -595,7 +668,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.login_policies4 WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.login_policies5 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
|
@@ -80,11 +80,12 @@ var (
|
||||
table: userIDPsCountTable,
|
||||
}
|
||||
|
||||
forceMFATable = loginPolicyTable.setAlias("auth_methods_force_mfa")
|
||||
forceMFAInstanceID = LoginPolicyColumnInstanceID.setTable(forceMFATable)
|
||||
forceMFAOrgID = LoginPolicyColumnOrgID.setTable(forceMFATable)
|
||||
forceMFAIsDefault = LoginPolicyColumnIsDefault.setTable(forceMFATable)
|
||||
forceMFAForce = LoginPolicyColumnForceMFA.setTable(forceMFATable)
|
||||
forceMFATable = loginPolicyTable.setAlias("auth_methods_force_mfa")
|
||||
forceMFAInstanceID = LoginPolicyColumnInstanceID.setTable(forceMFATable)
|
||||
forceMFAOrgID = LoginPolicyColumnOrgID.setTable(forceMFATable)
|
||||
forceMFAIsDefault = LoginPolicyColumnIsDefault.setTable(forceMFATable)
|
||||
forceMFAForce = LoginPolicyColumnForceMFA.setTable(forceMFATable)
|
||||
forceMFAForceLocalOnly = LoginPolicyColumnForceMFALocalOnly.setTable(forceMFATable)
|
||||
)
|
||||
|
||||
type AuthMethods struct {
|
||||
@@ -176,11 +177,11 @@ func (q *Queries) ListActiveUserAuthMethodTypes(ctx context.Context, userID stri
|
||||
return userAuthMethodTypes, err
|
||||
}
|
||||
|
||||
func (q *Queries) ListUserAuthMethodTypesRequired(ctx context.Context, userID string, withOwnerRemoved bool) (userAuthMethodTypes []domain.UserAuthMethodType, forceMFA bool, err error) {
|
||||
func (q *Queries) ListUserAuthMethodTypesRequired(ctx context.Context, userID string, withOwnerRemoved bool) (userAuthMethodTypes []domain.UserAuthMethodType, forceMFA, forceMFALocalOnly bool, err error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
if ctxData.UserID != userID {
|
||||
if err := q.checkPermission(ctx, domain.PermissionUserRead, ctxData.OrgID, userID); err != nil {
|
||||
return nil, false, err
|
||||
return nil, false, false, err
|
||||
}
|
||||
}
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
@@ -196,12 +197,12 @@ func (q *Queries) ListUserAuthMethodTypesRequired(ctx context.Context, userID st
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, false, errors.ThrowInvalidArgument(err, "QUERY-E5ut4", "Errors.Query.InvalidRequest")
|
||||
return nil, false, false, errors.ThrowInvalidArgument(err, "QUERY-E5ut4", "Errors.Query.InvalidRequest")
|
||||
}
|
||||
|
||||
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||
if err != nil || rows.Err() != nil {
|
||||
return nil, false, errors.ThrowInternal(err, "QUERY-Dun75", "Errors.Internal")
|
||||
return nil, false, false, errors.ThrowInternal(err, "QUERY-Dun75", "Errors.Internal")
|
||||
}
|
||||
return scan(rows)
|
||||
}
|
||||
@@ -408,7 +409,7 @@ func prepareActiveUserAuthMethodTypesQuery(ctx context.Context, db prepareDataba
|
||||
}
|
||||
}
|
||||
|
||||
func prepareUserAuthMethodTypesRequiredQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) ([]domain.UserAuthMethodType, bool, error)) {
|
||||
func prepareUserAuthMethodTypesRequiredQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (_ []domain.UserAuthMethodType, forceMFA, forceMFALocalOnly bool, err error)) {
|
||||
loginPolicyQuery, err := prepareAuthMethodsForceMFAQuery()
|
||||
if err != nil {
|
||||
return sq.SelectBuilder{}, nil
|
||||
@@ -425,7 +426,8 @@ func prepareUserAuthMethodTypesRequiredQuery(ctx context.Context, db prepareData
|
||||
NotifyPasswordSetCol.identifier(),
|
||||
authMethodTypeTypes.identifier(),
|
||||
userIDPsCountCount.identifier(),
|
||||
forceMFAForce.identifier()).
|
||||
forceMFAForce.identifier(),
|
||||
forceMFAForceLocalOnly.identifier()).
|
||||
From(userTable.identifier()).
|
||||
LeftJoin(join(NotifyUserIDCol, UserIDCol)).
|
||||
LeftJoin("("+authMethodsQuery+") AS "+authMethodTypeTable.alias+" ON "+
|
||||
@@ -439,11 +441,12 @@ func prepareUserAuthMethodTypesRequiredQuery(ctx context.Context, db prepareData
|
||||
"(" + forceMFAOrgID.identifier() + " = " + UserInstanceIDCol.identifier() + " OR " + forceMFAOrgID.identifier() + " = " + UserResourceOwnerCol.identifier() + ") AND " +
|
||||
forceMFAInstanceID.identifier() + " = " + UserInstanceIDCol.identifier() + db.Timetravel(call.Took(ctx))).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(rows *sql.Rows) ([]domain.UserAuthMethodType, bool, error) {
|
||||
func(rows *sql.Rows) ([]domain.UserAuthMethodType, bool, bool, error) {
|
||||
userAuthMethodTypes := make([]domain.UserAuthMethodType, 0)
|
||||
var passwordSet sql.NullBool
|
||||
var idp sql.NullInt64
|
||||
var forceMFA sql.NullBool
|
||||
var forceMFALocalOnly sql.NullBool
|
||||
for rows.Next() {
|
||||
var authMethodType sql.NullInt16
|
||||
err := rows.Scan(
|
||||
@@ -451,9 +454,10 @@ func prepareUserAuthMethodTypesRequiredQuery(ctx context.Context, db prepareData
|
||||
&authMethodType,
|
||||
&idp,
|
||||
&forceMFA,
|
||||
&forceMFALocalOnly,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, false, false, err
|
||||
}
|
||||
if authMethodType.Valid {
|
||||
userAuthMethodTypes = append(userAuthMethodTypes, domain.UserAuthMethodType(authMethodType.Int16))
|
||||
@@ -468,10 +472,10 @@ func prepareUserAuthMethodTypesRequiredQuery(ctx context.Context, db prepareData
|
||||
}
|
||||
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, false, errors.ThrowInternal(err, "QUERY-W4zje", "Errors.Query.CloseRows")
|
||||
return nil, false, false, errors.ThrowInternal(err, "QUERY-W4zje", "Errors.Query.CloseRows")
|
||||
}
|
||||
|
||||
return userAuthMethodTypes, forceMFA.Bool, nil
|
||||
return userAuthMethodTypes, forceMFA.Bool, forceMFALocalOnly.Bool, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,6 +506,7 @@ func prepareAuthMethodQuery() (string, []interface{}, error) {
|
||||
func prepareAuthMethodsForceMFAQuery() (string, error) {
|
||||
loginPolicyQuery, _, err := sq.Select(
|
||||
forceMFAForce.identifier(),
|
||||
forceMFAForceLocalOnly.identifier(),
|
||||
forceMFAInstanceID.identifier(),
|
||||
forceMFAOrgID.identifier(),
|
||||
).
|
||||
|
@@ -59,7 +59,8 @@ var (
|
||||
prepareAuthMethodTypesRequiredStmt = `SELECT projections.users8_notifications.password_set,` +
|
||||
` auth_method_types.method_type,` +
|
||||
` user_idps_count.count,` +
|
||||
` auth_methods_force_mfa.force_mfa` +
|
||||
` auth_methods_force_mfa.force_mfa,` +
|
||||
` auth_methods_force_mfa.force_mfa_local_only` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_notifications ON projections.users8.id = projections.users8_notifications.user_id AND projections.users8.instance_id = projections.users8_notifications.instance_id` +
|
||||
` LEFT JOIN (SELECT DISTINCT(auth_method_types.method_type), auth_method_types.user_id, auth_method_types.instance_id FROM projections.user_auth_methods4 AS auth_method_types` +
|
||||
@@ -68,7 +69,7 @@ var (
|
||||
` LEFT JOIN (SELECT user_idps_count.user_id, user_idps_count.instance_id, COUNT(user_idps_count.user_id) AS count FROM projections.idp_user_links3 AS user_idps_count` +
|
||||
` GROUP BY user_idps_count.user_id, user_idps_count.instance_id) AS user_idps_count` +
|
||||
` ON user_idps_count.user_id = projections.users8.id AND user_idps_count.instance_id = projections.users8.instance_id` +
|
||||
` LEFT JOIN (SELECT auth_methods_force_mfa.force_mfa, auth_methods_force_mfa.instance_id, auth_methods_force_mfa.aggregate_id FROM projections.login_policies4 AS auth_methods_force_mfa ORDER BY auth_methods_force_mfa.is_default) AS auth_methods_force_mfa` +
|
||||
` LEFT JOIN (SELECT auth_methods_force_mfa.force_mfa, auth_methods_force_mfa.force_mfa_local_only, auth_methods_force_mfa.instance_id, auth_methods_force_mfa.aggregate_id FROM projections.login_policies5 AS auth_methods_force_mfa ORDER BY auth_methods_force_mfa.is_default) AS auth_methods_force_mfa` +
|
||||
` ON (auth_methods_force_mfa.aggregate_id = projections.users8.instance_id OR auth_methods_force_mfa.aggregate_id = projections.users8.resource_owner) AND auth_methods_force_mfa.instance_id = projections.users8.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms
|
||||
`
|
||||
@@ -77,6 +78,7 @@ var (
|
||||
"method_type",
|
||||
"idps_count",
|
||||
"force_mfa",
|
||||
"force_mfa_local_only",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -318,11 +320,11 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*testUserAuthMethodTypesRequired, error)) {
|
||||
builder, scan := prepareUserAuthMethodTypesRequiredQuery(ctx, db)
|
||||
return builder, func(rows *sql.Rows) (*testUserAuthMethodTypesRequired, error) {
|
||||
authMethods, forceMFA, err := scan(rows)
|
||||
authMethods, forceMFA, forceMFALocalOnly, err := scan(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &testUserAuthMethodTypesRequired{authMethods: authMethods, forceMFA: forceMFA}, nil
|
||||
return &testUserAuthMethodTypesRequired{authMethods: authMethods, forceMFA: forceMFA, forceMFALocalOnly: forceMFALocalOnly}, nil
|
||||
}
|
||||
},
|
||||
want: want{
|
||||
@@ -339,11 +341,11 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*testUserAuthMethodTypesRequired, error)) {
|
||||
builder, scan := prepareUserAuthMethodTypesRequiredQuery(ctx, db)
|
||||
return builder, func(rows *sql.Rows) (*testUserAuthMethodTypesRequired, error) {
|
||||
authMethods, forceMFA, err := scan(rows)
|
||||
authMethods, forceMFA, forceMFALocalOnly, err := scan(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &testUserAuthMethodTypesRequired{authMethods: authMethods, forceMFA: forceMFA}, nil
|
||||
return &testUserAuthMethodTypesRequired{authMethods: authMethods, forceMFA: forceMFA, forceMFALocalOnly: forceMFALocalOnly}, nil
|
||||
}
|
||||
},
|
||||
want: want{
|
||||
@@ -356,6 +358,7 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
domain.UserAuthMethodTypePasswordless,
|
||||
1,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
},
|
||||
),
|
||||
@@ -366,7 +369,8 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
domain.UserAuthMethodTypePassword,
|
||||
domain.UserAuthMethodTypeIDP,
|
||||
},
|
||||
forceMFA: true,
|
||||
forceMFA: true,
|
||||
forceMFALocalOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -374,11 +378,11 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*testUserAuthMethodTypesRequired, error)) {
|
||||
builder, scan := prepareUserAuthMethodTypesRequiredQuery(ctx, db)
|
||||
return builder, func(rows *sql.Rows) (*testUserAuthMethodTypesRequired, error) {
|
||||
authMethods, forceMFA, err := scan(rows)
|
||||
authMethods, forceMFA, forceMFALocalOnly, err := scan(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &testUserAuthMethodTypesRequired{authMethods: authMethods, forceMFA: forceMFA}, nil
|
||||
return &testUserAuthMethodTypesRequired{authMethods: authMethods, forceMFA: forceMFA, forceMFALocalOnly: forceMFALocalOnly}, nil
|
||||
}
|
||||
},
|
||||
want: want{
|
||||
@@ -391,12 +395,14 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
domain.UserAuthMethodTypePasswordless,
|
||||
1,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
true,
|
||||
domain.UserAuthMethodTypeOTP,
|
||||
1,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
},
|
||||
),
|
||||
@@ -409,7 +415,8 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
domain.UserAuthMethodTypePassword,
|
||||
domain.UserAuthMethodTypeIDP,
|
||||
},
|
||||
forceMFA: true,
|
||||
forceMFA: true,
|
||||
forceMFALocalOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -417,11 +424,11 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*testUserAuthMethodTypesRequired, error)) {
|
||||
builder, scan := prepareUserAuthMethodTypesRequiredQuery(ctx, db)
|
||||
return builder, func(rows *sql.Rows) (*testUserAuthMethodTypesRequired, error) {
|
||||
authMethods, forceMFA, err := scan(rows)
|
||||
authMethods, forceMFA, forceMFALocalOnly, err := scan(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &testUserAuthMethodTypesRequired{authMethods: authMethods, forceMFA: forceMFA}, nil
|
||||
return &testUserAuthMethodTypesRequired{authMethods: authMethods, forceMFA: forceMFA, forceMFALocalOnly: forceMFALocalOnly}, nil
|
||||
}
|
||||
},
|
||||
want: want{
|
||||
@@ -448,6 +455,7 @@ func Test_UserAuthMethodPrepares(t *testing.T) {
|
||||
|
||||
// testUserAuthMethodTypesRequired is required as assetPrepare is only able to return a single object from scan
|
||||
type testUserAuthMethodTypesRequired struct {
|
||||
authMethods []domain.UserAuthMethodType
|
||||
forceMFA bool
|
||||
authMethods []domain.UserAuthMethodType
|
||||
forceMFA bool
|
||||
forceMFALocalOnly bool
|
||||
}
|
||||
|
Reference in New Issue
Block a user