From c9dcce5a41137937df1aad7ac81170b443740f88 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Fri, 8 Mar 2024 13:23:47 +0100 Subject: [PATCH] feat: control edge cache ttl (#3808) --- .schemastore/config.schema.json | 8 +++++++- driver/config/config.go | 5 +++++ embedx/config.schema.json | 6 ++++++ internal/client-go/go.sum | 1 + session/handler.go | 7 ++++++- session/handler_test.go | 18 ++++++++++++++---- 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/.schemastore/config.schema.json b/.schemastore/config.schema.json index d03c22b37246..829b71bae3fb 100644 --- a/.schemastore/config.schema.json +++ b/.schemastore/config.schema.json @@ -2731,10 +2731,16 @@ "properties": { "cacheable_sessions": { "type": "boolean", - "title": "Enable Ory Sessions caching", + "title": "Enable Ory Session Edge Caching", "description": "If enabled allows Ory Sessions to be cached. Only effective in the Ory Network.", "default": false }, + "cacheable_sessions_max_age": { + "title": "Set Ory Session Edge Caching maximum age", + "description": "Set how long Ory Sessions are cached on the edge. If unset, the session expiry will be used. Only effective in the Ory Network.", + "type": "string", + "pattern": "^([0-9]+(ns|us|ms|s|m|h))+$" + }, "use_continue_with_transitions": { "type": "boolean", "title": "Enable new flow transitions using `continue_with` items", diff --git a/driver/config/config.go b/driver/config/config.go index 92ba9cee38e2..78fa3d633854 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -115,6 +115,7 @@ const ( ViperKeySessionTokenizerTemplates = "session.whoami.tokenizer.templates" ViperKeySessionWhoAmIAAL = "session.whoami.required_aal" ViperKeySessionWhoAmICaching = "feature_flags.cacheable_sessions" + ViperKeySessionWhoAmICachingMaxAge = "feature_flags.cacheable_sessions_max_age" ViperKeyUseContinueWithTransitions = "feature_flags.use_continue_with_transitions" ViperKeySessionRefreshMinTimeLeft = "session.earliest_possible_extend" ViperKeyCookieSameSite = "cookies.same_site" @@ -1353,6 +1354,10 @@ func (p *Config) SessionWhoAmICaching(ctx context.Context) bool { return p.GetProvider(ctx).Bool(ViperKeySessionWhoAmICaching) } +func (p *Config) SessionWhoAmICachingMaxAge(ctx context.Context) time.Duration { + return p.GetProvider(ctx).DurationF(ViperKeySessionWhoAmICachingMaxAge, 0) +} + func (p *Config) UseContinueWithTransitions(ctx context.Context) bool { return p.GetProvider(ctx).Bool(ViperKeyUseContinueWithTransitions) } diff --git a/embedx/config.schema.json b/embedx/config.schema.json index 65eacf9cfbd4..6a355e055639 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -2736,6 +2736,12 @@ "description": "If enabled allows Ory Sessions to be cached. Only effective in the Ory Network.", "default": false }, + "cacheable_sessions_max_age": { + "title": "Set Ory Session Edge Caching maximum age", + "description": "Set how long Ory Sessions are cached on the edge. If unset, the session expiry will be used. Only effective in the Ory Network.", + "type": "string", + "pattern": "^([0-9]+(ns|us|ms|s|m|h))+$" + }, "use_continue_with_transitions": { "type": "boolean", "title": "Enable new flow transitions using `continue_with` items", diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/session/handler.go b/session/handler.go index f69e9c655c67..56bd053e551a 100644 --- a/session/handler.go +++ b/session/handler.go @@ -255,7 +255,12 @@ func (h *Handler) whoami(w http.ResponseWriter, r *http.Request, _ httprouter.Pa // Set Cache header only when configured, and when no tokenization is requested. if c.SessionWhoAmICaching(ctx) && len(tokenizeTemplate) == 0 { - w.Header().Set("Ory-Session-Cache-For", fmt.Sprintf("%d", int64(time.Until(s.ExpiresAt).Seconds()))) + expiry := time.Until(s.ExpiresAt) + if c.SessionWhoAmICachingMaxAge(ctx) > 0 && expiry > c.SessionWhoAmICachingMaxAge(ctx) { + expiry = c.SessionWhoAmICachingMaxAge(ctx) + } + + w.Header().Set("Ory-Session-Cache-For", fmt.Sprintf("%0.f", expiry.Seconds())) } if err := h.r.SessionManager().RefreshCookie(ctx, w, r, s); err != nil { diff --git a/session/handler_test.go b/session/handler_test.go index dce2f7b05116..cd0a9ddca6c1 100644 --- a/session/handler_test.go +++ b/session/handler_test.go @@ -142,8 +142,9 @@ func TestSessionWhoAmI(t *testing.T) { }) t.Run("case=http methods", func(t *testing.T) { - run := func(t *testing.T, cacheEnabled bool) { + run := func(t *testing.T, cacheEnabled bool, maxAge time.Duration) { conf.MustSet(ctx, config.ViperKeySessionWhoAmICaching, cacheEnabled) + conf.MustSet(ctx, config.ViperKeySessionWhoAmICachingMaxAge, maxAge) client := testhelpers.NewClientWithCookies(t) // No cookie yet -> 401 @@ -153,6 +154,7 @@ func TestSessionWhoAmI(t *testing.T) { if cacheEnabled { assert.NotEmpty(t, res.Header.Get("Ory-Session-Cache-For")) + assert.Equal(t, "60", res.Header.Get("Ory-Session-Cache-For")) } else { assert.Empty(t, res.Header.Get("Ory-Session-Cache-For")) } @@ -182,7 +184,11 @@ func TestSessionWhoAmI(t *testing.T) { assert.NotEmpty(t, res.Header.Get("X-Kratos-Authenticated-Identity-Id")) if cacheEnabled { - assert.NotEmpty(t, res.Header.Get("Ory-Session-Cache-For")) + if maxAge > 0 { + assert.Equal(t, fmt.Sprintf("%0.f", maxAge.Seconds()), res.Header.Get("Ory-Session-Cache-For")) + } else { + assert.Equal(t, fmt.Sprintf("%0.f", conf.SessionLifespan(ctx).Seconds()), res.Header.Get("Ory-Session-Cache-For")) + } } else { assert.Empty(t, res.Header.Get("Ory-Session-Cache-For")) } @@ -198,11 +204,15 @@ func TestSessionWhoAmI(t *testing.T) { } t.Run("cache disabled", func(t *testing.T) { - run(t, false) + run(t, false, 0) }) t.Run("cache enabled", func(t *testing.T) { - run(t, true) + run(t, true, 0) + }) + + t.Run("cache enabled with max age", func(t *testing.T) { + run(t, true, time.Minute) }) })