Просмотр исходного кода

fix: CSRF with api client (#4577)

* add _csrf

* fix for uploading attachment

* fix lint errors
Yuki Takei 4 лет назад
Родитель
Сommit
9cd8c4ca73

+ 2 - 1
packages/app/src/client/services/AppContainer.js

@@ -31,8 +31,9 @@ export default class AppContainer extends Container {
       preferDarkModeByMediaQuery: false,
     };
 
+    // get csrf token from body element
+    // DO NOT REMOVE: uploading attachment data requires appContainer.csrfToken
     const body = document.querySelector('body');
-
     this.csrfToken = body.dataset.csrftoken;
 
     this.config = JSON.parse(document.getElementById('growi-context-hydrate').textContent || '{}');

+ 18 - 2
packages/app/src/client/util/apiv1-client.ts

@@ -4,6 +4,14 @@ import axios from '~/utils/axios';
 
 const apiv1Root = '/_api';
 
+// get csrf token from body element
+const body = document.querySelector('body');
+const csrfToken = body?.dataset.csrftoken;
+
+
+type ParamWithCsrfKey = {
+  _csrf: string,
+}
 
 class Apiv1ErrorHandler extends Error {
 
@@ -38,10 +46,18 @@ export async function apiGet(path: string, params: unknown = {}): Promise<unknow
   return apiRequest('get', path, { params });
 }
 
-export async function apiPost(path: string, params: unknown = {}): Promise<unknown> {
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export async function apiPost(path: string, params: any & ParamWithCsrfKey = {}): Promise<unknown> {
+  if (params._csrf == null) {
+    params._csrf = csrfToken;
+  }
   return apiRequest('post', path, params);
 }
 
-export async function apiDelete(path: string, params: unknown = {}): Promise<unknown> {
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export async function apiDelete(path: string, params: any & ParamWithCsrfKey = {}): Promise<unknown> {
+  if (params._csrf == null) {
+    params._csrf = csrfToken;
+  }
   return apiRequest('delete', path, { data: params });
 }

+ 21 - 3
packages/app/src/client/util/apiv3-client.ts

@@ -11,6 +11,15 @@ const apiv3Root = '/_api/v3';
 
 const logger = loggerFactory('growi:apiv3');
 
+// get csrf token from body element
+const body = document.querySelector('body');
+const csrfToken = body?.dataset.csrftoken;
+
+
+type ParamWithCsrfKey = {
+  _csrf: string,
+}
+
 const apiv3ErrorHandler = (_err) => {
   // extract api errors from general 400 err
   const err = _err.response ? _err.response.data.errors : _err;
@@ -41,16 +50,25 @@ export async function apiv3Get<T = any>(path: string, params: unknown = {}): Pro
 }
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiv3Post<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
+export async function apiv3Post<T = any>(path: string, params: any & ParamWithCsrfKey = {}): Promise<AxiosResponse<T>> {
+  if (params._csrf == null) {
+    params._csrf = csrfToken;
+  }
   return apiv3Request('post', path, params);
 }
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiv3Put<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
+export async function apiv3Put<T = any>(path: string, params: any & ParamWithCsrfKey = {}): Promise<AxiosResponse<T>> {
+  if (params._csrf == null) {
+    params._csrf = csrfToken;
+  }
   return apiv3Request('put', path, params);
 }
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiv3Delete<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
+export async function apiv3Delete<T = any>(path: string, params: any & ParamWithCsrfKey = {}): Promise<AxiosResponse<T>> {
+  if (params._csrf == null) {
+    params._csrf = csrfToken;
+  }
   return apiv3Request('delete', path, { params });
 }