From fe417b0c297ab5d933cf5e40d2c0c22322a3bc77 Mon Sep 17 00:00:00 2001 From: Sli Date: Mon, 3 Mar 2025 13:33:58 +0100 Subject: [PATCH] Enable csrf tokens on API routes * Upgrade openapi-ts * Migrate openapi-ts settings to new version * Add csrf token to headers of all API calls * Force csrf token authentication on API routes --- openapi-csrf.ts | 9 +++++++++ openapi-ts.config.ts | 15 +++++++++++++-- package-lock.json | 36 +++++++++++++++++++++++------------- package.json | 5 +++-- sith/urls.py | 2 +- 5 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 openapi-csrf.ts diff --git a/openapi-csrf.ts b/openapi-csrf.ts new file mode 100644 index 00000000..5d1cf84b --- /dev/null +++ b/openapi-csrf.ts @@ -0,0 +1,9 @@ +import Cookies from "js-cookie"; +import type { CreateClientConfig } from "#openapi"; + +export const createClientConfig: CreateClientConfig = (config) => ({ + ...config, + headers: { + "X-CSRFToken": Cookies.get("csrftoken"), + }, +}); diff --git a/openapi-ts.config.ts b/openapi-ts.config.ts index d583ffee..32403271 100644 --- a/openapi-ts.config.ts +++ b/openapi-ts.config.ts @@ -4,7 +4,18 @@ import { defineConfig } from "@hey-api/openapi-ts"; // biome-ignore lint/style/noDefaultExport: needed for openapi-ts export default defineConfig({ - client: "@hey-api/client-fetch", input: resolve(__dirname, "./staticfiles/generated/openapi/schema.json"), - output: resolve(__dirname, "./staticfiles/generated/openapi"), + output: { + lint: "biome", + format: "biome", + path: resolve(__dirname, "./staticfiles/generated/openapi"), + }, + plugins: [ + { + name: "@hey-api/client-fetch", + baseUrl: false, + runtimeConfigPath: "./openapi-csrf.ts", + exportFromIndex: true, + }, + ], }); diff --git a/package-lock.json b/package-lock.json index 012e48e7..9126ac34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@fullcalendar/daygrid": "^6.1.15", "@fullcalendar/icalendar": "^6.1.15", "@fullcalendar/list": "^6.1.15", - "@hey-api/client-fetch": "^0.6.0", + "@hey-api/client-fetch": "^0.8.2", "@sentry/browser": "^8.34.0", "@zip.js/zip.js": "^2.7.52", "3d-force-graph": "^1.73.4", @@ -31,6 +31,7 @@ "htmx.org": "^2.0.3", "jquery": "^3.7.1", "jquery-ui": "^1.14.0", + "js-cookie": "^3.0.5", "native-file-system-adapter": "^3.0.1", "three": "^0.172.0", "three-spritetext": "^1.9.0", @@ -40,7 +41,7 @@ "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.4", "@biomejs/biome": "1.9.4", - "@hey-api/openapi-ts": "^0.61.3", + "@hey-api/openapi-ts": "^0.64.0", "@rollup/plugin-inject": "^5.0.5", "@types/alpinejs": "^3.13.10", "@types/jquery": "^3.5.31", @@ -2207,18 +2208,18 @@ } }, "node_modules/@hey-api/client-fetch": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.6.0.tgz", - "integrity": "sha512-FlhFsVeH8RxJe/nq8xUzxNbiOpe+GadxlD2pfvDyOyLdCTU4o/LRv46ZVWstaW7DgF4nxhI328chy3+AulwVXw==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.8.2.tgz", + "integrity": "sha512-61T4UGfAzY5345vMxWDX8qnSTNRJcOpWuZyvNu3vNebCTLPwMQAM85mhEuBoACdWeRtLhNoUjU0UR5liRyD1bA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/hey-api" } }, "node_modules/@hey-api/json-schema-ref-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.1.tgz", - "integrity": "sha512-dBt0A7op9kf4BcK++x6HBYDmvCvnJUZEGe5QytghPFHnMXPyKwDKomwL/v5e9ERk6E0e1GzL/e/y6pWUso9zrQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.2.tgz", + "integrity": "sha512-F6LSkttZcT/XiX3ydeDqTY3uRN3BLJMwyMTk4kg/ichZlKUp3+3Odv0WokSmXGSoZGTW/N66FROMYAm5NPdJlA==", "dev": true, "license": "MIT", "dependencies": { @@ -2234,13 +2235,13 @@ } }, "node_modules/@hey-api/openapi-ts": { - "version": "0.61.3", - "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.61.3.tgz", - "integrity": "sha512-Ls9MBRa5+vg7UHw6fIcfdgcCyZ9vKtRw63nWxwF9zjJIPlzVOZO6xKuzGmDc6o0Pb6XCdTz6lPV5hcV0R4b/ag==", + "version": "0.64.8", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.64.8.tgz", + "integrity": "sha512-ytPt/k+ecK7zcpxVocPWzD1bKn98a+9WDK8eJITvbOEkvYsWlozAPO63tQg+65Qpl2pr37025fEo8YcX+DPhBQ==", "dev": true, "license": "MIT", "dependencies": { - "@hey-api/json-schema-ref-parser": "1.0.1", + "@hey-api/json-schema-ref-parser": "1.0.2", "c12": "2.0.1", "commander": "13.0.0", "handlebars": "4.7.8" @@ -2249,7 +2250,7 @@ "openapi-ts": "bin/index.cjs" }, "engines": { - "node": "^18.20.5 || ^20.11.1 || >=22.11.0" + "node": "^18.18.0 || ^20.9.0 || >=22.10.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" @@ -4295,6 +4296,15 @@ "jquery": ">=1.12.0 <5.0.0" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index 93494819..f46df7db 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.4", "@biomejs/biome": "1.9.4", - "@hey-api/openapi-ts": "^0.61.3", + "@hey-api/openapi-ts": "^0.64.0", "@rollup/plugin-inject": "^5.0.5", "@types/alpinejs": "^3.13.10", "@types/jquery": "^3.5.31", @@ -42,7 +42,7 @@ "@fullcalendar/daygrid": "^6.1.15", "@fullcalendar/icalendar": "^6.1.15", "@fullcalendar/list": "^6.1.15", - "@hey-api/client-fetch": "^0.6.0", + "@hey-api/client-fetch": "^0.8.2", "@sentry/browser": "^8.34.0", "@zip.js/zip.js": "^2.7.52", "3d-force-graph": "^1.73.4", @@ -57,6 +57,7 @@ "htmx.org": "^2.0.3", "jquery": "^3.7.1", "jquery-ui": "^1.14.0", + "js-cookie": "^3.0.5", "native-file-system-adapter": "^3.0.1", "three": "^0.172.0", "three-spritetext": "^1.9.0", diff --git a/sith/urls.py b/sith/urls.py index f6f7c8bb..0dfa1310 100644 --- a/sith/urls.py +++ b/sith/urls.py @@ -27,7 +27,7 @@ handler403 = "core.views.forbidden" handler404 = "core.views.not_found" handler500 = "core.views.internal_servor_error" -api = NinjaExtraAPI(version="0.2.0", urls_namespace="api") +api = NinjaExtraAPI(version="0.2.0", urls_namespace="api", csrf=True) api.auto_discover_controllers() urlpatterns = [