1
0
mirror of https://github.com/0rangebananaspy/authelia.git synced 2024-09-14 22:47:21 +07:00

fix(server): incorrect remote ip logged in error handler ()

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.
This commit is contained in:
James Elliott 2022-04-08 14:13:47 +10:00 committed by GitHub
parent 90edf11b88
commit ce6bf74c8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 427 additions and 434 deletions

View File

@ -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 {

View File

@ -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,
})

View File

@ -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),
}

View File

@ -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{},

View File

@ -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

View File

@ -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)

View File

@ -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()
}

View File

@ -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}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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 {

View File

@ -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(

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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())
}

View File

@ -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)

View File

@ -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) {

View File

@ -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&amp;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&amp;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&amp;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()))

View File

@ -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()

View File

@ -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
}

View File

@ -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