From 92383e94cf7ac37c41c19eea67d50ce34fc0e702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Harasimowicz?= Date: Tue, 11 Jan 2022 18:59:26 +0100 Subject: [PATCH] PLATFORM-6607| add configurable session refresh time window --- driver/config/config.go | 5 +++++ embedx/config.schema.json | 12 ++++++++++++ session/handler.go | 7 ++++--- session/session.go | 8 ++++++++ session/session_test.go | 9 +++++++++ 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/driver/config/config.go b/driver/config/config.go index 2a23ceaacc48..b9c40dbeac63 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -96,6 +96,7 @@ const ( ViperKeySessionPersistentCookie = "session.cookie.persistent" ViperKeySessionWhoAmIAAL = "session.whoami.required_aal" ViperKeySessionWhoAmIRefresh = "session.whoami.refresh" + ViperKeySessionRefreshTimeWindow = "session.refresh_time_window" ViperKeyCookieSameSite = "cookies.same_site" ViperKeyCookieDomain = "cookies.domain" ViperKeyCookiePath = "cookies.path" @@ -1034,6 +1035,10 @@ func (p *Config) SessionWhoAmIRefresh() bool { return p.p.Bool(ViperKeySessionWhoAmIRefresh) } +func (p *Config) SessionRefreshTimeWindow() time.Duration { + return p.p.DurationF(ViperKeySessionRefreshTimeWindow, p.SessionLifespan()/2) +} + func (p *Config) SelfServiceSettingsRequiredAAL() string { return p.p.String(ViperKeySelfServiceSettingsRequiredAAL) } diff --git a/embedx/config.schema.json b/embedx/config.schema.json index 24f0e3d5033d..dd9626c54087 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -2174,6 +2174,18 @@ } }, "additionalProperties": false + }, + "refresh_time_window": { + "title": "Session refresh time window", + "description": "Time window when session can be refreshed to avoid excess refreshes. It is calculated as duration from the session expiration time.", + "type": "string", + "pattern": "^[0-9]+(ns|us|ms|s|m|h)$", + "default": "12h", + "examples": [ + "1h", + "1m", + "1s" + ] } } }, diff --git a/session/handler.go b/session/handler.go index 7f317ab52114..0929dcaa935d 100644 --- a/session/handler.go +++ b/session/handler.go @@ -170,7 +170,8 @@ func (h *Handler) whoami(w http.ResponseWriter, r *http.Request, ps httprouter.P } var aalErr *ErrAALNotSatisfied - if err := h.r.SessionManager().DoesSessionSatisfy(r, s, h.r.Config(r.Context()).SessionWhoAmIAAL()); errors.As(err, &aalErr) { + c := h.r.Config(r.Context()) + if err := h.r.SessionManager().DoesSessionSatisfy(r, s, c.SessionWhoAmIAAL()); errors.As(err, &aalErr) { h.r.Audit().WithRequest(r).WithError(err).Info("Session was found but AAL is not satisfied for calling this endpoint.") h.r.Writer().WriteError(w, r, err) return @@ -182,8 +183,8 @@ func (h *Handler) whoami(w http.ResponseWriter, r *http.Request, ps httprouter.P // Refresh session if param was true refresh := r.URL.Query().Get("refresh") - if h.r.Config(r.Context()).SessionWhoAmIRefresh() && refresh == "true" { - if err := h.r.SessionPersister().UpsertSession(r.Context(), s.Refresh(h.r.Config(r.Context()))); err != nil { + if c.SessionWhoAmIRefresh() && refresh == "true" && s.CanBeRefreshed(c) { + if err := h.r.SessionPersister().UpsertSession(r.Context(), s.Refresh(c)); err != nil { h.r.Writer().WriteError(w, r, err) return } diff --git a/session/session.go b/session/session.go index 96d9be4909c3..d842a3c7d6af 100644 --- a/session/session.go +++ b/session/session.go @@ -24,6 +24,10 @@ type lifespanProvider interface { SessionLifespan() time.Duration } +type refreshWindowProvider interface { + SessionRefreshTimeWindow() time.Duration +} + // A Session // // swagger:model session @@ -161,6 +165,10 @@ func (s *Session) Refresh(c lifespanProvider) *Session { return s } +func (s *Session) CanBeRefreshed(c refreshWindowProvider) bool { + return time.Now().Add(c.SessionRefreshTimeWindow()).After(s.ExpiresAt) +} + func (s *Session) IsActive() bool { return s.Active && s.ExpiresAt.After(time.Now()) && (s.Identity == nil || s.Identity.IsActive()) } diff --git a/session/session_test.go b/session/session_test.go index 63bed7b20324..b5e2ee317b71 100644 --- a/session/session_test.go +++ b/session/session_test.go @@ -60,6 +60,15 @@ func TestSession(t *testing.T) { assert.Empty(t, s.AuthenticatedAt) }) + t.Run("case=session refresh", func(t *testing.T) { + s := session.NewInactiveSession() + assert.False(t, s.CanBeRefreshed(conf), "fresh session is not refreshable") + + s.ExpiresAt = time.Now().Add(13 * time.Hour) + assert.True(t, s.CanBeRefreshed(conf), "session is refreshable after 13hrs") + + }) + t.Run("case=aal", func(t *testing.T) { for _, tc := range []struct { d string