feat: enable otp email and sms (#6260)

* feat: enable otp email and sms

* feat: enable otp factors in login settings

* remove tests without value

* translate second factors

* don't add new factors yet

* add comment

* add factors to docs

* backward compatible settings api

* compile tests

* add available 2fa types

* test: add mapping tests

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Elio Bischof 2023-07-28 07:39:30 +02:00 committed by GitHub
parent d3e403f645
commit 31ec1d83b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 696 additions and 124 deletions

View File

@ -146,7 +146,12 @@ export class FactorTableComponent {
this.componentType === LoginMethodComponentType.MultiFactor
? [MultiFactorType.MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION]
: this.componentType === LoginMethodComponentType.SecondFactor
? [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP]
? [
SecondFactorType.SECOND_FACTOR_TYPE_U2F,
SecondFactorType.SECOND_FACTOR_TYPE_OTP,
SecondFactorType.SECOND_FACTOR_TYPE_OTP_SMS,
SecondFactorType.SECOND_FACTOR_TYPE_OTP_EMAIL,
]
: [];
const filtered = (allTypes as Array<MultiFactorType | SecondFactorType>).filter((type) => !this.list.includes(type));

View File

@ -1840,8 +1840,10 @@
},
"SECONDFACTORTYPES": {
"0": "неизвестен",
"1": "Еднократна парола (OTP)",
"2": "Пръстов отпечатък, ключове за сигурност, Face ID и други"
"1": "Еднократна парола чрез приложение за удостоверяване на автентичността (TOTP)",
"2": "Пръстов отпечатък, ключове за сигурност, Face ID и други",
"3": "Еднократна парола по имейл (Email OTP)",
"4": "Еднократна парола чрез SMS (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1849,8 +1849,10 @@
},
"SECONDFACTORTYPES": {
"0": "Unknown",
"1": "One Time Password (OTP)",
"2": "Fingerabdruck, Security Keys, Face ID und andere"
"1": "One Time Password per Authenticator App (TOTP)",
"2": "Fingerabdruck, Security Keys, Face ID und andere",
"3": "One Time Password per Email (Email OTP)",
"4": "One Time Password per SMS (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1846,8 +1846,10 @@
},
"SECONDFACTORTYPES": {
"0": "Unknown",
"1": "One Time Password (OTP)",
"2": "Fingerprint, Security Keys, Face ID and other"
"1": "One Time Password by Authenticator App (TOTP)",
"2": "Fingerprint, Security Keys, Face ID and other",
"3": "One Time Password by Email (Email OTP)",
"4": "One Time Password by SMS (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1846,8 +1846,10 @@
},
"SECONDFACTORTYPES": {
"0": "Desconocido",
"1": "One Time Password (OTP)",
"2": "Huella dactilar, claves de seguridad, Face ID y otros"
"1": "One Time Password por Authenticator App (TOTP)",
"2": "Huella dactilar, claves de seguridad, Face ID y otros",
"3": "One Time Password por email (Email OTP)",
"4": "One Time Password por SMS (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1850,8 +1850,10 @@
},
"SECONDFACTORTYPES": {
"0": "Inconnu",
"1": "Mot de passe à usage unique (OTP)",
"2": "Empreinte digitale, clés de sécurité, Face ID et autres"
"1": "One Time Password par authenticator app (TOTP)",
"2": "Empreinte digitale, clés de sécurité, Face ID et autres",
"3": "One Time Password par email (Email OTP)",
"4": "One Time Password par SMS (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1850,8 +1850,10 @@
},
"SECONDFACTORTYPES": {
"0": "Sconosciuto",
"1": "One Time Password (OTP)",
"2": "Impronta digitale, chiave di sicurezza, Face ID e altri"
"1": "One Time Password per Authenticator App (TOTP)",
"2": "Impronta digitale, chiave di sicurezza, Face ID e altri",
"3": "One Time Password per Email (Email OTP)",
"4": "One Time Password per SMS (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1841,8 +1841,10 @@
},
"SECONDFACTORTYPES": {
"0": "不明",
"1": "ワンタイムパスワードOTP",
"2": "指紋、セキュリティキー、フェイスIDなど"
"1": "認証アプリ用ワンタイムパスワード(TOTP)",
"2": "指紋、セキュリティキー、フェイスIDなど",
"3": "Eメール用ワンタイムパスワードemail OTP",
"4": "SMS用ワンタイムパスワードSMS OTP"
}
},
"LOGINPOLICY": {

View File

@ -1846,8 +1846,10 @@
},
"SECONDFACTORTYPES": {
"0": "Непознато",
"1": "Еднократна лозинка (OTP)",
"2": "Отисок на прст, безбедносни клучеви, Face ID и другo"
"1": "Еднократна лозинка преку апликација за автентикатор (TOTP)",
"2": "Отпечаток на прст, безбедносни клучеви, Face ID и други",
"3": "Еднократна лозинка по е-пошта (Еmail OTP)",
"4": "Еднократна лозинка преку СМС (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1850,8 +1850,10 @@
},
"SECONDFACTORTYPES": {
"0": "Nieznany",
"1": "Jednorazowe hasło (OTP)",
"2": "Odcisk palca, klucze bezpieczeństwa, Face ID i inne"
"1": "Hasło jednorazowe dla aplikacji uwierzytelniającej (TOTP)",
"2": "Odcisk palca, Klucze Bezpieczeństwa, Face ID i inne",
"3": "Hasło jednorazowe dla wiadomości e-mail (Email OTP)",
"4": "Hasło jednorazowe dla wiadomości SMS (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1844,8 +1844,10 @@
},
"SECONDFACTORTYPES": {
"0": "Desconhecido",
"1": "Senha de Uso Único (OTP)",
"2": "Impressão Digital, Chaves de Segurança, Face ID e outros"
"1": "Senha de uso único para o aplicativo autenticador (TOTP)",
"2": "Impressão digital, Chaves de Segurança, Face ID e outros",
"3": "Senha de uso único para e-mail (Email OTP)",
"4": "Senha de uso único para SMS (SMS OTP)"
}
},
"LOGINPOLICY": {

View File

@ -1849,8 +1849,10 @@
},
"SECONDFACTORTYPES": {
"0": "未知",
"1": "一次性密码 (OTP)",
"2": "指纹、安全密钥、Face ID 等"
"1": "身份验证应用程序的一次性密码TOTP",
"2": "指纹、安全密钥、Face ID 等",
"3": "电子邮件一次性密码email OTP",
"4": "短信一次性密码SMS OTP"
}
},
"LOGINPOLICY": {

View File

@ -120,8 +120,10 @@ Multifactors:
Secondfactors (2FA):
- OTP (One Time Password), Authenticator Apps like Google/Microsoft Authenticator, Authy, etc.
- U2F (Universal Second Factor), e.g FaceID, WindowsHello, Fingerprint, Hardwaretokens like Yubikey
- Time-based One Time Password (TOTP), Authenticator Apps like Google/Microsoft Authenticator, Authy, etc.
- Universal Second Factor (U2F), e.g FaceID, WindowsHello, Fingerprint, Hardwaretokens like Yubikey
- One Time Password with Email (Email OTP)
- One Time Password with SMS (SMS OTP)
Force a user to register and use a multifactor authentication, by checking the option "Force MFA".
Ensure that you have added the MFA methods you want to allow.

View File

@ -16,9 +16,13 @@ func SecondFactorsTypesToDomain(secondFactorTypes []policy_pb.SecondFactorType)
func SecondFactorTypeToDomain(secondFactorType policy_pb.SecondFactorType) domain.SecondFactorType {
switch secondFactorType {
case policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP:
return domain.SecondFactorTypeOTP
return domain.SecondFactorTypeTOTP
case policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_U2F:
return domain.SecondFactorTypeU2F
case policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP_EMAIL:
return domain.SecondFactorTypeOTPEmail
case policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP_SMS:
return domain.SecondFactorTypeOTPSMS
default:
return domain.SecondFactorTypeUnspecified
}
@ -34,10 +38,14 @@ func ModelSecondFactorTypesToPb(types []domain.SecondFactorType) []policy_pb.Sec
func ModelSecondFactorTypeToPb(secondFactorType domain.SecondFactorType) policy_pb.SecondFactorType {
switch secondFactorType {
case domain.SecondFactorTypeOTP:
case domain.SecondFactorTypeTOTP:
return policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP
case domain.SecondFactorTypeU2F:
return policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_U2F
case domain.SecondFactorTypeOTPEmail:
return policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP_EMAIL
case domain.SecondFactorTypeOTPSMS:
return policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP_SMS
default:
return policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_UNSPECIFIED
}

View File

@ -62,10 +62,14 @@ func passkeysTypeToPb(passwordlessType domain.PasswordlessType) settings.Passkey
func secondFactorTypeToPb(secondFactorType domain.SecondFactorType) settings.SecondFactorType {
switch secondFactorType {
case domain.SecondFactorTypeOTP:
case domain.SecondFactorTypeTOTP:
return settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP
case domain.SecondFactorTypeU2F:
return settings.SecondFactorType_SECOND_FACTOR_TYPE_U2F
case domain.SecondFactorTypeOTPEmail:
return settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP_EMAIL
case domain.SecondFactorTypeOTPSMS:
return settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP_SMS
case domain.SecondFactorTypeUnspecified:
return settings.SecondFactorType_SECOND_FACTOR_TYPE_UNSPECIFIED
default:

View File

@ -39,8 +39,10 @@ func Test_loginSettingsToPb(t *testing.T) {
SecondFactorCheckLifetime: time.Microsecond,
MultiFactorCheckLifetime: time.Nanosecond,
SecondFactors: []domain.SecondFactorType{
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeTOTP,
domain.SecondFactorTypeU2F,
domain.SecondFactorTypeOTPEmail,
domain.SecondFactorTypeOTPSMS,
},
MultiFactors: []domain.MultiFactorType{
domain.MultiFactorTypeU2FWithPIN,
@ -69,6 +71,8 @@ func Test_loginSettingsToPb(t *testing.T) {
SecondFactors: []settings.SecondFactorType{
settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP,
settings.SecondFactorType_SECOND_FACTOR_TYPE_U2F,
settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP_EMAIL,
settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP_SMS,
},
MultiFactors: []settings.MultiFactorType{
settings.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION,
@ -146,13 +150,21 @@ func Test_secondFactorTypeToPb(t *testing.T) {
want settings.SecondFactorType
}{
{
args: args{domain.SecondFactorTypeOTP},
args: args{domain.SecondFactorTypeTOTP},
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP,
},
{
args: args{domain.SecondFactorTypeU2F},
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_U2F,
},
{
args: args{domain.SecondFactorTypeOTPSMS},
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP_SMS,
},
{
args: args{domain.SecondFactorTypeOTPEmail},
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP_EMAIL,
},
{
args: args{domain.SecondFactorTypeUnspecified},
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_UNSPECIFIED,

View File

@ -889,7 +889,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
&domain.AuthRequest{
UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -923,7 +923,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
&domain.AuthRequest{
UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -959,7 +959,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
UserID: "UserID",
SelectedIDPConfigID: "IDPConfigID",
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
ExternalLoginCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
@ -996,7 +996,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
&domain.AuthRequest{
UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1027,7 +1027,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
args{&domain.AuthRequest{
UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1059,7 +1059,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
args{&domain.AuthRequest{
UserID: "UserID",
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1095,7 +1095,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
UserID: "UserID",
Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1132,7 +1132,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1169,7 +1169,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1208,7 +1208,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1248,7 +1248,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1288,7 +1288,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1329,7 +1329,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
Prompt: []domain.Prompt{domain.PromptNone},
Request: &domain.AuthRequestOIDC{},
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
PasswordCheckLifetime: 10 * 24 * time.Hour,
SecondFactorCheckLifetime: 18 * time.Hour,
},
@ -1399,7 +1399,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
SelectedIDPConfigID: "IDPConfigID",
LinkingUsers: []*domain.ExternalUser{{IDPConfigID: "IDPConfigID", ExternalUserID: "UserID", DisplayName: "DisplayName"}},
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
PasswordCheckLifetime: 10 * 24 * time.Hour,
},
@ -1503,7 +1503,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
args{
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
MFAInitSkipLifetime: 30 * 24 * time.Hour,
},
},
@ -1528,7 +1528,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
ForceMFA: true,
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
MFAInitSkipLifetime: 30 * 24 * time.Hour,
},
},
@ -1573,7 +1573,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
args{
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
},
},
@ -1595,7 +1595,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
args{
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
},
},
@ -1620,7 +1620,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
args{
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
},
},
@ -1644,7 +1644,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
args{
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
},
},
@ -1666,7 +1666,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
args{
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
ForceMFA: true,
},
@ -1693,7 +1693,7 @@ func TestAuthRequestRepo_mfaChecked(t *testing.T) {
args{
request: &domain.AuthRequest{
LoginPolicy: &domain.LoginPolicy{
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
SecondFactorCheckLifetime: 18 * time.Hour,
ForceMFA: true,
ForceMFALocalOnly: true,

View File

@ -245,8 +245,12 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
setup.LoginPolicy.SecondFactorCheckLifetime,
setup.LoginPolicy.MultiFactorCheckLifetime,
),
prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeOTP),
prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeTOTP),
prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeU2F),
/* TODO: incomment when usable
prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeOTPEmail),
prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeOTPSMS),
*/
prepareAddMultiFactorToDefaultLoginPolicy(instanceAgg, domain.MultiFactorTypeU2FWithPIN),
prepareAddDefaultPrivacyPolicy(instanceAgg, setup.PrivacyPolicy.TOSLink, setup.PrivacyPolicy.PrivacyLink, setup.PrivacyPolicy.HelpLink, setup.PrivacyPolicy.SupportEmail),

View File

@ -884,7 +884,7 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) {
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeTOTP,
),
),
),
@ -892,14 +892,14 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
},
res: res{
err: caos_errs.IsErrorAlreadyExists,
},
},
{
name: "add factor, ok",
name: "add factor totp, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
@ -910,7 +910,7 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) {
"INSTANCE",
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTP),
domain.SecondFactorTypeTOTP),
),
},
),
@ -918,7 +918,98 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) {
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "INSTANCE",
},
},
},
{
name: "add factor otp email, ok ",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPEmail),
),
},
),
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
factor: domain.SecondFactorTypeOTPEmail,
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "INSTANCE",
},
},
},
{
name: "add factor otp sms, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPSMS),
),
},
),
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
factor: domain.SecondFactorTypeOTPSMS,
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "INSTANCE",
},
},
},
{
name: "add factor totp, add otp sms, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeTOTP,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPSMS),
),
},
),
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
factor: domain.SecondFactorTypeOTPSMS,
},
res: res{
want: &domain.ObjectDetails{
@ -989,14 +1080,14 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "factor removed, not found error",
name: "factor removed totp, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
@ -1004,13 +1095,13 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeTOTP,
),
),
eventFromEventPusher(
instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeTOTP,
),
),
),
@ -1018,14 +1109,14 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "add factor, ok",
name: "factor removed otp email, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
@ -1033,7 +1124,65 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeOTPEmail,
),
),
eventFromEventPusher(
instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPEmail,
),
),
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPEmail,
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "factor removed otp sms, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPSMS,
),
),
eventFromEventPusher(
instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPSMS,
),
),
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPSMS,
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "remove factor totp, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeTOTP,
),
),
),
@ -1042,7 +1191,7 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
eventFromEventPusher(
instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTP),
domain.SecondFactorTypeTOTP),
),
},
),
@ -1050,7 +1199,7 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
},
res: res{
want: &domain.ObjectDetails{
@ -1058,6 +1207,97 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
},
},
},
{
name: "remove factor email, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPEmail,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPEmail),
),
},
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPEmail,
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "INSTANCE",
},
},
},
{
name: "remove factor sms, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPSMS,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeOTPSMS),
),
},
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPSMS,
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "INSTANCE",
},
},
},
{
name: "factor added totp, removed otp sms, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecondFactorTypeTOTP,
),
),
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPSMS,
},
res: res{
err: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -231,7 +231,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeTOTP,
),
),
eventFromEventPusher(
@ -265,7 +265,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP},
MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN},
},
},
@ -1504,7 +1504,7 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) {
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeTOTP,
),
),
),
@ -1512,7 +1512,7 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
resourceOwner: "org1",
},
res: res{
@ -1520,7 +1520,7 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) {
},
},
{
name: "add factor, ok",
name: "add factor totp, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
@ -1530,7 +1530,7 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) {
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTP),
domain.SecondFactorTypeTOTP),
),
},
),
@ -1538,11 +1538,96 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
resourceOwner: "org1",
},
res: res{
want: domain.SecondFactorTypeOTP,
want: domain.SecondFactorTypeTOTP,
},
},
{
name: "add factor otp email, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPEmail),
),
},
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPEmail,
resourceOwner: "org1",
},
res: res{
want: domain.SecondFactorTypeOTPEmail,
},
},
{
name: "add factor otp sms, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPSMS),
),
},
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPSMS,
resourceOwner: "org1",
},
res: res{
want: domain.SecondFactorTypeOTPSMS,
},
},
{
name: "add factor totp, add otp sms, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeTOTP,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPSMS),
),
},
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPSMS,
resourceOwner: "org1",
},
res: res{
want: domain.SecondFactorTypeOTPSMS,
},
},
}
@ -1593,7 +1678,7 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
@ -1624,7 +1709,7 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
resourceOwner: "org1",
},
res: res{
@ -1632,7 +1717,7 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
},
},
{
name: "factor removed, not found error",
name: "factor totp removed, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
@ -1640,13 +1725,13 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeTOTP,
),
),
eventFromEventPusher(
org.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeTOTP,
),
),
),
@ -1654,7 +1739,7 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
resourceOwner: "org1",
},
res: res{
@ -1662,7 +1747,7 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
},
},
{
name: "add factor, ok",
name: "factor otp email removed, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
@ -1670,7 +1755,67 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTP,
domain.SecondFactorTypeOTPEmail,
),
),
eventFromEventPusher(
org.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPEmail,
),
),
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPEmail,
resourceOwner: "org1",
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "factor otp sms removed, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPSMS,
),
),
eventFromEventPusher(
org.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPSMS,
),
),
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPSMS,
resourceOwner: "org1",
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "add factor totp, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeTOTP,
),
),
),
@ -1679,7 +1824,7 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
eventFromEventPusher(
org.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTP),
domain.SecondFactorTypeTOTP),
),
},
),
@ -1687,7 +1832,77 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTP,
factor: domain.SecondFactorTypeTOTP,
resourceOwner: "org1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "add factor otp email, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPEmail,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
org.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPEmail),
),
},
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPEmail,
resourceOwner: "org1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "add factor otp sms, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPSMS,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
org.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
domain.SecondFactorTypeOTPSMS),
),
},
),
),
},
args: args{
ctx: context.Background(),
factor: domain.SecondFactorTypeOTPSMS,
resourceOwner: "org1",
},
res: res{

View File

@ -4,20 +4,14 @@ type SecondFactorType int32
const (
SecondFactorTypeUnspecified SecondFactorType = iota
SecondFactorTypeOTP
SecondFactorTypeTOTP
SecondFactorTypeU2F
SecondFactorTypeOTPEmail
SecondFactorTypeOTPSMS
secondFactorCount
)
func SecondFactorTypes() []SecondFactorType {
types := make([]SecondFactorType, 0, secondFactorCount-1)
for i := SecondFactorTypeUnspecified + 1; i < secondFactorCount; i++ {
types = append(types, i)
}
return types
}
type MultiFactorType int32
const (
@ -27,14 +21,6 @@ const (
multiFactorCount
)
func MultiFactorTypes() []MultiFactorType {
types := make([]MultiFactorType, 0, multiFactorCount-1)
for i := MultiFactorTypeUnspecified + 1; i < multiFactorCount; i++ {
types = append(types, i)
}
return types
}
type FactorState int32
const (

View File

@ -106,8 +106,12 @@ func secondFactorsToDomain(types []domain.SecondFactorType) []domain.SecondFacto
switch secondfactorType {
case domain.SecondFactorTypeU2F:
secondfactors[i] = domain.SecondFactorTypeU2F
case domain.SecondFactorTypeOTP:
secondfactors[i] = domain.SecondFactorTypeOTP
case domain.SecondFactorTypeTOTP:
secondfactors[i] = domain.SecondFactorTypeTOTP
case domain.SecondFactorTypeOTPEmail:
secondfactors[i] = domain.SecondFactorTypeOTPEmail
case domain.SecondFactorTypeOTPSMS:
secondfactors[i] = domain.SecondFactorTypeOTPSMS
}
}
return secondfactors

View File

@ -129,7 +129,7 @@ func Test_LoginPolicyPrepares(t *testing.T) {
true,
true,
true,
database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeOTP},
database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeTOTP},
database.EnumArray[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN},
domain.PasswordlessTypeAllowed,
true,
@ -157,7 +157,7 @@ func Test_LoginPolicyPrepares(t *testing.T) {
AllowExternalIDPs: true,
ForceMFA: true,
ForceMFALocalOnly: true,
SecondFactors: database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeOTP},
SecondFactors: database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeTOTP},
MultiFactors: database.EnumArray[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN},
PasswordlessType: domain.PasswordlessTypeAllowed,
IsDefault: true,
@ -217,7 +217,7 @@ func Test_LoginPolicyPrepares(t *testing.T) {
regexp.QuoteMeta(prepareLoginPolicy2FAsStmt),
prepareLoginPolicy2FAsCols,
[]driver.Value{
database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeOTP},
database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeTOTP},
},
),
},
@ -225,7 +225,7 @@ func Test_LoginPolicyPrepares(t *testing.T) {
SearchResponse: SearchResponse{
Count: 1,
},
Factors: database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeOTP},
Factors: database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeTOTP},
},
},
{

View File

@ -115,11 +115,11 @@ func (p *loginPolicyProjection) reducers() []handler.AggregateReducer {
},
{
Event: org.LoginPolicySecondFactorAddedEventType,
Reduce: p.reduce2FAAdded,
Reduce: p.reduceSecondFactorAdded,
},
{
Event: org.LoginPolicySecondFactorRemovedEventType,
Reduce: p.reduce2FARemoved,
Reduce: p.reduceSecondFactorRemoved,
},
{
Event: org.OrgRemovedEventType,
@ -148,11 +148,11 @@ func (p *loginPolicyProjection) reducers() []handler.AggregateReducer {
},
{
Event: instance.LoginPolicySecondFactorAddedEventType,
Reduce: p.reduce2FAAdded,
Reduce: p.reduceSecondFactorAdded,
},
{
Event: instance.LoginPolicySecondFactorRemovedEventType,
Reduce: p.reduce2FARemoved,
Reduce: p.reduceSecondFactorRemoved,
},
{
Event: instance.InstanceRemovedEventType,
@ -345,7 +345,7 @@ func (p *loginPolicyProjection) reduceLoginPolicyRemoved(event eventstore.Event)
), nil
}
func (p *loginPolicyProjection) reduce2FAAdded(event eventstore.Event) (*handler.Statement, error) {
func (p *loginPolicyProjection) reduceSecondFactorAdded(event eventstore.Event) (*handler.Statement, error) {
var policyEvent policy.SecondFactorAddedEvent
switch e := event.(type) {
case *instance.LoginPolicySecondFactorAddedEvent:
@ -370,7 +370,7 @@ func (p *loginPolicyProjection) reduce2FAAdded(event eventstore.Event) (*handler
), nil
}
func (p *loginPolicyProjection) reduce2FARemoved(event eventstore.Event) (*handler.Statement, error) {
func (p *loginPolicyProjection) reduceSecondFactorRemoved(event eventstore.Event) (*handler.Statement, error) {
var policyEvent policy.SecondFactorRemovedEvent
switch e := event.(type) {
case *instance.LoginPolicySecondFactorRemovedEvent:

View File

@ -310,8 +310,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
},
},
{
name: "org reduce2FAAdded",
reduce: (&loginPolicyProjection{}).reduce2FAAdded,
name: "org reduceSecondFactorAdded",
reduce: (&loginPolicyProjection{}).reduceSecondFactorAdded,
args: args{
event: getEvent(testEvent(
repository.EventType(org.LoginPolicySecondFactorAddedEventType),
@ -342,8 +342,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
},
},
{
name: "org reduce2FARemoved",
reduce: (&loginPolicyProjection{}).reduce2FARemoved,
name: "org reduceSecondFactorRemoved",
reduce: (&loginPolicyProjection{}).reduceSecondFactorRemoved,
args: args{
event: getEvent(testEvent(
repository.EventType(org.LoginPolicySecondFactorRemovedEventType),
@ -558,8 +558,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
},
},
{
name: "instance reduce2FAAdded",
reduce: (&loginPolicyProjection{}).reduce2FAAdded,
name: "instance reduceSecondFactorAdded u2f",
reduce: (&loginPolicyProjection{}).reduceSecondFactorAdded,
args: args{
event: getEvent(testEvent(
repository.EventType(instance.LoginPolicySecondFactorAddedEventType),
@ -590,8 +590,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
},
},
{
name: "instance reduce2FARemoved",
reduce: (&loginPolicyProjection{}).reduce2FARemoved,
name: "instance reduceSecondFactorRemoved u2f",
reduce: (&loginPolicyProjection{}).reduceSecondFactorRemoved,
args: args{
event: getEvent(testEvent(
repository.EventType(instance.LoginPolicySecondFactorRemovedEventType),
@ -621,6 +621,70 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
},
},
},
{
name: "instance reduceSecondFactorAdded otp email",
reduce: (&loginPolicyProjection{}).reduceSecondFactorAdded,
args: args{
event: getEvent(testEvent(
repository.EventType(instance.LoginPolicySecondFactorAddedEventType),
instance.AggregateType,
[]byte(`{
"mfaType": 3
}`),
), instance.SecondFactorAddedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
executions: []execution{
{
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),
domain.SecondFactorTypeOTPEmail,
"agg-id",
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceSecondFactorRemoved otp email",
reduce: (&loginPolicyProjection{}).reduceSecondFactorRemoved,
args: args{
event: getEvent(testEvent(
repository.EventType(instance.LoginPolicySecondFactorRemovedEventType),
instance.AggregateType,
[]byte(`{
"mfaType": 3
}`),
), instance.SecondFactorRemovedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
executions: []execution{
{
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),
domain.SecondFactorTypeOTPEmail,
"agg-id",
"instance-id",
},
},
},
},
},
},
{
name: "org.reduceOwnerRemoved",
reduce: (&loginPolicyProjection{}).reduceOwnerRemoved,

View File

@ -156,7 +156,7 @@ func (u *UserView) MFATypesSetupPossible(level domain.MFALevel, policy *domain.L
if policy.HasSecondFactors() {
for _, mfaType := range policy.SecondFactors {
switch mfaType {
case domain.SecondFactorTypeOTP:
case domain.SecondFactorTypeTOTP:
if u.OTPState != MFAStateReady {
types = append(types, domain.MFATypeOTP)
}
@ -181,7 +181,7 @@ func (u *UserView) MFATypesAllowed(level domain.MFALevel, policy *domain.LoginPo
if policy.HasSecondFactors() {
for _, mfaType := range policy.SecondFactors {
switch mfaType {
case domain.SecondFactorTypeOTP:
case domain.SecondFactorTypeTOTP:
if u.OTPState == MFAStateReady {
types = append(types, domain.MFATypeOTP)
}

View File

@ -248,8 +248,11 @@ message LoginPolicy {
enum SecondFactorType {
SECOND_FACTOR_TYPE_UNSPECIFIED = 0;
// SECOND_FACTOR_TYPE_OTP is the type for TOTP
SECOND_FACTOR_TYPE_OTP = 1;
SECOND_FACTOR_TYPE_U2F = 2;
SECOND_FACTOR_TYPE_OTP_EMAIL = 3;
SECOND_FACTOR_TYPE_OTP_SMS = 4;
}
enum MultiFactorType {

View File

@ -113,8 +113,11 @@ message LoginSettings {
enum SecondFactorType {
SECOND_FACTOR_TYPE_UNSPECIFIED = 0;
// This is the type for TOTP
SECOND_FACTOR_TYPE_OTP = 1;
SECOND_FACTOR_TYPE_U2F = 2;
SECOND_FACTOR_TYPE_OTP_EMAIL = 3;
SECOND_FACTOR_TYPE_OTP_SMS = 4;
}
enum MultiFactorType {