diff --git a/config.template.yml b/config.template.yml index 1739cec81..c4eaabfa1 100644 --- a/config.template.yml +++ b/config.template.yml @@ -158,6 +158,7 @@ webauthn: ## Parameters used to contact the Duo API. Those are generated when you protect an application of type ## "Partner Auth API" in the management panel. duo_api: + disable: false hostname: api-123456789.example.com integration_key: ABCDEF ## Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html diff --git a/docs/configuration/duo-push-notifications.md b/docs/configuration/duo-push-notifications.md index b69c93960..26af6e27c 100644 --- a/docs/configuration/duo-push-notifications.md +++ b/docs/configuration/duo-push-notifications.md @@ -21,6 +21,7 @@ section of the configuration. The configuration is as follows: ```yaml duo_api: + disable: false hostname: api-123456789.example.com integration_key: ABCDEF secret_key: 1234567890abcdefghifjkl @@ -32,6 +33,19 @@ variable as described [here](./secrets.md). ## Options +### disable: +
+type: boolean +{: .label .label-config .label-purple } +default: false +{: .label .label-config .label-blue } +required: no +{: .label .label-config .label-green } +
+ +Disables Duo. If the hostname, integration_key, and secret_key are all empty strings or undefined this is automatically +true. + ### hostname
type: string diff --git a/internal/configuration/config.template.yml b/internal/configuration/config.template.yml index 1739cec81..c4eaabfa1 100644 --- a/internal/configuration/config.template.yml +++ b/internal/configuration/config.template.yml @@ -158,6 +158,7 @@ webauthn: ## Parameters used to contact the Duo API. Those are generated when you protect an application of type ## "Partner Auth API" in the management panel. duo_api: + disable: false hostname: api-123456789.example.com integration_key: ABCDEF ## Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html diff --git a/internal/configuration/provider_test.go b/internal/configuration/provider_test.go index 68c0a0b02..d3fd9cb5a 100644 --- a/internal/configuration/provider_test.go +++ b/internal/configuration/provider_test.go @@ -273,7 +273,7 @@ func TestShouldHandleErrInvalidatorWhenSMTPSenderBlank(t *testing.T) { assert.Equal(t, "", config.Notifier.SMTP.Sender.Name) assert.Equal(t, "", config.Notifier.SMTP.Sender.Address) - validator.ValidateNotifier(config.Notifier, val) + validator.ValidateNotifier(&config.Notifier, val) require.Len(t, val.Errors(), 1) assert.Len(t, val.Warnings(), 0) diff --git a/internal/configuration/schema/configuration.go b/internal/configuration/schema/configuration.go index bcaf2f344..54cfeadd6 100644 --- a/internal/configuration/schema/configuration.go +++ b/internal/configuration/schema/configuration.go @@ -12,12 +12,12 @@ type Configuration struct { AuthenticationBackend AuthenticationBackendConfiguration `koanf:"authentication_backend"` Session SessionConfiguration `koanf:"session"` TOTP TOTPConfiguration `koanf:"totp"` - DuoAPI *DuoAPIConfiguration `koanf:"duo_api"` + DuoAPI DuoAPIConfiguration `koanf:"duo_api"` AccessControl AccessControlConfiguration `koanf:"access_control"` NTP NTPConfiguration `koanf:"ntp"` Regulation RegulationConfiguration `koanf:"regulation"` Storage StorageConfiguration `koanf:"storage"` - Notifier *NotifierConfiguration `koanf:"notifier"` + Notifier NotifierConfiguration `koanf:"notifier"` Server ServerConfiguration `koanf:"server"` Webauthn WebauthnConfiguration `koanf:"webauthn"` PasswordPolicy PasswordPolicyConfiguration `koanf:"password_policy"` diff --git a/internal/configuration/schema/duo.go b/internal/configuration/schema/duo.go index 55e81d217..75e383d3e 100644 --- a/internal/configuration/schema/duo.go +++ b/internal/configuration/schema/duo.go @@ -2,8 +2,9 @@ package schema // DuoAPIConfiguration represents the configuration related to Duo API. type DuoAPIConfiguration struct { + Disable bool `koanf:"disable"` Hostname string `koanf:"hostname"` - EnableSelfEnrollment bool `koanf:"enable_self_enrollment"` IntegrationKey string `koanf:"integration_key"` SecretKey string `koanf:"secret_key"` + EnableSelfEnrollment bool `koanf:"enable_self_enrollment"` } diff --git a/internal/configuration/validator/configuration.go b/internal/configuration/validator/configuration.go index ae34d6b74..ef8b05086 100644 --- a/internal/configuration/validator/configuration.go +++ b/internal/configuration/validator/configuration.go @@ -37,6 +37,8 @@ func ValidateConfiguration(config *schema.Configuration, validator *schema.Struc ValidateLog(config, validator) + ValidateDuo(config, validator) + ValidateTOTP(config, validator) ValidateWebauthn(config, validator) @@ -55,7 +57,7 @@ func ValidateConfiguration(config *schema.Configuration, validator *schema.Struc ValidateStorage(config.Storage, validator) - ValidateNotifier(config.Notifier, validator) + ValidateNotifier(&config.Notifier, validator) ValidateIdentityProviders(&config.IdentityProviders, validator) diff --git a/internal/configuration/validator/configuration_test.go b/internal/configuration/validator/configuration_test.go index 8cff7dc52..00dc7e519 100644 --- a/internal/configuration/validator/configuration_test.go +++ b/internal/configuration/validator/configuration_test.go @@ -32,7 +32,7 @@ func newDefaultConfig() schema.Configuration { config.Storage.Local = &schema.LocalStorageConfiguration{ Path: "abc", } - config.Notifier = &schema.NotifierConfiguration{ + config.Notifier = schema.NotifierConfiguration{ FileSystem: &schema.FileSystemNotifierConfiguration{ Filename: "/tmp/file", }, @@ -48,7 +48,8 @@ func TestShouldEnsureNotifierConfigIsProvided(t *testing.T) { ValidateConfiguration(&config, validator) require.Len(t, validator.Errors(), 0) - config.Notifier = nil + config.Notifier.SMTP = nil + config.Notifier.FileSystem = nil ValidateConfiguration(&config, validator) require.Len(t, validator.Errors(), 1) diff --git a/internal/configuration/validator/const.go b/internal/configuration/validator/const.go index 9eb02cac0..14617ecac 100644 --- a/internal/configuration/validator/const.go +++ b/internal/configuration/validator/const.go @@ -249,6 +249,10 @@ const ( errFmtPasswordPolicyZXCVBNMinScoreInvalid = "password_policy: zxcvbn: option 'min_score' is invalid: must be between 1 and 4 but it's configured as %d" ) +const ( + errFmtDuoMissingOption = "duo_api: option '%s' is required when duo is enabled but it is missing" +) + // Error constants. const ( /* diff --git a/internal/configuration/validator/duo.go b/internal/configuration/validator/duo.go new file mode 100644 index 000000000..5d6938b6d --- /dev/null +++ b/internal/configuration/validator/duo.go @@ -0,0 +1,34 @@ +package validator + +import ( + "fmt" + + "github.com/authelia/authelia/v4/internal/configuration/schema" +) + +// ValidateDuo validates and updates the Duo configuration. +func ValidateDuo(config *schema.Configuration, validator *schema.StructValidator) { + if config.DuoAPI.Disable { + return + } + + if config.DuoAPI.Hostname == "" && config.DuoAPI.IntegrationKey == "" && config.DuoAPI.SecretKey == "" { + config.DuoAPI.Disable = true + } + + if config.DuoAPI.Disable { + return + } + + if config.DuoAPI.Hostname == "" { + validator.Push(fmt.Errorf(errFmtDuoMissingOption, "hostname")) + } + + if config.DuoAPI.IntegrationKey == "" { + validator.Push(fmt.Errorf(errFmtDuoMissingOption, "integration_key")) + } + + if config.DuoAPI.SecretKey == "" { + validator.Push(fmt.Errorf(errFmtDuoMissingOption, "secret_key")) + } +} diff --git a/internal/configuration/validator/duo_test.go b/internal/configuration/validator/duo_test.go new file mode 100644 index 000000000..ef4856b56 --- /dev/null +++ b/internal/configuration/validator/duo_test.go @@ -0,0 +1,105 @@ +package validator + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/authelia/authelia/v4/internal/configuration/schema" +) + +func TestValidateDuo(t *testing.T) { + testCases := []struct { + desc string + have *schema.Configuration + expected schema.DuoAPIConfiguration + errs []string + }{ + { + desc: "ShouldDisableDuo", + have: &schema.Configuration{}, + expected: schema.DuoAPIConfiguration{Disable: true}, + }, + { + desc: "ShouldNotDisableDuo", + have: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{ + Hostname: "test", + IntegrationKey: "test", + SecretKey: "test", + }}, + expected: schema.DuoAPIConfiguration{ + Hostname: "test", + IntegrationKey: "test", + SecretKey: "test", + }, + }, + { + desc: "ShouldDetectMissingSecretKey", + have: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{ + Hostname: "test", + IntegrationKey: "test", + }}, + expected: schema.DuoAPIConfiguration{ + Hostname: "test", + IntegrationKey: "test", + }, + errs: []string{ + "duo_api: option 'secret_key' is required when duo is enabled but it is missing", + }, + }, + { + desc: "ShouldDetectMissingIntegrationKey", + have: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{ + Hostname: "test", + SecretKey: "test", + }}, + expected: schema.DuoAPIConfiguration{ + Hostname: "test", + SecretKey: "test", + }, + errs: []string{ + "duo_api: option 'integration_key' is required when duo is enabled but it is missing", + }, + }, + { + desc: "ShouldDetectMissingHostname", + have: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{ + IntegrationKey: "test", + SecretKey: "test", + }}, + expected: schema.DuoAPIConfiguration{ + IntegrationKey: "test", + SecretKey: "test", + }, + errs: []string{ + "duo_api: option 'hostname' is required when duo is enabled but it is missing", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + val := schema.NewStructValidator() + + ValidateDuo(tc.have, val) + + assert.Equal(t, tc.expected.Disable, tc.have.DuoAPI.Disable) + assert.Equal(t, tc.expected.Hostname, tc.have.DuoAPI.Hostname) + assert.Equal(t, tc.expected.IntegrationKey, tc.have.DuoAPI.IntegrationKey) + assert.Equal(t, tc.expected.SecretKey, tc.have.DuoAPI.SecretKey) + assert.Equal(t, tc.expected.EnableSelfEnrollment, tc.have.DuoAPI.EnableSelfEnrollment) + + require.Len(t, val.Errors(), len(tc.errs)) + + if len(tc.errs) != 0 { + for i, err := range tc.errs { + t.Run(fmt.Sprintf("Err%d", i+1), func(t *testing.T) { + assert.EqualError(t, val.Errors()[i], err) + }) + } + } + }) + } +} diff --git a/internal/configuration/validator/notifier.go b/internal/configuration/validator/notifier.go index 56810a60d..b52bb646a 100644 --- a/internal/configuration/validator/notifier.go +++ b/internal/configuration/validator/notifier.go @@ -12,7 +12,7 @@ import ( // ValidateNotifier validates and update notifier configuration. func ValidateNotifier(config *schema.NotifierConfiguration, validator *schema.StructValidator) { - if config == nil || (config.SMTP == nil && config.FileSystem == nil) { + if config.SMTP == nil && config.FileSystem == nil { validator.Push(fmt.Errorf(errFmtNotifierNotConfigured)) return diff --git a/internal/handlers/handler_configuration_test.go b/internal/handlers/handler_configuration_test.go index 480f80e2c..2812479d8 100644 --- a/internal/handlers/handler_configuration_test.go +++ b/internal/handlers/handler_configuration_test.go @@ -30,7 +30,9 @@ func (s *SecondFactorAvailableMethodsFixture) TearDownTest() { func (s *SecondFactorAvailableMethodsFixture) TestShouldHaveAllConfiguredMethods() { s.mock.Ctx.Configuration = schema.Configuration{ - DuoAPI: &schema.DuoAPIConfiguration{}, + DuoAPI: schema.DuoAPIConfiguration{ + Disable: false, + }, TOTP: schema.TOTPConfiguration{ Disable: false, }, @@ -58,7 +60,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldHaveAllConfiguredMethods func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveTOTPFromAvailableMethodsWhenDisabled() { s.mock.Ctx.Configuration = schema.Configuration{ - DuoAPI: &schema.DuoAPIConfiguration{}, + DuoAPI: schema.DuoAPIConfiguration{ + Disable: false, + }, TOTP: schema.TOTPConfiguration{ Disable: true, }, @@ -86,7 +90,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveTOTPFromAvailableM func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvailableMethodsWhenDisabled() { s.mock.Ctx.Configuration = schema.Configuration{ - DuoAPI: &schema.DuoAPIConfiguration{}, + DuoAPI: schema.DuoAPIConfiguration{ + Disable: false, + }, TOTP: schema.TOTPConfiguration{ Disable: false, }, @@ -114,7 +120,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvaila func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveDuoFromAvailableMethodsWhenNotConfigured() { s.mock.Ctx.Configuration = schema.Configuration{ - DuoAPI: nil, + DuoAPI: schema.DuoAPIConfiguration{ + Disable: true, + }, TOTP: schema.TOTPConfiguration{ Disable: false, }, @@ -142,7 +150,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveDuoFromAvailableMe func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenNoTwoFactorACLRulesConfigured() { s.mock.Ctx.Configuration = schema.Configuration{ - DuoAPI: &schema.DuoAPIConfiguration{}, + DuoAPI: schema.DuoAPIConfiguration{ + Disable: false, + }, TOTP: schema.TOTPConfiguration{ Disable: false, }, @@ -170,7 +180,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenNoTw func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenAllDisabledOrNotConfigured() { s.mock.Ctx.Configuration = schema.Configuration{ - DuoAPI: nil, + DuoAPI: schema.DuoAPIConfiguration{ + Disable: true, + }, TOTP: schema.TOTPConfiguration{ Disable: true, }, diff --git a/internal/handlers/handler_reset_password_step2.go b/internal/handlers/handler_reset_password_step2.go index 69a77caf1..ae3f814b6 100644 --- a/internal/handlers/handler_reset_password_step2.go +++ b/internal/handlers/handler_reset_password_step2.go @@ -80,7 +80,7 @@ func ResetPasswordPOST(ctx *middlewares.AutheliaCtx) { bufHTML := new(bytes.Buffer) disableHTML := false - if ctx.Configuration.Notifier != nil && ctx.Configuration.Notifier.SMTP != nil { + if ctx.Configuration.Notifier.SMTP != nil { disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails } diff --git a/internal/handlers/handler_user_info_test.go b/internal/handlers/handler_user_info_test.go index e4d0fa659..47bc9a6b8 100644 --- a/internal/handlers/handler_user_info_test.go +++ b/internal/handlers/handler_user_info_test.go @@ -101,8 +101,6 @@ func TestUserInfoEndpoint_SetCorrectMethod(t *testing.T) { mock := mocks.NewMockAutheliaCtx(t) - mock.Ctx.Configuration.DuoAPI = &schema.DuoAPIConfiguration{} - // Set the initial user session. userSession := mock.Ctx.GetSession() userSession.Username = testUsername @@ -172,9 +170,7 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) { HasWebauthn: false, HasDuo: false, }, - config: &schema.Configuration{ - DuoAPI: &schema.DuoAPIConfiguration{}, - }, + config: &schema.Configuration{}, loadErr: nil, saveErr: nil, }, @@ -192,9 +188,7 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) { HasWebauthn: false, HasDuo: true, }, - config: &schema.Configuration{ - DuoAPI: &schema.DuoAPIConfiguration{}, - }, + config: &schema.Configuration{}, loadErr: nil, saveErr: nil, }, @@ -212,6 +206,7 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) { HasWebauthn: false, HasDuo: true, }, + config: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{Disable: true}}, loadErr: nil, saveErr: nil, }, @@ -233,7 +228,6 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) { TOTP: schema.TOTPConfiguration{ Disable: true, }, - DuoAPI: &schema.DuoAPIConfiguration{}, }, loadErr: nil, saveErr: nil, @@ -252,104 +246,104 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) { HasWebauthn: true, HasDuo: true, }, - config: &schema.Configuration{ - DuoAPI: &schema.DuoAPIConfiguration{}, - }, + config: &schema.Configuration{}, loadErr: nil, saveErr: errors.New("could not save"), }, } for _, resp := range expectedResponses { - if resp.api == nil { - resp.api = &resp.db - } + t.Run(resp.description, func(t *testing.T) { + if resp.api == nil { + resp.api = &resp.db + } - mock := mocks.NewMockAutheliaCtx(t) + mock := mocks.NewMockAutheliaCtx(t) - if resp.config != nil { - mock.Ctx.Configuration = *resp.config - } + if resp.config != nil { + mock.Ctx.Configuration = *resp.config + } - // Set the initial user session. - userSession := mock.Ctx.GetSession() - userSession.Username = testUsername - userSession.AuthenticationLevel = 1 - err := mock.Ctx.SaveSession(userSession) - require.NoError(t, err) + // Set the initial user session. + userSession := mock.Ctx.GetSession() + userSession.Username = testUsername + userSession.AuthenticationLevel = 1 + err := mock.Ctx.SaveSession(userSession) + require.NoError(t, err) - if resp.db.Method == "" { - gomock.InOrder( - mock.StorageMock. - EXPECT(). - LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")). - Return("", sql.ErrNoRows), - mock.StorageMock. - EXPECT(). - SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq("")). - Return(resp.saveErr), - mock.StorageMock. - EXPECT(). - LoadUserInfo(mock.Ctx, gomock.Eq("john")). - Return(resp.db, nil), - mock.StorageMock.EXPECT(). - SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)). - Return(resp.saveErr), - ) - } else { - gomock.InOrder( - mock.StorageMock. - EXPECT(). - LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")). - Return(resp.db.Method, nil), - mock.StorageMock. - EXPECT(). - LoadUserInfo(mock.Ctx, gomock.Eq("john")). - Return(resp.db, nil), - mock.StorageMock.EXPECT(). - SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)). - Return(resp.saveErr), - ) - } + if resp.db.Method == "" { + gomock.InOrder( + mock.StorageMock. + EXPECT(). + LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")). + Return("", sql.ErrNoRows), + mock.StorageMock. + EXPECT(). + SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq("")). + Return(resp.saveErr), + mock.StorageMock. + EXPECT(). + LoadUserInfo(mock.Ctx, gomock.Eq("john")). + Return(resp.db, nil), + mock.StorageMock.EXPECT(). + SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)). + Return(resp.saveErr), + ) + } else { + gomock.InOrder( + mock.StorageMock. + EXPECT(). + LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")). + Return(resp.db.Method, nil), + mock.StorageMock. + EXPECT(). + LoadUserInfo(mock.Ctx, gomock.Eq("john")). + Return(resp.db, nil), + mock.StorageMock.EXPECT(). + SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)). + Return(resp.saveErr), + ) + } - UserInfoPOST(mock.Ctx) + UserInfoPOST(mock.Ctx) - if resp.loadErr == nil && resp.saveErr == nil { - t.Run(fmt.Sprintf("%s/%s", resp.description, "expected status code"), func(t *testing.T) { - assert.Equal(t, 200, mock.Ctx.Response.StatusCode()) - }) + if resp.loadErr == nil && resp.saveErr == nil { + t.Run(fmt.Sprintf("%s/%s", resp.description, "expected status code"), func(t *testing.T) { + assert.Equal(t, 200, mock.Ctx.Response.StatusCode()) + }) - actualPreferences := model.UserInfo{} + actualPreferences := model.UserInfo{} - mock.GetResponseData(t, &actualPreferences) + mock.GetResponseData(t, &actualPreferences) - t.Run(fmt.Sprintf("%s/%s", resp.description, "expected method"), func(t *testing.T) { - assert.Equal(t, resp.api.Method, actualPreferences.Method) - }) + t.Run("expected method", func(t *testing.T) { + assert.Equal(t, resp.api.Method, actualPreferences.Method) + }) - t.Run(fmt.Sprintf("%s/%s", resp.description, "registered webauthn"), func(t *testing.T) { - assert.Equal(t, resp.api.HasWebauthn, actualPreferences.HasWebauthn) - }) + t.Run("registered webauthn", func(t *testing.T) { + assert.Equal(t, resp.api.HasWebauthn, actualPreferences.HasWebauthn) + }) - t.Run(fmt.Sprintf("%s/%s", resp.description, "registered totp"), func(t *testing.T) { - assert.Equal(t, resp.api.HasTOTP, actualPreferences.HasTOTP) - }) + t.Run("registered totp", func(t *testing.T) { + assert.Equal(t, resp.api.HasTOTP, actualPreferences.HasTOTP) + }) - t.Run(fmt.Sprintf("%s/%s", resp.description, "registered duo"), func(t *testing.T) { - assert.Equal(t, resp.api.HasDuo, actualPreferences.HasDuo) - }) - } else { - t.Run("expected status code", func(t *testing.T) { - assert.Equal(t, 200, mock.Ctx.Response.StatusCode()) - }) + t.Run("registered duo", func(t *testing.T) { + assert.Equal(t, resp.api.HasDuo, actualPreferences.HasDuo) + }) + } else { + t.Run("expected status code", func(t *testing.T) { + assert.Equal(t, 200, mock.Ctx.Response.StatusCode()) + }) - errResponse := mock.GetResponseError(t) + errResponse := mock.GetResponseError(t) - assert.Equal(t, "KO", errResponse.Status) - assert.Equal(t, "Operation failed.", errResponse.Message) - } + assert.Equal(t, "KO", errResponse.Status) + assert.Equal(t, "Operation failed.", errResponse.Message) + } - mock.Close() + mock.Close() + }) } } @@ -420,7 +414,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenBadMethodProvided() { MethodPreferencePOST(s.mock.Ctx) s.mock.Assert200KO(s.T(), "Operation failed.") - assert.Equal(s.T(), "unknown or unavailable method 'abc', it should be one of totp, webauthn", s.mock.Hook.LastEntry().Message) + assert.Equal(s.T(), "unknown or unavailable method 'abc', it should be one of totp, webauthn, mobile_push", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level) } diff --git a/internal/middlewares/authelia_context.go b/internal/middlewares/authelia_context.go index 4b209176b..57805401e 100644 --- a/internal/middlewares/authelia_context.go +++ b/internal/middlewares/authelia_context.go @@ -67,7 +67,7 @@ func (ctx *AutheliaCtx) AvailableSecondFactorMethods() (methods []string) { methods = append(methods, model.SecondFactorMethodWebauthn) } - if ctx.Configuration.DuoAPI != nil { + if !ctx.Configuration.DuoAPI.Disable { methods = append(methods, model.SecondFactorMethodDuo) } diff --git a/internal/middlewares/authelia_context_test.go b/internal/middlewares/authelia_context_test.go index 0dbb29512..e120b8c29 100644 --- a/internal/middlewares/authelia_context_test.go +++ b/internal/middlewares/authelia_context_test.go @@ -121,9 +121,11 @@ func TestShouldReturnCorrectSecondFactorMethods(t *testing.T) { mock := mocks.NewMockAutheliaCtx(t) defer mock.Close() + mock.Ctx.Configuration.DuoAPI.Disable = true + assert.Equal(t, []string{model.SecondFactorMethodTOTP, model.SecondFactorMethodWebauthn}, mock.Ctx.AvailableSecondFactorMethods()) - mock.Ctx.Configuration.DuoAPI = &schema.DuoAPIConfiguration{} + mock.Ctx.Configuration.DuoAPI.Disable = false assert.Equal(t, []string{model.SecondFactorMethodTOTP, model.SecondFactorMethodWebauthn, model.SecondFactorMethodDuo}, mock.Ctx.AvailableSecondFactorMethods()) @@ -135,7 +137,7 @@ func TestShouldReturnCorrectSecondFactorMethods(t *testing.T) { assert.Equal(t, []string{model.SecondFactorMethodDuo}, mock.Ctx.AvailableSecondFactorMethods()) - mock.Ctx.Configuration.DuoAPI = nil + mock.Ctx.Configuration.DuoAPI.Disable = true assert.Equal(t, []string{}, mock.Ctx.AvailableSecondFactorMethods()) } diff --git a/internal/middlewares/identity_verification.go b/internal/middlewares/identity_verification.go index db6c7076d..0658978b7 100644 --- a/internal/middlewares/identity_verification.go +++ b/internal/middlewares/identity_verification.go @@ -73,7 +73,7 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs, delayFunc Tim bufHTML := new(bytes.Buffer) disableHTML := false - if ctx.Configuration.Notifier != nil && ctx.Configuration.Notifier.SMTP != nil { + if ctx.Configuration.Notifier.SMTP != nil { disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails } diff --git a/internal/server/handlers.go b/internal/server/handlers.go index aea535e91..b7f726823 100644 --- a/internal/server/handlers.go +++ b/internal/server/handlers.go @@ -87,7 +87,7 @@ func getHandler(config schema.Configuration, providers middlewares.Providers) fa resetPasswordCustomURL := config.AuthenticationBackend.PasswordReset.CustomURL.String() duoSelfEnrollment := f - if config.DuoAPI != nil { + if !config.DuoAPI.Disable { duoSelfEnrollment = strconv.FormatBool(config.DuoAPI.EnableSelfEnrollment) } @@ -184,7 +184,7 @@ func getHandler(config schema.Configuration, providers middlewares.Providers) fa } // Configure DUO api endpoint only if configuration exists. - if config.DuoAPI != nil { + if !config.DuoAPI.Disable { var duoAPI duo.API if os.Getenv("ENVIRONMENT") == dev { duoAPI = duo.NewDuoAPI(duoapi.NewDuoApi(