fix(server): incorrect remote ip logged in error handler (#3139)
This fixes edge cases where the remote IP was not correctly logged. Generally this is not an issue as most errors do not hit this handler, but in instances where a transport error occurs this is important.pull/3140/head^2
parent
90edf11b88
commit
ce6bf74c8d
|
@ -8,8 +8,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// CheckSafeRedirection handler checking whether the redirection to a given URL provided in body is safe.
|
||||
func CheckSafeRedirection(ctx *middlewares.AutheliaCtx) {
|
||||
// CheckSafeRedirectionPOST handler checking whether the redirection to a given URL provided in body is safe.
|
||||
func CheckSafeRedirectionPOST(ctx *middlewares.AutheliaCtx) {
|
||||
userSession := ctx.GetSession()
|
||||
|
||||
if userSession.AuthenticationLevel == authentication.NotAuthenticated {
|
||||
|
|
|
@ -24,7 +24,7 @@ func TestCheckSafeRedirection_ForbiddenCall(t *testing.T) {
|
|||
URI: "http://myapp.example.com",
|
||||
})
|
||||
|
||||
CheckSafeRedirection(mock.Ctx)
|
||||
CheckSafeRedirectionPOST(mock.Ctx)
|
||||
assert.Equal(t, 401, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ func TestCheckSafeRedirection_UnsafeRedirection(t *testing.T) {
|
|||
URI: "http://myapp.com",
|
||||
})
|
||||
|
||||
CheckSafeRedirection(mock.Ctx)
|
||||
CheckSafeRedirectionPOST(mock.Ctx)
|
||||
mock.Assert200OK(t, checkURIWithinDomainResponseBody{
|
||||
OK: false,
|
||||
})
|
||||
|
@ -58,7 +58,7 @@ func TestCheckSafeRedirection_SafeRedirection(t *testing.T) {
|
|||
URI: "https://myapp.example.com",
|
||||
})
|
||||
|
||||
CheckSafeRedirection(mock.Ctx)
|
||||
CheckSafeRedirectionPOST(mock.Ctx)
|
||||
mock.Assert200OK(t, checkURIWithinDomainResponseBody{
|
||||
OK: true,
|
||||
})
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
)
|
||||
|
||||
// ConfigurationGet get the configuration accessible to authenticated users.
|
||||
func ConfigurationGet(ctx *middlewares.AutheliaCtx) {
|
||||
// ConfigurationGET get the configuration accessible to authenticated users.
|
||||
func ConfigurationGET(ctx *middlewares.AutheliaCtx) {
|
||||
body := configurationBody{
|
||||
AvailableMethods: make(MethodList, 0, 3),
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldHaveAllConfiguredMethods
|
|||
|
||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration)
|
||||
|
||||
ConfigurationGet(s.mock.Ctx)
|
||||
ConfigurationGET(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), configurationBody{
|
||||
AvailableMethods: []string{"totp", "webauthn", "mobile_push"},
|
||||
|
@ -77,7 +77,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveTOTPFromAvailableM
|
|||
|
||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration)
|
||||
|
||||
ConfigurationGet(s.mock.Ctx)
|
||||
ConfigurationGET(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), configurationBody{
|
||||
AvailableMethods: []string{"webauthn", "mobile_push"},
|
||||
|
@ -105,7 +105,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvaila
|
|||
|
||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration)
|
||||
|
||||
ConfigurationGet(s.mock.Ctx)
|
||||
ConfigurationGET(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), configurationBody{
|
||||
AvailableMethods: []string{"totp", "mobile_push"},
|
||||
|
@ -133,7 +133,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveDuoFromAvailableMe
|
|||
|
||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration)
|
||||
|
||||
ConfigurationGet(s.mock.Ctx)
|
||||
ConfigurationGET(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), configurationBody{
|
||||
AvailableMethods: []string{"totp", "webauthn"},
|
||||
|
@ -161,7 +161,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenNoTw
|
|||
|
||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration)
|
||||
|
||||
ConfigurationGet(s.mock.Ctx)
|
||||
ConfigurationGET(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), configurationBody{
|
||||
AvailableMethods: []string{},
|
||||
|
@ -189,7 +189,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenAllD
|
|||
|
||||
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration)
|
||||
|
||||
ConfigurationGet(s.mock.Ctx)
|
||||
ConfigurationGET(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), configurationBody{
|
||||
AvailableMethods: []string{},
|
||||
|
|
|
@ -10,9 +10,9 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/session"
|
||||
)
|
||||
|
||||
// FirstFactorPost is the handler performing the first factory.
|
||||
// FirstFactorPOST is the handler performing the first factory.
|
||||
//nolint:gocyclo // TODO: Consider refactoring time permitting.
|
||||
func FirstFactorPost(delayFunc middlewares.TimingAttackDelayFunc) middlewares.RequestHandler {
|
||||
func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.RequestHandler {
|
||||
return func(ctx *middlewares.AutheliaCtx) {
|
||||
var successful bool
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ func (s *FirstFactorSuite) TearDownTest() {
|
|||
}
|
||||
|
||||
func (s *FirstFactorSuite) TestShouldFailIfBodyIsNil() {
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
// No body.
|
||||
assert.Equal(s.T(), "Failed to parse 1FA request body: unable to parse body: unexpected end of JSON input", s.mock.Hook.LastEntry().Message)
|
||||
|
@ -43,7 +43,7 @@ func (s *FirstFactorSuite) TestShouldFailIfBodyIsInBadFormat() {
|
|||
s.mock.Ctx.Request.SetBodyString(`{
|
||||
"username": "test"
|
||||
}`)
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), "Failed to parse 1FA request body: unable to validate body: password: non zero value required", s.mock.Hook.LastEntry().Message)
|
||||
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
|
||||
|
@ -71,7 +71,7 @@ func (s *FirstFactorSuite) TestShouldFailIfUserProviderCheckPasswordFail() {
|
|||
"password": "hello",
|
||||
"keepMeLoggedIn": true
|
||||
}`)
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), "Unsuccessful 1FA authentication attempt by user 'test': failed", s.mock.Hook.LastEntry().Message)
|
||||
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
|
||||
|
@ -100,7 +100,7 @@ func (s *FirstFactorSuite) TestShouldCheckAuthenticationIsNotMarkedWhenProviderC
|
|||
"keepMeLoggedIn": true
|
||||
}`)
|
||||
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
}
|
||||
|
||||
func (s *FirstFactorSuite) TestShouldCheckAuthenticationIsMarkedWhenInvalidCredentials() {
|
||||
|
@ -126,7 +126,7 @@ func (s *FirstFactorSuite) TestShouldCheckAuthenticationIsMarkedWhenInvalidCrede
|
|||
"keepMeLoggedIn": true
|
||||
}`)
|
||||
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
}
|
||||
|
||||
func (s *FirstFactorSuite) TestShouldFailIfUserProviderGetDetailsFail() {
|
||||
|
@ -150,7 +150,7 @@ func (s *FirstFactorSuite) TestShouldFailIfUserProviderGetDetailsFail() {
|
|||
"password": "hello",
|
||||
"keepMeLoggedIn": true
|
||||
}`)
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), "Could not obtain profile details during 1FA authentication for user 'test': failed", s.mock.Hook.LastEntry().Message)
|
||||
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
|
||||
|
@ -172,7 +172,7 @@ func (s *FirstFactorSuite) TestShouldFailIfAuthenticationMarkFail() {
|
|||
"password": "hello",
|
||||
"keepMeLoggedIn": true
|
||||
}`)
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), "Unable to mark 1FA authentication attempt by user 'test': failed", s.mock.Hook.LastEntry().Message)
|
||||
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
|
||||
|
@ -203,7 +203,7 @@ func (s *FirstFactorSuite) TestShouldAuthenticateUserWithRememberMeChecked() {
|
|||
"password": "hello",
|
||||
"keepMeLoggedIn": true
|
||||
}`)
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
// Respond with 200.
|
||||
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
||||
|
@ -244,7 +244,7 @@ func (s *FirstFactorSuite) TestShouldAuthenticateUserWithRememberMeUnchecked() {
|
|||
"requestMethod": "GET",
|
||||
"keepMeLoggedIn": false
|
||||
}`)
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
// Respond with 200.
|
||||
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
||||
|
@ -288,7 +288,7 @@ func (s *FirstFactorSuite) TestShouldSaveUsernameFromAuthenticationBackendInSess
|
|||
"requestMethod": "GET",
|
||||
"keepMeLoggedIn": true
|
||||
}`)
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
// Respond with 200.
|
||||
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
||||
|
@ -358,7 +358,7 @@ func (s *FirstFactorRedirectionSuite) TestShouldRedirectToDefaultURLWhenNoTarget
|
|||
"requestMethod": "GET",
|
||||
"keepMeLoggedIn": false
|
||||
}`)
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
// Respond with 200.
|
||||
s.mock.Assert200OK(s.T(), redirectResponse{Redirect: "https://default.local"})
|
||||
|
@ -379,7 +379,7 @@ func (s *FirstFactorRedirectionSuite) TestShouldRedirectToDefaultURLWhenURLIsUns
|
|||
"targetURL": "http://notsafe.local"
|
||||
}`)
|
||||
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
// Respond with 200.
|
||||
s.mock.Assert200OK(s.T(), redirectResponse{Redirect: "https://default.local"})
|
||||
|
@ -402,7 +402,7 @@ func (s *FirstFactorRedirectionSuite) TestShouldReply200WhenNoTargetURLProvidedA
|
|||
"keepMeLoggedIn": false
|
||||
}`)
|
||||
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
// Respond with 200.
|
||||
s.mock.Assert200OK(s.T(), nil)
|
||||
|
@ -434,7 +434,7 @@ func (s *FirstFactorRedirectionSuite) TestShouldReply200WhenUnsafeTargetURLProvi
|
|||
"keepMeLoggedIn": false
|
||||
}`)
|
||||
|
||||
FirstFactorPost(nil)(s.mock.Ctx)
|
||||
FirstFactorPOST(nil)(s.mock.Ctx)
|
||||
|
||||
// Respond with 200.
|
||||
s.mock.Assert200OK(s.T(), nil)
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
)
|
||||
|
||||
// HealthGet can be used by health checks.
|
||||
func HealthGet(ctx *middlewares.AutheliaCtx) {
|
||||
// HealthGET can be used by health checks.
|
||||
func HealthGET(ctx *middlewares.AutheliaCtx) {
|
||||
ctx.ReplyOK()
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ type logoutResponseBody struct {
|
|||
SafeTargetURL bool `json:"safeTargetURL"`
|
||||
}
|
||||
|
||||
// LogoutPost is the handler logging out the user attached to the given cookie.
|
||||
func LogoutPost(ctx *middlewares.AutheliaCtx) {
|
||||
// LogoutPOST is the handler logging out the user attached to the given cookie.
|
||||
func LogoutPOST(ctx *middlewares.AutheliaCtx) {
|
||||
body := logoutBody{}
|
||||
responseBody := logoutResponseBody{SafeTargetURL: false}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ func (s *LogoutSuite) TearDownTest() {
|
|||
}
|
||||
|
||||
func (s *LogoutSuite) TestShouldDestroySession() {
|
||||
LogoutPost(s.mock.Ctx)
|
||||
LogoutPOST(s.mock.Ctx)
|
||||
b := s.mock.Ctx.Response.Header.PeekCookie("authelia_session")
|
||||
|
||||
// Reset the cookie, meaning it resets the value and expires the cookie by setting
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// SecondFactorDuoDevicesGet handler for retrieving available devices and capabilities from duo api.
|
||||
func SecondFactorDuoDevicesGet(duoAPI duo.API) middlewares.RequestHandler {
|
||||
// DuoDevicesGET handler for retrieving available devices and capabilities from duo api.
|
||||
func DuoDevicesGET(duoAPI duo.API) middlewares.RequestHandler {
|
||||
return func(ctx *middlewares.AutheliaCtx) {
|
||||
userSession := ctx.GetSession()
|
||||
values := url.Values{}
|
||||
|
@ -78,8 +78,8 @@ func SecondFactorDuoDevicesGet(duoAPI duo.API) middlewares.RequestHandler {
|
|||
}
|
||||
}
|
||||
|
||||
// SecondFactorDuoDevicePost update the user preferences regarding Duo device and method.
|
||||
func SecondFactorDuoDevicePost(ctx *middlewares.AutheliaCtx) {
|
||||
// DuoDevicePOST update the user preferences regarding Duo device and method.
|
||||
func DuoDevicePOST(ctx *middlewares.AutheliaCtx) {
|
||||
device := DuoDeviceBody{}
|
||||
|
||||
err := ctx.ParseBody(&device)
|
||||
|
|
|
@ -40,7 +40,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldCallDuoAPIAndFail() {
|
|||
|
||||
duoMock.EXPECT().PreAuthCall(s.mock.Ctx, gomock.Eq(values)).Return(nil, fmt.Errorf("Connnection error"))
|
||||
|
||||
SecondFactorDuoDevicesGet(duoMock)(s.mock.Ctx)
|
||||
DuoDevicesGET(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Authentication failed, please retry later.")
|
||||
assert.Equal(s.T(), "duo PreAuth API errored: Connnection error", s.mock.Hook.LastEntry().Message)
|
||||
|
@ -70,7 +70,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondWithSelection() {
|
|||
|
||||
duoMock.EXPECT().PreAuthCall(s.mock.Ctx, gomock.Eq(values)).Return(&response, nil)
|
||||
|
||||
SecondFactorDuoDevicesGet(duoMock)(s.mock.Ctx)
|
||||
DuoDevicesGET(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), DuoDevicesResponse{Result: auth, Devices: apiDevices})
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondWithAllowOnBypass() {
|
|||
|
||||
duoMock.EXPECT().PreAuthCall(s.mock.Ctx, gomock.Eq(values)).Return(&response, nil)
|
||||
|
||||
SecondFactorDuoDevicesGet(duoMock)(s.mock.Ctx)
|
||||
DuoDevicesGET(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), DuoDevicesResponse{Result: allow})
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondWithEnroll() {
|
|||
|
||||
duoMock.EXPECT().PreAuthCall(s.mock.Ctx, gomock.Eq(values)).Return(&response, nil)
|
||||
|
||||
SecondFactorDuoDevicesGet(duoMock)(s.mock.Ctx)
|
||||
DuoDevicesGET(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), DuoDevicesResponse{Result: enroll, EnrollURL: enrollURL})
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondWithDeny() {
|
|||
|
||||
duoMock.EXPECT().PreAuthCall(s.mock.Ctx, gomock.Eq(values)).Return(&response, nil)
|
||||
|
||||
SecondFactorDuoDevicesGet(duoMock)(s.mock.Ctx)
|
||||
DuoDevicesGET(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), DuoDevicesResponse{Result: deny})
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondOK() {
|
|||
SavePreferredDuoDevice(gomock.Eq(s.mock.Ctx), gomock.Eq(model.DuoDevice{Username: "john", Device: "1234567890123456", Method: "push"})).
|
||||
Return(nil)
|
||||
|
||||
SecondFactorDuoDevicePost(s.mock.Ctx)
|
||||
DuoDevicePOST(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondOK() {
|
|||
func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnInvalidMethod() {
|
||||
s.mock.Ctx.Request.SetBodyString("{\"device\":\"1234567890123456\", \"method\":\"testfailure\"}")
|
||||
|
||||
SecondFactorDuoDevicePost(s.mock.Ctx)
|
||||
DuoDevicePOST(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Authentication failed, please retry later.")
|
||||
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
|
||||
|
@ -149,7 +149,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnInvalidMethod() {
|
|||
func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnEmptyMethod() {
|
||||
s.mock.Ctx.Request.SetBodyString("{\"device\":\"1234567890123456\", \"method\":\"\"}")
|
||||
|
||||
SecondFactorDuoDevicePost(s.mock.Ctx)
|
||||
DuoDevicePOST(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Authentication failed, please retry later.")
|
||||
assert.Equal(s.T(), "unable to validate body: method: non zero value required", s.mock.Hook.LastEntry().Message)
|
||||
|
@ -159,7 +159,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnEmptyMethod() {
|
|||
func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnEmptyDevice() {
|
||||
s.mock.Ctx.Request.SetBodyString("{\"device\":\"\", \"method\":\"push\"}")
|
||||
|
||||
SecondFactorDuoDevicePost(s.mock.Ctx)
|
||||
DuoDevicePOST(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Authentication failed, please retry later.")
|
||||
assert.Equal(s.T(), "unable to validate body: device: non zero value required", s.mock.Hook.LastEntry().Message)
|
||||
|
|
|
@ -26,8 +26,8 @@ func isTokenUserValidFor2FARegistration(ctx *middlewares.AutheliaCtx, username s
|
|||
return ctx.GetSession().Username == username
|
||||
}
|
||||
|
||||
// SecondFactorTOTPIdentityStart the handler for initiating the identity validation.
|
||||
var SecondFactorTOTPIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
||||
// TOTPIdentityStart the handler for initiating the identity validation.
|
||||
var TOTPIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
||||
MailTitle: "Register your mobile",
|
||||
MailButtonContent: "Register",
|
||||
TargetEndpoint: "/one-time-password/register",
|
||||
|
@ -35,7 +35,7 @@ var SecondFactorTOTPIdentityStart = middlewares.IdentityVerificationStart(middle
|
|||
IdentityRetrieverFunc: identityRetrieverFromSession,
|
||||
}, nil)
|
||||
|
||||
func secondFactorTOTPIdentityFinish(ctx *middlewares.AutheliaCtx, username string) {
|
||||
func totpIdentityFinish(ctx *middlewares.AutheliaCtx, username string) {
|
||||
var (
|
||||
config *model.TOTPConfiguration
|
||||
err error
|
||||
|
@ -62,9 +62,9 @@ func secondFactorTOTPIdentityFinish(ctx *middlewares.AutheliaCtx, username strin
|
|||
}
|
||||
}
|
||||
|
||||
// SecondFactorTOTPIdentityFinish the handler for finishing the identity validation.
|
||||
var SecondFactorTOTPIdentityFinish = middlewares.IdentityVerificationFinish(
|
||||
// TOTPIdentityFinish the handler for finishing the identity validation.
|
||||
var TOTPIdentityFinish = middlewares.IdentityVerificationFinish(
|
||||
middlewares.IdentityVerificationFinishArgs{
|
||||
ActionClaim: ActionTOTPRegistration,
|
||||
IsTokenUserValidFunc: isTokenUserValidFor2FARegistration,
|
||||
}, secondFactorTOTPIdentityFinish)
|
||||
}, totpIdentityFinish)
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/regulation"
|
||||
)
|
||||
|
||||
// SecondFactorWebauthnIdentityStart the handler for initiating the identity validation.
|
||||
var SecondFactorWebauthnIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
||||
// WebauthnIdentityStart the handler for initiating the identity validation.
|
||||
var WebauthnIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
|
||||
MailTitle: "Register your key",
|
||||
MailButtonContent: "Register",
|
||||
TargetEndpoint: "/webauthn/register",
|
||||
|
@ -21,8 +21,8 @@ var SecondFactorWebauthnIdentityStart = middlewares.IdentityVerificationStart(mi
|
|||
IdentityRetrieverFunc: identityRetrieverFromSession,
|
||||
}, nil)
|
||||
|
||||
// SecondFactorWebauthnIdentityFinish the handler for finishing the identity validation.
|
||||
var SecondFactorWebauthnIdentityFinish = middlewares.IdentityVerificationFinish(
|
||||
// WebauthnIdentityFinish the handler for finishing the identity validation.
|
||||
var WebauthnIdentityFinish = middlewares.IdentityVerificationFinish(
|
||||
middlewares.IdentityVerificationFinishArgs{
|
||||
ActionClaim: ActionWebauthnRegistration,
|
||||
IsTokenUserValidFunc: isTokenUserValidFor2FARegistration,
|
||||
|
@ -81,8 +81,8 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
|
|||
}
|
||||
}
|
||||
|
||||
// SecondFactorWebauthnAttestationPOST processes the attestation challenge response from the client.
|
||||
func SecondFactorWebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
|
||||
// WebauthnAttestationPOST processes the attestation challenge response from the client.
|
||||
func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
|
||||
var (
|
||||
err error
|
||||
w *webauthn.WebAuthn
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// ResetPasswordPost handler for resetting passwords.
|
||||
func ResetPasswordPost(ctx *middlewares.AutheliaCtx) {
|
||||
// ResetPasswordPOST handler for resetting passwords.
|
||||
func ResetPasswordPOST(ctx *middlewares.AutheliaCtx) {
|
||||
userSession := ctx.GetSession()
|
||||
|
||||
// Those checks unsure that the identity verification process has been initiated and completed successfully
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// SecondFactorDuoPost handler for sending a push notification via duo api.
|
||||
func SecondFactorDuoPost(duoAPI duo.API) middlewares.RequestHandler {
|
||||
// DuoPOST handler for sending a push notification via duo api.
|
||||
func DuoPOST(duoAPI duo.API) middlewares.RequestHandler {
|
||||
return func(ctx *middlewares.AutheliaCtx) {
|
||||
var (
|
||||
requestBody signDuoRequestBody
|
||||
|
|
|
@ -58,7 +58,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldEnroll() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), DuoSignResponse{
|
||||
Result: enroll,
|
||||
|
@ -117,7 +117,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldAutoSelect() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldDenyAutoSelect() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), DuoSignResponse{
|
||||
Result: deny,
|
||||
|
@ -166,7 +166,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldFailAutoSelect() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.")
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldDeleteOldDeviceAndEnroll() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), DuoSignResponse{
|
||||
Result: enroll,
|
||||
|
@ -229,7 +229,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldDeleteOldDeviceAndCallPreauthAPIWit
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200OK(s.T(), DuoSignResponse{
|
||||
Result: enroll,
|
||||
|
@ -267,7 +267,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldUseOldDeviceAndSelect() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), DuoDevicesResponse{Result: auth, Devices: apiDevices})
|
||||
}
|
||||
|
||||
|
@ -323,7 +323,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldUseInvalidMethodAndAutoSelect() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
||||
|
@ -346,7 +346,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoPreauthAPIAndAllowAccess() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -376,7 +376,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoPreauthAPIAndDenyAccess() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 401, s.mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -394,7 +394,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoPreauthAPIAndFail() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.")
|
||||
}
|
||||
|
@ -446,7 +446,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoAPIAndDenyAccess() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 401, s.mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -477,7 +477,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoAPIAndFail() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.")
|
||||
}
|
||||
|
@ -525,7 +525,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldRedirectUserToDefaultURL() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), redirectResponse{
|
||||
Redirect: testRedirectionURL,
|
||||
})
|
||||
|
@ -572,7 +572,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldNotReturnRedirectURL() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), nil)
|
||||
}
|
||||
|
||||
|
@ -619,7 +619,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldRedirectUserToSafeTargetURL() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), redirectResponse{
|
||||
Redirect: "https://mydomain.local",
|
||||
})
|
||||
|
@ -668,7 +668,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldNotRedirectToUnsafeURL() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), nil)
|
||||
}
|
||||
|
||||
|
@ -718,7 +718,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldRegenerateSessionForPreventingSessi
|
|||
r := regexp.MustCompile("^authelia_session=(.*); path=")
|
||||
res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1)
|
||||
|
||||
SecondFactorDuoPost(duoMock)(s.mock.Ctx)
|
||||
DuoPOST(duoMock)(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), nil)
|
||||
|
||||
s.Assert().NotEqual(
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/regulation"
|
||||
)
|
||||
|
||||
// SecondFactorTOTPPost validate the TOTP passcode provided by the user.
|
||||
func SecondFactorTOTPPost(ctx *middlewares.AutheliaCtx) {
|
||||
// TimeBasedOneTimePasswordPOST validate the TOTP passcode provided by the user.
|
||||
func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {
|
||||
requestBody := signTOTPRequestBody{}
|
||||
|
||||
if err := ctx.ParseBody(&requestBody); err != nil {
|
||||
|
|
|
@ -65,7 +65,7 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToDefaultURL() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorTOTPPost(s.mock.Ctx)
|
||||
TimeBasedOneTimePasswordPOST(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), redirectResponse{
|
||||
Redirect: testRedirectionURL,
|
||||
})
|
||||
|
@ -103,7 +103,7 @@ func (s *HandlerSignTOTPSuite) TestShouldFailWhenTOTPSignInInfoFailsToUpdate() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorTOTPPost(s.mock.Ctx)
|
||||
TimeBasedOneTimePasswordPOST(s.mock.Ctx)
|
||||
s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.")
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ func (s *HandlerSignTOTPSuite) TestShouldNotReturnRedirectURL() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorTOTPPost(s.mock.Ctx)
|
||||
TimeBasedOneTimePasswordPOST(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), nil)
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToSafeTargetURL() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorTOTPPost(s.mock.Ctx)
|
||||
TimeBasedOneTimePasswordPOST(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), redirectResponse{
|
||||
Redirect: "https://mydomain.local",
|
||||
})
|
||||
|
@ -211,7 +211,7 @@ func (s *HandlerSignTOTPSuite) TestShouldNotRedirectToUnsafeURL() {
|
|||
s.Require().NoError(err)
|
||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||
|
||||
SecondFactorTOTPPost(s.mock.Ctx)
|
||||
TimeBasedOneTimePasswordPOST(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), nil)
|
||||
}
|
||||
|
||||
|
@ -250,7 +250,7 @@ func (s *HandlerSignTOTPSuite) TestShouldRegenerateSessionForPreventingSessionFi
|
|||
r := regexp.MustCompile("^authelia_session=(.*); path=")
|
||||
res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1)
|
||||
|
||||
SecondFactorTOTPPost(s.mock.Ctx)
|
||||
TimeBasedOneTimePasswordPOST(s.mock.Ctx)
|
||||
s.mock.Assert200OK(s.T(), nil)
|
||||
|
||||
s.Assert().NotEqual(
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/regulation"
|
||||
)
|
||||
|
||||
// SecondFactorWebauthnAssertionGET handler starts the assertion ceremony.
|
||||
func SecondFactorWebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
|
||||
// WebauthnAssertionGET handler starts the assertion ceremony.
|
||||
func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
|
||||
var (
|
||||
w *webauthn.WebAuthn
|
||||
user *model.WebauthnUser
|
||||
|
@ -78,8 +78,8 @@ func SecondFactorWebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
|
|||
}
|
||||
}
|
||||
|
||||
// SecondFactorWebauthnAssertionPOST handler completes the assertion ceremony after verifying the challenge.
|
||||
func SecondFactorWebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
|
||||
// WebauthnAssertionPOST handler completes the assertion ceremony after verifying the challenge.
|
||||
func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
|
||||
var (
|
||||
err error
|
||||
w *webauthn.WebAuthn
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
)
|
||||
|
||||
// StateGet is the handler serving the user state.
|
||||
func StateGet(ctx *middlewares.AutheliaCtx) {
|
||||
// StateGET is the handler serving the user state.
|
||||
func StateGET(ctx *middlewares.AutheliaCtx) {
|
||||
userSession := ctx.GetSession()
|
||||
stateResponse := StateResponse{
|
||||
Username: userSession.Username,
|
||||
|
|
|
@ -32,7 +32,7 @@ func (s *StateGetSuite) TestShouldReturnUsernameFromSession() {
|
|||
err := s.mock.Ctx.SaveSession(userSession)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
StateGet(s.mock.Ctx)
|
||||
StateGET(s.mock.Ctx)
|
||||
|
||||
type Response struct {
|
||||
Status string
|
||||
|
@ -62,7 +62,7 @@ func (s *StateGetSuite) TestShouldReturnAuthenticationLevelFromSession() {
|
|||
err := s.mock.Ctx.SaveSession(userSession)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
StateGet(s.mock.Ctx)
|
||||
StateGET(s.mock.Ctx)
|
||||
|
||||
type Response struct {
|
||||
Status string
|
||||
|
|
|
@ -72,8 +72,8 @@ func UserInfoGET(ctx *middlewares.AutheliaCtx) {
|
|||
}
|
||||
}
|
||||
|
||||
// MethodPreferencePost update the user preferences regarding 2FA method.
|
||||
func MethodPreferencePost(ctx *middlewares.AutheliaCtx) {
|
||||
// MethodPreferencePOST update the user preferences regarding 2FA method.
|
||||
func MethodPreferencePOST(ctx *middlewares.AutheliaCtx) {
|
||||
bodyJSON := preferred2FAMethodBody{}
|
||||
|
||||
err := ctx.ParseBody(&bodyJSON)
|
||||
|
|
|
@ -390,7 +390,7 @@ func (s *SaveSuite) TearDownTest() {
|
|||
|
||||
func (s *SaveSuite) TestShouldReturnError500WhenNoBodyProvided() {
|
||||
s.mock.Ctx.Request.SetBody(nil)
|
||||
MethodPreferencePost(s.mock.Ctx)
|
||||
MethodPreferencePOST(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Operation failed.")
|
||||
assert.Equal(s.T(), "unable to parse body: unexpected end of JSON input", s.mock.Hook.LastEntry().Message)
|
||||
|
@ -399,7 +399,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenNoBodyProvided() {
|
|||
|
||||
func (s *SaveSuite) TestShouldReturnError500WhenMalformedBodyProvided() {
|
||||
s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"abc\""))
|
||||
MethodPreferencePost(s.mock.Ctx)
|
||||
MethodPreferencePOST(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Operation failed.")
|
||||
assert.Equal(s.T(), "unable to parse body: unexpected end of JSON input", s.mock.Hook.LastEntry().Message)
|
||||
|
@ -408,7 +408,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenMalformedBodyProvided() {
|
|||
|
||||
func (s *SaveSuite) TestShouldReturnError500WhenBadBodyProvided() {
|
||||
s.mock.Ctx.Request.SetBody([]byte("{\"weird_key\":\"abc\"}"))
|
||||
MethodPreferencePost(s.mock.Ctx)
|
||||
MethodPreferencePOST(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Operation failed.")
|
||||
assert.Equal(s.T(), "unable to validate body: method: non zero value required", s.mock.Hook.LastEntry().Message)
|
||||
|
@ -417,7 +417,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenBadBodyProvided() {
|
|||
|
||||
func (s *SaveSuite) TestShouldReturnError500WhenBadMethodProvided() {
|
||||
s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"abc\"}"))
|
||||
MethodPreferencePost(s.mock.Ctx)
|
||||
MethodPreferencePOST(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Operation failed.")
|
||||
assert.Equal(s.T(), "unknown or unavailable method 'abc', it should be one of totp, webauthn", s.mock.Hook.LastEntry().Message)
|
||||
|
@ -430,7 +430,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenDatabaseFailsToSave() {
|
|||
SavePreferred2FAMethod(s.mock.Ctx, gomock.Eq("john"), gomock.Eq("webauthn")).
|
||||
Return(fmt.Errorf("Failure"))
|
||||
|
||||
MethodPreferencePost(s.mock.Ctx)
|
||||
MethodPreferencePOST(s.mock.Ctx)
|
||||
|
||||
s.mock.Assert200KO(s.T(), "Operation failed.")
|
||||
assert.Equal(s.T(), "unable to save new preferred 2FA method: Failure", s.mock.Hook.LastEntry().Message)
|
||||
|
@ -443,7 +443,7 @@ func (s *SaveSuite) TestShouldReturn200WhenMethodIsSuccessfullySaved() {
|
|||
SavePreferred2FAMethod(s.mock.Ctx, gomock.Eq("john"), gomock.Eq("webauthn")).
|
||||
Return(nil)
|
||||
|
||||
MethodPreferencePost(s.mock.Ctx)
|
||||
MethodPreferencePOST(s.mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/storage"
|
||||
)
|
||||
|
||||
// UserTOTPGet returns the users TOTP configuration.
|
||||
func UserTOTPGet(ctx *middlewares.AutheliaCtx) {
|
||||
// UserTOTPInfoGET returns the users TOTP configuration.
|
||||
func UserTOTPInfoGET(ctx *middlewares.AutheliaCtx) {
|
||||
userSession := ctx.GetSession()
|
||||
|
||||
config, err := ctx.Providers.StorageProvider.LoadTOTPConfiguration(ctx, userSession.Username)
|
||||
|
|
|
@ -440,8 +440,8 @@ func verifyAuth(ctx *middlewares.AutheliaCtx, targetURL *url.URL, refreshProfile
|
|||
return
|
||||
}
|
||||
|
||||
// VerifyGet returns the handler verifying if a request is allowed to go through.
|
||||
func VerifyGet(cfg schema.AuthenticationBackendConfiguration) middlewares.RequestHandler {
|
||||
// VerifyGET returns the handler verifying if a request is allowed to go through.
|
||||
func VerifyGET(cfg schema.AuthenticationBackendConfiguration) middlewares.RequestHandler {
|
||||
refreshProfile, refreshProfileInterval := getProfileRefreshSettings(cfg)
|
||||
|
||||
return func(ctx *middlewares.AutheliaCtx) {
|
||||
|
|
|
@ -196,7 +196,7 @@ func (s *BasicAuthorizationSuite) TestShouldNotBeAbleToParseBasicAuth() {
|
|||
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpaaaaaaaaaaaaaaaa")
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyDefaultPolicy() {
|
|||
Groups: []string{"dev", "admins"},
|
||||
}, nil)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 403, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfBypassDomain() {
|
|||
Groups: []string{"dev", "admins"},
|
||||
}, nil)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfOneFactorDomain() {
|
|||
Groups: []string{"dev", "admins"},
|
||||
}, nil)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfTwoFactorDomain() {
|
|||
Groups: []string{"dev", "admins"},
|
||||
}, nil)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -311,7 +311,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfDenyDomain() {
|
|||
Groups: []string{"dev", "admins"},
|
||||
}, nil)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 403, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgOk() {
|
|||
Groups: []string{"dev", "admins"},
|
||||
}, nil)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgFailingNoHeader()
|
|||
mock.Ctx.QueryArgs().Add("auth", "basic")
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body()))
|
||||
|
@ -363,7 +363,7 @@ func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgFailingEmptyHeader
|
|||
mock.Ctx.Request.Header.Set("Authorization", "")
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body()))
|
||||
|
@ -383,7 +383,7 @@ func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgFailingWrongPasswo
|
|||
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
|
||||
Return(false, nil)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body()))
|
||||
|
@ -399,7 +399,7 @@ func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgFailingWrongHeader
|
|||
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body()))
|
||||
|
@ -422,7 +422,7 @@ func TestShouldVerifyWrongCredentialsInBasicAuth(t *testing.T) {
|
|||
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objp3cm9uZ3Bhc3M=")
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
|
||||
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
|
||||
"https://test.example.com", actualStatus, expStatus)
|
||||
|
@ -439,7 +439,7 @@ func TestShouldVerifyFailingPasswordCheckingInBasicAuth(t *testing.T) {
|
|||
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objp3cm9uZ3Bhc3M=")
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
|
||||
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
|
||||
"https://test.example.com", actualStatus, expStatus)
|
||||
|
@ -460,7 +460,7 @@ func TestShouldVerifyFailingDetailsFetchingInBasicAuth(t *testing.T) {
|
|||
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
|
||||
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
|
||||
"https://test.example.com", actualStatus, expStatus)
|
||||
|
@ -484,7 +484,7 @@ func TestShouldNotCrashOnEmptyEmail(t *testing.T) {
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://bypass.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
expStatus, actualStatus := 200, mock.Ctx.Response.StatusCode()
|
||||
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
|
||||
|
@ -545,7 +545,7 @@ func TestShouldVerifyAuthorizationsUsingSessionCookie(t *testing.T) {
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", testCase.URL)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
expStatus, actualStatus := testCase.ExpectedStatusCode, mock.Ctx.Response.StatusCode()
|
||||
assert.Equal(t, expStatus, actualStatus, "URL=%s -> AuthLevel=%d, StatusCode=%d != ExpectedStatusCode=%d",
|
||||
testCase.URL, testCase.AuthenticationLevel, actualStatus, expStatus)
|
||||
|
@ -584,7 +584,7 @@ func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) {
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
// The session has been destroyed.
|
||||
newUserSession := mock.Ctx.GetSession()
|
||||
|
@ -617,7 +617,7 @@ func TestShouldDestroySessionWhenInactiveForTooLongUsingDurationNotation(t *test
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
// The session has been destroyed.
|
||||
newUserSession := mock.Ctx.GetSession()
|
||||
|
@ -646,7 +646,7 @@ func TestShouldKeepSessionWhenUserCheckedRememberMeAndIsInactiveForTooLong(t *te
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
// Check the session is still active.
|
||||
newUserSession := mock.Ctx.GetSession()
|
||||
|
@ -679,7 +679,7 @@ func TestShouldKeepSessionWhenInactivityTimeoutHasNotBeenExceeded(t *testing.T)
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
// The session has been destroyed.
|
||||
newUserSession := mock.Ctx.GetSession()
|
||||
|
@ -718,7 +718,7 @@ func TestShouldRedirectWhenSessionInactiveForTooLongAndRDParamProvided(t *testin
|
|||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
|
||||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", "GET")
|
||||
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8")
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(t, "<a href=\"https://login.example.com/?rd=https%3A%2F%2Ftwo-factor.example.com&rm=GET\">Found</a>",
|
||||
string(mock.Ctx.Response.Body()))
|
||||
|
@ -738,7 +738,7 @@ func TestShouldRedirectWithCorrectStatusCodeBasedOnRequestMethod(t *testing.T) {
|
|||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", "GET")
|
||||
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(t, "<a href=\"https://login.example.com/?rd=https%3A%2F%2Ftwo-factor.example.com&rm=GET\">Found</a>",
|
||||
string(mock.Ctx.Response.Body()))
|
||||
|
@ -749,7 +749,7 @@ func TestShouldRedirectWithCorrectStatusCodeBasedOnRequestMethod(t *testing.T) {
|
|||
mock.Ctx.Request.Header.Set("X-Forwarded-Method", "POST")
|
||||
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(t, "<a href=\"https://login.example.com/?rd=https%3A%2F%2Ftwo-factor.example.com&rm=POST\">See Other</a>",
|
||||
string(mock.Ctx.Response.Body()))
|
||||
|
@ -777,7 +777,7 @@ func TestShouldUpdateInactivityTimestampEvenWhenHittingForbiddenResources(t *tes
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://deny.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
// The resource if forbidden.
|
||||
assert.Equal(t, 403, mock.Ctx.Response.StatusCode())
|
||||
|
@ -806,7 +806,7 @@ func TestShouldURLEncodeRedirectionURLParameter(t *testing.T) {
|
|||
mock.Ctx.Request.SetHost("mydomain.com")
|
||||
mock.Ctx.Request.SetRequestURI("/?rd=https://auth.mydomain.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(t, "<a href=\"https://auth.mydomain.com/?rd=https%3A%2F%2Ftwo-factor.example.com\">Found</a>",
|
||||
string(mock.Ctx.Response.Body()))
|
||||
|
@ -889,7 +889,7 @@ func TestShouldNotRefreshUserGroupsFromBackend(t *testing.T) {
|
|||
|
||||
cfg := verifyGetCfg
|
||||
cfg.RefreshInterval = "disable"
|
||||
verifyGet := VerifyGet(cfg)
|
||||
verifyGet := VerifyGET(cfg)
|
||||
|
||||
mock.UserProviderMock.EXPECT().GetDetails("john").Times(0)
|
||||
|
||||
|
@ -973,7 +973,7 @@ func TestShouldNotRefreshUserGroupsFromBackendWhenDisabled(t *testing.T) {
|
|||
config := verifyGetCfg
|
||||
config.RefreshInterval = schema.ProfileRefreshDisabled
|
||||
|
||||
VerifyGet(config)(mock.Ctx)
|
||||
VerifyGET(config)(mock.Ctx)
|
||||
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
|
||||
|
||||
// Session time should NOT have been updated, it should still have a refresh TTL 1 minute in the past.
|
||||
|
@ -1016,7 +1016,7 @@ func TestShouldDestroySessionWhenUserNotExist(t *testing.T) {
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
|
||||
|
||||
// Session time should NOT have been updated, it should still have a refresh TTL 1 minute in the past.
|
||||
|
@ -1031,7 +1031,7 @@ func TestShouldDestroySessionWhenUserNotExist(t *testing.T) {
|
|||
|
||||
mock.UserProviderMock.EXPECT().GetDetails("john").Return(nil, authentication.ErrUserNotFound).Times(1)
|
||||
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(t, 401, mock.Ctx.Response.StatusCode())
|
||||
|
||||
|
@ -1056,7 +1056,7 @@ func TestShouldGetRemovedUserGroupsFromBackend(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
verifyGet := VerifyGet(verifyGetCfg)
|
||||
verifyGet := VerifyGET(verifyGetCfg)
|
||||
|
||||
mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(2)
|
||||
|
||||
|
@ -1127,7 +1127,7 @@ func TestShouldGetAddedUserGroupsFromBackend(t *testing.T) {
|
|||
|
||||
mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(1)
|
||||
|
||||
verifyGet := VerifyGet(verifyGetCfg)
|
||||
verifyGet := VerifyGET(verifyGetCfg)
|
||||
|
||||
mock.Clock.Set(time.Now())
|
||||
|
||||
|
@ -1180,7 +1180,7 @@ func TestShouldGetAddedUserGroupsFromBackend(t *testing.T) {
|
|||
)
|
||||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://grafana.example.com")
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
|
||||
|
||||
// Check admin group is removed from the session.
|
||||
|
@ -1212,7 +1212,7 @@ func TestShouldCheckValidSessionUsernameHeaderAndReturn200(t *testing.T) {
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
|
||||
mock.Ctx.Request.Header.SetBytesK(headerSessionUsername, testUsername)
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(t, expectedStatusCode, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, "", string(mock.Ctx.Response.Body()))
|
||||
|
@ -1236,7 +1236,7 @@ func TestShouldCheckInvalidSessionUsernameHeaderAndReturn401(t *testing.T) {
|
|||
|
||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
|
||||
mock.Ctx.Request.Header.SetBytesK(headerSessionUsername, "root")
|
||||
VerifyGet(verifyGetCfg)(mock.Ctx)
|
||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||
|
||||
assert.Equal(t, expectedStatusCode, mock.Ctx.Response.StatusCode())
|
||||
assert.Equal(t, "Unauthorized", string(mock.Ctx.Response.Body()))
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
)
|
||||
|
||||
// RequireFirstFactor check if user has enough permissions to execute the next handler.
|
||||
func RequireFirstFactor(next RequestHandler) RequestHandler {
|
||||
// Require1FA check if user has enough permissions to execute the next handler.
|
||||
func Require1FA(next RequestHandler) RequestHandler {
|
||||
return func(ctx *AutheliaCtx) {
|
||||
if ctx.GetSession().AuthenticationLevel < authentication.OneFactor {
|
||||
ctx.ReplyForbidden()
|
||||
|
|
|
@ -2,36 +2,61 @@ package server
|
|||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
duoapi "github.com/duosecurity/duo_api_golang"
|
||||
"github.com/fasthttp/router"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp/expvarhandler"
|
||||
"github.com/valyala/fasthttp/pprofhandler"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/duo"
|
||||
"github.com/authelia/authelia/v4/internal/handlers"
|
||||
"github.com/authelia/authelia/v4/internal/logging"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/oidc"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// Replacement for the default error handler in fasthttp.
|
||||
func handlerErrors(ctx *fasthttp.RequestCtx, err error) {
|
||||
func handlerError() func(ctx *fasthttp.RequestCtx, err error) {
|
||||
logger := logging.Logger()
|
||||
|
||||
switch e := err.(type) {
|
||||
case *fasthttp.ErrSmallBuffer:
|
||||
logger.Tracef("Request was too large to handle from client %s. Response Code %d.", ctx.RemoteIP().String(), fasthttp.StatusRequestHeaderFieldsTooLarge)
|
||||
ctx.Error("request header too large", fasthttp.StatusRequestHeaderFieldsTooLarge)
|
||||
case *net.OpError:
|
||||
if e.Timeout() {
|
||||
// TODO: Add X-Forwarded-For Check here.
|
||||
logger.Tracef("Request timeout occurred while handling from client %s: %s. Response Code %d.", ctx.RemoteIP().String(), ctx.RequestURI(), fasthttp.StatusRequestTimeout)
|
||||
ctx.Error("request timeout", fasthttp.StatusRequestTimeout)
|
||||
} else {
|
||||
// TODO: Add X-Forwarded-For Check here.
|
||||
logger.Tracef("An unknown error occurred while handling a request from client %s: %s. Response Code %d.", ctx.RemoteIP().String(), ctx.RequestURI(), fasthttp.StatusBadRequest)
|
||||
headerXForwardedFor := []byte(fasthttp.HeaderXForwardedFor)
|
||||
|
||||
getRemoteIP := func(ctx *fasthttp.RequestCtx) string {
|
||||
if hdr := ctx.Request.Header.PeekBytes(headerXForwardedFor); hdr != nil {
|
||||
ips := strings.Split(string(hdr), ",")
|
||||
|
||||
if len(ips) > 0 {
|
||||
return strings.Trim(ips[0], " ")
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.RemoteIP().String()
|
||||
}
|
||||
|
||||
return func(ctx *fasthttp.RequestCtx, err error) {
|
||||
switch e := err.(type) {
|
||||
case *fasthttp.ErrSmallBuffer:
|
||||
logger.Tracef("Request was too large to handle from client %s. Response Code %d.", getRemoteIP(ctx), fasthttp.StatusRequestHeaderFieldsTooLarge)
|
||||
ctx.Error("request header too large", fasthttp.StatusRequestHeaderFieldsTooLarge)
|
||||
case *net.OpError:
|
||||
if e.Timeout() {
|
||||
logger.Tracef("Request timeout occurred while handling from client %s: %s. Response Code %d.", getRemoteIP(ctx), ctx.RequestURI(), fasthttp.StatusRequestTimeout)
|
||||
ctx.Error("request timeout", fasthttp.StatusRequestTimeout)
|
||||
} else {
|
||||
logger.Tracef("An unknown error occurred while handling a request from client %s: %s. Response Code %d.", getRemoteIP(ctx), ctx.RequestURI(), fasthttp.StatusBadRequest)
|
||||
ctx.Error("error when parsing request", fasthttp.StatusBadRequest)
|
||||
}
|
||||
default:
|
||||
logger.Tracef("An unknown error occurred while handling a request from client %s: %s. Response Code %d.", getRemoteIP(ctx), ctx.RequestURI(), fasthttp.StatusBadRequest)
|
||||
ctx.Error("error when parsing request", fasthttp.StatusBadRequest)
|
||||
}
|
||||
default:
|
||||
// TODO: Add X-Forwarded-For Check here.
|
||||
logger.Tracef("An unknown error occurred while handling a request from client %s: %s. Response Code %d.", ctx.RemoteIP().String(), ctx.RequestURI(), fasthttp.StatusBadRequest)
|
||||
ctx.Error("error when parsing request", fasthttp.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,3 +79,229 @@ func handlerNotFound(next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
|||
func handlerMethodNotAllowed(ctx *fasthttp.RequestCtx) {
|
||||
handlers.SetStatusCodeResponse(ctx, fasthttp.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
func getHandler(config schema.Configuration, providers middlewares.Providers) fasthttp.RequestHandler {
|
||||
rememberMe := strconv.FormatBool(config.Session.RememberMeDuration != schema.RememberMeDisabled)
|
||||
resetPassword := strconv.FormatBool(!config.AuthenticationBackend.DisableResetPassword)
|
||||
|
||||
resetPasswordCustomURL := config.AuthenticationBackend.PasswordReset.CustomURL.String()
|
||||
|
||||
duoSelfEnrollment := f
|
||||
if config.DuoAPI != nil {
|
||||
duoSelfEnrollment = strconv.FormatBool(config.DuoAPI.EnableSelfEnrollment)
|
||||
}
|
||||
|
||||
https := config.Server.TLS.Key != "" && config.Server.TLS.Certificate != ""
|
||||
|
||||
serveIndexHandler := ServeTemplatedFile(embeddedAssets, indexFile, config.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, config.Session.Name, config.Theme, https)
|
||||
serveSwaggerHandler := ServeTemplatedFile(swaggerAssets, indexFile, config.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, config.Session.Name, config.Theme, https)
|
||||
serveSwaggerAPIHandler := ServeTemplatedFile(swaggerAssets, apiFile, config.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, config.Session.Name, config.Theme, https)
|
||||
|
||||
handlerPublicHTML := newPublicHTMLEmbeddedHandler()
|
||||
handlerLocales := newLocalesEmbeddedHandler()
|
||||
|
||||
middleware := middlewares.AutheliaMiddleware(config, providers)
|
||||
|
||||
policyCORSPublicGET := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowedMethods("OPTIONS", "GET").
|
||||
WithAllowedOrigins("*").
|
||||
Build()
|
||||
|
||||
r := router.New()
|
||||
|
||||
// Static Assets.
|
||||
r.GET("/", middleware(serveIndexHandler))
|
||||
|
||||
for _, f := range rootFiles {
|
||||
r.GET("/"+f, handlerPublicHTML)
|
||||
}
|
||||
|
||||
r.GET("/favicon.ico", middlewares.AssetOverrideMiddleware(config.Server.AssetPath, 0, handlerPublicHTML))
|
||||
r.GET("/static/media/logo.png", middlewares.AssetOverrideMiddleware(config.Server.AssetPath, 2, handlerPublicHTML))
|
||||
r.GET("/static/{filepath:*}", handlerPublicHTML)
|
||||
|
||||
// Locales.
|
||||
r.GET("/locales/{language:[a-z]{1,3}}-{variant:[a-z0-9-]+}/{namespace:[a-z]+}.json", middlewares.AssetOverrideMiddleware(config.Server.AssetPath, 0, handlerLocales))
|
||||
r.GET("/locales/{language:[a-z]{1,3}}/{namespace:[a-z]+}.json", middlewares.AssetOverrideMiddleware(config.Server.AssetPath, 0, handlerLocales))
|
||||
|
||||
// Swagger.
|
||||
r.GET("/api/", middleware(serveSwaggerHandler))
|
||||
r.OPTIONS("/api/", policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET("/api/"+apiFile, policyCORSPublicGET.Middleware(middleware(serveSwaggerAPIHandler)))
|
||||
r.OPTIONS("/api/"+apiFile, policyCORSPublicGET.HandleOPTIONS)
|
||||
|
||||
for _, file := range swaggerFiles {
|
||||
r.GET("/api/"+file, handlerPublicHTML)
|
||||
}
|
||||
|
||||
r.GET("/api/health", middleware(handlers.HealthGET))
|
||||
r.GET("/api/state", middleware(handlers.StateGET))
|
||||
|
||||
r.GET("/api/configuration", middleware(middlewares.Require1FA(handlers.ConfigurationGET)))
|
||||
|
||||
r.GET("/api/configuration/password-policy", middleware(handlers.PasswordPolicyConfigurationGet))
|
||||
|
||||
r.GET("/api/verify", middleware(handlers.VerifyGET(config.AuthenticationBackend)))
|
||||
r.HEAD("/api/verify", middleware(handlers.VerifyGET(config.AuthenticationBackend)))
|
||||
|
||||
r.POST("/api/checks/safe-redirection", middleware(handlers.CheckSafeRedirectionPOST))
|
||||
|
||||
delayFunc := middlewares.TimingAttackDelay(10, 250, 85, time.Second)
|
||||
|
||||
r.POST("/api/firstfactor", middleware(handlers.FirstFactorPOST(delayFunc)))
|
||||
r.POST("/api/logout", middleware(handlers.LogoutPOST))
|
||||
|
||||
// Only register endpoints if forgot password is not disabled.
|
||||
if !config.AuthenticationBackend.DisableResetPassword &&
|
||||
config.AuthenticationBackend.PasswordReset.CustomURL.String() == "" {
|
||||
// Password reset related endpoints.
|
||||
r.POST("/api/reset-password/identity/start", middleware(handlers.ResetPasswordIdentityStart))
|
||||
r.POST("/api/reset-password/identity/finish", middleware(handlers.ResetPasswordIdentityFinish))
|
||||
r.POST("/api/reset-password", middleware(handlers.ResetPasswordPOST))
|
||||
}
|
||||
|
||||
// Information about the user.
|
||||
r.GET("/api/user/info", middleware(middlewares.Require1FA(handlers.UserInfoGET)))
|
||||
r.POST("/api/user/info", middleware(middlewares.Require1FA(handlers.UserInfoPOST)))
|
||||
r.POST("/api/user/info/2fa_method", middleware(middlewares.Require1FA(handlers.MethodPreferencePOST)))
|
||||
|
||||
if !config.TOTP.Disable {
|
||||
// TOTP related endpoints.
|
||||
r.GET("/api/user/info/totp", middleware(middlewares.Require1FA(handlers.UserTOTPInfoGET)))
|
||||
r.POST("/api/secondfactor/totp/identity/start", middleware(middlewares.Require1FA(handlers.TOTPIdentityStart)))
|
||||
r.POST("/api/secondfactor/totp/identity/finish", middleware(middlewares.Require1FA(handlers.TOTPIdentityFinish)))
|
||||
r.POST("/api/secondfactor/totp", middleware(middlewares.Require1FA(handlers.TimeBasedOneTimePasswordPOST)))
|
||||
}
|
||||
|
||||
if !config.Webauthn.Disable {
|
||||
// Webauthn Endpoints.
|
||||
r.POST("/api/secondfactor/webauthn/identity/start", middleware(middlewares.Require1FA(handlers.WebauthnIdentityStart)))
|
||||
r.POST("/api/secondfactor/webauthn/identity/finish", middleware(middlewares.Require1FA(handlers.WebauthnIdentityFinish)))
|
||||
r.POST("/api/secondfactor/webauthn/attestation", middleware(middlewares.Require1FA(handlers.WebauthnAttestationPOST)))
|
||||
|
||||
r.GET("/api/secondfactor/webauthn/assertion", middleware(middlewares.Require1FA(handlers.WebauthnAssertionGET)))
|
||||
r.POST("/api/secondfactor/webauthn/assertion", middleware(middlewares.Require1FA(handlers.WebauthnAssertionPOST)))
|
||||
}
|
||||
|
||||
// Configure DUO api endpoint only if configuration exists.
|
||||
if config.DuoAPI != nil {
|
||||
var duoAPI duo.API
|
||||
if os.Getenv("ENVIRONMENT") == dev {
|
||||
duoAPI = duo.NewDuoAPI(duoapi.NewDuoApi(
|
||||
config.DuoAPI.IntegrationKey,
|
||||
config.DuoAPI.SecretKey,
|
||||
config.DuoAPI.Hostname, "", duoapi.SetInsecure()))
|
||||
} else {
|
||||
duoAPI = duo.NewDuoAPI(duoapi.NewDuoApi(
|
||||
config.DuoAPI.IntegrationKey,
|
||||
config.DuoAPI.SecretKey,
|
||||
config.DuoAPI.Hostname, ""))
|
||||
}
|
||||
|
||||
r.GET("/api/secondfactor/duo_devices", middleware(middlewares.Require1FA(handlers.DuoDevicesGET(duoAPI))))
|
||||
r.POST("/api/secondfactor/duo", middleware(middlewares.Require1FA(handlers.DuoPOST(duoAPI))))
|
||||
r.POST("/api/secondfactor/duo_device", middleware(middlewares.Require1FA(handlers.DuoDevicePOST)))
|
||||
}
|
||||
|
||||
if config.Server.EnablePprof {
|
||||
r.GET("/debug/pprof/{name?}", pprofhandler.PprofHandler)
|
||||
}
|
||||
|
||||
if config.Server.EnableExpvars {
|
||||
r.GET("/debug/vars", expvarhandler.ExpvarHandler)
|
||||
}
|
||||
|
||||
if providers.OpenIDConnect.Fosite != nil {
|
||||
r.GET("/api/oidc/consent", middleware(handlers.OpenIDConnectConsentGET))
|
||||
r.POST("/api/oidc/consent", middleware(handlers.OpenIDConnectConsentPOST))
|
||||
|
||||
allowedOrigins := utils.StringSliceFromURLs(config.IdentityProviders.OIDC.CORS.AllowedOrigins)
|
||||
|
||||
r.OPTIONS(oidc.WellKnownOpenIDConfigurationPath, policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET(oidc.WellKnownOpenIDConfigurationPath, policyCORSPublicGET.Middleware(middleware(handlers.OpenIDConnectConfigurationWellKnownGET)))
|
||||
|
||||
r.OPTIONS(oidc.WellKnownOAuthAuthorizationServerPath, policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET(oidc.WellKnownOAuthAuthorizationServerPath, policyCORSPublicGET.Middleware(middleware(handlers.OAuthAuthorizationServerWellKnownGET)))
|
||||
|
||||
r.OPTIONS(oidc.JWKsPath, policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET(oidc.JWKsPath, policyCORSPublicGET.Middleware(middleware(handlers.JSONWebKeySetGET)))
|
||||
|
||||
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
|
||||
r.OPTIONS("/api/oidc/jwks", policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET("/api/oidc/jwks", policyCORSPublicGET.Middleware(middleware(handlers.JSONWebKeySetGET)))
|
||||
|
||||
policyCORSAuthorization := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowedMethods("OPTIONS", "GET").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.AuthorizationEndpoint, config.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.AuthorizationPath, policyCORSAuthorization.HandleOnlyOPTIONS)
|
||||
r.GET(oidc.AuthorizationPath, middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorizationGET)))
|
||||
|
||||
// TODO (james-d-elliott): Remove in GA. This is a legacy endpoint.
|
||||
r.OPTIONS("/api/oidc/authorize", policyCORSAuthorization.HandleOnlyOPTIONS)
|
||||
r.GET("/api/oidc/authorize", middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorizationGET)))
|
||||
|
||||
policyCORSToken := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods("OPTIONS", "POST").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.TokenEndpoint, config.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.TokenPath, policyCORSToken.HandleOPTIONS)
|
||||
r.POST(oidc.TokenPath, policyCORSToken.Middleware(middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectTokenPOST))))
|
||||
|
||||
policyCORSUserinfo := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods("OPTIONS", "GET", "POST").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.UserinfoEndpoint, config.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.UserinfoPath, policyCORSUserinfo.HandleOPTIONS)
|
||||
r.GET(oidc.UserinfoPath, policyCORSUserinfo.Middleware(middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectUserinfo))))
|
||||
r.POST(oidc.UserinfoPath, policyCORSUserinfo.Middleware(middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectUserinfo))))
|
||||
|
||||
policyCORSIntrospection := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods("OPTIONS", "POST").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.IntrospectionEndpoint, config.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.IntrospectionPath, policyCORSIntrospection.HandleOPTIONS)
|
||||
r.POST(oidc.IntrospectionPath, policyCORSIntrospection.Middleware(middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthIntrospectionPOST))))
|
||||
|
||||
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
|
||||
r.OPTIONS("/api/oidc/introspect", policyCORSIntrospection.HandleOPTIONS)
|
||||
r.POST("/api/oidc/introspect", policyCORSIntrospection.Middleware(middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthIntrospectionPOST))))
|
||||
|
||||
policyCORSRevocation := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods("OPTIONS", "POST").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.RevocationEndpoint, config.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.RevocationPath, policyCORSRevocation.HandleOPTIONS)
|
||||
r.POST(oidc.RevocationPath, policyCORSRevocation.Middleware(middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthRevocationPOST))))
|
||||
|
||||
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
|
||||
r.OPTIONS("/api/oidc/revoke", policyCORSRevocation.HandleOPTIONS)
|
||||
r.POST("/api/oidc/revoke", policyCORSRevocation.Middleware(middleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthRevocationPOST))))
|
||||
}
|
||||
|
||||
r.NotFound = handlerNotFound(middleware(serveIndexHandler))
|
||||
|
||||
r.HandleMethodNotAllowed = true
|
||||
r.MethodNotAllowed = handlerMethodNotAllowed
|
||||
|
||||
handler := middlewares.LogRequestMiddleware(r.Handler)
|
||||
if config.Server.Path != "" {
|
||||
handler = middlewares.StripPathMiddleware(config.Server.Path, handler)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
|
|
@ -6,285 +6,27 @@ import (
|
|||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
duoapi "github.com/duosecurity/duo_api_golang"
|
||||
"github.com/fasthttp/router"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp/expvarhandler"
|
||||
"github.com/valyala/fasthttp/pprofhandler"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/duo"
|
||||
"github.com/authelia/authelia/v4/internal/handlers"
|
||||
"github.com/authelia/authelia/v4/internal/logging"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/oidc"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// TODO: move to its own file and rename configuration -> config.
|
||||
func registerRoutes(configuration schema.Configuration, providers middlewares.Providers) fasthttp.RequestHandler {
|
||||
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)
|
||||
}
|
||||
|
||||
https := configuration.Server.TLS.Key != "" && configuration.Server.TLS.Certificate != ""
|
||||
|
||||
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)
|
||||
|
||||
handlerPublicHTML := newPublicHTMLEmbeddedHandler()
|
||||
handlerLocales := newLocalesEmbeddedHandler()
|
||||
|
||||
autheliaMiddleware := middlewares.AutheliaMiddleware(configuration, providers)
|
||||
|
||||
policyCORSPublicGET := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowedMethods("OPTIONS", "GET").
|
||||
WithAllowedOrigins("*").
|
||||
Build()
|
||||
|
||||
r := router.New()
|
||||
|
||||
// Static Assets.
|
||||
r.GET("/", autheliaMiddleware(serveIndexHandler))
|
||||
|
||||
for _, f := range rootFiles {
|
||||
r.GET("/"+f, handlerPublicHTML)
|
||||
}
|
||||
|
||||
r.GET("/favicon.ico", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, 0, handlerPublicHTML))
|
||||
r.GET("/static/media/logo.png", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, 2, handlerPublicHTML))
|
||||
r.GET("/static/{filepath:*}", handlerPublicHTML)
|
||||
|
||||
// Locales.
|
||||
r.GET("/locales/{language:[a-z]{1,3}}-{variant:[a-z0-9-]+}/{namespace:[a-z]+}.json", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, 0, handlerLocales))
|
||||
r.GET("/locales/{language:[a-z]{1,3}}/{namespace:[a-z]+}.json", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, 0, handlerLocales))
|
||||
|
||||
// Swagger.
|
||||
r.GET("/api/", autheliaMiddleware(serveSwaggerHandler))
|
||||
r.OPTIONS("/api/", policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET("/api/"+apiFile, policyCORSPublicGET.Middleware(autheliaMiddleware(serveSwaggerAPIHandler)))
|
||||
r.OPTIONS("/api/"+apiFile, policyCORSPublicGET.HandleOPTIONS)
|
||||
|
||||
for _, file := range swaggerFiles {
|
||||
r.GET("/api/"+file, handlerPublicHTML)
|
||||
}
|
||||
|
||||
r.GET("/api/health", autheliaMiddleware(handlers.HealthGet))
|
||||
r.GET("/api/state", autheliaMiddleware(handlers.StateGet))
|
||||
|
||||
r.GET("/api/configuration", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.ConfigurationGet)))
|
||||
|
||||
r.GET("/api/configuration/password-policy", autheliaMiddleware(handlers.PasswordPolicyConfigurationGet))
|
||||
|
||||
r.GET("/api/verify", autheliaMiddleware(handlers.VerifyGet(configuration.AuthenticationBackend)))
|
||||
r.HEAD("/api/verify", autheliaMiddleware(handlers.VerifyGet(configuration.AuthenticationBackend)))
|
||||
|
||||
r.POST("/api/checks/safe-redirection", autheliaMiddleware(handlers.CheckSafeRedirection))
|
||||
|
||||
r.POST("/api/firstfactor", autheliaMiddleware(handlers.FirstFactorPost(middlewares.TimingAttackDelay(10, 250, 85, time.Second))))
|
||||
r.POST("/api/logout", autheliaMiddleware(handlers.LogoutPost))
|
||||
|
||||
// Only register endpoints if forgot password is not disabled.
|
||||
if !configuration.AuthenticationBackend.DisableResetPassword &&
|
||||
configuration.AuthenticationBackend.PasswordReset.CustomURL.String() == "" {
|
||||
// Password reset related endpoints.
|
||||
r.POST("/api/reset-password/identity/start", autheliaMiddleware(
|
||||
handlers.ResetPasswordIdentityStart))
|
||||
r.POST("/api/reset-password/identity/finish", autheliaMiddleware(
|
||||
handlers.ResetPasswordIdentityFinish))
|
||||
r.POST("/api/reset-password", autheliaMiddleware(
|
||||
handlers.ResetPasswordPost))
|
||||
}
|
||||
|
||||
// Information about the user.
|
||||
r.GET("/api/user/info", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.UserInfoGET)))
|
||||
r.POST("/api/user/info", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.UserInfoPOST)))
|
||||
r.POST("/api/user/info/2fa_method", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.MethodPreferencePost)))
|
||||
|
||||
if !configuration.TOTP.Disable {
|
||||
// TOTP related endpoints.
|
||||
r.GET("/api/user/info/totp", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.UserTOTPGet)))
|
||||
|
||||
r.POST("/api/secondfactor/totp/identity/start", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorTOTPIdentityStart)))
|
||||
r.POST("/api/secondfactor/totp/identity/finish", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorTOTPIdentityFinish)))
|
||||
r.POST("/api/secondfactor/totp", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorTOTPPost)))
|
||||
}
|
||||
|
||||
if !configuration.Webauthn.Disable {
|
||||
// Webauthn Endpoints.
|
||||
r.POST("/api/secondfactor/webauthn/identity/start", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorWebauthnIdentityStart)))
|
||||
r.POST("/api/secondfactor/webauthn/identity/finish", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorWebauthnIdentityFinish)))
|
||||
r.POST("/api/secondfactor/webauthn/attestation", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorWebauthnAttestationPOST)))
|
||||
|
||||
r.GET("/api/secondfactor/webauthn/assertion", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorWebauthnAssertionGET)))
|
||||
r.POST("/api/secondfactor/webauthn/assertion", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorWebauthnAssertionPOST)))
|
||||
}
|
||||
|
||||
// Configure DUO api endpoint only if configuration exists.
|
||||
if configuration.DuoAPI != nil {
|
||||
var duoAPI duo.API
|
||||
if os.Getenv("ENVIRONMENT") == dev {
|
||||
duoAPI = duo.NewDuoAPI(duoapi.NewDuoApi(
|
||||
configuration.DuoAPI.IntegrationKey,
|
||||
configuration.DuoAPI.SecretKey,
|
||||
configuration.DuoAPI.Hostname, "", duoapi.SetInsecure()))
|
||||
} else {
|
||||
duoAPI = duo.NewDuoAPI(duoapi.NewDuoApi(
|
||||
configuration.DuoAPI.IntegrationKey,
|
||||
configuration.DuoAPI.SecretKey,
|
||||
configuration.DuoAPI.Hostname, ""))
|
||||
}
|
||||
|
||||
r.GET("/api/secondfactor/duo_devices", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorDuoDevicesGet(duoAPI))))
|
||||
|
||||
r.POST("/api/secondfactor/duo", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorDuoPost(duoAPI))))
|
||||
|
||||
r.POST("/api/secondfactor/duo_device", autheliaMiddleware(
|
||||
middlewares.RequireFirstFactor(handlers.SecondFactorDuoDevicePost)))
|
||||
}
|
||||
|
||||
if configuration.Server.EnablePprof {
|
||||
r.GET("/debug/pprof/{name?}", pprofhandler.PprofHandler)
|
||||
}
|
||||
|
||||
if configuration.Server.EnableExpvars {
|
||||
r.GET("/debug/vars", expvarhandler.ExpvarHandler)
|
||||
}
|
||||
|
||||
if providers.OpenIDConnect.Fosite != nil {
|
||||
r.GET("/api/oidc/consent", autheliaMiddleware(handlers.OpenIDConnectConsentGET))
|
||||
r.POST("/api/oidc/consent", autheliaMiddleware(handlers.OpenIDConnectConsentPOST))
|
||||
|
||||
allowedOrigins := utils.StringSliceFromURLs(configuration.IdentityProviders.OIDC.CORS.AllowedOrigins)
|
||||
|
||||
r.OPTIONS(oidc.WellKnownOpenIDConfigurationPath, policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET(oidc.WellKnownOpenIDConfigurationPath, policyCORSPublicGET.Middleware(autheliaMiddleware(handlers.OpenIDConnectConfigurationWellKnownGET)))
|
||||
|
||||
r.OPTIONS(oidc.WellKnownOAuthAuthorizationServerPath, policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET(oidc.WellKnownOAuthAuthorizationServerPath, policyCORSPublicGET.Middleware(autheliaMiddleware(handlers.OAuthAuthorizationServerWellKnownGET)))
|
||||
|
||||
r.OPTIONS(oidc.JWKsPath, policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET(oidc.JWKsPath, policyCORSPublicGET.Middleware(autheliaMiddleware(handlers.JSONWebKeySetGET)))
|
||||
|
||||
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
|
||||
r.OPTIONS("/api/oidc/jwks", policyCORSPublicGET.HandleOPTIONS)
|
||||
r.GET("/api/oidc/jwks", policyCORSPublicGET.Middleware(autheliaMiddleware(handlers.JSONWebKeySetGET)))
|
||||
|
||||
policyCORSAuthorization := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowedMethods("OPTIONS", "GET").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.AuthorizationEndpoint, configuration.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.AuthorizationPath, policyCORSAuthorization.HandleOnlyOPTIONS)
|
||||
r.GET(oidc.AuthorizationPath, autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorizationGET)))
|
||||
|
||||
// TODO (james-d-elliott): Remove in GA. This is a legacy endpoint.
|
||||
r.OPTIONS("/api/oidc/authorize", policyCORSAuthorization.HandleOnlyOPTIONS)
|
||||
r.GET("/api/oidc/authorize", autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorizationGET)))
|
||||
|
||||
policyCORSToken := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods("OPTIONS", "POST").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.TokenEndpoint, configuration.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.TokenPath, policyCORSToken.HandleOPTIONS)
|
||||
r.POST(oidc.TokenPath, policyCORSToken.Middleware(autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectTokenPOST))))
|
||||
|
||||
policyCORSUserinfo := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods("OPTIONS", "GET", "POST").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.UserinfoEndpoint, configuration.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.UserinfoPath, policyCORSUserinfo.HandleOPTIONS)
|
||||
r.GET(oidc.UserinfoPath, policyCORSUserinfo.Middleware(autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectUserinfo))))
|
||||
r.POST(oidc.UserinfoPath, policyCORSUserinfo.Middleware(autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectUserinfo))))
|
||||
|
||||
policyCORSIntrospection := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods("OPTIONS", "POST").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.IntrospectionEndpoint, configuration.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.IntrospectionPath, policyCORSIntrospection.HandleOPTIONS)
|
||||
r.POST(oidc.IntrospectionPath, policyCORSIntrospection.Middleware(autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthIntrospectionPOST))))
|
||||
|
||||
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
|
||||
r.OPTIONS("/api/oidc/introspect", policyCORSIntrospection.HandleOPTIONS)
|
||||
r.POST("/api/oidc/introspect", policyCORSIntrospection.Middleware(autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthIntrospectionPOST))))
|
||||
|
||||
policyCORSRevocation := middlewares.NewCORSPolicyBuilder().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods("OPTIONS", "POST").
|
||||
WithAllowedOrigins(allowedOrigins...).
|
||||
WithEnabled(utils.IsStringInSlice(oidc.RevocationEndpoint, configuration.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||
Build()
|
||||
|
||||
r.OPTIONS(oidc.RevocationPath, policyCORSRevocation.HandleOPTIONS)
|
||||
r.POST(oidc.RevocationPath, policyCORSRevocation.Middleware(autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthRevocationPOST))))
|
||||
|
||||
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
|
||||
r.OPTIONS("/api/oidc/revoke", policyCORSRevocation.HandleOPTIONS)
|
||||
r.POST("/api/oidc/revoke", policyCORSRevocation.Middleware(autheliaMiddleware(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthRevocationPOST))))
|
||||
}
|
||||
|
||||
r.NotFound = handlerNotFound(autheliaMiddleware(serveIndexHandler))
|
||||
|
||||
r.HandleMethodNotAllowed = true
|
||||
r.MethodNotAllowed = handlerMethodNotAllowed
|
||||
|
||||
handler := middlewares.LogRequestMiddleware(r.Handler)
|
||||
if configuration.Server.Path != "" {
|
||||
handler = middlewares.StripPathMiddleware(configuration.Server.Path, handler)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
// CreateServer Create Authelia's internal webserver with the given configuration and providers.
|
||||
func CreateServer(configuration schema.Configuration, providers middlewares.Providers) (*fasthttp.Server, net.Listener) {
|
||||
handler := registerRoutes(configuration, providers)
|
||||
|
||||
func CreateServer(config schema.Configuration, providers middlewares.Providers) (*fasthttp.Server, net.Listener) {
|
||||
server := &fasthttp.Server{
|
||||
ErrorHandler: handlerErrors,
|
||||
Handler: handler,
|
||||
ErrorHandler: handlerError(),
|
||||
Handler: getHandler(config, providers),
|
||||
NoDefaultServerHeader: true,
|
||||
ReadBufferSize: configuration.Server.ReadBufferSize,
|
||||
WriteBufferSize: configuration.Server.WriteBufferSize,
|
||||
ReadBufferSize: config.Server.ReadBufferSize,
|
||||
WriteBufferSize: config.Server.WriteBufferSize,
|
||||
}
|
||||
|
||||
logger := logging.Logger()
|
||||
|
||||
address := net.JoinHostPort(configuration.Server.Host, strconv.Itoa(configuration.Server.Port))
|
||||
address := net.JoinHostPort(config.Server.Host, strconv.Itoa(config.Server.Port))
|
||||
|
||||
var (
|
||||
listener net.Listener
|
||||
|
@ -293,17 +35,17 @@ func CreateServer(configuration schema.Configuration, providers middlewares.Prov
|
|||
connectionScheme string
|
||||
)
|
||||
|
||||
if configuration.Server.TLS.Certificate != "" && configuration.Server.TLS.Key != "" {
|
||||
if config.Server.TLS.Certificate != "" && config.Server.TLS.Key != "" {
|
||||
connectionType, connectionScheme = "TLS", schemeHTTPS
|
||||
|
||||
if err = server.AppendCert(configuration.Server.TLS.Certificate, configuration.Server.TLS.Key); err != nil {
|
||||
if err = server.AppendCert(config.Server.TLS.Certificate, config.Server.TLS.Key); err != nil {
|
||||
logger.Fatalf("unable to load certificate: %v", err)
|
||||
}
|
||||
|
||||
if len(configuration.Server.TLS.ClientCertificates) > 0 {
|
||||
if len(config.Server.TLS.ClientCertificates) > 0 {
|
||||
caCertPool := x509.NewCertPool()
|
||||
|
||||
for _, path := range configuration.Server.TLS.ClientCertificates {
|
||||
for _, path := range config.Server.TLS.ClientCertificates {
|
||||
cert, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
logger.Fatalf("Cannot read client TLS certificate %s: %s", path, err)
|
||||
|
@ -329,15 +71,15 @@ func CreateServer(configuration schema.Configuration, providers middlewares.Prov
|
|||
}
|
||||
}
|
||||
|
||||
if err = writeHealthCheckEnv(configuration.Server.DisableHealthcheck, connectionScheme, configuration.Server.Host,
|
||||
configuration.Server.Path, configuration.Server.Port); err != nil {
|
||||
if err = writeHealthCheckEnv(config.Server.DisableHealthcheck, connectionScheme, config.Server.Host,
|
||||
config.Server.Path, config.Server.Port); err != nil {
|
||||
logger.Fatalf("Could not configure healthcheck: %v", err)
|
||||
}
|
||||
|
||||
if configuration.Server.Path == "" {
|
||||
if config.Server.Path == "" {
|
||||
logger.Infof("Initializing server for %s connections on '%s' path '/'", connectionType, listener.Addr().String())
|
||||
} else {
|
||||
logger.Infof("Initializing server for %s connections on '%s' paths '/' and '%s'", connectionType, listener.Addr().String(), configuration.Server.Path)
|
||||
logger.Infof("Initializing server for %s connections on '%s' paths '/' and '%s'", connectionType, listener.Addr().String(), config.Server.Path)
|
||||
}
|
||||
|
||||
return server, listener
|
||||
|
|
Loading…
Reference in New Issue