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
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" "github.com/authelia/authelia/v4/internal/utils"
) )
// CheckSafeRedirection handler checking whether the redirection to a given URL provided in body is safe. // CheckSafeRedirectionPOST handler checking whether the redirection to a given URL provided in body is safe.
func CheckSafeRedirection(ctx *middlewares.AutheliaCtx) { func CheckSafeRedirectionPOST(ctx *middlewares.AutheliaCtx) {
userSession := ctx.GetSession() userSession := ctx.GetSession()
if userSession.AuthenticationLevel == authentication.NotAuthenticated { if userSession.AuthenticationLevel == authentication.NotAuthenticated {

View File

@ -24,7 +24,7 @@ func TestCheckSafeRedirection_ForbiddenCall(t *testing.T) {
URI: "http://myapp.example.com", URI: "http://myapp.example.com",
}) })
CheckSafeRedirection(mock.Ctx) CheckSafeRedirectionPOST(mock.Ctx)
assert.Equal(t, 401, mock.Ctx.Response.StatusCode()) assert.Equal(t, 401, mock.Ctx.Response.StatusCode())
} }
@ -40,7 +40,7 @@ func TestCheckSafeRedirection_UnsafeRedirection(t *testing.T) {
URI: "http://myapp.com", URI: "http://myapp.com",
}) })
CheckSafeRedirection(mock.Ctx) CheckSafeRedirectionPOST(mock.Ctx)
mock.Assert200OK(t, checkURIWithinDomainResponseBody{ mock.Assert200OK(t, checkURIWithinDomainResponseBody{
OK: false, OK: false,
}) })
@ -58,7 +58,7 @@ func TestCheckSafeRedirection_SafeRedirection(t *testing.T) {
URI: "https://myapp.example.com", URI: "https://myapp.example.com",
}) })
CheckSafeRedirection(mock.Ctx) CheckSafeRedirectionPOST(mock.Ctx)
mock.Assert200OK(t, checkURIWithinDomainResponseBody{ mock.Assert200OK(t, checkURIWithinDomainResponseBody{
OK: true, OK: true,
}) })

View File

@ -4,8 +4,8 @@ import (
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
) )
// ConfigurationGet get the configuration accessible to authenticated users. // ConfigurationGET get the configuration accessible to authenticated users.
func ConfigurationGet(ctx *middlewares.AutheliaCtx) { func ConfigurationGET(ctx *middlewares.AutheliaCtx) {
body := configurationBody{ body := configurationBody{
AvailableMethods: make(MethodList, 0, 3), 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) 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{ s.mock.Assert200OK(s.T(), configurationBody{
AvailableMethods: []string{"totp", "webauthn", "mobile_push"}, 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) 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{ s.mock.Assert200OK(s.T(), configurationBody{
AvailableMethods: []string{"webauthn", "mobile_push"}, AvailableMethods: []string{"webauthn", "mobile_push"},
@ -105,7 +105,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvaila
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration) 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{ s.mock.Assert200OK(s.T(), configurationBody{
AvailableMethods: []string{"totp", "mobile_push"}, AvailableMethods: []string{"totp", "mobile_push"},
@ -133,7 +133,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveDuoFromAvailableMe
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration) 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{ s.mock.Assert200OK(s.T(), configurationBody{
AvailableMethods: []string{"totp", "webauthn"}, AvailableMethods: []string{"totp", "webauthn"},
@ -161,7 +161,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenNoTw
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration) 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{ s.mock.Assert200OK(s.T(), configurationBody{
AvailableMethods: []string{}, AvailableMethods: []string{},
@ -189,7 +189,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenAllD
s.mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&s.mock.Ctx.Configuration) 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{ s.mock.Assert200OK(s.T(), configurationBody{
AvailableMethods: []string{}, AvailableMethods: []string{},

View File

@ -10,9 +10,9 @@ import (
"github.com/authelia/authelia/v4/internal/session" "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. //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) { return func(ctx *middlewares.AutheliaCtx) {
var successful bool var successful bool

View File

@ -31,7 +31,7 @@ func (s *FirstFactorSuite) TearDownTest() {
} }
func (s *FirstFactorSuite) TestShouldFailIfBodyIsNil() { func (s *FirstFactorSuite) TestShouldFailIfBodyIsNil() {
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
// No body. // 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) 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(`{ s.mock.Ctx.Request.SetBodyString(`{
"username": "test" "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) 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.") s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
@ -71,7 +71,7 @@ func (s *FirstFactorSuite) TestShouldFailIfUserProviderCheckPasswordFail() {
"password": "hello", "password": "hello",
"keepMeLoggedIn": true "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) 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.") s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
@ -100,7 +100,7 @@ func (s *FirstFactorSuite) TestShouldCheckAuthenticationIsNotMarkedWhenProviderC
"keepMeLoggedIn": true "keepMeLoggedIn": true
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
} }
func (s *FirstFactorSuite) TestShouldCheckAuthenticationIsMarkedWhenInvalidCredentials() { func (s *FirstFactorSuite) TestShouldCheckAuthenticationIsMarkedWhenInvalidCredentials() {
@ -126,7 +126,7 @@ func (s *FirstFactorSuite) TestShouldCheckAuthenticationIsMarkedWhenInvalidCrede
"keepMeLoggedIn": true "keepMeLoggedIn": true
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
} }
func (s *FirstFactorSuite) TestShouldFailIfUserProviderGetDetailsFail() { func (s *FirstFactorSuite) TestShouldFailIfUserProviderGetDetailsFail() {
@ -150,7 +150,7 @@ func (s *FirstFactorSuite) TestShouldFailIfUserProviderGetDetailsFail() {
"password": "hello", "password": "hello",
"keepMeLoggedIn": true "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) 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.") s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
@ -172,7 +172,7 @@ func (s *FirstFactorSuite) TestShouldFailIfAuthenticationMarkFail() {
"password": "hello", "password": "hello",
"keepMeLoggedIn": true "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) 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.") s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
@ -203,7 +203,7 @@ func (s *FirstFactorSuite) TestShouldAuthenticateUserWithRememberMeChecked() {
"password": "hello", "password": "hello",
"keepMeLoggedIn": true "keepMeLoggedIn": true
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
// Respond with 200. // Respond with 200.
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
@ -244,7 +244,7 @@ func (s *FirstFactorSuite) TestShouldAuthenticateUserWithRememberMeUnchecked() {
"requestMethod": "GET", "requestMethod": "GET",
"keepMeLoggedIn": false "keepMeLoggedIn": false
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
// Respond with 200. // Respond with 200.
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
@ -288,7 +288,7 @@ func (s *FirstFactorSuite) TestShouldSaveUsernameFromAuthenticationBackendInSess
"requestMethod": "GET", "requestMethod": "GET",
"keepMeLoggedIn": true "keepMeLoggedIn": true
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
// Respond with 200. // Respond with 200.
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
@ -358,7 +358,7 @@ func (s *FirstFactorRedirectionSuite) TestShouldRedirectToDefaultURLWhenNoTarget
"requestMethod": "GET", "requestMethod": "GET",
"keepMeLoggedIn": false "keepMeLoggedIn": false
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
// Respond with 200. // Respond with 200.
s.mock.Assert200OK(s.T(), redirectResponse{Redirect: "https://default.local"}) s.mock.Assert200OK(s.T(), redirectResponse{Redirect: "https://default.local"})
@ -379,7 +379,7 @@ func (s *FirstFactorRedirectionSuite) TestShouldRedirectToDefaultURLWhenURLIsUns
"targetURL": "http://notsafe.local" "targetURL": "http://notsafe.local"
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
// Respond with 200. // Respond with 200.
s.mock.Assert200OK(s.T(), redirectResponse{Redirect: "https://default.local"}) s.mock.Assert200OK(s.T(), redirectResponse{Redirect: "https://default.local"})
@ -402,7 +402,7 @@ func (s *FirstFactorRedirectionSuite) TestShouldReply200WhenNoTargetURLProvidedA
"keepMeLoggedIn": false "keepMeLoggedIn": false
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
// Respond with 200. // Respond with 200.
s.mock.Assert200OK(s.T(), nil) s.mock.Assert200OK(s.T(), nil)
@ -434,7 +434,7 @@ func (s *FirstFactorRedirectionSuite) TestShouldReply200WhenUnsafeTargetURLProvi
"keepMeLoggedIn": false "keepMeLoggedIn": false
}`) }`)
FirstFactorPost(nil)(s.mock.Ctx) FirstFactorPOST(nil)(s.mock.Ctx)
// Respond with 200. // Respond with 200.
s.mock.Assert200OK(s.T(), nil) s.mock.Assert200OK(s.T(), nil)

View File

@ -4,7 +4,7 @@ import (
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
) )
// HealthGet can be used by health checks. // HealthGET can be used by health checks.
func HealthGet(ctx *middlewares.AutheliaCtx) { func HealthGET(ctx *middlewares.AutheliaCtx) {
ctx.ReplyOK() ctx.ReplyOK()
} }

View File

@ -16,8 +16,8 @@ type logoutResponseBody struct {
SafeTargetURL bool `json:"safeTargetURL"` SafeTargetURL bool `json:"safeTargetURL"`
} }
// LogoutPost is the handler logging out the user attached to the given cookie. // LogoutPOST is the handler logging out the user attached to the given cookie.
func LogoutPost(ctx *middlewares.AutheliaCtx) { func LogoutPOST(ctx *middlewares.AutheliaCtx) {
body := logoutBody{} body := logoutBody{}
responseBody := logoutResponseBody{SafeTargetURL: false} responseBody := logoutResponseBody{SafeTargetURL: false}

View File

@ -30,7 +30,7 @@ func (s *LogoutSuite) TearDownTest() {
} }
func (s *LogoutSuite) TestShouldDestroySession() { func (s *LogoutSuite) TestShouldDestroySession() {
LogoutPost(s.mock.Ctx) LogoutPOST(s.mock.Ctx)
b := s.mock.Ctx.Response.Header.PeekCookie("authelia_session") b := s.mock.Ctx.Response.Header.PeekCookie("authelia_session")
// Reset the cookie, meaning it resets the value and expires the cookie by setting // 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" "github.com/authelia/authelia/v4/internal/utils"
) )
// SecondFactorDuoDevicesGet handler for retrieving available devices and capabilities from duo api. // DuoDevicesGET handler for retrieving available devices and capabilities from duo api.
func SecondFactorDuoDevicesGet(duoAPI duo.API) middlewares.RequestHandler { func DuoDevicesGET(duoAPI duo.API) middlewares.RequestHandler {
return func(ctx *middlewares.AutheliaCtx) { return func(ctx *middlewares.AutheliaCtx) {
userSession := ctx.GetSession() userSession := ctx.GetSession()
values := url.Values{} values := url.Values{}
@ -78,8 +78,8 @@ func SecondFactorDuoDevicesGet(duoAPI duo.API) middlewares.RequestHandler {
} }
} }
// SecondFactorDuoDevicePost update the user preferences regarding Duo device and method. // DuoDevicePOST update the user preferences regarding Duo device and method.
func SecondFactorDuoDevicePost(ctx *middlewares.AutheliaCtx) { func DuoDevicePOST(ctx *middlewares.AutheliaCtx) {
device := DuoDeviceBody{} device := DuoDeviceBody{}
err := ctx.ParseBody(&device) 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")) 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.") 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) 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) 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}) 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) 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}) 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) 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}) 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) 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}) 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"})). SavePreferredDuoDevice(gomock.Eq(s.mock.Ctx), gomock.Eq(model.DuoDevice{Username: "john", Device: "1234567890123456", Method: "push"})).
Return(nil) Return(nil)
SecondFactorDuoDevicePost(s.mock.Ctx) DuoDevicePOST(s.mock.Ctx)
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
} }
@ -140,7 +140,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondOK() {
func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnInvalidMethod() { func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnInvalidMethod() {
s.mock.Ctx.Request.SetBodyString("{\"device\":\"1234567890123456\", \"method\":\"testfailure\"}") 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.") s.mock.Assert200KO(s.T(), "Authentication failed, please retry later.")
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level) assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
@ -149,7 +149,7 @@ func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnInvalidMethod() {
func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnEmptyMethod() { func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnEmptyMethod() {
s.mock.Ctx.Request.SetBodyString("{\"device\":\"1234567890123456\", \"method\":\"\"}") 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.") 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) 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() { func (s *RegisterDuoDeviceSuite) TestShouldRespondKOOnEmptyDevice() {
s.mock.Ctx.Request.SetBodyString("{\"device\":\"\", \"method\":\"push\"}") 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.") 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) 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 return ctx.GetSession().Username == username
} }
// SecondFactorTOTPIdentityStart the handler for initiating the identity validation. // TOTPIdentityStart the handler for initiating the identity validation.
var SecondFactorTOTPIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{ var TOTPIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
MailTitle: "Register your mobile", MailTitle: "Register your mobile",
MailButtonContent: "Register", MailButtonContent: "Register",
TargetEndpoint: "/one-time-password/register", TargetEndpoint: "/one-time-password/register",
@ -35,7 +35,7 @@ var SecondFactorTOTPIdentityStart = middlewares.IdentityVerificationStart(middle
IdentityRetrieverFunc: identityRetrieverFromSession, IdentityRetrieverFunc: identityRetrieverFromSession,
}, nil) }, nil)
func secondFactorTOTPIdentityFinish(ctx *middlewares.AutheliaCtx, username string) { func totpIdentityFinish(ctx *middlewares.AutheliaCtx, username string) {
var ( var (
config *model.TOTPConfiguration config *model.TOTPConfiguration
err error err error
@ -62,9 +62,9 @@ func secondFactorTOTPIdentityFinish(ctx *middlewares.AutheliaCtx, username strin
} }
} }
// SecondFactorTOTPIdentityFinish the handler for finishing the identity validation. // TOTPIdentityFinish the handler for finishing the identity validation.
var SecondFactorTOTPIdentityFinish = middlewares.IdentityVerificationFinish( var TOTPIdentityFinish = middlewares.IdentityVerificationFinish(
middlewares.IdentityVerificationFinishArgs{ middlewares.IdentityVerificationFinishArgs{
ActionClaim: ActionTOTPRegistration, ActionClaim: ActionTOTPRegistration,
IsTokenUserValidFunc: isTokenUserValidFor2FARegistration, IsTokenUserValidFunc: isTokenUserValidFor2FARegistration,
}, secondFactorTOTPIdentityFinish) }, totpIdentityFinish)

View File

@ -12,8 +12,8 @@ import (
"github.com/authelia/authelia/v4/internal/regulation" "github.com/authelia/authelia/v4/internal/regulation"
) )
// SecondFactorWebauthnIdentityStart the handler for initiating the identity validation. // WebauthnIdentityStart the handler for initiating the identity validation.
var SecondFactorWebauthnIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{ var WebauthnIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
MailTitle: "Register your key", MailTitle: "Register your key",
MailButtonContent: "Register", MailButtonContent: "Register",
TargetEndpoint: "/webauthn/register", TargetEndpoint: "/webauthn/register",
@ -21,8 +21,8 @@ var SecondFactorWebauthnIdentityStart = middlewares.IdentityVerificationStart(mi
IdentityRetrieverFunc: identityRetrieverFromSession, IdentityRetrieverFunc: identityRetrieverFromSession,
}, nil) }, nil)
// SecondFactorWebauthnIdentityFinish the handler for finishing the identity validation. // WebauthnIdentityFinish the handler for finishing the identity validation.
var SecondFactorWebauthnIdentityFinish = middlewares.IdentityVerificationFinish( var WebauthnIdentityFinish = middlewares.IdentityVerificationFinish(
middlewares.IdentityVerificationFinishArgs{ middlewares.IdentityVerificationFinishArgs{
ActionClaim: ActionWebauthnRegistration, ActionClaim: ActionWebauthnRegistration,
IsTokenUserValidFunc: isTokenUserValidFor2FARegistration, IsTokenUserValidFunc: isTokenUserValidFor2FARegistration,
@ -81,8 +81,8 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
} }
} }
// SecondFactorWebauthnAttestationPOST processes the attestation challenge response from the client. // WebauthnAttestationPOST processes the attestation challenge response from the client.
func SecondFactorWebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) { func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
var ( var (
err error err error
w *webauthn.WebAuthn w *webauthn.WebAuthn

View File

@ -9,8 +9,8 @@ import (
"github.com/authelia/authelia/v4/internal/utils" "github.com/authelia/authelia/v4/internal/utils"
) )
// ResetPasswordPost handler for resetting passwords. // ResetPasswordPOST handler for resetting passwords.
func ResetPasswordPost(ctx *middlewares.AutheliaCtx) { func ResetPasswordPOST(ctx *middlewares.AutheliaCtx) {
userSession := ctx.GetSession() userSession := ctx.GetSession()
// Those checks unsure that the identity verification process has been initiated and completed successfully // 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" "github.com/authelia/authelia/v4/internal/utils"
) )
// SecondFactorDuoPost handler for sending a push notification via duo api. // DuoPOST handler for sending a push notification via duo api.
func SecondFactorDuoPost(duoAPI duo.API) middlewares.RequestHandler { func DuoPOST(duoAPI duo.API) middlewares.RequestHandler {
return func(ctx *middlewares.AutheliaCtx) { return func(ctx *middlewares.AutheliaCtx) {
var ( var (
requestBody signDuoRequestBody requestBody signDuoRequestBody

View File

@ -58,7 +58,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldEnroll() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorDuoPost(duoMock)(s.mock.Ctx) DuoPOST(duoMock)(s.mock.Ctx)
s.mock.Assert200OK(s.T(), DuoSignResponse{ s.mock.Assert200OK(s.T(), DuoSignResponse{
Result: enroll, Result: enroll,
@ -117,7 +117,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldAutoSelect() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
} }
@ -146,7 +146,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldDenyAutoSelect() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorDuoPost(duoMock)(s.mock.Ctx) DuoPOST(duoMock)(s.mock.Ctx)
s.mock.Assert200OK(s.T(), DuoSignResponse{ s.mock.Assert200OK(s.T(), DuoSignResponse{
Result: deny, Result: deny,
@ -166,7 +166,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldFailAutoSelect() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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.") s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.")
} }
@ -195,7 +195,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldDeleteOldDeviceAndEnroll() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorDuoPost(duoMock)(s.mock.Ctx) DuoPOST(duoMock)(s.mock.Ctx)
s.mock.Assert200OK(s.T(), DuoSignResponse{ s.mock.Assert200OK(s.T(), DuoSignResponse{
Result: enroll, Result: enroll,
@ -229,7 +229,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldDeleteOldDeviceAndCallPreauthAPIWit
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorDuoPost(duoMock)(s.mock.Ctx) DuoPOST(duoMock)(s.mock.Ctx)
s.mock.Assert200OK(s.T(), DuoSignResponse{ s.mock.Assert200OK(s.T(), DuoSignResponse{
Result: enroll, Result: enroll,
@ -267,7 +267,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldUseOldDeviceAndSelect() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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}) s.mock.Assert200OK(s.T(), DuoDevicesResponse{Result: auth, Devices: apiDevices})
} }
@ -323,7 +323,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldUseInvalidMethodAndAutoSelect() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
} }
@ -346,7 +346,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoPreauthAPIAndAllowAccess() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
} }
@ -376,7 +376,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoPreauthAPIAndDenyAccess() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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()) assert.Equal(s.T(), 401, s.mock.Ctx.Response.StatusCode())
} }
@ -394,7 +394,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoPreauthAPIAndFail() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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.") s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.")
} }
@ -446,7 +446,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoAPIAndDenyAccess() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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()) assert.Equal(s.T(), 401, s.mock.Ctx.Response.StatusCode())
} }
@ -477,7 +477,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldCallDuoAPIAndFail() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) 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.") s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.")
} }
@ -525,7 +525,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldRedirectUserToDefaultURL() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorDuoPost(duoMock)(s.mock.Ctx) DuoPOST(duoMock)(s.mock.Ctx)
s.mock.Assert200OK(s.T(), redirectResponse{ s.mock.Assert200OK(s.T(), redirectResponse{
Redirect: testRedirectionURL, Redirect: testRedirectionURL,
}) })
@ -572,7 +572,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldNotReturnRedirectURL() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorDuoPost(duoMock)(s.mock.Ctx) DuoPOST(duoMock)(s.mock.Ctx)
s.mock.Assert200OK(s.T(), nil) s.mock.Assert200OK(s.T(), nil)
} }
@ -619,7 +619,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldRedirectUserToSafeTargetURL() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorDuoPost(duoMock)(s.mock.Ctx) DuoPOST(duoMock)(s.mock.Ctx)
s.mock.Assert200OK(s.T(), redirectResponse{ s.mock.Assert200OK(s.T(), redirectResponse{
Redirect: "https://mydomain.local", Redirect: "https://mydomain.local",
}) })
@ -668,7 +668,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldNotRedirectToUnsafeURL() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorDuoPost(duoMock)(s.mock.Ctx) DuoPOST(duoMock)(s.mock.Ctx)
s.mock.Assert200OK(s.T(), nil) s.mock.Assert200OK(s.T(), nil)
} }
@ -718,7 +718,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldRegenerateSessionForPreventingSessi
r := regexp.MustCompile("^authelia_session=(.*); path=") r := regexp.MustCompile("^authelia_session=(.*); path=")
res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1) 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.mock.Assert200OK(s.T(), nil)
s.Assert().NotEqual( s.Assert().NotEqual(

View File

@ -5,8 +5,8 @@ import (
"github.com/authelia/authelia/v4/internal/regulation" "github.com/authelia/authelia/v4/internal/regulation"
) )
// SecondFactorTOTPPost validate the TOTP passcode provided by the user. // TimeBasedOneTimePasswordPOST validate the TOTP passcode provided by the user.
func SecondFactorTOTPPost(ctx *middlewares.AutheliaCtx) { func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {
requestBody := signTOTPRequestBody{} requestBody := signTOTPRequestBody{}
if err := ctx.ParseBody(&requestBody); err != nil { if err := ctx.ParseBody(&requestBody); err != nil {

View File

@ -65,7 +65,7 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToDefaultURL() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorTOTPPost(s.mock.Ctx) TimeBasedOneTimePasswordPOST(s.mock.Ctx)
s.mock.Assert200OK(s.T(), redirectResponse{ s.mock.Assert200OK(s.T(), redirectResponse{
Redirect: testRedirectionURL, Redirect: testRedirectionURL,
}) })
@ -103,7 +103,7 @@ func (s *HandlerSignTOTPSuite) TestShouldFailWhenTOTPSignInInfoFailsToUpdate() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorTOTPPost(s.mock.Ctx) TimeBasedOneTimePasswordPOST(s.mock.Ctx)
s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.") s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.")
} }
@ -137,7 +137,7 @@ func (s *HandlerSignTOTPSuite) TestShouldNotReturnRedirectURL() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorTOTPPost(s.mock.Ctx) TimeBasedOneTimePasswordPOST(s.mock.Ctx)
s.mock.Assert200OK(s.T(), nil) s.mock.Assert200OK(s.T(), nil)
} }
@ -173,7 +173,7 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToSafeTargetURL() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorTOTPPost(s.mock.Ctx) TimeBasedOneTimePasswordPOST(s.mock.Ctx)
s.mock.Assert200OK(s.T(), redirectResponse{ s.mock.Assert200OK(s.T(), redirectResponse{
Redirect: "https://mydomain.local", Redirect: "https://mydomain.local",
}) })
@ -211,7 +211,7 @@ func (s *HandlerSignTOTPSuite) TestShouldNotRedirectToUnsafeURL() {
s.Require().NoError(err) s.Require().NoError(err)
s.mock.Ctx.Request.SetBody(bodyBytes) s.mock.Ctx.Request.SetBody(bodyBytes)
SecondFactorTOTPPost(s.mock.Ctx) TimeBasedOneTimePasswordPOST(s.mock.Ctx)
s.mock.Assert200OK(s.T(), nil) s.mock.Assert200OK(s.T(), nil)
} }
@ -250,7 +250,7 @@ func (s *HandlerSignTOTPSuite) TestShouldRegenerateSessionForPreventingSessionFi
r := regexp.MustCompile("^authelia_session=(.*); path=") r := regexp.MustCompile("^authelia_session=(.*); path=")
res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1) 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.mock.Assert200OK(s.T(), nil)
s.Assert().NotEqual( s.Assert().NotEqual(

View File

@ -11,8 +11,8 @@ import (
"github.com/authelia/authelia/v4/internal/regulation" "github.com/authelia/authelia/v4/internal/regulation"
) )
// SecondFactorWebauthnAssertionGET handler starts the assertion ceremony. // WebauthnAssertionGET handler starts the assertion ceremony.
func SecondFactorWebauthnAssertionGET(ctx *middlewares.AutheliaCtx) { func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
var ( var (
w *webauthn.WebAuthn w *webauthn.WebAuthn
user *model.WebauthnUser user *model.WebauthnUser
@ -78,8 +78,8 @@ func SecondFactorWebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
} }
} }
// SecondFactorWebauthnAssertionPOST handler completes the assertion ceremony after verifying the challenge. // WebauthnAssertionPOST handler completes the assertion ceremony after verifying the challenge.
func SecondFactorWebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) { func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
var ( var (
err error err error
w *webauthn.WebAuthn w *webauthn.WebAuthn

View File

@ -4,8 +4,8 @@ import (
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
) )
// StateGet is the handler serving the user state. // StateGET is the handler serving the user state.
func StateGet(ctx *middlewares.AutheliaCtx) { func StateGET(ctx *middlewares.AutheliaCtx) {
userSession := ctx.GetSession() userSession := ctx.GetSession()
stateResponse := StateResponse{ stateResponse := StateResponse{
Username: userSession.Username, Username: userSession.Username,

View File

@ -32,7 +32,7 @@ func (s *StateGetSuite) TestShouldReturnUsernameFromSession() {
err := s.mock.Ctx.SaveSession(userSession) err := s.mock.Ctx.SaveSession(userSession)
require.NoError(s.T(), err) require.NoError(s.T(), err)
StateGet(s.mock.Ctx) StateGET(s.mock.Ctx)
type Response struct { type Response struct {
Status string Status string
@ -62,7 +62,7 @@ func (s *StateGetSuite) TestShouldReturnAuthenticationLevelFromSession() {
err := s.mock.Ctx.SaveSession(userSession) err := s.mock.Ctx.SaveSession(userSession)
require.NoError(s.T(), err) require.NoError(s.T(), err)
StateGet(s.mock.Ctx) StateGET(s.mock.Ctx)
type Response struct { type Response struct {
Status string Status string

View File

@ -72,8 +72,8 @@ func UserInfoGET(ctx *middlewares.AutheliaCtx) {
} }
} }
// MethodPreferencePost update the user preferences regarding 2FA method. // MethodPreferencePOST update the user preferences regarding 2FA method.
func MethodPreferencePost(ctx *middlewares.AutheliaCtx) { func MethodPreferencePOST(ctx *middlewares.AutheliaCtx) {
bodyJSON := preferred2FAMethodBody{} bodyJSON := preferred2FAMethodBody{}
err := ctx.ParseBody(&bodyJSON) err := ctx.ParseBody(&bodyJSON)

View File

@ -390,7 +390,7 @@ func (s *SaveSuite) TearDownTest() {
func (s *SaveSuite) TestShouldReturnError500WhenNoBodyProvided() { func (s *SaveSuite) TestShouldReturnError500WhenNoBodyProvided() {
s.mock.Ctx.Request.SetBody(nil) s.mock.Ctx.Request.SetBody(nil)
MethodPreferencePost(s.mock.Ctx) MethodPreferencePOST(s.mock.Ctx)
s.mock.Assert200KO(s.T(), "Operation failed.") s.mock.Assert200KO(s.T(), "Operation failed.")
assert.Equal(s.T(), "unable to parse body: unexpected end of JSON input", s.mock.Hook.LastEntry().Message) 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() { func (s *SaveSuite) TestShouldReturnError500WhenMalformedBodyProvided() {
s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"abc\"")) s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"abc\""))
MethodPreferencePost(s.mock.Ctx) MethodPreferencePOST(s.mock.Ctx)
s.mock.Assert200KO(s.T(), "Operation failed.") s.mock.Assert200KO(s.T(), "Operation failed.")
assert.Equal(s.T(), "unable to parse body: unexpected end of JSON input", s.mock.Hook.LastEntry().Message) 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() { func (s *SaveSuite) TestShouldReturnError500WhenBadBodyProvided() {
s.mock.Ctx.Request.SetBody([]byte("{\"weird_key\":\"abc\"}")) s.mock.Ctx.Request.SetBody([]byte("{\"weird_key\":\"abc\"}"))
MethodPreferencePost(s.mock.Ctx) MethodPreferencePOST(s.mock.Ctx)
s.mock.Assert200KO(s.T(), "Operation failed.") s.mock.Assert200KO(s.T(), "Operation failed.")
assert.Equal(s.T(), "unable to validate body: method: non zero value required", s.mock.Hook.LastEntry().Message) 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() { func (s *SaveSuite) TestShouldReturnError500WhenBadMethodProvided() {
s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"abc\"}")) s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"abc\"}"))
MethodPreferencePost(s.mock.Ctx) MethodPreferencePOST(s.mock.Ctx)
s.mock.Assert200KO(s.T(), "Operation failed.") s.mock.Assert200KO(s.T(), "Operation failed.")
assert.Equal(s.T(), "unknown or unavailable method 'abc', it should be one of totp, webauthn", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "unknown or unavailable method 'abc', it should be one of totp, webauthn", s.mock.Hook.LastEntry().Message)
@ -430,7 +430,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenDatabaseFailsToSave() {
SavePreferred2FAMethod(s.mock.Ctx, gomock.Eq("john"), gomock.Eq("webauthn")). SavePreferred2FAMethod(s.mock.Ctx, gomock.Eq("john"), gomock.Eq("webauthn")).
Return(fmt.Errorf("Failure")) Return(fmt.Errorf("Failure"))
MethodPreferencePost(s.mock.Ctx) MethodPreferencePOST(s.mock.Ctx)
s.mock.Assert200KO(s.T(), "Operation failed.") s.mock.Assert200KO(s.T(), "Operation failed.")
assert.Equal(s.T(), "unable to save new preferred 2FA method: Failure", s.mock.Hook.LastEntry().Message) 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")). SavePreferred2FAMethod(s.mock.Ctx, gomock.Eq("john"), gomock.Eq("webauthn")).
Return(nil) Return(nil)
MethodPreferencePost(s.mock.Ctx) MethodPreferencePOST(s.mock.Ctx)
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
} }

View File

@ -9,8 +9,8 @@ import (
"github.com/authelia/authelia/v4/internal/storage" "github.com/authelia/authelia/v4/internal/storage"
) )
// UserTOTPGet returns the users TOTP configuration. // UserTOTPInfoGET returns the users TOTP configuration.
func UserTOTPGet(ctx *middlewares.AutheliaCtx) { func UserTOTPInfoGET(ctx *middlewares.AutheliaCtx) {
userSession := ctx.GetSession() userSession := ctx.GetSession()
config, err := ctx.Providers.StorageProvider.LoadTOTPConfiguration(ctx, userSession.Username) 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 return
} }
// VerifyGet returns the handler verifying if a request is allowed to go through. // VerifyGET returns the handler verifying if a request is allowed to go through.
func VerifyGet(cfg schema.AuthenticationBackendConfiguration) middlewares.RequestHandler { func VerifyGET(cfg schema.AuthenticationBackendConfiguration) middlewares.RequestHandler {
refreshProfile, refreshProfileInterval := getProfileRefreshSettings(cfg) refreshProfile, refreshProfileInterval := getProfileRefreshSettings(cfg)
return func(ctx *middlewares.AutheliaCtx) { 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("Proxy-Authorization", "Basic am9objpaaaaaaaaaaaaaaaa")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com") 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()) assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
} }
@ -219,7 +219,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyDefaultPolicy() {
Groups: []string{"dev", "admins"}, Groups: []string{"dev", "admins"},
}, nil) }, nil)
VerifyGet(verifyGetCfg)(mock.Ctx) VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 403, mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 403, mock.Ctx.Response.StatusCode())
} }
@ -242,7 +242,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfBypassDomain() {
Groups: []string{"dev", "admins"}, Groups: []string{"dev", "admins"},
}, nil) }, nil)
VerifyGet(verifyGetCfg)(mock.Ctx) VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode())
} }
@ -265,7 +265,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfOneFactorDomain() {
Groups: []string{"dev", "admins"}, Groups: []string{"dev", "admins"},
}, nil) }, nil)
VerifyGet(verifyGetCfg)(mock.Ctx) VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode())
} }
@ -288,7 +288,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfTwoFactorDomain() {
Groups: []string{"dev", "admins"}, Groups: []string{"dev", "admins"},
}, nil) }, nil)
VerifyGet(verifyGetCfg)(mock.Ctx) VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
} }
@ -311,7 +311,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfDenyDomain() {
Groups: []string{"dev", "admins"}, Groups: []string{"dev", "admins"},
}, nil) }, nil)
VerifyGet(verifyGetCfg)(mock.Ctx) VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 403, mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 403, mock.Ctx.Response.StatusCode())
} }
@ -335,7 +335,7 @@ func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgOk() {
Groups: []string{"dev", "admins"}, Groups: []string{"dev", "admins"},
}, nil) }, nil)
VerifyGet(verifyGetCfg)(mock.Ctx) VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode()) 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.QueryArgs().Add("auth", "basic")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com") 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(), 401, mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body())) 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("Authorization", "")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com") 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(), 401, mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body())) 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")). CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(false, nil) Return(false, nil)
VerifyGet(verifyGetCfg)(mock.Ctx) VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body())) 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("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com") 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(), 401, mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body())) 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("Proxy-Authorization", "Basic am9objp3cm9uZ3Bhc3M=")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com") 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() expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d", assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
"https://test.example.com", actualStatus, expStatus) "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("Proxy-Authorization", "Basic am9objp3cm9uZ3Bhc3M=")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com") 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() expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d", assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
"https://test.example.com", actualStatus, expStatus) "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("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com") 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() expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d", assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
"https://test.example.com", actualStatus, expStatus) "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") 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() expStatus, actualStatus := 200, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d", 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) 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() expStatus, actualStatus := testCase.ExpectedStatusCode, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> AuthLevel=%d, StatusCode=%d != ExpectedStatusCode=%d", assert.Equal(t, expStatus, actualStatus, "URL=%s -> AuthLevel=%d, StatusCode=%d != ExpectedStatusCode=%d",
testCase.URL, testCase.AuthenticationLevel, actualStatus, expStatus) 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") 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. // The session has been destroyed.
newUserSession := mock.Ctx.GetSession() 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") 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. // The session has been destroyed.
newUserSession := mock.Ctx.GetSession() 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") 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. // Check the session is still active.
newUserSession := mock.Ctx.GetSession() 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") 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. // The session has been destroyed.
newUserSession := mock.Ctx.GetSession() 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-Original-URL", "https://two-factor.example.com")
mock.Ctx.Request.Header.Set("X-Forwarded-Method", "GET") mock.Ctx.Request.Header.Set("X-Forwarded-Method", "GET")
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8") 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>", 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())) 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("X-Forwarded-Method", "GET")
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8") 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>", 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())) 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("X-Forwarded-Method", "POST")
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8") 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>", 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())) 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") mock.Ctx.Request.Header.Set("X-Original-URL", "https://deny.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx) VerifyGET(verifyGetCfg)(mock.Ctx)
// The resource if forbidden. // The resource if forbidden.
assert.Equal(t, 403, mock.Ctx.Response.StatusCode()) 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.SetHost("mydomain.com")
mock.Ctx.Request.SetRequestURI("/?rd=https://auth.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>", assert.Equal(t, "<a href=\"https://auth.mydomain.com/?rd=https%3A%2F%2Ftwo-factor.example.com\">Found</a>",
string(mock.Ctx.Response.Body())) string(mock.Ctx.Response.Body()))
@ -889,7 +889,7 @@ func TestShouldNotRefreshUserGroupsFromBackend(t *testing.T) {
cfg := verifyGetCfg cfg := verifyGetCfg
cfg.RefreshInterval = "disable" cfg.RefreshInterval = "disable"
verifyGet := VerifyGet(cfg) verifyGet := VerifyGET(cfg)
mock.UserProviderMock.EXPECT().GetDetails("john").Times(0) mock.UserProviderMock.EXPECT().GetDetails("john").Times(0)
@ -973,7 +973,7 @@ func TestShouldNotRefreshUserGroupsFromBackendWhenDisabled(t *testing.T) {
config := verifyGetCfg config := verifyGetCfg
config.RefreshInterval = schema.ProfileRefreshDisabled config.RefreshInterval = schema.ProfileRefreshDisabled
VerifyGet(config)(mock.Ctx) VerifyGET(config)(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode()) 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. // 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") 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()) 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. // 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) 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()) 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) 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) mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(1)
verifyGet := VerifyGet(verifyGetCfg) verifyGet := VerifyGET(verifyGetCfg)
mock.Clock.Set(time.Now()) 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") 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()) assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
// Check admin group is removed from the session. // 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.Set("X-Original-URL", "https://one-factor.example.com")
mock.Ctx.Request.Header.SetBytesK(headerSessionUsername, testUsername) 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, expectedStatusCode, mock.Ctx.Response.StatusCode())
assert.Equal(t, "", string(mock.Ctx.Response.Body())) 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.Set("X-Original-URL", "https://one-factor.example.com")
mock.Ctx.Request.Header.SetBytesK(headerSessionUsername, "root") 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, expectedStatusCode, mock.Ctx.Response.StatusCode())
assert.Equal(t, "Unauthorized", string(mock.Ctx.Response.Body())) assert.Equal(t, "Unauthorized", string(mock.Ctx.Response.Body()))

View File

@ -4,8 +4,8 @@ import (
"github.com/authelia/authelia/v4/internal/authentication" "github.com/authelia/authelia/v4/internal/authentication"
) )
// RequireFirstFactor check if user has enough permissions to execute the next handler. // Require1FA check if user has enough permissions to execute the next handler.
func RequireFirstFactor(next RequestHandler) RequestHandler { func Require1FA(next RequestHandler) RequestHandler {
return func(ctx *AutheliaCtx) { return func(ctx *AutheliaCtx) {
if ctx.GetSession().AuthenticationLevel < authentication.OneFactor { if ctx.GetSession().AuthenticationLevel < authentication.OneFactor {
ctx.ReplyForbidden() ctx.ReplyForbidden()

View File

@ -2,36 +2,61 @@ package server
import ( import (
"net" "net"
"os"
"strconv"
"strings" "strings"
"time"
duoapi "github.com/duosecurity/duo_api_golang"
"github.com/fasthttp/router"
"github.com/valyala/fasthttp" "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/handlers"
"github.com/authelia/authelia/v4/internal/logging" "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. // 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() logger := logging.Logger()
switch e := err.(type) { headerXForwardedFor := []byte(fasthttp.HeaderXForwardedFor)
case *fasthttp.ErrSmallBuffer:
logger.Tracef("Request was too large to handle from client %s. Response Code %d.", ctx.RemoteIP().String(), fasthttp.StatusRequestHeaderFieldsTooLarge) getRemoteIP := func(ctx *fasthttp.RequestCtx) string {
ctx.Error("request header too large", fasthttp.StatusRequestHeaderFieldsTooLarge) if hdr := ctx.Request.Header.PeekBytes(headerXForwardedFor); hdr != nil {
case *net.OpError: ips := strings.Split(string(hdr), ",")
if e.Timeout() {
// TODO: Add X-Forwarded-For Check here. if len(ips) > 0 {
logger.Tracef("Request timeout occurred while handling from client %s: %s. Response Code %d.", ctx.RemoteIP().String(), ctx.RequestURI(), fasthttp.StatusRequestTimeout) return strings.Trim(ips[0], " ")
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) 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) 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) { func handlerMethodNotAllowed(ctx *fasthttp.RequestCtx) {
handlers.SetStatusCodeResponse(ctx, fasthttp.StatusMethodNotAllowed) 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" "net"
"os" "os"
"strconv" "strconv"
"time"
duoapi "github.com/duosecurity/duo_api_golang"
"github.com/fasthttp/router"
"github.com/valyala/fasthttp" "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/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/logging"
"github.com/authelia/authelia/v4/internal/middlewares" "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. // CreateServer Create Authelia's internal webserver with the given configuration and providers.
func CreateServer(configuration schema.Configuration, providers middlewares.Providers) (*fasthttp.Server, net.Listener) { func CreateServer(config schema.Configuration, providers middlewares.Providers) (*fasthttp.Server, net.Listener) {
handler := registerRoutes(configuration, providers)
server := &fasthttp.Server{ server := &fasthttp.Server{
ErrorHandler: handlerErrors, ErrorHandler: handlerError(),
Handler: handler, Handler: getHandler(config, providers),
NoDefaultServerHeader: true, NoDefaultServerHeader: true,
ReadBufferSize: configuration.Server.ReadBufferSize, ReadBufferSize: config.Server.ReadBufferSize,
WriteBufferSize: configuration.Server.WriteBufferSize, WriteBufferSize: config.Server.WriteBufferSize,
} }
logger := logging.Logger() 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 ( var (
listener net.Listener listener net.Listener
@ -293,17 +35,17 @@ func CreateServer(configuration schema.Configuration, providers middlewares.Prov
connectionScheme string connectionScheme string
) )
if configuration.Server.TLS.Certificate != "" && configuration.Server.TLS.Key != "" { if config.Server.TLS.Certificate != "" && config.Server.TLS.Key != "" {
connectionType, connectionScheme = "TLS", schemeHTTPS 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) 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() caCertPool := x509.NewCertPool()
for _, path := range configuration.Server.TLS.ClientCertificates { for _, path := range config.Server.TLS.ClientCertificates {
cert, err := os.ReadFile(path) cert, err := os.ReadFile(path)
if err != nil { if err != nil {
logger.Fatalf("Cannot read client TLS certificate %s: %s", path, err) 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, if err = writeHealthCheckEnv(config.Server.DisableHealthcheck, connectionScheme, config.Server.Host,
configuration.Server.Path, configuration.Server.Port); err != nil { config.Server.Path, config.Server.Port); err != nil {
logger.Fatalf("Could not configure healthcheck: %v", err) 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()) logger.Infof("Initializing server for %s connections on '%s' path '/'", connectionType, listener.Addr().String())
} else { } 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 return server, listener