Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,8 @@ export const resolveUserStrategy = function (
) {
for (const cacheConfig of cacheConfigs) {
const keyedPerCaller = cacheConfig.routes.filter(
(route) => route.keys?.useAuth === true
// Truthiness, as buildCacheControl and the key generator decide it.
(route) => Boolean(route.keys?.useAuth)
);

if (!keyedPerCaller.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ export const buildCacheControl = function (
// strategy level cannot be expected to re-check it against every route's
// keys. register.ts warns at boot when this applies.
// See https://github.com/strapi-community/plugin-rest-cache/issues/113
//
// Truthiness, not `=== true`: the key generator puts the caller in the key
// for any truthy useAuth - the raw string `useAuth: env("X")` included - so
// that is what decides whether the response is caller-specific.
const scope =
config.scope === 'public' && cacheRouteConfig.keys?.useAuth === true
? 'private'
: config.scope;
config.scope === 'public' && cacheRouteConfig.keys?.useAuth ? 'private' : config.scope;

directives.push(scope);

Expand Down
7 changes: 6 additions & 1 deletion shared/config/cache-strategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ module.exports = ({ env }) => ({
keys: {
useQueryParams: false,
useHeaders: ["accept-encoding"],
useAuth: env.bool("CATEGORY_USE_AUTH", true),
// CATEGORY_USE_AUTH_RAW passes the raw string through, as
// `useAuth: env("X")` without env.bool does: truthy, but not `true`.
useAuth:
env("CATEGORY_USE_AUTH_RAW", "") !== ""
? env("CATEGORY_USE_AUTH_RAW")
: env.bool("CATEGORY_USE_AUTH", true),
},
routes: [
// Deliberately configured for caching so the plugin's refusal to cache
Expand Down
51 changes: 51 additions & 0 deletions shared/tests/flag-cache-control.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,54 @@ describe("cacheControl: enabled with scope public", () => {
expect(cacheControlOf(first)).toBe("public, max-age=3600");
});
});

// keys.useAuth set to a truthy value that is not the boolean true - the raw
// string `useAuth: env("X")` gives - keys entries per caller exactly as `true`
// does. The downgrade compared `=== true`, so those caller-specific responses
// went out `public` and a shared cache could hand one caller's to another.
describe("cacheControl: scope public, useAuth set to a truthy non-boolean", () => {
let bootOutput = "";

beforeAll(async () => {
process.env.KEYS_PREFIX = undefined;
process.env.CACHE_CONTROL_ENABLED = "true";
process.env.CACHE_CONTROL_SCOPE = "public";
process.env.CATEGORY_USE_AUTH_RAW = "true";
process.env.STRAPI_LOG_LEVEL = "warn";

const streams = [process.stdout, console._stdout].filter(
(stream, index, all) => stream && all.indexOf(stream) === index
);
const spies = streams.map((stream) => {
const write = stream.write.bind(stream);
return jest.spyOn(stream, "write").mockImplementation((chunk, ...rest) => {
bootOutput += String(chunk);
return write(chunk, ...rest);
});
});

try {
await setup();
} finally {
spies.forEach((spy) => spy.mockRestore());
}
});
afterAll(async () => {
delete process.env.CACHE_CONTROL_ENABLED;
delete process.env.CACHE_CONTROL_SCOPE;
delete process.env.CATEGORY_USE_AUTH_RAW;
delete process.env.STRAPI_LOG_LEVEL;
await teardown();
});

beforeEach(() => store().reset());

it("still downgrades to private", async () => {
const first = await agent().get("/api/categories");
const second = await agent().get("/api/categories");

expect(second.get("x-cache")).toBe("HIT");
expect(cacheControlOf(first)).toBe("private, max-age=3600");
expect(cacheControlOf(second)).toBe("private, max-age=3600");
});
});
Loading