refactor(configuration): remove ptr for duoapi and notifier (#3200)
This adds to the ongoing effort to remove all pointers to structs in the configuration without breaking backwards compatibility.pull/3202/head^2
parent
c9568caf4d
commit
4710de33a4
|
@ -158,6 +158,7 @@ webauthn:
|
||||||
## Parameters used to contact the Duo API. Those are generated when you protect an application of type
|
## Parameters used to contact the Duo API. Those are generated when you protect an application of type
|
||||||
## "Partner Auth API" in the management panel.
|
## "Partner Auth API" in the management panel.
|
||||||
duo_api:
|
duo_api:
|
||||||
|
disable: false
|
||||||
hostname: api-123456789.example.com
|
hostname: api-123456789.example.com
|
||||||
integration_key: ABCDEF
|
integration_key: ABCDEF
|
||||||
## Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html
|
## Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html
|
||||||
|
|
|
@ -21,6 +21,7 @@ section of the configuration.
|
||||||
The configuration is as follows:
|
The configuration is as follows:
|
||||||
```yaml
|
```yaml
|
||||||
duo_api:
|
duo_api:
|
||||||
|
disable: false
|
||||||
hostname: api-123456789.example.com
|
hostname: api-123456789.example.com
|
||||||
integration_key: ABCDEF
|
integration_key: ABCDEF
|
||||||
secret_key: 1234567890abcdefghifjkl
|
secret_key: 1234567890abcdefghifjkl
|
||||||
|
@ -32,6 +33,19 @@ variable as described [here](./secrets.md).
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
|
### disable:
|
||||||
|
<div markdown="1">
|
||||||
|
type: boolean
|
||||||
|
{: .label .label-config .label-purple }
|
||||||
|
default: false
|
||||||
|
{: .label .label-config .label-blue }
|
||||||
|
required: no
|
||||||
|
{: .label .label-config .label-green }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Disables Duo. If the hostname, integration_key, and secret_key are all empty strings or undefined this is automatically
|
||||||
|
true.
|
||||||
|
|
||||||
### hostname
|
### hostname
|
||||||
<div markdown="1">
|
<div markdown="1">
|
||||||
type: string
|
type: string
|
||||||
|
|
|
@ -158,6 +158,7 @@ webauthn:
|
||||||
## Parameters used to contact the Duo API. Those are generated when you protect an application of type
|
## Parameters used to contact the Duo API. Those are generated when you protect an application of type
|
||||||
## "Partner Auth API" in the management panel.
|
## "Partner Auth API" in the management panel.
|
||||||
duo_api:
|
duo_api:
|
||||||
|
disable: false
|
||||||
hostname: api-123456789.example.com
|
hostname: api-123456789.example.com
|
||||||
integration_key: ABCDEF
|
integration_key: ABCDEF
|
||||||
## Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html
|
## Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html
|
||||||
|
|
|
@ -273,7 +273,7 @@ func TestShouldHandleErrInvalidatorWhenSMTPSenderBlank(t *testing.T) {
|
||||||
assert.Equal(t, "", config.Notifier.SMTP.Sender.Name)
|
assert.Equal(t, "", config.Notifier.SMTP.Sender.Name)
|
||||||
assert.Equal(t, "", config.Notifier.SMTP.Sender.Address)
|
assert.Equal(t, "", config.Notifier.SMTP.Sender.Address)
|
||||||
|
|
||||||
validator.ValidateNotifier(config.Notifier, val)
|
validator.ValidateNotifier(&config.Notifier, val)
|
||||||
|
|
||||||
require.Len(t, val.Errors(), 1)
|
require.Len(t, val.Errors(), 1)
|
||||||
assert.Len(t, val.Warnings(), 0)
|
assert.Len(t, val.Warnings(), 0)
|
||||||
|
|
|
@ -12,12 +12,12 @@ type Configuration struct {
|
||||||
AuthenticationBackend AuthenticationBackendConfiguration `koanf:"authentication_backend"`
|
AuthenticationBackend AuthenticationBackendConfiguration `koanf:"authentication_backend"`
|
||||||
Session SessionConfiguration `koanf:"session"`
|
Session SessionConfiguration `koanf:"session"`
|
||||||
TOTP TOTPConfiguration `koanf:"totp"`
|
TOTP TOTPConfiguration `koanf:"totp"`
|
||||||
DuoAPI *DuoAPIConfiguration `koanf:"duo_api"`
|
DuoAPI DuoAPIConfiguration `koanf:"duo_api"`
|
||||||
AccessControl AccessControlConfiguration `koanf:"access_control"`
|
AccessControl AccessControlConfiguration `koanf:"access_control"`
|
||||||
NTP NTPConfiguration `koanf:"ntp"`
|
NTP NTPConfiguration `koanf:"ntp"`
|
||||||
Regulation RegulationConfiguration `koanf:"regulation"`
|
Regulation RegulationConfiguration `koanf:"regulation"`
|
||||||
Storage StorageConfiguration `koanf:"storage"`
|
Storage StorageConfiguration `koanf:"storage"`
|
||||||
Notifier *NotifierConfiguration `koanf:"notifier"`
|
Notifier NotifierConfiguration `koanf:"notifier"`
|
||||||
Server ServerConfiguration `koanf:"server"`
|
Server ServerConfiguration `koanf:"server"`
|
||||||
Webauthn WebauthnConfiguration `koanf:"webauthn"`
|
Webauthn WebauthnConfiguration `koanf:"webauthn"`
|
||||||
PasswordPolicy PasswordPolicyConfiguration `koanf:"password_policy"`
|
PasswordPolicy PasswordPolicyConfiguration `koanf:"password_policy"`
|
||||||
|
|
|
@ -2,8 +2,9 @@ package schema
|
||||||
|
|
||||||
// DuoAPIConfiguration represents the configuration related to Duo API.
|
// DuoAPIConfiguration represents the configuration related to Duo API.
|
||||||
type DuoAPIConfiguration struct {
|
type DuoAPIConfiguration struct {
|
||||||
|
Disable bool `koanf:"disable"`
|
||||||
Hostname string `koanf:"hostname"`
|
Hostname string `koanf:"hostname"`
|
||||||
EnableSelfEnrollment bool `koanf:"enable_self_enrollment"`
|
|
||||||
IntegrationKey string `koanf:"integration_key"`
|
IntegrationKey string `koanf:"integration_key"`
|
||||||
SecretKey string `koanf:"secret_key"`
|
SecretKey string `koanf:"secret_key"`
|
||||||
|
EnableSelfEnrollment bool `koanf:"enable_self_enrollment"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ func ValidateConfiguration(config *schema.Configuration, validator *schema.Struc
|
||||||
|
|
||||||
ValidateLog(config, validator)
|
ValidateLog(config, validator)
|
||||||
|
|
||||||
|
ValidateDuo(config, validator)
|
||||||
|
|
||||||
ValidateTOTP(config, validator)
|
ValidateTOTP(config, validator)
|
||||||
|
|
||||||
ValidateWebauthn(config, validator)
|
ValidateWebauthn(config, validator)
|
||||||
|
@ -55,7 +57,7 @@ func ValidateConfiguration(config *schema.Configuration, validator *schema.Struc
|
||||||
|
|
||||||
ValidateStorage(config.Storage, validator)
|
ValidateStorage(config.Storage, validator)
|
||||||
|
|
||||||
ValidateNotifier(config.Notifier, validator)
|
ValidateNotifier(&config.Notifier, validator)
|
||||||
|
|
||||||
ValidateIdentityProviders(&config.IdentityProviders, validator)
|
ValidateIdentityProviders(&config.IdentityProviders, validator)
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ func newDefaultConfig() schema.Configuration {
|
||||||
config.Storage.Local = &schema.LocalStorageConfiguration{
|
config.Storage.Local = &schema.LocalStorageConfiguration{
|
||||||
Path: "abc",
|
Path: "abc",
|
||||||
}
|
}
|
||||||
config.Notifier = &schema.NotifierConfiguration{
|
config.Notifier = schema.NotifierConfiguration{
|
||||||
FileSystem: &schema.FileSystemNotifierConfiguration{
|
FileSystem: &schema.FileSystemNotifierConfiguration{
|
||||||
Filename: "/tmp/file",
|
Filename: "/tmp/file",
|
||||||
},
|
},
|
||||||
|
@ -48,7 +48,8 @@ func TestShouldEnsureNotifierConfigIsProvided(t *testing.T) {
|
||||||
ValidateConfiguration(&config, validator)
|
ValidateConfiguration(&config, validator)
|
||||||
require.Len(t, validator.Errors(), 0)
|
require.Len(t, validator.Errors(), 0)
|
||||||
|
|
||||||
config.Notifier = nil
|
config.Notifier.SMTP = nil
|
||||||
|
config.Notifier.FileSystem = nil
|
||||||
|
|
||||||
ValidateConfiguration(&config, validator)
|
ValidateConfiguration(&config, validator)
|
||||||
require.Len(t, validator.Errors(), 1)
|
require.Len(t, validator.Errors(), 1)
|
||||||
|
|
|
@ -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"
|
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.
|
// Error constants.
|
||||||
const (
|
const (
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
// ValidateNotifier validates and update notifier configuration.
|
// ValidateNotifier validates and update notifier configuration.
|
||||||
func ValidateNotifier(config *schema.NotifierConfiguration, validator *schema.StructValidator) {
|
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))
|
validator.Push(fmt.Errorf(errFmtNotifierNotConfigured))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -30,7 +30,9 @@ func (s *SecondFactorAvailableMethodsFixture) TearDownTest() {
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldHaveAllConfiguredMethods() {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldHaveAllConfiguredMethods() {
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
DuoAPI: schema.DuoAPIConfiguration{
|
||||||
|
Disable: false,
|
||||||
|
},
|
||||||
TOTP: schema.TOTPConfiguration{
|
TOTP: schema.TOTPConfiguration{
|
||||||
Disable: false,
|
Disable: false,
|
||||||
},
|
},
|
||||||
|
@ -58,7 +60,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldHaveAllConfiguredMethods
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveTOTPFromAvailableMethodsWhenDisabled() {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveTOTPFromAvailableMethodsWhenDisabled() {
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
DuoAPI: schema.DuoAPIConfiguration{
|
||||||
|
Disable: false,
|
||||||
|
},
|
||||||
TOTP: schema.TOTPConfiguration{
|
TOTP: schema.TOTPConfiguration{
|
||||||
Disable: true,
|
Disable: true,
|
||||||
},
|
},
|
||||||
|
@ -86,7 +90,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveTOTPFromAvailableM
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvailableMethodsWhenDisabled() {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvailableMethodsWhenDisabled() {
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
DuoAPI: schema.DuoAPIConfiguration{
|
||||||
|
Disable: false,
|
||||||
|
},
|
||||||
TOTP: schema.TOTPConfiguration{
|
TOTP: schema.TOTPConfiguration{
|
||||||
Disable: false,
|
Disable: false,
|
||||||
},
|
},
|
||||||
|
@ -114,7 +120,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvaila
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveDuoFromAvailableMethodsWhenNotConfigured() {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveDuoFromAvailableMethodsWhenNotConfigured() {
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
DuoAPI: nil,
|
DuoAPI: schema.DuoAPIConfiguration{
|
||||||
|
Disable: true,
|
||||||
|
},
|
||||||
TOTP: schema.TOTPConfiguration{
|
TOTP: schema.TOTPConfiguration{
|
||||||
Disable: false,
|
Disable: false,
|
||||||
},
|
},
|
||||||
|
@ -142,7 +150,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveDuoFromAvailableMe
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenNoTwoFactorACLRulesConfigured() {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenNoTwoFactorACLRulesConfigured() {
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
DuoAPI: schema.DuoAPIConfiguration{
|
||||||
|
Disable: false,
|
||||||
|
},
|
||||||
TOTP: schema.TOTPConfiguration{
|
TOTP: schema.TOTPConfiguration{
|
||||||
Disable: false,
|
Disable: false,
|
||||||
},
|
},
|
||||||
|
@ -170,7 +180,9 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenNoTw
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenAllDisabledOrNotConfigured() {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenAllDisabledOrNotConfigured() {
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
DuoAPI: nil,
|
DuoAPI: schema.DuoAPIConfiguration{
|
||||||
|
Disable: true,
|
||||||
|
},
|
||||||
TOTP: schema.TOTPConfiguration{
|
TOTP: schema.TOTPConfiguration{
|
||||||
Disable: true,
|
Disable: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -80,7 +80,7 @@ func ResetPasswordPOST(ctx *middlewares.AutheliaCtx) {
|
||||||
bufHTML := new(bytes.Buffer)
|
bufHTML := new(bytes.Buffer)
|
||||||
|
|
||||||
disableHTML := false
|
disableHTML := false
|
||||||
if ctx.Configuration.Notifier != nil && ctx.Configuration.Notifier.SMTP != nil {
|
if ctx.Configuration.Notifier.SMTP != nil {
|
||||||
disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails
|
disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,8 +101,6 @@ func TestUserInfoEndpoint_SetCorrectMethod(t *testing.T) {
|
||||||
|
|
||||||
mock := mocks.NewMockAutheliaCtx(t)
|
mock := mocks.NewMockAutheliaCtx(t)
|
||||||
|
|
||||||
mock.Ctx.Configuration.DuoAPI = &schema.DuoAPIConfiguration{}
|
|
||||||
|
|
||||||
// Set the initial user session.
|
// Set the initial user session.
|
||||||
userSession := mock.Ctx.GetSession()
|
userSession := mock.Ctx.GetSession()
|
||||||
userSession.Username = testUsername
|
userSession.Username = testUsername
|
||||||
|
@ -172,9 +170,7 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) {
|
||||||
HasWebauthn: false,
|
HasWebauthn: false,
|
||||||
HasDuo: false,
|
HasDuo: false,
|
||||||
},
|
},
|
||||||
config: &schema.Configuration{
|
config: &schema.Configuration{},
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
|
||||||
},
|
|
||||||
loadErr: nil,
|
loadErr: nil,
|
||||||
saveErr: nil,
|
saveErr: nil,
|
||||||
},
|
},
|
||||||
|
@ -192,9 +188,7 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) {
|
||||||
HasWebauthn: false,
|
HasWebauthn: false,
|
||||||
HasDuo: true,
|
HasDuo: true,
|
||||||
},
|
},
|
||||||
config: &schema.Configuration{
|
config: &schema.Configuration{},
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
|
||||||
},
|
|
||||||
loadErr: nil,
|
loadErr: nil,
|
||||||
saveErr: nil,
|
saveErr: nil,
|
||||||
},
|
},
|
||||||
|
@ -212,6 +206,7 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) {
|
||||||
HasWebauthn: false,
|
HasWebauthn: false,
|
||||||
HasDuo: true,
|
HasDuo: true,
|
||||||
},
|
},
|
||||||
|
config: &schema.Configuration{DuoAPI: schema.DuoAPIConfiguration{Disable: true}},
|
||||||
loadErr: nil,
|
loadErr: nil,
|
||||||
saveErr: nil,
|
saveErr: nil,
|
||||||
},
|
},
|
||||||
|
@ -233,7 +228,6 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) {
|
||||||
TOTP: schema.TOTPConfiguration{
|
TOTP: schema.TOTPConfiguration{
|
||||||
Disable: true,
|
Disable: true,
|
||||||
},
|
},
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
|
||||||
},
|
},
|
||||||
loadErr: nil,
|
loadErr: nil,
|
||||||
saveErr: nil,
|
saveErr: nil,
|
||||||
|
@ -252,104 +246,104 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) {
|
||||||
HasWebauthn: true,
|
HasWebauthn: true,
|
||||||
HasDuo: true,
|
HasDuo: true,
|
||||||
},
|
},
|
||||||
config: &schema.Configuration{
|
config: &schema.Configuration{},
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
|
||||||
},
|
|
||||||
loadErr: nil,
|
loadErr: nil,
|
||||||
saveErr: errors.New("could not save"),
|
saveErr: errors.New("could not save"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, resp := range expectedResponses {
|
for _, resp := range expectedResponses {
|
||||||
if resp.api == nil {
|
t.Run(resp.description, func(t *testing.T) {
|
||||||
resp.api = &resp.db
|
if resp.api == nil {
|
||||||
}
|
resp.api = &resp.db
|
||||||
|
}
|
||||||
|
|
||||||
mock := mocks.NewMockAutheliaCtx(t)
|
mock := mocks.NewMockAutheliaCtx(t)
|
||||||
|
|
||||||
if resp.config != nil {
|
if resp.config != nil {
|
||||||
mock.Ctx.Configuration = *resp.config
|
mock.Ctx.Configuration = *resp.config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the initial user session.
|
// Set the initial user session.
|
||||||
userSession := mock.Ctx.GetSession()
|
userSession := mock.Ctx.GetSession()
|
||||||
userSession.Username = testUsername
|
userSession.Username = testUsername
|
||||||
userSession.AuthenticationLevel = 1
|
userSession.AuthenticationLevel = 1
|
||||||
err := mock.Ctx.SaveSession(userSession)
|
err := mock.Ctx.SaveSession(userSession)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if resp.db.Method == "" {
|
if resp.db.Method == "" {
|
||||||
gomock.InOrder(
|
gomock.InOrder(
|
||||||
mock.StorageMock.
|
mock.StorageMock.
|
||||||
EXPECT().
|
EXPECT().
|
||||||
LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")).
|
LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")).
|
||||||
Return("", sql.ErrNoRows),
|
Return("", sql.ErrNoRows),
|
||||||
mock.StorageMock.
|
mock.StorageMock.
|
||||||
EXPECT().
|
EXPECT().
|
||||||
SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq("")).
|
SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq("")).
|
||||||
Return(resp.saveErr),
|
Return(resp.saveErr),
|
||||||
mock.StorageMock.
|
mock.StorageMock.
|
||||||
EXPECT().
|
EXPECT().
|
||||||
LoadUserInfo(mock.Ctx, gomock.Eq("john")).
|
LoadUserInfo(mock.Ctx, gomock.Eq("john")).
|
||||||
Return(resp.db, nil),
|
Return(resp.db, nil),
|
||||||
mock.StorageMock.EXPECT().
|
mock.StorageMock.EXPECT().
|
||||||
SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)).
|
SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)).
|
||||||
Return(resp.saveErr),
|
Return(resp.saveErr),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
gomock.InOrder(
|
gomock.InOrder(
|
||||||
mock.StorageMock.
|
mock.StorageMock.
|
||||||
EXPECT().
|
EXPECT().
|
||||||
LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")).
|
LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")).
|
||||||
Return(resp.db.Method, nil),
|
Return(resp.db.Method, nil),
|
||||||
mock.StorageMock.
|
mock.StorageMock.
|
||||||
EXPECT().
|
EXPECT().
|
||||||
LoadUserInfo(mock.Ctx, gomock.Eq("john")).
|
LoadUserInfo(mock.Ctx, gomock.Eq("john")).
|
||||||
Return(resp.db, nil),
|
Return(resp.db, nil),
|
||||||
mock.StorageMock.EXPECT().
|
mock.StorageMock.EXPECT().
|
||||||
SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)).
|
SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)).
|
||||||
Return(resp.saveErr),
|
Return(resp.saveErr),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInfoPOST(mock.Ctx)
|
UserInfoPOST(mock.Ctx)
|
||||||
|
|
||||||
if resp.loadErr == nil && resp.saveErr == nil {
|
if resp.loadErr == nil && resp.saveErr == nil {
|
||||||
t.Run(fmt.Sprintf("%s/%s", resp.description, "expected status code"), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s/%s", resp.description, "expected status code"), func(t *testing.T) {
|
||||||
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
|
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) {
|
t.Run("expected method", func(t *testing.T) {
|
||||||
assert.Equal(t, resp.api.Method, actualPreferences.Method)
|
assert.Equal(t, resp.api.Method, actualPreferences.Method)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("%s/%s", resp.description, "registered webauthn"), func(t *testing.T) {
|
t.Run("registered webauthn", func(t *testing.T) {
|
||||||
assert.Equal(t, resp.api.HasWebauthn, actualPreferences.HasWebauthn)
|
assert.Equal(t, resp.api.HasWebauthn, actualPreferences.HasWebauthn)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("%s/%s", resp.description, "registered totp"), func(t *testing.T) {
|
t.Run("registered totp", func(t *testing.T) {
|
||||||
assert.Equal(t, resp.api.HasTOTP, actualPreferences.HasTOTP)
|
assert.Equal(t, resp.api.HasTOTP, actualPreferences.HasTOTP)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("%s/%s", resp.description, "registered duo"), func(t *testing.T) {
|
t.Run("registered duo", func(t *testing.T) {
|
||||||
assert.Equal(t, resp.api.HasDuo, actualPreferences.HasDuo)
|
assert.Equal(t, resp.api.HasDuo, actualPreferences.HasDuo)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
t.Run("expected status code", func(t *testing.T) {
|
t.Run("expected status code", func(t *testing.T) {
|
||||||
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
|
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, "KO", errResponse.Status)
|
||||||
assert.Equal(t, "Operation failed.", errResponse.Message)
|
assert.Equal(t, "Operation failed.", errResponse.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
mock.Close()
|
mock.Close()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +414,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenBadMethodProvided() {
|
||||||
MethodPreferencePOST(s.mock.Ctx)
|
MethodPreferencePOST(s.mock.Ctx)
|
||||||
|
|
||||||
s.mock.Assert200KO(s.T(), "Operation failed.")
|
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)
|
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (ctx *AutheliaCtx) AvailableSecondFactorMethods() (methods []string) {
|
||||||
methods = append(methods, model.SecondFactorMethodWebauthn)
|
methods = append(methods, model.SecondFactorMethodWebauthn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Configuration.DuoAPI != nil {
|
if !ctx.Configuration.DuoAPI.Disable {
|
||||||
methods = append(methods, model.SecondFactorMethodDuo)
|
methods = append(methods, model.SecondFactorMethodDuo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,9 +121,11 @@ func TestShouldReturnCorrectSecondFactorMethods(t *testing.T) {
|
||||||
mock := mocks.NewMockAutheliaCtx(t)
|
mock := mocks.NewMockAutheliaCtx(t)
|
||||||
defer mock.Close()
|
defer mock.Close()
|
||||||
|
|
||||||
|
mock.Ctx.Configuration.DuoAPI.Disable = true
|
||||||
|
|
||||||
assert.Equal(t, []string{model.SecondFactorMethodTOTP, model.SecondFactorMethodWebauthn}, mock.Ctx.AvailableSecondFactorMethods())
|
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())
|
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())
|
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())
|
assert.Equal(t, []string{}, mock.Ctx.AvailableSecondFactorMethods())
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs, delayFunc Tim
|
||||||
bufHTML := new(bytes.Buffer)
|
bufHTML := new(bytes.Buffer)
|
||||||
|
|
||||||
disableHTML := false
|
disableHTML := false
|
||||||
if ctx.Configuration.Notifier != nil && ctx.Configuration.Notifier.SMTP != nil {
|
if ctx.Configuration.Notifier.SMTP != nil {
|
||||||
disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails
|
disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ func getHandler(config schema.Configuration, providers middlewares.Providers) fa
|
||||||
resetPasswordCustomURL := config.AuthenticationBackend.PasswordReset.CustomURL.String()
|
resetPasswordCustomURL := config.AuthenticationBackend.PasswordReset.CustomURL.String()
|
||||||
|
|
||||||
duoSelfEnrollment := f
|
duoSelfEnrollment := f
|
||||||
if config.DuoAPI != nil {
|
if !config.DuoAPI.Disable {
|
||||||
duoSelfEnrollment = strconv.FormatBool(config.DuoAPI.EnableSelfEnrollment)
|
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.
|
// Configure DUO api endpoint only if configuration exists.
|
||||||
if config.DuoAPI != nil {
|
if !config.DuoAPI.Disable {
|
||||||
var duoAPI duo.API
|
var duoAPI duo.API
|
||||||
if os.Getenv("ENVIRONMENT") == dev {
|
if os.Getenv("ENVIRONMENT") == dev {
|
||||||
duoAPI = duo.NewDuoAPI(duoapi.NewDuoApi(
|
duoAPI = duo.NewDuoAPI(duoapi.NewDuoApi(
|
||||||
|
|
Loading…
Reference in New Issue