feat(web): password reset custom url (#3111)
This allows providing a custom URL for password resets. If provided the disable_reset_password option is ignored, the password reset API is disabled, and the button provided in the UI to reset the password redirects users to the configured endpoint. Closes #1934, Closes #2854 Co-authored-by: you1996 <youssri@flyweight.tech>pull/3112/head
parent
b8280dfed6
commit
a2eb0316c8
|
@ -191,6 +191,13 @@ authentication_backend:
|
|||
## Disable both the HTML element and the API for reset password functionality.
|
||||
disable_reset_password: false
|
||||
|
||||
## Password Reset Options.
|
||||
password_reset:
|
||||
|
||||
## External reset password url that redirects the user to an external reset portal. This disables the internal reset
|
||||
## functionality.
|
||||
custom_url: ""
|
||||
|
||||
## The amount of time to wait before we refresh data from the authentication backend. Uses duration notation.
|
||||
## To disable this feature set it to 'disable', this will slightly reduce security because for Authelia, users will
|
||||
## always belong to groups they belonged to at the time of login even if they have been removed from them in LDAP.
|
||||
|
|
|
@ -18,6 +18,8 @@ There are two ways to store the users along with their password:
|
|||
```yaml
|
||||
authentication_backend:
|
||||
disable_reset_password: false
|
||||
password_reset:
|
||||
custom_url: ""
|
||||
file: {}
|
||||
ldap: {}
|
||||
```
|
||||
|
@ -36,6 +38,21 @@ required: no
|
|||
|
||||
This setting controls if users can reset their password from the web frontend or not.
|
||||
|
||||
### password_reset
|
||||
|
||||
#### custom_url
|
||||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
default: ""
|
||||
{: .label .label-config .label-blue }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
</div>
|
||||
|
||||
The custom password reset URL. This replaces the inbuilt password reset functionality and disables the endpoints if
|
||||
this is configured to anything other than nothing or an empty string.
|
||||
|
||||
### file
|
||||
|
||||
The [file](file.md) authentication provider.
|
||||
|
|
|
@ -191,6 +191,13 @@ authentication_backend:
|
|||
## Disable both the HTML element and the API for reset password functionality.
|
||||
disable_reset_password: false
|
||||
|
||||
## Password Reset Options.
|
||||
password_reset:
|
||||
|
||||
## External reset password url that redirects the user to an external reset portal. This disables the internal reset
|
||||
## functionality.
|
||||
custom_url: ""
|
||||
|
||||
## The amount of time to wait before we refresh data from the authentication backend. Uses duration notation.
|
||||
## To disable this feature set it to 'disable', this will slightly reduce security because for Authelia, users will
|
||||
## always belong to groups they belonged to at the time of login even if they have been removed from them in LDAP.
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LDAPAuthenticationBackendConfiguration represents the configuration related to LDAP server.
|
||||
type LDAPAuthenticationBackendConfiguration struct {
|
||||
|
@ -45,10 +48,18 @@ type PasswordConfiguration struct {
|
|||
|
||||
// AuthenticationBackendConfiguration represents the configuration related to the authentication backend.
|
||||
type AuthenticationBackendConfiguration struct {
|
||||
DisableResetPassword bool `koanf:"disable_reset_password"`
|
||||
RefreshInterval string `koanf:"refresh_interval"`
|
||||
LDAP *LDAPAuthenticationBackendConfiguration `koanf:"ldap"`
|
||||
File *FileAuthenticationBackendConfiguration `koanf:"file"`
|
||||
LDAP *LDAPAuthenticationBackendConfiguration `koanf:"ldap"`
|
||||
File *FileAuthenticationBackendConfiguration `koanf:"file"`
|
||||
|
||||
PasswordReset PasswordResetAuthenticationBackendConfiguration `koanf:"password_reset"`
|
||||
|
||||
DisableResetPassword bool `koanf:"disable_reset_password"`
|
||||
RefreshInterval string `koanf:"refresh_interval"`
|
||||
}
|
||||
|
||||
// PasswordResetAuthenticationBackendConfiguration represents the configuration related to password reset functionality.
|
||||
type PasswordResetAuthenticationBackendConfiguration struct {
|
||||
CustomURL url.URL `koanf:"custom_url"`
|
||||
}
|
||||
|
||||
// DefaultPasswordConfiguration represents the default configuration related to Argon2id hashing.
|
||||
|
|
|
@ -32,8 +32,8 @@ func (suite *AccessControl) SetupTest() {
|
|||
func (suite *AccessControl) TestShouldValidateCompleteConfiguration() {
|
||||
ValidateAccessControl(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *AccessControl) TestShouldValidateEitherDomainsOrDomainsRegex() {
|
||||
|
@ -55,7 +55,7 @@ func (suite *AccessControl) TestShouldValidateEitherDomainsOrDomainsRegex() {
|
|||
|
||||
ValidateRules(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
assert.EqualError(suite.T(), suite.validator.Errors()[0], "access control: rule #3: rule is invalid: must have the option 'domain' or 'domain_regex' configured")
|
||||
|
@ -66,7 +66,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidDefaultPolicy() {
|
|||
|
||||
ValidateAccessControl(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: option 'default_policy' must be one of 'bypass', 'one_factor', 'two_factor', 'deny' but it is configured as 'invalid'")
|
||||
|
@ -82,7 +82,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidNetworkGroupNetwork() {
|
|||
|
||||
ValidateAccessControl(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: networks: network group 'internal' is invalid: the network 'abc.def.ghi.jkl' is not a valid IP or CIDR notation")
|
||||
|
@ -93,7 +93,7 @@ func (suite *AccessControl) TestShouldRaiseErrorWithNoRulesDefined() {
|
|||
|
||||
ValidateRules(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: 'default_policy' option 'deny' is invalid: when no rules are specified it must be 'two_factor' or 'one_factor'")
|
||||
|
@ -106,7 +106,7 @@ func (suite *AccessControl) TestShouldRaiseWarningWithNoRulesDefined() {
|
|||
|
||||
ValidateRules(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
suite.Require().Len(suite.validator.Warnings(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Warnings()[0], "access control: no rules have been specified so the 'default_policy' of 'two_factor' is going to be applied to all requests")
|
||||
|
@ -122,7 +122,7 @@ func (suite *AccessControl) TestShouldRaiseErrorsWithEmptyRules() {
|
|||
|
||||
ValidateRules(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 4)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1: rule is invalid: must have the option 'domain' or 'domain_regex' configured")
|
||||
|
@ -141,7 +141,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidPolicy() {
|
|||
|
||||
ValidateRules(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1 (domain 'public.example.com'): rule 'policy' option 'invalid' is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'")
|
||||
|
@ -158,7 +158,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidNetwork() {
|
|||
|
||||
ValidateRules(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1 (domain 'public.example.com'): the network 'abc.def.ghi.jkl/32' is not a valid Group Name, IP, or CIDR notation")
|
||||
|
@ -175,7 +175,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidMethod() {
|
|||
|
||||
ValidateRules(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1 (domain 'public.example.com'): 'methods' option 'HOP' is invalid: must be one of 'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS', 'COPY', 'LOCK', 'MKCOL', 'MOVE', 'PROPFIND', 'PROPPATCH', 'UNLOCK'")
|
||||
|
|
|
@ -33,6 +33,15 @@ func ValidateAuthenticationBackend(config *schema.AuthenticationBackendConfigura
|
|||
validator.Push(fmt.Errorf(errFmtAuthBackendRefreshInterval, config.RefreshInterval, err))
|
||||
}
|
||||
}
|
||||
|
||||
if config.PasswordReset.CustomURL.String() != "" {
|
||||
switch config.PasswordReset.CustomURL.Scheme {
|
||||
case schemeHTTP, schemeHTTPS:
|
||||
config.DisableResetPassword = false
|
||||
default:
|
||||
validator.Push(fmt.Errorf(errFmtAuthBackendPasswordResetCustomURLScheme, config.PasswordReset.CustomURL.String(), config.PasswordReset.CustomURL.Scheme))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validateFileAuthenticationBackend validates and updates the file authentication backend configuration.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package validator
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -58,8 +59,8 @@ func (suite *FileBasedAuthenticationBackend) SetupTest() {
|
|||
func (suite *FileBasedAuthenticationBackend) TestShouldValidateCompleteConfiguration() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenNoPathProvided() {
|
||||
|
@ -67,7 +68,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenNoPathProvi
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: option 'path' is required")
|
||||
|
@ -79,7 +80,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenMemoryNotMo
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: option 'memory' must at least be parallelism multiplied by 8 when using algorithm 'argon2id' with parallelism 2 it should be at least 16 but it is configured as '8'")
|
||||
|
@ -97,7 +98,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultConfigurationWh
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal(schema.DefaultPasswordConfiguration.KeyLength, suite.config.File.Password.KeyLength)
|
||||
|
@ -115,7 +116,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultConfigurationWh
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal(schema.DefaultPasswordSHA512Configuration.KeyLength, suite.config.File.Password.KeyLength)
|
||||
|
@ -130,7 +131,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenKeyLengthTo
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: option 'key_length' must be 16 or more when using algorithm 'argon2id' but it is configured as '1'")
|
||||
|
@ -141,7 +142,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenSaltLengthT
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: option 'salt_length' must be 2 or more but it is configured a '-1'")
|
||||
|
@ -152,7 +153,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenBadAlgorith
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: option 'algorithm' must be either 'argon2id' or 'sha512' but it is configured as 'bogus'")
|
||||
|
@ -163,7 +164,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenIterationsT
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: option 'iterations' must be 1 or more but it is configured as '-1'")
|
||||
|
@ -174,7 +175,7 @@ func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenParallelism
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: file: password: option 'parallelism' must be 1 or more when using algorithm 'argon2id' but it is configured as '-1'")
|
||||
|
@ -189,8 +190,8 @@ func (suite *FileBasedAuthenticationBackend) TestShouldSetDefaultValues() {
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal(schema.DefaultPasswordConfiguration.Algorithm, suite.config.File.Password.Algorithm)
|
||||
suite.Assert().Equal(schema.DefaultPasswordConfiguration.Iterations, suite.config.File.Password.Iterations)
|
||||
|
@ -226,8 +227,47 @@ func (suite *LDAPAuthenticationBackendSuite) SetupTest() {
|
|||
func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateCompleteConfiguration() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *FileBasedAuthenticationBackend) TestShouldRaiseErrorWhenResetURLIsInvalid() {
|
||||
suite.config.PasswordReset.CustomURL = url.URL{Scheme: "ldap", Host: "google.com"}
|
||||
suite.config.DisableResetPassword = true
|
||||
|
||||
suite.Assert().True(suite.config.DisableResetPassword)
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: password_reset: option 'custom_url' is configured to 'ldap://google.com' which has the scheme 'ldap' but the scheme must be either 'http' or 'https'")
|
||||
|
||||
suite.Assert().True(suite.config.DisableResetPassword)
|
||||
}
|
||||
|
||||
func (suite *FileBasedAuthenticationBackend) TestShouldNotRaiseErrorWhenResetURLIsValid() {
|
||||
suite.config.PasswordReset.CustomURL = url.URL{Scheme: "https", Host: "google.com"}
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *FileBasedAuthenticationBackend) TestShouldConfigureDisableResetPasswordWhenCustomURL() {
|
||||
suite.config.PasswordReset.CustomURL = url.URL{Scheme: "https", Host: "google.com"}
|
||||
suite.config.DisableResetPassword = true
|
||||
|
||||
suite.Assert().True(suite.config.DisableResetPassword)
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().False(suite.config.DisableResetPassword)
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateDefaultImplementationAndUsernameAttribute() {
|
||||
|
@ -238,8 +278,8 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldValidateDefaultImplementa
|
|||
suite.Assert().Equal(schema.LDAPImplementationCustom, suite.config.LDAP.Implementation)
|
||||
|
||||
suite.Assert().Equal(suite.config.LDAP.UsernameAttribute, schema.DefaultLDAPAuthenticationBackendConfiguration.UsernameAttribute)
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementationIsInvalidMSAD() {
|
||||
|
@ -247,7 +287,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementat
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'implementation' is configured as 'masd' but must be one of the following values: 'custom', 'activedirectory'")
|
||||
|
@ -257,7 +297,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvi
|
|||
suite.config.LDAP.URL = ""
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'url' is required")
|
||||
|
@ -268,7 +308,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenUserNotProv
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'user' is required")
|
||||
|
@ -279,7 +319,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordNot
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'password' is required")
|
||||
|
@ -290,7 +330,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenBaseDNNotPr
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'base_dn' is required")
|
||||
|
@ -301,7 +341,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnEmptyGroupsFilter(
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'groups_filter' is required")
|
||||
|
@ -312,7 +352,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnEmptyUsersFilter()
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'users_filter' is required")
|
||||
|
@ -323,8 +363,8 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldNotRaiseOnEmptyUsernameAt
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnBadRefreshInterval() {
|
||||
|
@ -332,7 +372,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnBadRefreshInterval
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: option 'refresh_interval' is configured to 'blah' but it must be either a duration notation or one of 'disable', or 'always': could not parse 'blah' as a duration")
|
||||
|
@ -341,8 +381,8 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseOnBadRefreshInterval
|
|||
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultImplementation() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal(schema.LDAPImplementationCustom, suite.config.LDAP.Implementation)
|
||||
}
|
||||
|
@ -353,7 +393,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorOnBadFilterPlac
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().True(suite.validator.HasErrors())
|
||||
|
||||
suite.Require().Len(suite.validator.Errors(), 4)
|
||||
|
@ -366,8 +406,8 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorOnBadFilterPlac
|
|||
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultGroupNameAttribute() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal("cn", suite.config.LDAP.GroupNameAttribute)
|
||||
}
|
||||
|
@ -375,8 +415,8 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultGroupNameAttrib
|
|||
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultMailAttribute() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal("mail", suite.config.LDAP.MailAttribute)
|
||||
}
|
||||
|
@ -384,8 +424,8 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultMailAttribute()
|
|||
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultDisplayNameAttribute() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal("displayName", suite.config.LDAP.DisplayNameAttribute)
|
||||
}
|
||||
|
@ -393,8 +433,8 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultDisplayNameAttr
|
|||
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultRefreshInterval() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal("5m", suite.config.RefreshInterval)
|
||||
}
|
||||
|
@ -404,7 +444,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesN
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'users_filter' must contain enclosing parenthesis: '{username_attribute}={input}' should probably be '({username_attribute}={input})'")
|
||||
|
@ -415,7 +455,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenGroupsFilterDoes
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'groups_filter' must contain enclosing parenthesis: 'cn={input}' should probably be '(cn={input})'")
|
||||
|
@ -425,7 +465,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseWhenUsersFilterDoesN
|
|||
suite.config.LDAP.UsersFilter = "(&({mail_attribute}={input})(objectClass=person))"
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'users_filter' must contain the placeholder '{username_attribute}' but it is required")
|
||||
|
@ -436,7 +476,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlacehol
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'users_filter' must contain the placeholder '{input}' but it is required")
|
||||
|
@ -447,8 +487,8 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultTLSMinimumVersi
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal(schema.DefaultLDAPAuthenticationBackendConfiguration.TLS.MinimumVersion, suite.config.LDAP.TLS.MinimumVersion)
|
||||
}
|
||||
|
@ -460,7 +500,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowInvalidTLSValue()
|
|||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: tls: option 'minimum_tls_version' is invalid: SSL2.0: supplied tls version isn't supported")
|
||||
|
@ -491,8 +531,8 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) SetupTest() {
|
|||
func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirectoryDefaults() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfiguration.Timeout,
|
||||
|
|
|
@ -69,6 +69,8 @@ const (
|
|||
"backend is configured"
|
||||
errFmtAuthBackendRefreshInterval = "authentication_backend: option 'refresh_interval' is configured to '%s' but " +
|
||||
"it must be either a duration notation or one of 'disable', or 'always': %w"
|
||||
errFmtAuthBackendPasswordResetCustomURLScheme = "authentication_backend: password_reset: option 'custom_url' is" +
|
||||
" configured to '%s' which has the scheme '%s' but the scheme must be either 'http' or 'https'"
|
||||
|
||||
errFmtFileAuthBackendPathNotConfigured = "authentication_backend: file: option 'path' is required"
|
||||
errFmtFileAuthBackendPasswordSaltLength = "authentication_backend: file: password: option 'salt_length' " +
|
||||
|
@ -424,6 +426,7 @@ var ValidKeys = []string{
|
|||
|
||||
// Authentication Backend Keys.
|
||||
"authentication_backend.disable_reset_password",
|
||||
"authentication_backend.password_reset.custom_url",
|
||||
"authentication_backend.refresh_interval",
|
||||
|
||||
// LDAP Authentication Backend Keys.
|
||||
|
|
|
@ -34,14 +34,14 @@ func (suite *NotifierSuite) SetupTest() {
|
|||
func (suite *NotifierSuite) TestShouldEnsureAtLeastSMTPOrFilesystemIsProvided() {
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.config.SMTP = nil
|
||||
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().True(suite.validator.HasErrors())
|
||||
|
||||
suite.Assert().Len(suite.validator.Errors(), 1)
|
||||
|
@ -52,7 +52,7 @@ func (suite *NotifierSuite) TestShouldEnsureAtLeastSMTPOrFilesystemIsProvided()
|
|||
func (suite *NotifierSuite) TestShouldEnsureEitherSMTPOrFilesystemIsProvided() {
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.config.FileSystem = &schema.FileSystemNotifierConfiguration{
|
||||
Filename: "test",
|
||||
|
@ -60,7 +60,7 @@ func (suite *NotifierSuite) TestShouldEnsureEitherSMTPOrFilesystemIsProvided() {
|
|||
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().True(suite.validator.HasErrors())
|
||||
|
||||
suite.Assert().Len(suite.validator.Errors(), 1)
|
||||
|
@ -74,8 +74,8 @@ func (suite *NotifierSuite) TestShouldEnsureEitherSMTPOrFilesystemIsProvided() {
|
|||
func (suite *NotifierSuite) TestSMTPShouldSetTLSDefaults() {
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal("example.com", suite.config.SMTP.TLS.ServerName)
|
||||
suite.Assert().Equal("TLS1.2", suite.config.SMTP.TLS.MinimumVersion)
|
||||
|
@ -90,8 +90,8 @@ func (suite *NotifierSuite) TestSMTPShouldDefaultTLSServerNameToHost() {
|
|||
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal("google.com", suite.config.SMTP.TLS.ServerName)
|
||||
suite.Assert().Equal("TLS1.1", suite.config.SMTP.TLS.MinimumVersion)
|
||||
|
@ -102,15 +102,15 @@ func (suite *NotifierSuite) TestSMTPShouldEnsureHostAndPortAreProvided() {
|
|||
suite.config.FileSystem = nil
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.config.SMTP.Host = ""
|
||||
suite.config.SMTP.Port = 0
|
||||
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().True(suite.validator.HasErrors())
|
||||
|
||||
errors := suite.validator.Errors()
|
||||
|
@ -126,7 +126,7 @@ func (suite *NotifierSuite) TestSMTPShouldEnsureSenderIsProvided() {
|
|||
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().True(suite.validator.HasErrors())
|
||||
|
||||
suite.Assert().Len(suite.validator.Errors(), 1)
|
||||
|
@ -144,14 +144,14 @@ func (suite *NotifierSuite) TestFileShouldEnsureFilenameIsProvided() {
|
|||
}
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.config.FileSystem.Filename = ""
|
||||
|
||||
ValidateNotifier(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().True(suite.validator.HasErrors())
|
||||
|
||||
suite.Assert().Len(suite.validator.Errors(), 1)
|
||||
|
|
|
@ -24,8 +24,8 @@ func (suite *Theme) SetupTest() {
|
|||
func (suite *Theme) TestShouldValidateCompleteConfiguration() {
|
||||
ValidateTheme(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *Theme) TestShouldRaiseErrorWhenInvalidThemeProvided() {
|
||||
|
@ -33,7 +33,7 @@ func (suite *Theme) TestShouldRaiseErrorWhenInvalidThemeProvided() {
|
|||
|
||||
ValidateTheme(suite.config, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "option 'theme' must be one of 'light', 'dark', 'grey', 'auto' but it is configured as 'invalid'")
|
||||
|
|
|
@ -24,6 +24,8 @@ func registerRoutes(configuration schema.Configuration, providers middlewares.Pr
|
|||
rememberMe := strconv.FormatBool(configuration.Session.RememberMeDuration != schema.RememberMeDisabled)
|
||||
resetPassword := strconv.FormatBool(!configuration.AuthenticationBackend.DisableResetPassword)
|
||||
|
||||
resetPasswordCustomURL := configuration.AuthenticationBackend.PasswordReset.CustomURL.String()
|
||||
|
||||
duoSelfEnrollment := f
|
||||
if configuration.DuoAPI != nil {
|
||||
duoSelfEnrollment = strconv.FormatBool(configuration.DuoAPI.EnableSelfEnrollment)
|
||||
|
@ -34,9 +36,9 @@ func registerRoutes(configuration schema.Configuration, providers middlewares.Pr
|
|||
|
||||
https := configuration.Server.TLS.Key != "" && configuration.Server.TLS.Certificate != ""
|
||||
|
||||
serveIndexHandler := ServeTemplatedFile(embeddedAssets, indexFile, configuration.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, configuration.Session.Name, configuration.Theme, https)
|
||||
serveSwaggerHandler := ServeTemplatedFile(swaggerAssets, indexFile, configuration.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, configuration.Session.Name, configuration.Theme, https)
|
||||
serveSwaggerAPIHandler := ServeTemplatedFile(swaggerAssets, apiFile, configuration.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, configuration.Session.Name, configuration.Theme, https)
|
||||
serveIndexHandler := ServeTemplatedFile(embeddedAssets, indexFile, configuration.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, configuration.Session.Name, configuration.Theme, https)
|
||||
serveSwaggerHandler := ServeTemplatedFile(swaggerAssets, indexFile, configuration.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, configuration.Session.Name, configuration.Theme, https)
|
||||
serveSwaggerAPIHandler := ServeTemplatedFile(swaggerAssets, apiFile, configuration.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, configuration.Session.Name, configuration.Theme, https)
|
||||
|
||||
r := router.New()
|
||||
r.GET("/", autheliaMiddleware(serveIndexHandler))
|
||||
|
@ -77,7 +79,8 @@ func registerRoutes(configuration schema.Configuration, providers middlewares.Pr
|
|||
r.POST("/api/logout", autheliaMiddleware(handlers.LogoutPost))
|
||||
|
||||
// Only register endpoints if forgot password is not disabled.
|
||||
if !configuration.AuthenticationBackend.DisableResetPassword {
|
||||
if !configuration.AuthenticationBackend.DisableResetPassword &&
|
||||
configuration.AuthenticationBackend.PasswordReset.CustomURL.String() == "" {
|
||||
// Password reset related endpoints.
|
||||
r.POST("/api/reset-password/identity/start", autheliaMiddleware(
|
||||
handlers.ResetPasswordIdentityStart))
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
// ServeTemplatedFile serves a templated version of a specified file,
|
||||
// this is utilised to pass information between the backend and frontend
|
||||
// and generate a nonce to support a restrictive CSP while using material-ui.
|
||||
func ServeTemplatedFile(publicDir, file, assetPath, duoSelfEnrollment, rememberMe, resetPassword, session, theme string, https bool) middlewares.RequestHandler {
|
||||
func ServeTemplatedFile(publicDir, file, assetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, session, theme string, https bool) middlewares.RequestHandler {
|
||||
logger := logging.Logger()
|
||||
|
||||
a, err := assets.Open(publicDir + file)
|
||||
|
@ -81,7 +81,7 @@ func ServeTemplatedFile(publicDir, file, assetPath, duoSelfEnrollment, rememberM
|
|||
ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf(cspDefaultTemplate, nonce))
|
||||
}
|
||||
|
||||
err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ Base, BaseURL, CSPNonce, DuoSelfEnrollment, LogoOverride, RememberMe, ResetPassword, Session, Theme string }{Base: base, BaseURL: baseURL, CSPNonce: nonce, DuoSelfEnrollment: duoSelfEnrollment, LogoOverride: logoOverride, RememberMe: rememberMe, ResetPassword: resetPassword, Session: session, Theme: theme})
|
||||
err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ Base, BaseURL, CSPNonce, DuoSelfEnrollment, LogoOverride, RememberMe, ResetPassword, ResetPasswordCustomURL, Session, Theme string }{Base: base, BaseURL: baseURL, CSPNonce: nonce, DuoSelfEnrollment: duoSelfEnrollment, LogoOverride: logoOverride, RememberMe: rememberMe, ResetPassword: resetPassword, ResetPasswordCustomURL: resetPasswordCustomURL, Session: session, Theme: theme})
|
||||
if err != nil {
|
||||
ctx.RequestCtx.Error("an error occurred", 503)
|
||||
logger.Errorf("Unable to execute template: %v", err)
|
||||
|
|
|
@ -4,4 +4,5 @@ VITE_PUBLIC_URL=""
|
|||
VITE_DUO_SELF_ENROLLMENT=true
|
||||
VITE_REMEMBER_ME=true
|
||||
VITE_RESET_PASSWORD=true
|
||||
VITE_RESET_PASSWORD_CUSTOM_URL=""
|
||||
VITE_THEME=light
|
|
@ -3,4 +3,5 @@ VITE_PUBLIC_URL={{.Base}}
|
|||
VITE_DUO_SELF_ENROLLMENT={{.DuoSelfEnrollment}}
|
||||
VITE_REMEMBER_ME={{.RememberMe}}
|
||||
VITE_RESET_PASSWORD={{.ResetPassword}}
|
||||
VITE_RESET_PASSWORD_CUSTOM_URL={{.ResetPasswordCustomURL}}
|
||||
VITE_THEME={{.Theme}}
|
|
@ -1,8 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<base href="{{.BaseURL}}">
|
||||
<base href="{{.BaseURL}}" />
|
||||
<meta property="csp-nonce" content="{{.CSPNonce}}" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
@ -13,10 +12,17 @@
|
|||
<title>Login - Authelia</title>
|
||||
</head>
|
||||
|
||||
<body data-basepath="%VITE_PUBLIC_URL%" data-duoselfenrollment="%VITE_DUO_SELF_ENROLLMENT%" data-logooverride="%VITE_LOGO_OVERRIDE%" data-rememberme="%VITE_REMEMBER_ME%" data-resetpassword="%VITE_RESET_PASSWORD%" data-theme="%VITE_THEME%">
|
||||
<body
|
||||
data-basepath="%VITE_PUBLIC_URL%"
|
||||
data-duoselfenrollment="%VITE_DUO_SELF_ENROLLMENT%"
|
||||
data-logooverride="%VITE_LOGO_OVERRIDE%"
|
||||
data-rememberme="%VITE_REMEMBER_ME%"
|
||||
data-resetpassword="%VITE_RESET_PASSWORD%"
|
||||
data-resetpasswordcustomurl="%VITE_RESET_PASSWORD_CUSTOM_URL%"
|
||||
data-theme="%VITE_THEME%"
|
||||
>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -18,7 +18,13 @@ import NotificationsContext from "@hooks/NotificationsContext";
|
|||
import { Notification } from "@models/Notifications";
|
||||
import * as themes from "@themes/index";
|
||||
import { getBasePath } from "@utils/BasePath";
|
||||
import { getDuoSelfEnrollment, getRememberMe, getResetPassword, getTheme } from "@utils/Configuration";
|
||||
import {
|
||||
getDuoSelfEnrollment,
|
||||
getRememberMe,
|
||||
getResetPassword,
|
||||
getResetPasswordCustomURL,
|
||||
getTheme,
|
||||
} from "@utils/Configuration";
|
||||
import RegisterOneTimePassword from "@views/DeviceRegistration/RegisterOneTimePassword";
|
||||
import RegisterWebauthn from "@views/DeviceRegistration/RegisterWebauthn";
|
||||
import BaseLoadingPage from "@views/LoadingPage/BaseLoadingPage";
|
||||
|
@ -80,6 +86,7 @@ const App: React.FC = () => {
|
|||
duoSelfEnrollment={getDuoSelfEnrollment()}
|
||||
rememberMe={getRememberMe()}
|
||||
resetPassword={getResetPassword()}
|
||||
resetPasswordCustomURL={getResetPasswordCustomURL()}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -4,4 +4,5 @@ document.body.setAttribute("data-basepath", "");
|
|||
document.body.setAttribute("data-duoselfenrollment", "true");
|
||||
document.body.setAttribute("data-rememberme", "true");
|
||||
document.body.setAttribute("data-resetpassword", "true");
|
||||
document.body.setAttribute("data-resetpasswordcustomurl", "");
|
||||
document.body.setAttribute("data-theme", "light");
|
||||
|
|
|
@ -23,6 +23,10 @@ export function getResetPassword() {
|
|||
return getEmbeddedVariable("resetpassword") === "true";
|
||||
}
|
||||
|
||||
export function getResetPasswordCustomURL() {
|
||||
return getEmbeddedVariable("resetpasswordcustomurl");
|
||||
}
|
||||
|
||||
export function getTheme() {
|
||||
return getEmbeddedVariable("theme");
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@ import { postFirstFactor } from "@services/FirstFactor";
|
|||
export interface Props {
|
||||
disabled: boolean;
|
||||
rememberMe: boolean;
|
||||
|
||||
resetPassword: boolean;
|
||||
resetPasswordCustomURL: string;
|
||||
|
||||
onAuthenticationStart: () => void;
|
||||
onAuthenticationFailure: () => void;
|
||||
|
@ -76,7 +78,13 @@ const FirstFactorForm = function (props: Props) {
|
|||
};
|
||||
|
||||
const handleResetPasswordClick = () => {
|
||||
navigate(ResetPasswordStep1Route);
|
||||
if (props.resetPassword) {
|
||||
if (props.resetPasswordCustomURL !== "") {
|
||||
window.open(props.resetPasswordCustomURL);
|
||||
} else {
|
||||
navigate(ResetPasswordStep1Route);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -28,7 +28,9 @@ import SecondFactorForm from "@views/LoginPortal/SecondFactor/SecondFactorForm";
|
|||
export interface Props {
|
||||
duoSelfEnrollment: boolean;
|
||||
rememberMe: boolean;
|
||||
|
||||
resetPassword: boolean;
|
||||
resetPasswordCustomURL: string;
|
||||
}
|
||||
|
||||
const RedirectionErrorMessage =
|
||||
|
@ -175,6 +177,7 @@ const LoginPortal = function (props: Props) {
|
|||
disabled={firstFactorDisabled}
|
||||
rememberMe={props.rememberMe}
|
||||
resetPassword={props.resetPassword}
|
||||
resetPasswordCustomURL={props.resetPasswordCustomURL}
|
||||
onAuthenticationStart={() => setFirstFactorDisabled(true)}
|
||||
onAuthenticationFailure={() => setFirstFactorDisabled(false)}
|
||||
onAuthenticationSuccess={handleAuthSuccess}
|
||||
|
|
Loading…
Reference in New Issue