fix(handlers): fix redirect with timed out sessions on rules with bypass policy (#3599)

This change replaced a returned error with a warning when the idle timeout was exceeded.

Fixes #3587
pull/3661/head
Manuel Nuñez 2022-07-04 20:58:35 -03:00 committed by GitHub
parent 4c7a9ef5b2
commit da012ab2d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 5 deletions

View File

@ -171,7 +171,9 @@ func verifySessionCookie(ctx *middlewares.AutheliaCtx, targetURL *url.URL, userS
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) ctx.Logger.Warnf("User %s has been inactive for too long", userSession.Username)
return "", "", nil, nil, authentication.NotAuthenticated, nil
} }
} }
@ -260,8 +262,8 @@ func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, is
} }
} }
func updateActivityTimestamp(ctx *middlewares.AutheliaCtx, isBasicAuth bool, username string) error { func updateActivityTimestamp(ctx *middlewares.AutheliaCtx, isBasicAuth bool) error {
if isBasicAuth || username == "" { if isBasicAuth {
return nil return nil
} }
@ -477,7 +479,7 @@ func VerifyGET(cfg schema.AuthenticationBackendConfiguration) middlewares.Reques
if err != nil { if err != nil {
ctx.Logger.Errorf("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); 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
} }
@ -500,7 +502,7 @@ func VerifyGET(cfg schema.AuthenticationBackendConfiguration) middlewares.Reques
setForwardedHeaders(&ctx.Response.Header, username, name, groups, emails) setForwardedHeaders(&ctx.Response.Header, username, name, groups, emails)
} }
if err := updateActivityTimestamp(ctx, isBasicAuth, username); err != nil { if err := updateActivityTimestamp(ctx, isBasicAuth); 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

@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/valyala/fasthttp"
"github.com/authelia/authelia/v4/internal/authentication" "github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/authorization"
@ -1264,3 +1265,52 @@ func TestGetProfileRefreshSettings(t *testing.T) {
assert.Equal(t, true, refresh) assert.Equal(t, true, refresh)
assert.Equal(t, time.Duration(0), interval) assert.Equal(t, time.Duration(0), interval)
} }
func TestShouldNotRedirectRequestsForBypassACLWhenInactiveForTooLong(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
clock := mocks.TestingClock{}
clock.Set(time.Now())
past := clock.Now().Add(-1 * time.Hour)
mock.Ctx.Configuration.Session.Inactivity = testInactivity
// Reload the session provider since the configuration is indirect.
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = past.Unix()
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
// Should respond 200 OK.
mock.Ctx.QueryArgs().Add("rd", "https://login.example.com")
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("X-Original-URL", "https://bypass.example.com")
VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode())
assert.Nil(t, mock.Ctx.Response.Header.Peek("Location"))
// Should respond 302 Found.
mock.Ctx.QueryArgs().Add("rd", "https://login.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("Accept", "text/html; charset=utf-8")
VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode())
assert.Equal(t, "https://login.example.com/?rd=https%3A%2F%2Ftwo-factor.example.com&rm=GET", string(mock.Ctx.Response.Header.Peek("Location")))
// Should respond 401 Unauthorized.
mock.Ctx.QueryArgs().Del("rd")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
mock.Ctx.Request.Header.Set("X-Forwarded-Method", "GET")
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8")
VerifyGET(verifyGetCfg)(mock.Ctx)
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
assert.Nil(t, mock.Ctx.Response.Header.Peek("Location"))
}