From 64501c69a10323067dee739790b5a4fc5104e50d Mon Sep 17 00:00:00 2001 From: おさむのひと <46447427+samunohito@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:57:58 +0900 Subject: feat(frontend): Botプロテクションの設定変更時は実際に検証を通過しないと保存できないようにする (#15151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(frontend): CAPTCHAの設定変更時は実際に検証を通過しないと保存できないようにする * なしでも保存できるようにした * fix CHANGELOG.md * フォームが増殖するのを修正 * add comment * add server-side verify * fix ci * fix * fix * fix i18n * add current.ts * fix text * fix * regenerate locales * fix MkFormFooter.vue --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- .../server/api/endpoints/admin/captcha/current.ts | 70 +++++++++++ .../src/server/api/endpoints/admin/captcha/save.ts | 129 +++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 packages/backend/src/server/api/endpoints/admin/captcha/current.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/captcha/save.ts (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/admin/captcha/current.ts b/packages/backend/src/server/api/endpoints/admin/captcha/current.ts new file mode 100644 index 0000000000..63ec740348 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/captcha/current.ts @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { CaptchaService, supportedCaptchaProviders } from '@/core/CaptchaService.js'; + +export const meta = { + tags: ['admin', 'captcha'], + + requireCredential: true, + requireAdmin: true, + + // 実態はmetaの取得であるため + kind: 'read:admin:meta', + + res: { + type: 'object', + properties: { + provider: { + type: 'string', + enum: supportedCaptchaProviders, + }, + hcaptcha: { + type: 'object', + properties: { + siteKey: { type: 'string', nullable: true }, + secretKey: { type: 'string', nullable: true }, + }, + }, + mcaptcha: { + type: 'object', + properties: { + siteKey: { type: 'string', nullable: true }, + secretKey: { type: 'string', nullable: true }, + instanceUrl: { type: 'string', nullable: true }, + }, + }, + recaptcha: { + type: 'object', + properties: { + siteKey: { type: 'string', nullable: true }, + secretKey: { type: 'string', nullable: true }, + }, + }, + turnstile: { + type: 'object', + properties: { + siteKey: { type: 'string', nullable: true }, + secretKey: { type: 'string', nullable: true }, + }, + }, + }, + }, +} as const; + +export const paramDef = {} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private captchaService: CaptchaService, + ) { + super(meta, paramDef, async () => { + return this.captchaService.get(); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/captcha/save.ts b/packages/backend/src/server/api/endpoints/admin/captcha/save.ts new file mode 100644 index 0000000000..98ec278ebe --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/captcha/save.ts @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { captchaErrorCodes, CaptchaService, supportedCaptchaProviders } from '@/core/CaptchaService.js'; +import { ApiError } from '@/server/api/error.js'; + +export const meta = { + tags: ['admin', 'captcha'], + + requireCredential: true, + requireAdmin: true, + + // 実態はmetaの更新であるため + kind: 'write:admin:meta', + + errors: { + invalidProvider: { + message: 'Invalid provider.', + code: 'INVALID_PROVIDER', + id: '14bf7ae1-80cc-4363-acb2-4fd61d086af0', + httpStatusCode: 400, + }, + invalidParameters: { + message: 'Invalid parameters.', + code: 'INVALID_PARAMETERS', + id: '26654194-410e-44e2-b42e-460ff6f92476', + httpStatusCode: 400, + }, + noResponseProvided: { + message: 'No response provided.', + code: 'NO_RESPONSE_PROVIDED', + id: '40acbba8-0937-41fb-bb3f-474514d40afe', + httpStatusCode: 400, + }, + requestFailed: { + message: 'Request failed.', + code: 'REQUEST_FAILED', + id: '0f4fe2f1-2c15-4d6e-b714-efbfcde231cd', + httpStatusCode: 500, + }, + verificationFailed: { + message: 'Verification failed.', + code: 'VERIFICATION_FAILED', + id: 'c41c067f-24f3-4150-84b2-b5a3ae8c2214', + httpStatusCode: 400, + }, + unknown: { + message: 'unknown', + code: 'UNKNOWN', + id: 'f868d509-e257-42a9-99c1-42614b031a97', + httpStatusCode: 500, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + provider: { + type: 'string', + enum: supportedCaptchaProviders, + }, + captchaResult: { + type: 'string', nullable: true, + }, + sitekey: { + type: 'string', nullable: true, + }, + secret: { + type: 'string', nullable: true, + }, + instanceUrl: { + type: 'string', nullable: true, + }, + }, + required: ['provider'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private captchaService: CaptchaService, + ) { + super(meta, paramDef, async (ps) => { + const result = await this.captchaService.save(ps.provider, { + sitekey: ps.sitekey, + secret: ps.secret, + instanceUrl: ps.instanceUrl, + captchaResult: ps.captchaResult, + }); + + if (!result.success) { + switch (result.error.code) { + case captchaErrorCodes.invalidProvider: + throw new ApiError({ + ...meta.errors.invalidProvider, + message: result.error.message, + }); + case captchaErrorCodes.invalidParameters: + throw new ApiError({ + ...meta.errors.invalidParameters, + message: result.error.message, + }); + case captchaErrorCodes.noResponseProvided: + throw new ApiError({ + ...meta.errors.noResponseProvided, + message: result.error.message, + }); + case captchaErrorCodes.requestFailed: + throw new ApiError({ + ...meta.errors.requestFailed, + message: result.error.message, + }); + case captchaErrorCodes.verificationFailed: + throw new ApiError({ + ...meta.errors.verificationFailed, + message: result.error.message, + }); + default: + throw new ApiError(meta.errors.unknown); + } + } + }); + } +} -- cgit v1.2.3-freya