refactor(handlers): lower case error messages (#2289)

* refactor(handlers): lower case error messages

also refactor verifyAuth function to detect malicious activity both with session
cookie and authorization header.

* refacto(handlers): simplify error construction

* fix(handlers): check prefix in authorization header to determine auth method

* fix(handlers): determining the method should be done with headers instead of query arg

* refacto(handlers): rollback changes of verifyAuth

* don't lowercase log messages

* Apply suggestions from code review

Make sure logger errors are not lowercased.

* fix: uppercase logger errors and remove unused param

* Do not lowercase logger errors
* Remove unused param targetURL
* Rename url variable to not conflict with imported package

Co-authored-by: Amir Zarrinkafsh <nightah@me.com>
pull/2385/head^2
Clément Michaud 2021-09-17 07:53:40 +02:00 committed by GitHub
parent 05406cfc7b
commit 92d328926d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 112 additions and 118 deletions

View File

@ -9,5 +9,5 @@ const InternalError = "Internal error."
// UnauthorizedError is the error message sent when the user is not authorized. // UnauthorizedError is the error message sent when the user is not authorized.
const UnauthorizedError = "You're not authorized." const UnauthorizedError = "You're not authorized."
var errMissingXForwardedHost = errors.New("Missing header X-Forwarded-Host") var errMissingXForwardedHost = errors.New("missing header X-Forwarded-Host")
var errMissingXForwardedProto = errors.New("Missing header X-Forwarded-Proto") var errMissingXForwardedProto = errors.New("missing header X-Forwarded-Proto")

View File

@ -21,13 +21,13 @@ func CheckSafeRedirection(ctx *middlewares.AutheliaCtx) {
err := ctx.ParseBody(&reqBody) err := ctx.ParseBody(&reqBody)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to parse request body: %w", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to parse request body: %w", err), messageOperationFailed)
return return
} }
safe, err := utils.IsRedirectionURISafe(reqBody.URI, ctx.Configuration.Session.Domain) safe, err := utils.IsRedirectionURISafe(reqBody.URI, ctx.Configuration.Session.Domain)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to determine if uri %s is safe to redirect to: %w", reqBody.URI, err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to determine if uri %s is safe to redirect to: %w", reqBody.URI, err), messageOperationFailed)
return return
} }
@ -35,7 +35,7 @@ func CheckSafeRedirection(ctx *middlewares.AutheliaCtx) {
OK: safe, OK: safe,
}) })
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to create response body: %w", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to create response body: %w", err), messageOperationFailed)
return return
} }
} }

View File

@ -25,7 +25,6 @@ func ConfigurationGet(ctx *middlewares.AutheliaCtx) {
body.SecondFactorEnabled = ctx.Providers.Authorizer.IsSecondFactorEnabled() body.SecondFactorEnabled = ctx.Providers.Authorizer.IsSecondFactorEnabled()
ctx.Logger.Tracef("Second factor enabled: %v", body.SecondFactorEnabled) ctx.Logger.Tracef("Second factor enabled: %v", body.SecondFactorEnabled)
ctx.Logger.Tracef("Available methods are %s", body.AvailableMethods) ctx.Logger.Tracef("Available methods are %s", body.AvailableMethods)
err := ctx.SetJSONBody(body) err := ctx.SetJSONBody(body)

View File

@ -33,7 +33,7 @@ func calculateActualDelay(ctx *middlewares.AutheliaCtx, execDuration time.Durati
randomDelayMs := float64(rand.Int63n(loginDelayMaximumRandomDelayMilliseconds)) //nolint:gosec // TODO: Consider use of crypto/rand, this should be benchmarked and measured first. randomDelayMs := float64(rand.Int63n(loginDelayMaximumRandomDelayMilliseconds)) //nolint:gosec // TODO: Consider use of crypto/rand, this should be benchmarked and measured first.
totalDelayMs := math.Max(avgExecDurationMs, loginDelayMinimumDelayMilliseconds) + randomDelayMs totalDelayMs := math.Max(avgExecDurationMs, loginDelayMinimumDelayMilliseconds) + randomDelayMs
actualDelayMs := math.Max(totalDelayMs-float64(execDuration.Milliseconds()), 1.0) actualDelayMs := math.Max(totalDelayMs-float64(execDuration.Milliseconds()), 1.0)
ctx.Logger.Tracef("attempt successful: %t, exec duration: %d, avg execution duration: %d, random delay ms: %d, total delay ms: %d, actual delay ms: %d", *successful, execDuration.Milliseconds(), int64(avgExecDurationMs), int64(randomDelayMs), int64(totalDelayMs), int64(actualDelayMs)) ctx.Logger.Tracef("Attempt successful: %t, exec duration: %d, avg execution duration: %d, random delay ms: %d, total delay ms: %d, actual delay ms: %d", *successful, execDuration.Milliseconds(), int64(avgExecDurationMs), int64(randomDelayMs), int64(totalDelayMs), int64(actualDelayMs))
return actualDelayMs return actualDelayMs
} }
@ -81,11 +81,11 @@ func FirstFactorPost(msInitialDelay time.Duration, delayEnabled bool) middleware
if err != nil { if err != nil {
if err == regulation.ErrUserIsBanned { if err == regulation.ErrUserIsBanned {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("User %s is banned until %s", bodyJSON.Username, bannedUntil), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("user %s is banned until %s", bodyJSON.Username, bannedUntil), messageAuthenticationFailed)
return return
} }
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to regulate authentication: %s", err.Error()), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to regulate authentication: %s", err.Error()), messageAuthenticationFailed)
return return
} }
@ -99,7 +99,7 @@ func FirstFactorPost(msInitialDelay time.Duration, delayEnabled bool) middleware
ctx.Logger.Errorf("Unable to mark authentication: %s", err.Error()) ctx.Logger.Errorf("Unable to mark authentication: %s", err.Error())
} }
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Error while checking password for user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("error while checking password for user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed)
return return
} }
@ -111,7 +111,7 @@ func FirstFactorPost(msInitialDelay time.Duration, delayEnabled bool) middleware
ctx.Logger.Errorf("Unable to mark authentication: %s", err.Error()) ctx.Logger.Errorf("Unable to mark authentication: %s", err.Error())
} }
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Credentials are wrong for user %s", bodyJSON.Username), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("credentials are wrong for user %s", bodyJSON.Username), messageAuthenticationFailed)
return return
} }
@ -120,7 +120,7 @@ func FirstFactorPost(msInitialDelay time.Duration, delayEnabled bool) middleware
err = ctx.Providers.Regulator.Mark(bodyJSON.Username, true) err = ctx.Providers.Regulator.Mark(bodyJSON.Username, true)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to mark authentication: %s", err.Error()), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to mark authentication: %s", err.Error()), messageAuthenticationFailed)
return return
} }
@ -134,14 +134,14 @@ func FirstFactorPost(msInitialDelay time.Duration, delayEnabled bool) middleware
err = ctx.SaveSession(newSession) err = ctx.SaveSession(newSession)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to reset the session for user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to reset the session for user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed)
return return
} }
err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx) err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to regenerate session for user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to regenerate session for user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed)
return return
} }
@ -152,7 +152,7 @@ func FirstFactorPost(msInitialDelay time.Duration, delayEnabled bool) middleware
if keepMeLoggedIn { if keepMeLoggedIn {
err = ctx.Providers.SessionProvider.UpdateExpiration(ctx.RequestCtx, ctx.Providers.SessionProvider.RememberMe) err = ctx.Providers.SessionProvider.UpdateExpiration(ctx.RequestCtx, ctx.Providers.SessionProvider.RememberMe)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to update expiration timer for user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to update expiration timer for user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed)
return return
} }
} }
@ -161,7 +161,7 @@ func FirstFactorPost(msInitialDelay time.Duration, delayEnabled bool) middleware
userDetails, err := ctx.Providers.UserProvider.GetDetails(bodyJSON.Username) userDetails, err := ctx.Providers.UserProvider.GetDetails(bodyJSON.Username)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Error while retrieving details from user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("error while retrieving details from user %s: %s", bodyJSON.Username, err.Error()), messageAuthenticationFailed)
return return
} }
@ -175,7 +175,7 @@ func FirstFactorPost(msInitialDelay time.Duration, delayEnabled bool) middleware
err = ctx.SaveSession(userSession) err = ctx.SaveSession(userSession)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to save session of user %s", bodyJSON.Username), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to save session of user %s", bodyJSON.Username), messageAuthenticationFailed)
return return
} }

View File

@ -71,7 +71,7 @@ func (s *FirstFactorSuite) TestShouldFailIfUserProviderCheckPasswordFail() {
}`) }`)
FirstFactorPost(0, false)(s.mock.Ctx) FirstFactorPost(0, false)(s.mock.Ctx)
assert.Equal(s.T(), "Error while checking password for user test: Failed", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "error while checking password 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.")
} }
@ -121,7 +121,7 @@ func (s *FirstFactorSuite) TestShouldFailIfUserProviderGetDetailsFail() {
}`) }`)
FirstFactorPost(0, false)(s.mock.Ctx) FirstFactorPost(0, false)(s.mock.Ctx)
assert.Equal(s.T(), "Error while retrieving details from user test: Failed", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "error while retrieving details from 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.")
} }
@ -143,7 +143,7 @@ func (s *FirstFactorSuite) TestShouldFailIfAuthenticationMarkFail() {
}`) }`)
FirstFactorPost(0, false)(s.mock.Ctx) FirstFactorPost(0, false)(s.mock.Ctx)
assert.Equal(s.T(), "Unable to mark authentication: failed", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "unable to mark authentication: 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.")
} }

View File

@ -21,18 +21,14 @@ func LogoutPost(ctx *middlewares.AutheliaCtx) {
body := logoutBody{} body := logoutBody{}
responseBody := logoutResponseBody{SafeTargetURL: false} responseBody := logoutResponseBody{SafeTargetURL: false}
ctx.Logger.Tracef("Attempting to decode body")
err := ctx.ParseBody(&body) err := ctx.ParseBody(&body)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to parse body during logout: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to parse body during logout: %s", err), messageOperationFailed)
} }
ctx.Logger.Tracef("Attempting to destroy session")
err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx) err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to destroy session during logout: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to destroy session during logout: %s", err), messageOperationFailed)
} }
redirectionURL, err := url.Parse(body.TargetURL) redirectionURL, err := url.Parse(body.TargetURL)
@ -46,6 +42,6 @@ func LogoutPost(ctx *middlewares.AutheliaCtx) {
err = ctx.SetJSONBody(responseBody) err = ctx.SetJSONBody(responseBody)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to set body during logout: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to set body during logout: %s", err), messageOperationFailed)
} }
} }

View File

@ -30,7 +30,7 @@ func oidcAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *
client, err := ctx.Providers.OpenIDConnect.Store.GetInternalClient(clientID) client, err := ctx.Providers.OpenIDConnect.Store.GetInternalClient(clientID)
if err != nil { if err != nil {
err := fmt.Errorf("Unable to find related client configuration with name '%s': %v", ar.GetID(), err) err := fmt.Errorf("unable to find related client configuration with name '%s': %v", ar.GetID(), err)
ctx.Logger.Error(err) ctx.Logger.Error(err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err) ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err)
@ -56,7 +56,7 @@ func oidcAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *
userSession.OIDCWorkflowSession = nil userSession.OIDCWorkflowSession = nil
if err := ctx.SaveSession(userSession); err != nil { if err := ctx.SaveSession(userSession); err != nil {
ctx.Logger.Errorf("%v", err) ctx.Logger.Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
return return
@ -147,7 +147,7 @@ func oidcAuthorizeHandleAuthorizationOrConsentInsufficient(
ar fosite.AuthorizeRequester) { ar fosite.AuthorizeRequester) {
issuer, err := ctx.ExternalRootURL() issuer, err := ctx.ExternalRootURL()
if err != nil { if err != nil {
ctx.Logger.Errorf("%v", err) ctx.Logger.Error(err)
http.Error(rw, err.Error(), http.StatusBadRequest) http.Error(rw, err.Error(), http.StatusBadRequest)
return return
@ -169,7 +169,7 @@ func oidcAuthorizeHandleAuthorizationOrConsentInsufficient(
} }
if err := ctx.SaveSession(userSession); err != nil { if err := ctx.SaveSession(userSession); err != nil {
ctx.Logger.Errorf("%v", err) ctx.Logger.Errorf("Unable to save session: %v", err)
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
return return

View File

@ -35,7 +35,7 @@ func oidcConsent(ctx *middlewares.AutheliaCtx) {
} }
if err := ctx.SetJSONBody(client.GetConsentResponseBody(userSession.OIDCWorkflowSession)); err != nil { if err := ctx.SetJSONBody(client.GetConsentResponseBody(userSession.OIDCWorkflowSession)); err != nil {
ctx.Error(fmt.Errorf("Unable to set JSON body: %v", err), "Operation failed") ctx.Error(fmt.Errorf("unable to set JSON body: %v", err), "Operation failed")
} }
} }
@ -69,7 +69,7 @@ func oidcConsentPOST(ctx *middlewares.AutheliaCtx) {
err = json.Unmarshal(ctx.Request.Body(), &body) err = json.Unmarshal(ctx.Request.Body(), &body)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to unmarshal body: %v", err), "Operation failed") ctx.Error(fmt.Errorf("unable to unmarshal body: %v", err), "Operation failed")
return return
} }
@ -96,7 +96,7 @@ func oidcConsentPOST(ctx *middlewares.AutheliaCtx) {
userSession.OIDCWorkflowSession.GrantedAudience = userSession.OIDCWorkflowSession.RequestedAudience userSession.OIDCWorkflowSession.GrantedAudience = userSession.OIDCWorkflowSession.RequestedAudience
if err := ctx.SaveSession(userSession); err != nil { if err := ctx.SaveSession(userSession); err != nil {
ctx.Error(fmt.Errorf("Unable to write session: %v", err), "Operation failed") ctx.Error(fmt.Errorf("unable to write session: %v", err), "Operation failed")
return return
} }
} else if body.AcceptOrReject == reject { } else if body.AcceptOrReject == reject {
@ -105,7 +105,7 @@ func oidcConsentPOST(ctx *middlewares.AutheliaCtx) {
userSession.OIDCWorkflowSession = nil userSession.OIDCWorkflowSession = nil
if err := ctx.SaveSession(userSession); err != nil { if err := ctx.SaveSession(userSession); err != nil {
ctx.Error(fmt.Errorf("Unable to write session: %v", err), "Operation failed") ctx.Error(fmt.Errorf("unable to write session: %v", err), "Operation failed")
return return
} }
} }
@ -113,6 +113,6 @@ func oidcConsentPOST(ctx *middlewares.AutheliaCtx) {
response := ConsentPostResponseBody{RedirectURI: redirectionURL} response := ConsentPostResponseBody{RedirectURI: redirectionURL}
if err := ctx.SetJSONBody(response); err != nil { if err := ctx.SetJSONBody(response); err != nil {
ctx.Error(fmt.Errorf("Unable to set JSON body in response"), "Operation failed") ctx.Error(fmt.Errorf("unable to set JSON body in response"), "Operation failed")
} }
} }

View File

@ -30,7 +30,7 @@ func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *htt
} }
if tokenType != fosite.AccessToken { if tokenType != fosite.AccessToken {
errStr := "Authorization header must contain an OAuth access token." errStr := "authorization header must contain an OAuth access token."
rw.Header().Set("WWW-Authenticate", fmt.Sprintf("error_description=\"%s\"", errStr)) rw.Header().Set("WWW-Authenticate", fmt.Sprintf("error_description=\"%s\"", errStr))
ctx.Providers.OpenIDConnect.WriteErrorCode(rw, req, http.StatusUnauthorized, errors.New(errStr)) ctx.Providers.OpenIDConnect.WriteErrorCode(rw, req, http.StatusUnauthorized, errors.New(errStr))

View File

@ -14,7 +14,7 @@ func identityRetrieverFromSession(ctx *middlewares.AutheliaCtx) (*session.Identi
userSession := ctx.GetSession() userSession := ctx.GetSession()
if len(userSession.Emails) == 0 { if len(userSession.Emails) == 0 {
return nil, fmt.Errorf("User %s does not have any email address", userSession.Username) return nil, fmt.Errorf("user %s does not have any email address", userSession.Username)
} }
return &session.Identity{ return &session.Identity{
@ -45,13 +45,13 @@ func secondFactorTOTPIdentityFinish(ctx *middlewares.AutheliaCtx, username strin
}) })
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to generate TOTP key: %s", err), messageUnableToRegisterOneTimePassword) ctx.Error(fmt.Errorf("unable to generate TOTP key: %s", err), messageUnableToRegisterOneTimePassword)
return return
} }
err = ctx.Providers.StorageProvider.SaveTOTPSecret(username, key.Secret()) err = ctx.Providers.StorageProvider.SaveTOTPSecret(username, key.Secret())
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to save TOTP secret in DB: %s", err), messageUnableToRegisterOneTimePassword) ctx.Error(fmt.Errorf("unable to save TOTP secret in DB: %s", err), messageUnableToRegisterOneTimePassword)
return return
} }

View File

@ -42,7 +42,7 @@ func secondFactorU2FIdentityFinish(ctx *middlewares.AutheliaCtx, username string
challenge, err := u2f.NewChallenge(appID, trustedFacets) challenge, err := u2f.NewChallenge(appID, trustedFacets)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to generate new U2F challenge for registration: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to generate new U2F challenge for registration: %s", err), messageOperationFailed)
return return
} }
@ -52,7 +52,7 @@ func secondFactorU2FIdentityFinish(ctx *middlewares.AutheliaCtx, username string
err = ctx.SaveSession(userSession) err = ctx.SaveSession(userSession)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to save U2F challenge in session: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to save U2F challenge in session: %s", err), messageOperationFailed)
return return
} }

View File

@ -65,7 +65,7 @@ func (s *HandlerRegisterU2FStep1Suite) TestShouldRaiseWhenXForwardedProtoIsMissi
SecondFactorU2FIdentityFinish(s.mock.Ctx) SecondFactorU2FIdentityFinish(s.mock.Ctx)
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Missing header X-Forwarded-Proto", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "missing header X-Forwarded-Proto", s.mock.Hook.LastEntry().Message)
} }
func (s *HandlerRegisterU2FStep1Suite) TestShouldRaiseWhenXForwardedHostIsMissing() { func (s *HandlerRegisterU2FStep1Suite) TestShouldRaiseWhenXForwardedHostIsMissing() {
@ -85,7 +85,7 @@ func (s *HandlerRegisterU2FStep1Suite) TestShouldRaiseWhenXForwardedHostIsMissin
SecondFactorU2FIdentityFinish(s.mock.Ctx) SecondFactorU2FIdentityFinish(s.mock.Ctx)
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Missing header X-Forwarded-Host", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "missing header X-Forwarded-Host", s.mock.Hook.LastEntry().Message)
} }
func TestShouldRunHandlerRegisterU2FStep1Suite(t *testing.T) { func TestShouldRunHandlerRegisterU2FStep1Suite(t *testing.T) {

View File

@ -16,7 +16,7 @@ func SecondFactorU2FRegister(ctx *middlewares.AutheliaCtx) {
err := ctx.ParseBody(&responseBody) err := ctx.ParseBody(&responseBody)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to parse response body: %v", err), messageUnableToRegisterSecurityKey) ctx.Error(fmt.Errorf("unable to parse response body: %v", err), messageUnableToRegisterSecurityKey)
} }
userSession := ctx.GetSession() userSession := ctx.GetSession()
@ -38,7 +38,7 @@ func SecondFactorU2FRegister(ctx *middlewares.AutheliaCtx) {
registration, err := u2f.Register(responseBody, *userSession.U2FChallenge, u2fConfig) registration, err := u2f.Register(responseBody, *userSession.U2FChallenge, u2fConfig)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to verify U2F registration: %v", err), messageUnableToRegisterSecurityKey) ctx.Error(fmt.Errorf("unable to verify U2F registration: %v", err), messageUnableToRegisterSecurityKey)
return return
} }
@ -48,7 +48,7 @@ func SecondFactorU2FRegister(ctx *middlewares.AutheliaCtx) {
err = ctx.Providers.StorageProvider.SaveU2FDeviceHandle(userSession.Username, registration.KeyHandle, publicKey) err = ctx.Providers.StorageProvider.SaveU2FDeviceHandle(userSession.Username, registration.KeyHandle, publicKey)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to register U2F device for user %s: %v", userSession.Username, err), messageUnableToRegisterSecurityKey) ctx.Error(fmt.Errorf("unable to register U2F device for user %s: %v", userSession.Username, err), messageUnableToRegisterSecurityKey)
return return
} }

View File

@ -23,7 +23,7 @@ func identityRetrieverFromStorage(ctx *middlewares.AutheliaCtx) (*session.Identi
} }
if len(details.Emails) == 0 { if len(details.Emails) == 0 {
return nil, fmt.Errorf("User %s has no email address configured", requestBody.Username) return nil, fmt.Errorf("user %s has no email address configured", requestBody.Username)
} }
return &session.Identity{ return &session.Identity{

View File

@ -15,7 +15,7 @@ func ResetPasswordPost(ctx *middlewares.AutheliaCtx) {
// otherwise PasswordReset would not be set to true. We can improve the security of this check by making the // otherwise PasswordReset would not be set to true. We can improve the security of this check by making the
// request expire at some point because here it only expires when the cookie expires. // request expire at some point because here it only expires when the cookie expires.
if userSession.PasswordResetUsername == nil { if userSession.PasswordResetUsername == nil {
ctx.Error(fmt.Errorf("No identity verification process has been initiated"), messageUnableToResetPassword) ctx.Error(fmt.Errorf("no identity verification process has been initiated"), messageUnableToResetPassword)
return return
} }
@ -33,9 +33,9 @@ func ResetPasswordPost(ctx *middlewares.AutheliaCtx) {
switch { switch {
case utils.IsStringInSliceContains(err.Error(), ldapPasswordComplexityCodes), case utils.IsStringInSliceContains(err.Error(), ldapPasswordComplexityCodes),
utils.IsStringInSliceContains(err.Error(), ldapPasswordComplexityErrors): utils.IsStringInSliceContains(err.Error(), ldapPasswordComplexityErrors):
ctx.Error(fmt.Errorf("%s", err), ldapPasswordComplexityCode) ctx.Error(err, ldapPasswordComplexityCode)
default: default:
ctx.Error(fmt.Errorf("%s", err), messageUnableToResetPassword) ctx.Error(err, messageUnableToResetPassword)
} }
return return
@ -48,7 +48,7 @@ func ResetPasswordPost(ctx *middlewares.AutheliaCtx) {
err = ctx.SaveSession(userSession) err = ctx.SaveSession(userSession)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to update password reset state: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to update password reset state: %s", err), messageOperationFailed)
return return
} }

View File

@ -60,7 +60,7 @@ func SecondFactorDuoPost(duoAPI duo.API) middlewares.RequestHandler {
err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx) err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to regenerate session for user %s: %s", userSession.Username, err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to regenerate session for user %s: %s", userSession.Username, err), messageMFAValidationFailed)
return return
} }
@ -68,7 +68,7 @@ func SecondFactorDuoPost(duoAPI duo.API) middlewares.RequestHandler {
err = ctx.SaveSession(userSession) err = ctx.SaveSession(userSession)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to update authentication level with Duo: %s", err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to update authentication level with Duo: %s", err), messageMFAValidationFailed)
return return
} }

View File

@ -21,25 +21,25 @@ func SecondFactorTOTPPost(totpVerifier TOTPVerifier) middlewares.RequestHandler
secret, err := ctx.Providers.StorageProvider.LoadTOTPSecret(userSession.Username) secret, err := ctx.Providers.StorageProvider.LoadTOTPSecret(userSession.Username)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to load TOTP secret: %s", err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to load TOTP secret: %s", err), messageMFAValidationFailed)
return return
} }
isValid, err := totpVerifier.Verify(requestBody.Token, secret) isValid, err := totpVerifier.Verify(requestBody.Token, secret)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Error occurred during OTP validation for user %s: %s", userSession.Username, err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("error occurred during OTP validation for user %s: %s", userSession.Username, err), messageMFAValidationFailed)
return return
} }
if !isValid { if !isValid {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Wrong passcode during TOTP validation for user %s", userSession.Username), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("wrong passcode during TOTP validation for user %s", userSession.Username), messageMFAValidationFailed)
return return
} }
err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx) err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to regenerate session for user %s: %s", userSession.Username, err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to regenerate session for user %s: %s", userSession.Username, err), messageMFAValidationFailed)
return return
} }
@ -47,7 +47,7 @@ func SecondFactorTOTPPost(totpVerifier TOTPVerifier) middlewares.RequestHandler
err = ctx.SaveSession(userSession) err = ctx.SaveSession(userSession)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to update the authentication level with TOTP: %s", err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to update the authentication level with TOTP: %s", err), messageMFAValidationFailed)
return return
} }

View File

@ -29,7 +29,7 @@ func SecondFactorU2FSignGet(ctx *middlewares.AutheliaCtx) {
challenge, err := u2f.NewChallenge(appID, trustedFacets) challenge, err := u2f.NewChallenge(appID, trustedFacets)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to create U2F challenge: %s", err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to create U2F challenge: %s", err), messageMFAValidationFailed)
return return
} }
@ -38,11 +38,11 @@ func SecondFactorU2FSignGet(ctx *middlewares.AutheliaCtx) {
if err != nil { if err != nil {
if err == storage.ErrNoU2FDeviceHandle { if err == storage.ErrNoU2FDeviceHandle {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("No device handle found for user %s", userSession.Username), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("no device handle found for user %s", userSession.Username), messageMFAValidationFailed)
return return
} }
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to retrieve U2F device handle: %s", err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to retrieve U2F device handle: %s", err), messageMFAValidationFailed)
return return
} }
@ -63,7 +63,7 @@ func SecondFactorU2FSignGet(ctx *middlewares.AutheliaCtx) {
err = ctx.SaveSession(userSession) err = ctx.SaveSession(userSession)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to save U2F challenge and registration in session: %s", err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to save U2F challenge and registration in session: %s", err), messageMFAValidationFailed)
return return
} }
@ -71,7 +71,7 @@ func SecondFactorU2FSignGet(ctx *middlewares.AutheliaCtx) {
err = ctx.SetJSONBody(signRequest) err = ctx.SetJSONBody(signRequest)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to set sign request in body: %s", err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to set sign request in body: %s", err), messageMFAValidationFailed)
return return
} }
} }

View File

@ -27,7 +27,7 @@ func (s *HandlerSignU2FStep1Suite) TestShouldRaiseWhenXForwardedProtoIsMissing()
SecondFactorU2FSignGet(s.mock.Ctx) SecondFactorU2FSignGet(s.mock.Ctx)
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Missing header X-Forwarded-Proto", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "missing header X-Forwarded-Proto", s.mock.Hook.LastEntry().Message)
} }
func (s *HandlerSignU2FStep1Suite) TestShouldRaiseWhenXForwardedHostIsMissing() { func (s *HandlerSignU2FStep1Suite) TestShouldRaiseWhenXForwardedHostIsMissing() {
@ -35,7 +35,7 @@ func (s *HandlerSignU2FStep1Suite) TestShouldRaiseWhenXForwardedHostIsMissing()
SecondFactorU2FSignGet(s.mock.Ctx) SecondFactorU2FSignGet(s.mock.Ctx)
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode()) assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Missing header X-Forwarded-Host", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "missing header X-Forwarded-Host", s.mock.Hook.LastEntry().Message)
} }
func TestShouldRunHandlerSignU2FStep1Suite(t *testing.T) { func TestShouldRunHandlerSignU2FStep1Suite(t *testing.T) {

View File

@ -42,7 +42,7 @@ func SecondFactorU2FSignPost(u2fVerifier U2FVerifier) middlewares.RequestHandler
err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx) err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to regenerate session for user %s: %s", userSession.Username, err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to regenerate session for user %s: %s", userSession.Username, err), messageMFAValidationFailed)
return return
} }
@ -50,7 +50,7 @@ func SecondFactorU2FSignPost(u2fVerifier U2FVerifier) middlewares.RequestHandler
err = ctx.SaveSession(userSession) err = ctx.SaveSession(userSession)
if err != nil { if err != nil {
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to update authentication level with U2F: %s", err), messageMFAValidationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to update authentication level with U2F: %s", err), messageMFAValidationFailed)
return return
} }

View File

@ -87,7 +87,7 @@ func UserInfoGet(ctx *middlewares.AutheliaCtx) {
errors := loadInfo(userSession.Username, ctx.Providers.StorageProvider, &userInfo, ctx.Logger) errors := loadInfo(userSession.Username, ctx.Providers.StorageProvider, &userInfo, ctx.Logger)
if len(errors) > 0 { if len(errors) > 0 {
ctx.Error(fmt.Errorf("Unable to load user information"), messageOperationFailed) ctx.Error(fmt.Errorf("unable to load user information"), messageOperationFailed)
return return
} }
@ -115,7 +115,7 @@ func MethodPreferencePost(ctx *middlewares.AutheliaCtx) {
} }
if !utils.IsStringInSlice(bodyJSON.Method, authentication.PossibleMethods) { if !utils.IsStringInSlice(bodyJSON.Method, authentication.PossibleMethods) {
ctx.Error(fmt.Errorf("Unknown method '%s', it should be one of %s", bodyJSON.Method, strings.Join(authentication.PossibleMethods, ", ")), messageOperationFailed) ctx.Error(fmt.Errorf("unknown method '%s', it should be one of %s", bodyJSON.Method, strings.Join(authentication.PossibleMethods, ", ")), messageOperationFailed)
return return
} }
@ -124,7 +124,7 @@ func MethodPreferencePost(ctx *middlewares.AutheliaCtx) {
err = ctx.Providers.StorageProvider.SavePreferred2FAMethod(userSession.Username, bodyJSON.Method) err = ctx.Providers.StorageProvider.SavePreferred2FAMethod(userSession.Username, bodyJSON.Method)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to save new preferred 2FA method: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to save new preferred 2FA method: %s", err), messageOperationFailed)
return return
} }

View File

@ -154,7 +154,7 @@ func (s *FetchSuite) TestShouldReturnError500WhenStorageFailsToLoad() {
UserInfoGet(s.mock.Ctx) UserInfoGet(s.mock.Ctx)
s.mock.Assert200KO(s.T(), "Operation failed.") s.mock.Assert200KO(s.T(), "Operation failed.")
assert.Equal(s.T(), "Unable to load user information", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "unable to load user information", s.mock.Hook.LastEntry().Message)
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level) assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
} }
@ -213,7 +213,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenBadMethodProvided() {
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 method 'abc', it should be one of totp, u2f, mobile_push", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), "unknown method 'abc', it should be one of totp, u2f, mobile_push", s.mock.Hook.LastEntry().Message)
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level) assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
} }
@ -226,7 +226,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenDatabaseFailsToSave() {
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)
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level) assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
} }

View File

@ -47,7 +47,7 @@ func parseBasicAuth(header, auth string) (username, password string, err error)
s := strings.IndexByte(cs, ':') s := strings.IndexByte(cs, ':')
if s < 0 { if s < 0 {
return "", "", fmt.Errorf("Format of %s header must be user:password", header) return "", "", fmt.Errorf("format of %s header must be user:password", header)
} }
return cs[:s], cs[s+1:], nil return cs[:s], cs[s+1:], nil
@ -85,29 +85,29 @@ func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL url.U
// verifyBasicAuth verify that the provided username and password are correct and // verifyBasicAuth verify that the provided username and password are correct and
// that the user is authorized to target the resource. // that the user is authorized to target the resource.
func verifyBasicAuth(header string, auth []byte, targetURL url.URL, ctx *middlewares.AutheliaCtx) (username, name string, groups, emails []string, authLevel authentication.Level, err error) { //nolint:unparam func verifyBasicAuth(header string, auth []byte, ctx *middlewares.AutheliaCtx) (username, name string, groups, emails []string, authLevel authentication.Level, err error) {
username, password, err := parseBasicAuth(header, string(auth)) username, password, err := parseBasicAuth(header, string(auth))
if err != nil { if err != nil {
return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("Unable to parse content of %s header: %s", header, err) return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("unable to parse content of %s header: %s", header, err)
} }
authenticated, err := ctx.Providers.UserProvider.CheckUserPassword(username, password) authenticated, err := ctx.Providers.UserProvider.CheckUserPassword(username, password)
if err != nil { if err != nil {
return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("Unable to check credentials extracted from %s header: %s", header, err) return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("unable to check credentials extracted from %s header: %s", header, err)
} }
// If the user is not correctly authenticated, send a 401. // If the user is not correctly authenticated, send a 401.
if !authenticated { if !authenticated {
// Request Basic Authentication otherwise // Request Basic Authentication otherwise
return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("User %s is not authenticated", username) return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("user %s is not authenticated", username)
} }
details, err := ctx.Providers.UserProvider.GetDetails(username) details, err := ctx.Providers.UserProvider.GetDetails(username)
if err != nil { if err != nil {
return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("Unable to retrieve details of user %s: %s", username, err) return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("unable to retrieve details of user %s: %s", username, err)
} }
return username, details.DisplayName, details.Groups, details.Emails, authentication.OneFactor, nil return username, details.DisplayName, details.Groups, details.Emails, authentication.OneFactor, nil
@ -155,20 +155,20 @@ func verifySessionCookie(ctx *middlewares.AutheliaCtx, targetURL *url.URL, userS
isUserAnonymous := userSession.Username == "" isUserAnonymous := userSession.Username == ""
if isUserAnonymous && userSession.AuthenticationLevel != authentication.NotAuthenticated { if isUserAnonymous && userSession.AuthenticationLevel != authentication.NotAuthenticated {
return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("An anonymous user cannot be authenticated. That might be the sign of a compromise") return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("an anonymous user cannot be authenticated. That might be the sign of a compromise")
} }
if !userSession.KeepMeLoggedIn && !isUserAnonymous { if !userSession.KeepMeLoggedIn && !isUserAnonymous {
inactiveLongEnough, err := hasUserBeenInactiveTooLong(ctx) inactiveLongEnough, err := hasUserBeenInactiveTooLong(ctx)
if err != nil { if err != nil {
return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("Unable to check if user has been inactive for a long time: %s", err) return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("unable to check if user has been inactive for a long time: %s", err)
} }
if inactiveLongEnough { if inactiveLongEnough {
// Destroy the session a new one will be regenerated on next request. // Destroy the session a new one will be regenerated on next request.
err := ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx) err := ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
if err != nil { if err != nil {
return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("Unable to destroy user session after long inactivity: %s", err) return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("unable to destroy user session after long inactivity: %s", err)
} }
return userSession.Username, userSession.DisplayName, userSession.Groups, userSession.Emails, authentication.NotAuthenticated, fmt.Errorf("User %s has been inactive for too long", userSession.Username) return userSession.Username, userSession.DisplayName, userSession.Groups, userSession.Emails, authentication.NotAuthenticated, fmt.Errorf("User %s has been inactive for too long", userSession.Username)
@ -180,13 +180,15 @@ func verifySessionCookie(ctx *middlewares.AutheliaCtx, targetURL *url.URL, userS
if err == authentication.ErrUserNotFound { if err == authentication.ErrUserNotFound {
err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx) err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
if err != nil { if err != nil {
ctx.Logger.Error(fmt.Errorf("Unable to destroy user session after provider refresh didn't find the user: %s", err)) ctx.Logger.Errorf("Unable to destroy user session after provider refresh didn't find the user: %s", err)
} }
return userSession.Username, userSession.DisplayName, userSession.Groups, userSession.Emails, authentication.NotAuthenticated, err return userSession.Username, userSession.DisplayName, userSession.Groups, userSession.Emails, authentication.NotAuthenticated, err
} }
ctx.Logger.Warnf("Error occurred while attempting to update user details from LDAP: %s", err) ctx.Logger.Errorf("Error occurred while attempting to update user details from LDAP: %s", err)
return "", "", nil, nil, authentication.NotAuthenticated, err
} }
return userSession.Username, userSession.DisplayName, userSession.Groups, userSession.Emails, userSession.AuthenticationLevel, nil return userSession.Username, userSession.DisplayName, userSession.Groups, userSession.Emails, userSession.AuthenticationLevel, nil
@ -286,11 +288,11 @@ func generateVerifySessionHasUpToDateProfileTraceLogs(ctx *middlewares.AutheliaC
// Check Groups. // Check Groups.
var groupsDelta []string var groupsDelta []string
if len(groupsAdded) != 0 { if len(groupsAdded) != 0 {
groupsDelta = append(groupsDelta, fmt.Sprintf("Added: %s.", strings.Join(groupsAdded, ", "))) groupsDelta = append(groupsDelta, fmt.Sprintf("added: %s.", strings.Join(groupsAdded, ", ")))
} }
if len(groupsRemoved) != 0 { if len(groupsRemoved) != 0 {
groupsDelta = append(groupsDelta, fmt.Sprintf("Removed: %s.", strings.Join(groupsRemoved, ", "))) groupsDelta = append(groupsDelta, fmt.Sprintf("removed: %s.", strings.Join(groupsRemoved, ", ")))
} }
if len(groupsDelta) != 0 { if len(groupsDelta) != 0 {
@ -302,11 +304,11 @@ func generateVerifySessionHasUpToDateProfileTraceLogs(ctx *middlewares.AutheliaC
// Check Emails. // Check Emails.
var emailsDelta []string var emailsDelta []string
if len(emailsAdded) != 0 { if len(emailsAdded) != 0 {
emailsDelta = append(emailsDelta, fmt.Sprintf("Added: %s.", strings.Join(emailsAdded, ", "))) emailsDelta = append(emailsDelta, fmt.Sprintf("added: %s.", strings.Join(emailsAdded, ", ")))
} }
if len(emailsRemoved) != 0 { if len(emailsRemoved) != 0 {
emailsDelta = append(emailsDelta, fmt.Sprintf("Removed: %s.", strings.Join(emailsRemoved, ", "))) emailsDelta = append(emailsDelta, fmt.Sprintf("removed: %s.", strings.Join(emailsRemoved, ", ")))
} }
if len(emailsDelta) != 0 { if len(emailsDelta) != 0 {
@ -350,7 +352,7 @@ func verifySessionHasUpToDateProfile(ctx *middlewares.AutheliaCtx, targetURL *ur
if !groupsDiff && !emailsDiff && !nameDiff { if !groupsDiff && !emailsDiff && !nameDiff {
ctx.Logger.Tracef("Updated profile not detected for %s.", userSession.Username) ctx.Logger.Tracef("Updated profile not detected for %s.", userSession.Username)
// Only update TTL if the user has a interval set. // Only update TTL if the user has an interval set.
// We get to this check when there were no changes. // We get to this check when there were no changes.
// Also make sure to update the session even if no difference was found. // Also make sure to update the session even if no difference was found.
// This is so that we don't check every subsequent request after this one. // This is so that we don't check every subsequent request after this one.
@ -411,12 +413,12 @@ func verifyAuth(ctx *middlewares.AutheliaCtx, targetURL *url.URL, refreshProfile
if authValue != nil { if authValue != nil {
isBasicAuth = true isBasicAuth = true
} else if isBasicAuth { } else if isBasicAuth {
err = fmt.Errorf("Basic auth requested via query arg, but no value provided via %s header", authHeader) err = fmt.Errorf("basic auth requested via query arg, but no value provided via %s header", authHeader)
return return
} }
if isBasicAuth { if isBasicAuth {
username, name, groups, emails, authLevel, err = verifyBasicAuth(authHeader, authValue, *targetURL, ctx) username, name, groups, emails, authLevel, err = verifyBasicAuth(authHeader, authValue, ctx)
return return
} }
@ -429,13 +431,10 @@ func verifyAuth(ctx *middlewares.AutheliaCtx, targetURL *url.URL, refreshProfile
err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx) err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
if err != nil { if err != nil {
ctx.Logger.Error( ctx.Logger.Errorf("Unable to destroy user session after handler could not match them to their %s header: %s", HeaderSessionUsername, err)
fmt.Errorf(
"Unable to destroy user session after handler could not match them to their %s header: %s",
HeaderSessionUsername, err))
} }
err = fmt.Errorf("Could not match user %s to their %s header with a value of %s when visiting %s", username, HeaderSessionUsername, sessionUsername, targetURL.String()) err = fmt.Errorf("could not match user %s to their %s header with a value of %s when visiting %s", username, HeaderSessionUsername, sessionUsername, targetURL.String())
} }
return return
@ -450,37 +449,36 @@ func VerifyGet(cfg schema.AuthenticationBackendConfiguration) middlewares.Reques
targetURL, err := ctx.GetOriginalURL() targetURL, err := ctx.GetOriginalURL()
if err != nil { if err != nil {
ctx.Logger.Error(fmt.Errorf("Unable to parse target URL: %s", err)) ctx.Logger.Errorf("Unable to parse target URL: %s", err)
ctx.ReplyUnauthorized() ctx.ReplyUnauthorized()
return return
} }
if !isSchemeHTTPS(targetURL) && !isSchemeWSS(targetURL) { if !isSchemeHTTPS(targetURL) && !isSchemeWSS(targetURL) {
ctx.Logger.Error(fmt.Errorf("Scheme of target URL %s must be secure since cookies are "+ ctx.Logger.Errorf("Scheme of target URL %s must be secure since cookies are "+
"only transported over a secure connection for security reasons", targetURL.String())) "only transported over a secure connection for security reasons", targetURL.String())
ctx.ReplyUnauthorized() ctx.ReplyUnauthorized()
return return
} }
if !isURLUnderProtectedDomain(targetURL, ctx.Configuration.Session.Domain) { if !isURLUnderProtectedDomain(targetURL, ctx.Configuration.Session.Domain) {
ctx.Logger.Error(fmt.Errorf("The target URL %s is not under the protected domain %s", ctx.Logger.Errorf("Target URL %s is not under the protected domain %s",
targetURL.String(), ctx.Configuration.Session.Domain)) targetURL.String(), ctx.Configuration.Session.Domain)
ctx.ReplyUnauthorized() ctx.ReplyUnauthorized()
return return
} }
method := ctx.XForwardedMethod()
isBasicAuth, username, name, groups, emails, authLevel, err := verifyAuth(ctx, targetURL, refreshProfile, refreshProfileInterval) isBasicAuth, username, name, groups, emails, authLevel, err := verifyAuth(ctx, targetURL, refreshProfile, refreshProfileInterval)
method := ctx.XForwardedMethod()
if err != nil { if err != nil {
ctx.Logger.Error(fmt.Sprintf("Error caught when verifying user authorization: %s", err)) ctx.Logger.Errorf("Error caught when verifying user authorization: %s", err)
if err := updateActivityTimestamp(ctx, isBasicAuth, username); err != nil { if err := updateActivityTimestamp(ctx, isBasicAuth, username); err != nil {
ctx.Error(fmt.Errorf("Unable to update last activity: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to update last activity: %s", err), messageOperationFailed)
return return
} }
@ -503,7 +501,7 @@ func VerifyGet(cfg schema.AuthenticationBackendConfiguration) middlewares.Reques
} }
if err := updateActivityTimestamp(ctx, isBasicAuth, username); err != nil { if err := updateActivityTimestamp(ctx, isBasicAuth, username); err != nil {
ctx.Error(fmt.Errorf("Unable to update last activity: %s", err), messageOperationFailed) ctx.Error(fmt.Errorf("unable to update last activity: %s", err), messageOperationFailed)
} }
} }
} }

View File

@ -100,7 +100,7 @@ func TestShouldRaiseWhenCredentialsAreNotInCorrectForm(t *testing.T) {
// The decoded format should be user:password. // The decoded format should be user:password.
_, _, err := parseBasicAuth(HeaderProxyAuthorization, "Basic am9obiBwYXNzd29yZA==") _, _, err := parseBasicAuth(HeaderProxyAuthorization, "Basic am9obiBwYXNzd29yZA==")
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, "Format of Proxy-Authorization header must be user:password", err.Error()) assert.Equal(t, "format of Proxy-Authorization header must be user:password", err.Error())
} }
func TestShouldUseProvidedHeaderName(t *testing.T) { func TestShouldUseProvidedHeaderName(t *testing.T) {
@ -144,7 +144,7 @@ func TestShouldCheckAuthorizationMatching(t *testing.T) {
{"deny", authentication.TwoFactor, Forbidden}, {"deny", authentication.TwoFactor, Forbidden},
} }
url, _ := url.ParseRequestURI("https://test.example.com") u, _ := url.ParseRequestURI("https://test.example.com")
for _, rule := range rules { for _, rule := range rules {
authorizer := authorization.NewAuthorizer(&schema.Configuration{ authorizer := authorization.NewAuthorizer(&schema.Configuration{
@ -161,7 +161,7 @@ func TestShouldCheckAuthorizationMatching(t *testing.T) {
username = testUsername username = testUsername
} }
matching := isTargetURLAuthorized(authorizer, *url, username, []string{}, net.ParseIP("127.0.0.1"), []byte("GET"), rule.AuthLevel) matching := isTargetURLAuthorized(authorizer, *u, username, []string{}, net.ParseIP("127.0.0.1"), []byte("GET"), rule.AuthLevel)
assert.Equal(t, rule.ExpectedMatching, matching, "policy=%s, authLevel=%v, expected=%v, actual=%v", assert.Equal(t, rule.ExpectedMatching, matching, "policy=%s, authLevel=%v, expected=%v, actual=%v",
rule.Policy, rule.AuthLevel, rule.ExpectedMatching, matching) rule.Policy, rule.AuthLevel, rule.ExpectedMatching, matching)
} }
@ -176,8 +176,7 @@ func TestShouldVerifyWrongCredentials(t *testing.T) {
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")). CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(false, nil) Return(false, nil)
url, _ := url.ParseRequestURI("https://test.example.com") _, _, _, _, _, err := verifyBasicAuth(HeaderProxyAuthorization, []byte("Basic am9objpwYXNzd29yZA=="), mock.Ctx)
_, _, _, _, _, err := verifyBasicAuth(HeaderProxyAuthorization, []byte("Basic am9objpwYXNzd29yZA=="), *url, mock.Ctx)
assert.Error(t, err) assert.Error(t, err)
} }

View File

@ -16,7 +16,7 @@ func handleOIDCWorkflowResponse(ctx *middlewares.AutheliaCtx) {
userSession := ctx.GetSession() userSession := ctx.GetSession()
if !authorization.IsAuthLevelSufficient(userSession.AuthenticationLevel, userSession.OIDCWorkflowSession.RequiredAuthorizationLevel) { if !authorization.IsAuthLevelSufficient(userSession.AuthenticationLevel, userSession.OIDCWorkflowSession.RequiredAuthorizationLevel) {
ctx.Logger.Warn("OIDC requires 2FA, cannot be redirected yet") ctx.Logger.Warn("OpenID Connect requires 2FA, cannot be redirected yet")
ctx.ReplyOK() ctx.ReplyOK()
return return
@ -24,8 +24,8 @@ func handleOIDCWorkflowResponse(ctx *middlewares.AutheliaCtx) {
uri, err := ctx.ExternalRootURL() uri, err := ctx.ExternalRootURL()
if err != nil { if err != nil {
ctx.Logger.Errorf("%v", err) ctx.Logger.Errorf("Unable to extract external root URL: %v", err)
handleAuthenticationUnauthorized(ctx, fmt.Errorf("Unable to get forward facing URI"), messageAuthenticationFailed) handleAuthenticationUnauthorized(ctx, fmt.Errorf("unable to get forward facing URI"), messageAuthenticationFailed)
return return
} }
@ -64,7 +64,7 @@ func Handle1FAResponse(ctx *middlewares.AutheliaCtx, targetURI, requestMethod st
targetURL, err := url.ParseRequestURI(targetURI) targetURL, err := url.ParseRequestURI(targetURI)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to parse target URL %s: %s", targetURI, err), messageAuthenticationFailed) ctx.Error(fmt.Errorf("unable to parse target URL %s: %s", targetURI, err), messageAuthenticationFailed)
return return
} }
@ -88,6 +88,8 @@ func Handle1FAResponse(ctx *middlewares.AutheliaCtx, targetURI, requestMethod st
safeRedirection := utils.IsRedirectionSafe(*targetURL, ctx.Configuration.Session.Domain) safeRedirection := utils.IsRedirectionSafe(*targetURL, ctx.Configuration.Session.Domain)
if !safeRedirection { if !safeRedirection {
ctx.Logger.Debugf("Redirection URL %s is not safe", targetURI)
if !ctx.Providers.Authorizer.IsSecondFactorEnabled() && ctx.Configuration.DefaultRedirectionURL != "" { if !ctx.Providers.Authorizer.IsSecondFactorEnabled() && ctx.Configuration.DefaultRedirectionURL != "" {
err := ctx.SetJSONBody(redirectResponse{Redirect: ctx.Configuration.DefaultRedirectionURL}) err := ctx.SetJSONBody(redirectResponse{Redirect: ctx.Configuration.DefaultRedirectionURL})
if err != nil { if err != nil {
@ -126,7 +128,7 @@ func Handle2FAResponse(ctx *middlewares.AutheliaCtx, targetURI string) {
safe, err := utils.IsRedirectionURISafe(targetURI, ctx.Configuration.Session.Domain) safe, err := utils.IsRedirectionURISafe(targetURI, ctx.Configuration.Session.Domain)
if err != nil { if err != nil {
ctx.Error(fmt.Errorf("Unable to check target URL: %s", err), messageMFAValidationFailed) ctx.Error(fmt.Errorf("unable to check target URL: %s", err), messageMFAValidationFailed)
return return
} }

View File

@ -100,7 +100,7 @@ func NewHTTPToAutheliaHandlerAdaptor(h AutheliaHandlerFunc) RequestHandler {
rURL, err := url.ParseRequestURI(r.RequestURI) rURL, err := url.ParseRequestURI(r.RequestURI)
if err != nil { if err != nil {
ctx.Logger.Errorf("cannot parse requestURI %q: %s", r.RequestURI, err) ctx.Logger.Errorf("Cannot parse requestURI %q: %s", r.RequestURI, err)
ctx.RequestCtx.Error("Internal Server Error", fasthttp.StatusInternalServerError) ctx.RequestCtx.Error("Internal Server Error", fasthttp.StatusInternalServerError)
return return