[MISC] Template global config and refactor some /api endpoints (#1135)
* [MISC] Template global config and refactor some /api endpoints * /api/configuration has been removed in favour of templating said global config * /api/configuration/extended has been renamed to /api/configuration and display_name has been removed * /api/user/info has been modified to include display_name Co-authored-by: Clement Michaud <clement.michaud34@gmail.com>pull/1148/head
parent
ddfce52939
commit
29e54c231b
|
@ -1,18 +1,30 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import "github.com/authelia/authelia/internal/middlewares"
|
import (
|
||||||
|
"github.com/authelia/authelia/internal/authentication"
|
||||||
|
"github.com/authelia/authelia/internal/middlewares"
|
||||||
|
)
|
||||||
|
|
||||||
// ConfigurationBody configuration parameters exposed to the frontend.
|
// ConfigurationBody the content returned by the configuration endpoint.
|
||||||
type ConfigurationBody struct {
|
type ConfigurationBody struct {
|
||||||
RememberMe bool `json:"remember_me"` // whether remember me is enabled or not
|
AvailableMethods MethodList `json:"available_methods"`
|
||||||
ResetPassword bool `json:"reset_password"`
|
SecondFactorEnabled bool `json:"second_factor_enabled"` // whether second factor is enabled or not.
|
||||||
|
TOTPPeriod int `json:"totp_period"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigurationGet fetches configuration parameters for frontend mutation.
|
// ConfigurationGet get the configuration accessible to authenticated users.
|
||||||
func ConfigurationGet(ctx *middlewares.AutheliaCtx) {
|
func ConfigurationGet(ctx *middlewares.AutheliaCtx) {
|
||||||
body := ConfigurationBody{
|
body := ConfigurationBody{}
|
||||||
RememberMe: ctx.Providers.SessionProvider.RememberMe != 0,
|
body.AvailableMethods = MethodList{authentication.TOTP, authentication.U2F}
|
||||||
ResetPassword: !ctx.Configuration.AuthenticationBackend.DisableResetPassword,
|
body.TOTPPeriod = ctx.Configuration.TOTP.Period
|
||||||
|
|
||||||
|
if ctx.Configuration.DuoAPI != nil {
|
||||||
|
body.AvailableMethods = append(body.AvailableMethods, authentication.Push)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.SecondFactorEnabled = ctx.Providers.Authorizer.IsSecondFactorEnabled()
|
||||||
|
ctx.Logger.Tracef("Second factor enabled: %v", body.SecondFactorEnabled)
|
||||||
|
|
||||||
|
ctx.Logger.Tracef("Available methods are %s", body.AvailableMethods)
|
||||||
ctx.SetJSONBody(body) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
ctx.SetJSONBody(body) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,49 +5,155 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/internal/authorization"
|
||||||
|
"github.com/authelia/authelia/internal/configuration/schema"
|
||||||
"github.com/authelia/authelia/internal/mocks"
|
"github.com/authelia/authelia/internal/mocks"
|
||||||
"github.com/authelia/authelia/internal/session"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigurationSuite struct {
|
type SecondFactorAvailableMethodsFixture struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
mock *mocks.MockAutheliaCtx
|
mock *mocks.MockAutheliaCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConfigurationSuite) SetupTest() {
|
func (s *SecondFactorAvailableMethodsFixture) SetupTest() {
|
||||||
s.mock = mocks.NewMockAutheliaCtx(s.T())
|
s.mock = mocks.NewMockAutheliaCtx(s.T())
|
||||||
|
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(schema.AccessControlConfiguration{
|
||||||
|
DefaultPolicy: "deny",
|
||||||
|
Rules: []schema.ACLRule{},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConfigurationSuite) TearDownTest() {
|
func (s *SecondFactorAvailableMethodsFixture) TearDownTest() {
|
||||||
s.mock.Close()
|
s.mock.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConfigurationSuite) TestShouldDisableRememberMe() {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldServeDefaultMethods() {
|
||||||
s.mock.Ctx.Configuration.Session.RememberMeDuration = "0"
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
s.mock.Ctx.Providers.SessionProvider = session.NewProvider(
|
TOTP: &schema.TOTPConfiguration{
|
||||||
s.mock.Ctx.Configuration.Session)
|
Period: schema.DefaultTOTPConfiguration.Period,
|
||||||
|
},
|
||||||
|
}
|
||||||
expectedBody := ConfigurationBody{
|
expectedBody := ConfigurationBody{
|
||||||
RememberMe: false,
|
AvailableMethods: []string{"totp", "u2f"},
|
||||||
ResetPassword: true,
|
SecondFactorEnabled: false,
|
||||||
|
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigurationGet(s.mock.Ctx)
|
ConfigurationGet(s.mock.Ctx)
|
||||||
s.mock.Assert200OK(s.T(), expectedBody)
|
s.mock.Assert200OK(s.T(), expectedBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConfigurationSuite) TestShouldDisableResetPassword() {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldServeDefaultMethodsAndMobilePush() {
|
||||||
s.mock.Ctx.Configuration.AuthenticationBackend.DisableResetPassword = true
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
|
DuoAPI: &schema.DuoAPIConfiguration{},
|
||||||
|
TOTP: &schema.TOTPConfiguration{
|
||||||
|
Period: schema.DefaultTOTPConfiguration.Period,
|
||||||
|
},
|
||||||
|
}
|
||||||
expectedBody := ConfigurationBody{
|
expectedBody := ConfigurationBody{
|
||||||
RememberMe: true,
|
AvailableMethods: []string{"totp", "u2f", "mobile_push"},
|
||||||
ResetPassword: false,
|
SecondFactorEnabled: false,
|
||||||
|
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigurationGet(s.mock.Ctx)
|
ConfigurationGet(s.mock.Ctx)
|
||||||
s.mock.Assert200OK(s.T(), expectedBody)
|
s.mock.Assert200OK(s.T(), expectedBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunHandlerConfigurationSuite(t *testing.T) {
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldCheckSecondFactorIsDisabledWhenNoRuleIsSetToTwoFactor() {
|
||||||
s := new(ConfigurationSuite)
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
|
TOTP: &schema.TOTPConfiguration{
|
||||||
|
Period: schema.DefaultTOTPConfiguration.Period,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(schema.AccessControlConfiguration{
|
||||||
|
DefaultPolicy: "bypass",
|
||||||
|
Rules: []schema.ACLRule{
|
||||||
|
{
|
||||||
|
Domains: []string{"example.com"},
|
||||||
|
Policy: "deny",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"abc.example.com"},
|
||||||
|
Policy: "single_factor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"def.example.com"},
|
||||||
|
Policy: "bypass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ConfigurationGet(s.mock.Ctx)
|
||||||
|
s.mock.Assert200OK(s.T(), ConfigurationBody{
|
||||||
|
AvailableMethods: []string{"totp", "u2f"},
|
||||||
|
SecondFactorEnabled: false,
|
||||||
|
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldCheckSecondFactorIsEnabledWhenDefaultPolicySetToTwoFactor() {
|
||||||
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
|
TOTP: &schema.TOTPConfiguration{
|
||||||
|
Period: schema.DefaultTOTPConfiguration.Period,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(schema.AccessControlConfiguration{
|
||||||
|
DefaultPolicy: "two_factor",
|
||||||
|
Rules: []schema.ACLRule{
|
||||||
|
{
|
||||||
|
Domains: []string{"example.com"},
|
||||||
|
Policy: "deny",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"abc.example.com"},
|
||||||
|
Policy: "single_factor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"def.example.com"},
|
||||||
|
Policy: "bypass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ConfigurationGet(s.mock.Ctx)
|
||||||
|
s.mock.Assert200OK(s.T(), ConfigurationBody{
|
||||||
|
AvailableMethods: []string{"totp", "u2f"},
|
||||||
|
SecondFactorEnabled: true,
|
||||||
|
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SecondFactorAvailableMethodsFixture) TestShouldCheckSecondFactorIsEnabledWhenSomePolicySetToTwoFactor() {
|
||||||
|
s.mock.Ctx.Configuration = schema.Configuration{
|
||||||
|
TOTP: &schema.TOTPConfiguration{
|
||||||
|
Period: schema.DefaultTOTPConfiguration.Period,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(schema.AccessControlConfiguration{
|
||||||
|
DefaultPolicy: "bypass",
|
||||||
|
Rules: []schema.ACLRule{
|
||||||
|
{
|
||||||
|
Domains: []string{"example.com"},
|
||||||
|
Policy: "deny",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"abc.example.com"},
|
||||||
|
Policy: "two_factor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"def.example.com"},
|
||||||
|
Policy: "bypass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ConfigurationGet(s.mock.Ctx)
|
||||||
|
s.mock.Assert200OK(s.T(), ConfigurationBody{
|
||||||
|
AvailableMethods: []string{"totp", "u2f"},
|
||||||
|
SecondFactorEnabled: true,
|
||||||
|
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunSuite(t *testing.T) {
|
||||||
|
s := new(SecondFactorAvailableMethodsFixture)
|
||||||
suite.Run(t, s)
|
suite.Run(t, s)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/authelia/authelia/internal/authentication"
|
|
||||||
"github.com/authelia/authelia/internal/middlewares"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExtendedConfigurationBody the content returned by extended configuration endpoint.
|
|
||||||
type ExtendedConfigurationBody struct {
|
|
||||||
AvailableMethods MethodList `json:"available_methods"`
|
|
||||||
DisplayName string `json:"display_name"`
|
|
||||||
SecondFactorEnabled bool `json:"second_factor_enabled"` // whether second factor is enabled or not.
|
|
||||||
TOTPPeriod int `json:"totp_period"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtendedConfigurationGet get the extended configuration accessible to authenticated users.
|
|
||||||
func ExtendedConfigurationGet(ctx *middlewares.AutheliaCtx) {
|
|
||||||
body := ExtendedConfigurationBody{}
|
|
||||||
body.AvailableMethods = MethodList{authentication.TOTP, authentication.U2F}
|
|
||||||
body.DisplayName = ctx.GetSession().DisplayName
|
|
||||||
body.TOTPPeriod = ctx.Configuration.TOTP.Period
|
|
||||||
|
|
||||||
if ctx.Configuration.DuoAPI != nil {
|
|
||||||
body.AvailableMethods = append(body.AvailableMethods, authentication.Push)
|
|
||||||
}
|
|
||||||
|
|
||||||
body.SecondFactorEnabled = ctx.Providers.Authorizer.IsSecondFactorEnabled()
|
|
||||||
ctx.Logger.Tracef("Second factor enabled: %v", body.SecondFactorEnabled)
|
|
||||||
|
|
||||||
ctx.Logger.Tracef("Available methods are %s", body.AvailableMethods)
|
|
||||||
ctx.SetJSONBody(body) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
|
||||||
}
|
|
|
@ -1,159 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
|
|
||||||
"github.com/authelia/authelia/internal/authorization"
|
|
||||||
"github.com/authelia/authelia/internal/configuration/schema"
|
|
||||||
"github.com/authelia/authelia/internal/mocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SecondFactorAvailableMethodsFixture struct {
|
|
||||||
suite.Suite
|
|
||||||
mock *mocks.MockAutheliaCtx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) SetupTest() {
|
|
||||||
s.mock = mocks.NewMockAutheliaCtx(s.T())
|
|
||||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(schema.AccessControlConfiguration{
|
|
||||||
DefaultPolicy: "deny",
|
|
||||||
Rules: []schema.ACLRule{},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TearDownTest() {
|
|
||||||
s.mock.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldServeDefaultMethods() {
|
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
|
||||||
TOTP: &schema.TOTPConfiguration{
|
|
||||||
Period: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
expectedBody := ExtendedConfigurationBody{
|
|
||||||
AvailableMethods: []string{"totp", "u2f"},
|
|
||||||
SecondFactorEnabled: false,
|
|
||||||
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtendedConfigurationGet(s.mock.Ctx)
|
|
||||||
s.mock.Assert200OK(s.T(), expectedBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldServeDefaultMethodsAndMobilePush() {
|
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
|
||||||
DuoAPI: &schema.DuoAPIConfiguration{},
|
|
||||||
TOTP: &schema.TOTPConfiguration{
|
|
||||||
Period: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
expectedBody := ExtendedConfigurationBody{
|
|
||||||
AvailableMethods: []string{"totp", "u2f", "mobile_push"},
|
|
||||||
SecondFactorEnabled: false,
|
|
||||||
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtendedConfigurationGet(s.mock.Ctx)
|
|
||||||
s.mock.Assert200OK(s.T(), expectedBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldCheckSecondFactorIsDisabledWhenNoRuleIsSetToTwoFactor() {
|
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
|
||||||
TOTP: &schema.TOTPConfiguration{
|
|
||||||
Period: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(schema.AccessControlConfiguration{
|
|
||||||
DefaultPolicy: "bypass",
|
|
||||||
Rules: []schema.ACLRule{
|
|
||||||
{
|
|
||||||
Domains: []string{"example.com"},
|
|
||||||
Policy: "deny",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Domains: []string{"abc.example.com"},
|
|
||||||
Policy: "single_factor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Domains: []string{"def.example.com"},
|
|
||||||
Policy: "bypass",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
ExtendedConfigurationGet(s.mock.Ctx)
|
|
||||||
s.mock.Assert200OK(s.T(), ExtendedConfigurationBody{
|
|
||||||
AvailableMethods: []string{"totp", "u2f"},
|
|
||||||
SecondFactorEnabled: false,
|
|
||||||
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldCheckSecondFactorIsEnabledWhenDefaultPolicySetToTwoFactor() {
|
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
|
||||||
TOTP: &schema.TOTPConfiguration{
|
|
||||||
Period: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(schema.AccessControlConfiguration{
|
|
||||||
DefaultPolicy: "two_factor",
|
|
||||||
Rules: []schema.ACLRule{
|
|
||||||
{
|
|
||||||
Domains: []string{"example.com"},
|
|
||||||
Policy: "deny",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Domains: []string{"abc.example.com"},
|
|
||||||
Policy: "single_factor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Domains: []string{"def.example.com"},
|
|
||||||
Policy: "bypass",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
ExtendedConfigurationGet(s.mock.Ctx)
|
|
||||||
s.mock.Assert200OK(s.T(), ExtendedConfigurationBody{
|
|
||||||
AvailableMethods: []string{"totp", "u2f"},
|
|
||||||
SecondFactorEnabled: true,
|
|
||||||
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecondFactorAvailableMethodsFixture) TestShouldCheckSecondFactorIsEnabledWhenSomePolicySetToTwoFactor() {
|
|
||||||
s.mock.Ctx.Configuration = schema.Configuration{
|
|
||||||
TOTP: &schema.TOTPConfiguration{
|
|
||||||
Period: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(schema.AccessControlConfiguration{
|
|
||||||
DefaultPolicy: "bypass",
|
|
||||||
Rules: []schema.ACLRule{
|
|
||||||
{
|
|
||||||
Domains: []string{"example.com"},
|
|
||||||
Policy: "deny",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Domains: []string{"abc.example.com"},
|
|
||||||
Policy: "two_factor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Domains: []string{"def.example.com"},
|
|
||||||
Policy: "bypass",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
ExtendedConfigurationGet(s.mock.Ctx)
|
|
||||||
s.mock.Assert200OK(s.T(), ExtendedConfigurationBody{
|
|
||||||
AvailableMethods: []string{"totp", "u2f"},
|
|
||||||
SecondFactorEnabled: true,
|
|
||||||
TOTPPeriod: schema.DefaultTOTPConfiguration.Period,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunSuite(t *testing.T) {
|
|
||||||
s := new(SecondFactorAvailableMethodsFixture)
|
|
||||||
suite.Run(t, s)
|
|
||||||
}
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadInfo(username string, storageProvider storage.Provider, preferences *UserPreferences, logger *logrus.Entry) []error {
|
func loadInfo(username string, storageProvider storage.Provider, userInfo *UserInfo, logger *logrus.Entry) []error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
wg.Add(3)
|
wg.Add(3)
|
||||||
|
@ -32,9 +32,9 @@ func loadInfo(username string, storageProvider storage.Provider, preferences *Us
|
||||||
}
|
}
|
||||||
|
|
||||||
if method == "" {
|
if method == "" {
|
||||||
preferences.Method = authentication.PossibleMethods[0]
|
userInfo.Method = authentication.PossibleMethods[0]
|
||||||
} else {
|
} else {
|
||||||
preferences.Method = method
|
userInfo.Method = method
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ func loadInfo(username string, storageProvider storage.Provider, preferences *Us
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences.HasU2F = true
|
userInfo.HasU2F = true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -71,7 +71,7 @@ func loadInfo(username string, storageProvider storage.Provider, preferences *Us
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences.HasTOTP = true
|
userInfo.HasTOTP = true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
@ -83,15 +83,17 @@ func loadInfo(username string, storageProvider storage.Provider, preferences *Us
|
||||||
func UserInfoGet(ctx *middlewares.AutheliaCtx) {
|
func UserInfoGet(ctx *middlewares.AutheliaCtx) {
|
||||||
userSession := ctx.GetSession()
|
userSession := ctx.GetSession()
|
||||||
|
|
||||||
preferences := UserPreferences{}
|
userInfo := UserInfo{}
|
||||||
errors := loadInfo(userSession.Username, ctx.Providers.StorageProvider, &preferences, ctx.Logger)
|
errors := loadInfo(userSession.Username, ctx.Providers.StorageProvider, &userInfo, ctx.Logger)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
ctx.Error(fmt.Errorf("Unable to load user information"), operationFailedMessage)
|
ctx.Error(fmt.Errorf("Unable to load user information"), operationFailedMessage)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.SetJSONBody(preferences) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
userInfo.DisplayName = userSession.DisplayName
|
||||||
|
|
||||||
|
ctx.SetJSONBody(userInfo) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodBody the selected 2FA method.
|
// MethodBody the selected 2FA method.
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (s *FetchSuite) TearDownTest() {
|
||||||
s.mock.Close()
|
s.mock.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPreferencesExpectations(preferences UserPreferences, provider *storage.MockProvider) {
|
func setPreferencesExpectations(preferences UserInfo, provider *storage.MockProvider) {
|
||||||
provider.
|
provider.
|
||||||
EXPECT().
|
EXPECT().
|
||||||
LoadPreferred2FAMethod(gomock.Eq("john")).
|
LoadPreferred2FAMethod(gomock.Eq("john")).
|
||||||
|
@ -65,7 +65,7 @@ func setPreferencesExpectations(preferences UserPreferences, provider *storage.M
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMethodSetToU2F(t *testing.T) {
|
func TestMethodSetToU2F(t *testing.T) {
|
||||||
table := []UserPreferences{
|
table := []UserInfo{
|
||||||
{
|
{
|
||||||
Method: "totp",
|
Method: "totp",
|
||||||
},
|
},
|
||||||
|
@ -97,7 +97,7 @@ func TestMethodSetToU2F(t *testing.T) {
|
||||||
setPreferencesExpectations(expectedPreferences, mock.StorageProviderMock)
|
setPreferencesExpectations(expectedPreferences, mock.StorageProviderMock)
|
||||||
UserInfoGet(mock.Ctx)
|
UserInfoGet(mock.Ctx)
|
||||||
|
|
||||||
actualPreferences := UserPreferences{}
|
actualPreferences := UserInfo{}
|
||||||
mock.GetResponseData(t, &actualPreferences)
|
mock.GetResponseData(t, &actualPreferences)
|
||||||
|
|
||||||
t.Run("expected method", func(t *testing.T) {
|
t.Run("expected method", func(t *testing.T) {
|
||||||
|
@ -132,7 +132,7 @@ func (s *FetchSuite) TestShouldGetDefaultPreferenceIfNotInDB() {
|
||||||
Return("", storage.ErrNoTOTPSecret)
|
Return("", storage.ErrNoTOTPSecret)
|
||||||
|
|
||||||
UserInfoGet(s.mock.Ctx)
|
UserInfoGet(s.mock.Ctx)
|
||||||
s.mock.Assert200OK(s.T(), UserPreferences{Method: "totp"})
|
s.mock.Assert200OK(s.T(), UserInfo{Method: "totp"})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FetchSuite) TestShouldReturnError500WhenStorageFailsToLoad() {
|
func (s *FetchSuite) TestShouldReturnError500WhenStorageFailsToLoad() {
|
||||||
|
|
|
@ -11,8 +11,11 @@ type MethodList = []string
|
||||||
|
|
||||||
type authorizationMatching int
|
type authorizationMatching int
|
||||||
|
|
||||||
// UserPreferences is the model of user second factor preferences.
|
// UserInfo is the model of user info and second factor preferences.
|
||||||
type UserPreferences struct {
|
type UserInfo struct {
|
||||||
|
// The users display name.
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
|
||||||
// The preferred 2FA method.
|
// The preferred 2FA method.
|
||||||
Method string `json:"method" valid:"required"`
|
Method string `json:"method" valid:"required"`
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ var alphaNumericRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
|
||||||
// ServeIndex serve the index.html file with nonce generated for supporting
|
// ServeIndex serve the index.html file with nonce generated for supporting
|
||||||
// restrictive CSP while using material-ui from the embedded virtual filesystem.
|
// restrictive CSP while using material-ui from the embedded virtual filesystem.
|
||||||
//go:generate broccoli -src ../../public_html -o public_html
|
//go:generate broccoli -src ../../public_html -o public_html
|
||||||
func ServeIndex(publicDir, base string) fasthttp.RequestHandler {
|
func ServeIndex(publicDir, base, rememberMe, resetPassword string) fasthttp.RequestHandler {
|
||||||
f, err := br.Open(publicDir + "/index.html")
|
f, err := br.Open(publicDir + "/index.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Logger().Fatalf("Unable to open index.html: %v", err)
|
logging.Logger().Fatalf("Unable to open index.html: %v", err)
|
||||||
|
@ -38,7 +38,7 @@ func ServeIndex(publicDir, base string) fasthttp.RequestHandler {
|
||||||
ctx.SetContentType("text/html; charset=utf-8")
|
ctx.SetContentType("text/html; charset=utf-8")
|
||||||
ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self'; object-src 'none'; style-src 'self' 'nonce-%s'", nonce))
|
ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self'; object-src 'none'; style-src 'self' 'nonce-%s'", nonce))
|
||||||
|
|
||||||
err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ CSPNonce, Base string }{CSPNonce: nonce, Base: base})
|
err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ Base, CSPNonce, RememberMe, ResetPassword string }{Base: base, CSPNonce: nonce, RememberMe: rememberMe, ResetPassword: resetPassword})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error("An error occurred", 503)
|
ctx.Error("An error occurred", 503)
|
||||||
logging.Logger().Errorf("Unable to execute template: %v", err)
|
logging.Logger().Errorf("Unable to execute template: %v", err)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package server
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
duoapi "github.com/duosecurity/duo_api_golang"
|
duoapi "github.com/duosecurity/duo_api_golang"
|
||||||
"github.com/fasthttp/router"
|
"github.com/fasthttp/router"
|
||||||
|
@ -22,10 +23,15 @@ import (
|
||||||
func StartServer(configuration schema.Configuration, providers middlewares.Providers) {
|
func StartServer(configuration schema.Configuration, providers middlewares.Providers) {
|
||||||
autheliaMiddleware := middlewares.AutheliaMiddleware(configuration, providers)
|
autheliaMiddleware := middlewares.AutheliaMiddleware(configuration, providers)
|
||||||
embeddedAssets := "/public_html"
|
embeddedAssets := "/public_html"
|
||||||
|
rememberMe := strconv.FormatBool(configuration.Session.RememberMeDuration != "0")
|
||||||
|
resetPassword := strconv.FormatBool(!configuration.AuthenticationBackend.DisableResetPassword)
|
||||||
|
|
||||||
rootFiles := []string{"favicon.ico", "manifest.json", "robots.txt"}
|
rootFiles := []string{"favicon.ico", "manifest.json", "robots.txt"}
|
||||||
|
|
||||||
|
serveIndexHandler := ServeIndex(embeddedAssets, configuration.Server.Path, rememberMe, resetPassword)
|
||||||
|
|
||||||
r := router.New()
|
r := router.New()
|
||||||
r.GET("/", ServeIndex(embeddedAssets, configuration.Server.Path))
|
r.GET("/", serveIndexHandler)
|
||||||
|
|
||||||
for _, f := range rootFiles {
|
for _, f := range rootFiles {
|
||||||
r.GET("/"+f, fasthttpadaptor.NewFastHTTPHandler(br.Serve(embeddedAssets)))
|
r.GET("/"+f, fasthttpadaptor.NewFastHTTPHandler(br.Serve(embeddedAssets)))
|
||||||
|
@ -35,9 +41,8 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi
|
||||||
|
|
||||||
r.GET("/api/state", autheliaMiddleware(handlers.StateGet))
|
r.GET("/api/state", autheliaMiddleware(handlers.StateGet))
|
||||||
|
|
||||||
r.GET("/api/configuration", autheliaMiddleware(handlers.ConfigurationGet))
|
r.GET("/api/configuration", autheliaMiddleware(
|
||||||
r.GET("/api/configuration/extended", autheliaMiddleware(
|
middlewares.RequireFirstFactor(handlers.ConfigurationGet)))
|
||||||
middlewares.RequireFirstFactor(handlers.ExtendedConfigurationGet)))
|
|
||||||
|
|
||||||
r.GET("/api/verify", autheliaMiddleware(handlers.VerifyGet(configuration.AuthenticationBackend)))
|
r.GET("/api/verify", autheliaMiddleware(handlers.VerifyGet(configuration.AuthenticationBackend)))
|
||||||
r.HEAD("/api/verify", autheliaMiddleware(handlers.VerifyGet(configuration.AuthenticationBackend)))
|
r.HEAD("/api/verify", autheliaMiddleware(handlers.VerifyGet(configuration.AuthenticationBackend)))
|
||||||
|
@ -113,7 +118,7 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi
|
||||||
r.GET("/debug/vars", expvarhandler.ExpvarHandler)
|
r.GET("/debug/vars", expvarhandler.ExpvarHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.NotFound = ServeIndex(embeddedAssets, configuration.Server.Path)
|
r.NotFound = serveIndexHandler
|
||||||
|
|
||||||
handler := middlewares.LogRequestMiddleware(r.Handler)
|
handler := middlewares.LogRequestMiddleware(r.Handler)
|
||||||
if configuration.Server.Path != "" {
|
if configuration.Server.Path != "" {
|
||||||
|
|
|
@ -47,10 +47,7 @@ func (s *BackendProtectionScenario) TestProtectionOfBackendEndpoints() {
|
||||||
s.AssertRequestStatusCode("POST", fmt.Sprintf("%s/api/user/info/2fa_method", AutheliaBaseURL), 403)
|
s.AssertRequestStatusCode("POST", fmt.Sprintf("%s/api/user/info/2fa_method", AutheliaBaseURL), 403)
|
||||||
|
|
||||||
s.AssertRequestStatusCode("GET", fmt.Sprintf("%s/api/user/info", AutheliaBaseURL), 403)
|
s.AssertRequestStatusCode("GET", fmt.Sprintf("%s/api/user/info", AutheliaBaseURL), 403)
|
||||||
s.AssertRequestStatusCode("GET", fmt.Sprintf("%s/api/configuration/extended", AutheliaBaseURL), 403)
|
s.AssertRequestStatusCode("GET", fmt.Sprintf("%s/api/configuration", AutheliaBaseURL), 403)
|
||||||
|
|
||||||
// This is the global configuration, it's safe to let it open.
|
|
||||||
s.AssertRequestStatusCode("GET", fmt.Sprintf("%s/api/configuration", AutheliaBaseURL), 200)
|
|
||||||
|
|
||||||
s.AssertRequestStatusCode("POST", fmt.Sprintf("%s/api/secondfactor/u2f/identity/start", AutheliaBaseURL), 403)
|
s.AssertRequestStatusCode("POST", fmt.Sprintf("%s/api/secondfactor/u2f/identity/start", AutheliaBaseURL), 403)
|
||||||
s.AssertRequestStatusCode("POST", fmt.Sprintf("%s/api/secondfactor/u2f/identity/finish", AutheliaBaseURL), 403)
|
s.AssertRequestStatusCode("POST", fmt.Sprintf("%s/api/secondfactor/u2f/identity/finish", AutheliaBaseURL), 403)
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<title>Login - Authelia</title>
|
<title>Login - Authelia</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-basepath="%PUBLIC_URL%">
|
<body data-basepath="%PUBLIC_URL%" data-rememberme="{{.RememberMe}}" data-disable-resetpassword="{{.ResetPassword}}">
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
<!--
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
BrowserRouter as Router, Route, Switch, Redirect
|
BrowserRouter as Router, Route, Switch, Redirect
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
|
@ -17,7 +17,7 @@ import NotificationsContext from './hooks/NotificationsContext';
|
||||||
import { Notification } from './models/Notifications';
|
import { Notification } from './models/Notifications';
|
||||||
import NotificationBar from './components/NotificationBar';
|
import NotificationBar from './components/NotificationBar';
|
||||||
import SignOut from './views/LoginPortal/SignOut/SignOut';
|
import SignOut from './views/LoginPortal/SignOut/SignOut';
|
||||||
import { useConfiguration } from './hooks/Configuration';
|
import { useRememberMe, useResetPassword } from './hooks/Configuration';
|
||||||
import '@fortawesome/fontawesome-svg-core/styles.css'
|
import '@fortawesome/fontawesome-svg-core/styles.css'
|
||||||
import { config as faConfig } from '@fortawesome/fontawesome-svg-core';
|
import { config as faConfig } from '@fortawesome/fontawesome-svg-core';
|
||||||
import { useBasePath } from './hooks/BasePath';
|
import { useBasePath } from './hooks/BasePath';
|
||||||
|
@ -26,15 +26,6 @@ faConfig.autoAddCss = false;
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
const [notification, setNotification] = useState(null as Notification | null);
|
const [notification, setNotification] = useState(null as Notification | null);
|
||||||
const [configuration, fetchConfig, , fetchConfigError] = useConfiguration();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (fetchConfigError) {
|
|
||||||
console.error(fetchConfigError);
|
|
||||||
}
|
|
||||||
}, [fetchConfigError]);
|
|
||||||
|
|
||||||
useEffect(() => { fetchConfig() }, [fetchConfig]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NotificationsContext.Provider value={{ notification, setNotification }} >
|
<NotificationsContext.Provider value={{ notification, setNotification }} >
|
||||||
|
@ -58,8 +49,8 @@ const App: React.FC = () => {
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={FirstFactorRoute}>
|
<Route path={FirstFactorRoute}>
|
||||||
<LoginPortal
|
<LoginPortal
|
||||||
rememberMe={configuration?.remember_me === true}
|
rememberMe={useRememberMe()}
|
||||||
resetPassword={configuration?.reset_password === true} />
|
resetPassword={useResetPassword()} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/">
|
<Route path="/">
|
||||||
<Redirect to={FirstFactorRoute} />
|
<Redirect to={FirstFactorRoute} />
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
export function useBasePath() {
|
import { useEmbeddedVariable } from "./Configuration";
|
||||||
const basePath = document.body.getAttribute("data-basepath");
|
|
||||||
if (basePath === null) {
|
|
||||||
throw new Error("No base path detected");
|
|
||||||
}
|
|
||||||
|
|
||||||
return basePath;
|
export function useBasePath() {
|
||||||
|
return useEmbeddedVariable("basepath");
|
||||||
}
|
}
|
|
@ -1,10 +1,23 @@
|
||||||
import { useRemoteCall } from "./RemoteCall";
|
import { useRemoteCall } from "./RemoteCall";
|
||||||
import { getConfiguration, getExtendedConfiguration } from "../services/Configuration";
|
import { getConfiguration } from "../services/Configuration";
|
||||||
|
|
||||||
|
export function useEmbeddedVariable(variableName: string) {
|
||||||
|
const value = document.body.getAttribute(`data-${variableName}`);
|
||||||
|
if (value === null) {
|
||||||
|
throw new Error(`No ${variableName} embedded variable detected`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRememberMe() {
|
||||||
|
return useEmbeddedVariable("rememberme") === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useResetPassword() {
|
||||||
|
return useEmbeddedVariable("disable-resetpassword") === "true";
|
||||||
|
}
|
||||||
|
|
||||||
export function useConfiguration() {
|
export function useConfiguration() {
|
||||||
return useRemoteCall(getConfiguration, []);
|
return useRemoteCall(getConfiguration, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useExtendedConfiguration() {
|
|
||||||
return useRemoteCall(getExtendedConfiguration, []);
|
|
||||||
}
|
|
|
@ -1,13 +1,7 @@
|
||||||
import { SecondFactorMethod } from "./Methods";
|
import { SecondFactorMethod } from "./Methods";
|
||||||
|
|
||||||
export interface Configuration {
|
export interface Configuration {
|
||||||
remember_me: boolean;
|
|
||||||
reset_password: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExtendedConfiguration {
|
|
||||||
available_methods: Set<SecondFactorMethod>;
|
available_methods: Set<SecondFactorMethod>;
|
||||||
display_name: string;
|
|
||||||
second_factor_enabled: boolean;
|
second_factor_enabled: boolean;
|
||||||
totp_period: number;
|
totp_period: number;
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { SecondFactorMethod } from "./Methods";
|
import { SecondFactorMethod } from "./Methods";
|
||||||
|
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
|
display_name: string;
|
||||||
method: SecondFactorMethod;
|
method: SecondFactorMethod;
|
||||||
has_u2f: boolean;
|
has_u2f: boolean;
|
||||||
has_totp: boolean;
|
has_totp: boolean;
|
||||||
|
|
|
@ -28,7 +28,6 @@ export const UserInfoPath = basePath + "/api/user/info";
|
||||||
export const UserInfo2FAMethodPath = basePath + "/api/user/info/2fa_method";
|
export const UserInfo2FAMethodPath = basePath + "/api/user/info/2fa_method";
|
||||||
|
|
||||||
export const ConfigurationPath = basePath + "/api/configuration";
|
export const ConfigurationPath = basePath + "/api/configuration";
|
||||||
export const ExtendedConfigurationPath = basePath + "/api/configuration/extended";
|
|
||||||
|
|
||||||
export interface ErrorResponse {
|
export interface ErrorResponse {
|
||||||
status: "KO";
|
status: "KO";
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
import { Get } from "./Client";
|
import { Get } from "./Client";
|
||||||
import { ExtendedConfigurationPath, ConfigurationPath } from "./Api";
|
import { ConfigurationPath } from "./Api";
|
||||||
import { toEnum, Method2FA } from "./UserPreferences";
|
import { toEnum, Method2FA } from "./UserPreferences";
|
||||||
import { Configuration, ExtendedConfiguration } from "../models/Configuration";
|
import { Configuration } from "../models/Configuration";
|
||||||
|
|
||||||
export async function getConfiguration(): Promise<Configuration> {
|
interface ConfigurationPayload {
|
||||||
return Get<Configuration>(ConfigurationPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExtendedConfigurationPayload {
|
|
||||||
available_methods: Method2FA[];
|
available_methods: Method2FA[];
|
||||||
display_name: string;
|
|
||||||
second_factor_enabled: boolean;
|
second_factor_enabled: boolean;
|
||||||
totp_period: number;
|
totp_period: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getExtendedConfiguration(): Promise<ExtendedConfiguration> {
|
export async function getConfiguration(): Promise<Configuration> {
|
||||||
const config = await Get<ExtendedConfigurationPayload>(ExtendedConfigurationPath);
|
const config = await Get<ConfigurationPayload>(ConfigurationPath);
|
||||||
return { ...config, available_methods: new Set(config.available_methods.map(toEnum)) };
|
return { ...config, available_methods: new Set(config.available_methods.map(toEnum)) };
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@ import { UserInfo } from "../models/UserInfo";
|
||||||
export type Method2FA = "u2f" | "totp" | "mobile_push";
|
export type Method2FA = "u2f" | "totp" | "mobile_push";
|
||||||
|
|
||||||
export interface UserInfoPayload {
|
export interface UserInfoPayload {
|
||||||
|
display_name: string;
|
||||||
method: Method2FA;
|
method: Method2FA;
|
||||||
has_u2f: boolean;
|
has_u2f: boolean;
|
||||||
has_totp: boolean;
|
has_totp: boolean;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { configure } from 'enzyme';
|
import { configure } from 'enzyme';
|
||||||
import Adapter from 'enzyme-adapter-react-16';
|
import Adapter from 'enzyme-adapter-react-16';
|
||||||
document.body.setAttribute("data-basepath", "");
|
document.body.setAttribute("data-basepath", "");
|
||||||
|
document.body.setAttribute("data-rememberme", "false");
|
||||||
|
document.body.setAttribute("data-disable-resetpassword", "false");
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { useNotifications } from "../../hooks/NotificationsContext";
|
||||||
import { useRedirectionURL } from "../../hooks/RedirectionURL";
|
import { useRedirectionURL } from "../../hooks/RedirectionURL";
|
||||||
import { useUserPreferences as userUserInfo } from "../../hooks/UserInfo";
|
import { useUserPreferences as userUserInfo } from "../../hooks/UserInfo";
|
||||||
import { SecondFactorMethod } from "../../models/Methods";
|
import { SecondFactorMethod } from "../../models/Methods";
|
||||||
import { useExtendedConfiguration } from "../../hooks/Configuration";
|
import { useConfiguration } from "../../hooks/Configuration";
|
||||||
import AuthenticatedView from "./AuthenticatedView/AuthenticatedView";
|
import AuthenticatedView from "./AuthenticatedView/AuthenticatedView";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -30,7 +30,7 @@ export default function (props: Props) {
|
||||||
|
|
||||||
const [state, fetchState, , fetchStateError] = useAutheliaState();
|
const [state, fetchState, , fetchStateError] = useAutheliaState();
|
||||||
const [userInfo, fetchUserInfo, , fetchUserInfoError] = userUserInfo();
|
const [userInfo, fetchUserInfo, , fetchUserInfoError] = userUserInfo();
|
||||||
const [configuration, fetchConfiguration, , fetchConfigurationError] = useExtendedConfiguration();
|
const [configuration, fetchConfiguration, , fetchConfigurationError] = useConfiguration();
|
||||||
|
|
||||||
const redirect = useCallback((url: string) => history.push(url), [history]);
|
const redirect = useCallback((url: string) => history.push(url), [history]);
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ export default function (props: Props) {
|
||||||
onAuthenticationSuccess={handleAuthSuccess} /> : null}
|
onAuthenticationSuccess={handleAuthSuccess} /> : null}
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={AuthenticatedRoute} exact>
|
<Route path={AuthenticatedRoute} exact>
|
||||||
{configuration ? <AuthenticatedView name={configuration.display_name} /> : null}
|
{userInfo ? <AuthenticatedView name={userInfo.display_name} /> : null}
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/">
|
<Route path="/">
|
||||||
<Redirect to={FirstFactorRoute} />
|
<Redirect to={FirstFactorRoute} />
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
} from "../../../Routes";
|
} from "../../../Routes";
|
||||||
import { setPreferred2FAMethod } from "../../../services/UserPreferences";
|
import { setPreferred2FAMethod } from "../../../services/UserPreferences";
|
||||||
import { UserInfo } from "../../../models/UserInfo";
|
import { UserInfo } from "../../../models/UserInfo";
|
||||||
import { ExtendedConfiguration } from "../../../models/Configuration";
|
import { Configuration } from "../../../models/Configuration";
|
||||||
import u2fApi from "u2f-api";
|
import u2fApi from "u2f-api";
|
||||||
import { AuthenticationLevel } from "../../../services/State";
|
import { AuthenticationLevel } from "../../../services/State";
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export interface Props {
|
||||||
authenticationLevel: AuthenticationLevel;
|
authenticationLevel: AuthenticationLevel;
|
||||||
|
|
||||||
userInfo: UserInfo;
|
userInfo: UserInfo;
|
||||||
configuration: ExtendedConfiguration;
|
configuration: Configuration;
|
||||||
|
|
||||||
onMethodChanged: (method: SecondFactorMethod) => void;
|
onMethodChanged: (method: SecondFactorMethod) => void;
|
||||||
onAuthenticationSuccess: (redirectURL: string | undefined) => void;
|
onAuthenticationSuccess: (redirectURL: string | undefined) => void;
|
||||||
|
@ -88,7 +88,7 @@ export default function (props: Props) {
|
||||||
return (
|
return (
|
||||||
<LoginLayout
|
<LoginLayout
|
||||||
id="second-factor-stage"
|
id="second-factor-stage"
|
||||||
title={`Hi ${props.configuration.display_name}`}
|
title={`Hi ${props.userInfo.display_name}`}
|
||||||
showBrand>
|
showBrand>
|
||||||
<MethodSelectionDialog
|
<MethodSelectionDialog
|
||||||
open={methodSelectionOpen}
|
open={methodSelectionOpen}
|
||||||
|
|
Loading…
Reference in New Issue