From 31c7f3d4ba5881b0a5ca1c3e23028d7145c4330e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20M=C3=A4nnchen?= Date: Mon, 12 Aug 2024 11:32:24 +0200 Subject: [PATCH] Cache Header max-age=0 fix (#371) Fixes #370 --- src/oidcc_http_util.erl | 33 ++++++++++++++++++++++ src/oidcc_provider_configuration.erl | 41 ++++------------------------ test/oidcc_http_util_test.erl | 23 ++++++++++++++++ 3 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 test/oidcc_http_util_test.erl diff --git a/src/oidcc_http_util.erl b/src/oidcc_http_util.erl index 2b93444..964f450 100644 --- a/src/oidcc_http_util.erl +++ b/src/oidcc_http_util.erl @@ -8,6 +8,7 @@ -export([basic_auth_header/2]). -export([bearer_auth_header/1]). +-export([headers_to_cache_deadline/2]). -export([request/4]). -export_type([ @@ -170,3 +171,35 @@ fetch_content_type(Headers) -> _Other -> unknown end. + +-spec headers_to_cache_deadline(Headers, DefaultExpiry) -> pos_integer() when + Headers :: [{Header :: binary(), Value :: binary()}], DefaultExpiry :: non_neg_integer(). +headers_to_cache_deadline(Headers, DefaultExpiry) -> + case proplists:lookup("cache-control", Headers) of + {"cache-control", Cache} -> + try + cache_deadline(Cache, DefaultExpiry) + catch + _:_ -> + DefaultExpiry + end; + none -> + DefaultExpiry + end. + +-spec cache_deadline(Cache :: iodata(), Fallback :: pos_integer()) -> pos_integer(). +cache_deadline(Cache, Fallback) -> + Entries = + binary:split(iolist_to_binary(Cache), [<<",">>, <<"=">>, <<" ">>], [global, trim_all]), + MaxAge = + fun + (<<"0">>, true) -> + Fallback; + (Entry, true) -> + erlang:convert_time_unit(binary_to_integer(Entry), second, millisecond); + (<<"max-age">>, _) -> + true; + (_, Res) -> + Res + end, + lists:foldl(MaxAge, Fallback, Entries). diff --git a/src/oidcc_provider_configuration.erl b/src/oidcc_provider_configuration.erl index d22ef18..8a833df 100644 --- a/src/oidcc_provider_configuration.erl +++ b/src/oidcc_provider_configuration.erl @@ -212,10 +212,12 @@ load_configuration(Issuer0, Opts) -> % this quirk is deprecated, but we keep the support for backwards compatibility. AllowIssuerMismatch = maps:get(allow_issuer_mismatch, Quirks, false), + DefaultExpiry = maps:get(fallback_expiry, Opts, ?DEFAULT_CONFIG_EXPIRY), + maybe {ok, {{json, ConfigurationMap}, Headers}} ?= oidcc_http_util:request(get, Request, TelemetryOpts, RequestOpts), - Expiry = headers_to_deadline(Headers, Opts), + Expiry = oidcc_http_util:headers_to_cache_deadline(Headers, DefaultExpiry), {ok, #oidcc_provider_configuration{issuer = ConfigIssuer} = Configuration} ?= decode_configuration(ConfigurationMap, #{quirks => Quirks}), case ConfigIssuer of @@ -259,10 +261,12 @@ load_jwks(JwksUri, Opts) -> TelemetryOpts = #{topic => [oidcc, load_jwks], extra_meta => #{jwks_uri => JwksUri}}, RequestOpts = maps:get(request_opts, Opts, #{}), + DefaultExpiry = maps:get(fallback_expiry, Opts, ?DEFAULT_CONFIG_EXPIRY), + maybe {ok, {{json, JwksBinary}, Headers}} ?= oidcc_http_util:request(get, {JwksUri, []}, TelemetryOpts, RequestOpts), - Expiry = headers_to_deadline(Headers, Opts), + Expiry = oidcc_http_util:headers_to_cache_deadline(Headers, DefaultExpiry), Jwks = jose_jwk:from(JwksBinary), {ok, {Jwks, Expiry}} else @@ -569,39 +573,6 @@ decode_configuration(Configuration0, Opts) -> Configuration :: map(). decode_configuration(Configuration) -> decode_configuration(Configuration, #{}). --spec headers_to_deadline(Headers, Opts) -> pos_integer() when - Headers :: [{Header :: binary(), Value :: binary()}], Opts :: opts(). -headers_to_deadline(Headers, Opts) -> - DefaultExpiry = maps:get(fallback_expiry, Opts, ?DEFAULT_CONFIG_EXPIRY), - case proplists:lookup("cache-control", Headers) of - {"cache-control", Cache} -> - try - cache_deadline(Cache, DefaultExpiry) - catch - _:_ -> - DefaultExpiry - end; - none -> - DefaultExpiry - end. - --spec cache_deadline(Cache :: iodata(), Fallback :: pos_integer()) -> pos_integer(). -cache_deadline(Cache, Fallback) -> - Entries = - binary:split(iolist_to_binary(Cache), [<<",">>, <<"=">>, <<" ">>], [global, trim_all]), - MaxAge = - fun - (<<"0">>, Res) -> - Res; - (Entry, true) -> - erlang:convert_time_unit(binary_to_integer(Entry), second, millisecond); - (<<"max-age">>, _) -> - true; - (_, Res) -> - Res - end, - lists:foldl(MaxAge, Fallback, Entries). - -spec parse_scopes_supported(Setting :: term(), Field :: atom()) -> {ok, [binary()]} | {error, error()}. parse_scopes_supported(Setting, Field) -> diff --git a/test/oidcc_http_util_test.erl b/test/oidcc_http_util_test.erl new file mode 100644 index 0000000..73ff787 --- /dev/null +++ b/test/oidcc_http_util_test.erl @@ -0,0 +1,23 @@ +-module(oidcc_http_util_test). + +-include_lib("eunit/include/eunit.hrl"). + +headers_to_cache_deadline_test() -> + ?assertEqual( + 1000, + oidcc_http_util:headers_to_cache_deadline([], 1000) + ), + ?assertEqual( + timer:seconds(300), + oidcc_http_util:headers_to_cache_deadline( + [{"cache-control", "no-store, no-cache, max-age=300"}], 1000 + ) + ), + ?assertEqual( + 1000, + oidcc_http_util:headers_to_cache_deadline( + [{"cache-control", "no-store, no-cache, max-age=0"}], 1000 + ) + ), + + ok.