From ec3bcfa124f27ce4e975607823a859bdbaadd88a Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Thu, 10 Apr 2025 11:49:35 +0800 Subject: [PATCH] fix: password check (#4497) * fix: password check * add doc * fix: password check --- .../zh-cn/docs/development/upgrading/495.md | 20 ++++++++ pnpm-lock.yaml | 5 ++ .../account/info/UpdatePswModal.tsx | 10 ++-- .../login/ForgetPasswordForm.tsx | 10 ++-- .../src/pageComponents/login/RegisterForm.tsx | 10 ++-- projects/app/src/pages/api/admin/initv495.ts | 48 +++++++++++++++++++ .../src/web/support/user/login/constants.ts | 20 +++++++- test/cases/global/common/string/utils.test.ts | 35 ++++++++++++++ 8 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 docSite/content/zh-cn/docs/development/upgrading/495.md create mode 100644 projects/app/src/pages/api/admin/initv495.ts create mode 100644 test/cases/global/common/string/utils.test.ts diff --git a/docSite/content/zh-cn/docs/development/upgrading/495.md b/docSite/content/zh-cn/docs/development/upgrading/495.md new file mode 100644 index 000000000..a862ac792 --- /dev/null +++ b/docSite/content/zh-cn/docs/development/upgrading/495.md @@ -0,0 +1,20 @@ +--- +title: 'V4.9.5(进行中)' +description: 'FastGPT V4.9.5 更新说明' +icon: 'upgrade' +draft: false +toc: true +weight: 795 +--- + + +## 🚀 新增内容 + +1. 团队成员权限细分,可分别控制是否可创建在根目录应用/知识库以及 API Key + +## ⚙️ 优化 + + +## 🐛 修复 + +1. password 检测规则错误 \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45d579c5e..cf7736dd7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + mdast-util-gfm-autolink-literal@2.0.1: + hash: f63d515781110436299ab612306211a9621c6dfaec1ce1a19e2f27454dc70251 + path: patches/mdast-util-gfm-autolink-literal@2.0.1.patch + importers: .: diff --git a/projects/app/src/pageComponents/account/info/UpdatePswModal.tsx b/projects/app/src/pageComponents/account/info/UpdatePswModal.tsx index 092625db5..e517e0a38 100644 --- a/projects/app/src/pageComponents/account/info/UpdatePswModal.tsx +++ b/projects/app/src/pageComponents/account/info/UpdatePswModal.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'next-i18next'; import { useForm } from 'react-hook-form'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { updatePasswordByOld } from '@/web/support/user/api'; -import { PasswordRule } from '@/web/support/user/login/constants'; +import { checkPasswordRule } from '@/web/support/user/login/constants'; import { useToast } from '@fastgpt/web/hooks/useToast'; type FormType = { @@ -70,9 +70,11 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => { placeholder={t('account_info:password_tip')} {...register('newPsw', { required: true, - pattern: { - value: PasswordRule, - message: t('account_info:password_tip') + validate: (val) => { + if (!checkPasswordRule(val)) { + return t('login:password_tip'); + } + return true; } })} > diff --git a/projects/app/src/pageComponents/login/ForgetPasswordForm.tsx b/projects/app/src/pageComponents/login/ForgetPasswordForm.tsx index 43dcfe0f8..abf37eb1f 100644 --- a/projects/app/src/pageComponents/login/ForgetPasswordForm.tsx +++ b/projects/app/src/pageComponents/login/ForgetPasswordForm.tsx @@ -1,7 +1,7 @@ import React, { Dispatch } from 'react'; import { FormControl, Box, Input, Button } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; -import { LoginPageTypeEnum, PasswordRule } from '@/web/support/user/login/constants'; +import { LoginPageTypeEnum, checkPasswordRule } from '@/web/support/user/login/constants'; import { postFindPassword } from '@/web/support/user/api'; import { useSendCode } from '@/web/support/user/hooks/useSendCode'; import type { ResLogin } from '@/global/support/api/userRes.d'; @@ -138,9 +138,11 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { placeholder={t('login:password_tip')} {...register('password', { required: true, - pattern: { - value: PasswordRule, - message: t('login:password_tip') + validate: (val) => { + if (!checkPasswordRule(val)) { + return t('login:password_tip'); + } + return true; } })} > diff --git a/projects/app/src/pageComponents/login/RegisterForm.tsx b/projects/app/src/pageComponents/login/RegisterForm.tsx index 44643b06a..20688b031 100644 --- a/projects/app/src/pageComponents/login/RegisterForm.tsx +++ b/projects/app/src/pageComponents/login/RegisterForm.tsx @@ -1,7 +1,7 @@ import React, { Dispatch } from 'react'; import { FormControl, Box, Input, Button } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; -import { LoginPageTypeEnum, PasswordRule } from '@/web/support/user/login/constants'; +import { LoginPageTypeEnum, checkPasswordRule } from '@/web/support/user/login/constants'; import { postRegister } from '@/web/support/user/api'; import { useSendCode } from '@/web/support/user/hooks/useSendCode'; import type { ResLogin } from '@/global/support/api/userRes'; @@ -166,9 +166,11 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { placeholder={t('login:password_tip')} {...register('password', { required: true, - pattern: { - value: PasswordRule, - message: t('login:password_tip') + validate: (val) => { + if (!checkPasswordRule(val)) { + return t('login:password_tip'); + } + return true; } })} > diff --git a/projects/app/src/pages/api/admin/initv495.ts b/projects/app/src/pages/api/admin/initv495.ts new file mode 100644 index 000000000..4170e2718 --- /dev/null +++ b/projects/app/src/pages/api/admin/initv495.ts @@ -0,0 +1,48 @@ +import { NextAPI } from '@/service/middleware/entry'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; +import { TeamPermission } from '@fastgpt/global/support/permission/user/controller'; +import { + TeamApikeyCreatePermissionVal, + TeamAppCreatePermissionVal, + TeamDatasetCreatePermissionVal +} from '@fastgpt/global/support/permission/user/constant'; +import { retryFn } from '@fastgpt/global/common/system/utils'; + +async function handler(req: NextApiRequest, _res: NextApiResponse) { + await authCert({ req, authRoot: true }); + // 更新团队权限: + // 目前所有有 TeamWritePermission 的,都需要添加三个新的权限。 + + const rps = await MongoResourcePermission.find({ + resourceType: 'team', + teamId: { $exists: true }, + resourceId: null + }); + + for await (const rp of rps) { + const per = new TeamPermission({ per: rp.permission }); + console.log(per.hasWritePer, per.value); + if (per.hasWritePer) { + const newPer = per.addPer( + TeamAppCreatePermissionVal, + TeamDatasetCreatePermissionVal, + TeamApikeyCreatePermissionVal + ); + rp.permission = newPer.value; + + try { + await retryFn(async () => { + await rp.save(); + }); + } catch (error) { + console.log('更新权限异常', error); + } + } + } + + return { success: true }; +} + +export default NextAPI(handler); diff --git a/projects/app/src/web/support/user/login/constants.ts b/projects/app/src/web/support/user/login/constants.ts index 72fa301e9..0ec533b1c 100644 --- a/projects/app/src/web/support/user/login/constants.ts +++ b/projects/app/src/web/support/user/login/constants.ts @@ -5,5 +5,21 @@ export enum LoginPageTypeEnum { wechat = 'wechat' } -export const PasswordRule = - /^(?:(?=.*\d)(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])|(?=.*\d)(?=.*[!@#$%^&*_])|(?=.*[a-z])(?=.*[A-Z])|(?=.*[a-z])(?=.*[!@#$%^&*_])|(?=.*[A-Z])(?=.*[!@#$%^&*_]))[\dA-Za-z!@#$%^&*_]{6,}$/; +export const checkPasswordRule = (password: string) => { + const patterns = [ + /\d/, // Contains digits + /[a-z]/, // Contains lowercase letters + /[A-Z]/, // Contains uppercase letters + /[!@#$%^&*()_+=-]/ // Contains special characters + ]; + const validChars = /^[\dA-Za-z!@#$%^&*()_+=-]{6,100}$/; + + // Check length and valid characters + if (!validChars.test(password)) return false; + + // Count how many patterns are satisfied + const matchCount = patterns.filter((pattern) => pattern.test(password)).length; + + // Must satisfy at least 2 patterns + return matchCount >= 2; +}; diff --git a/test/cases/global/common/string/utils.test.ts b/test/cases/global/common/string/utils.test.ts new file mode 100644 index 000000000..1a0322fda --- /dev/null +++ b/test/cases/global/common/string/utils.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest'; +import { checkPasswordRule } from '@/web/support/user/login/constants'; + +describe('PasswordRule', () => { + it('should be a valid password', () => { + // Small password + expect(checkPasswordRule('123A')).toBe(false); + expect(checkPasswordRule('@ga21')).toBe(false); + + // Test single type characters + expect(checkPasswordRule('123456')).toBe(false); + expect(checkPasswordRule('abcdef')).toBe(false); // only lowercase + expect(checkPasswordRule('ABCDEF')).toBe(false); // only uppercase + expect(checkPasswordRule('!@#$%^')).toBe(false); // only special chars + + // Test two types combination + expect(checkPasswordRule('abc123')).toBe(true); // lowercase + numbers + expect(checkPasswordRule('abcABC')).toBe(true); // lowercase + uppercase + expect(checkPasswordRule('abc!@#')).toBe(true); // lowercase + special chars + expect(checkPasswordRule('ABC!@#')).toBe(true); // uppercase + special chars + expect(checkPasswordRule('ABC123')).toBe(true); // uppercase + numbers + expect(checkPasswordRule('123!@#')).toBe(true); // numbers + special chars + expect(checkPasswordRule('!@123fa')).toBe(true); // numbers + special chars + expect(checkPasswordRule('+2222()222')).toBe(true); // special chars + numbers + expect(checkPasswordRule('_2222()-+=22')).toBe(true); // special chars + numbers + + // Test three types combination + expect(checkPasswordRule('abcABC123')).toBe(true); // lower + upper + numbers + expect(checkPasswordRule('abc123!@#')).toBe(true); // lower + numbers + special + expect(checkPasswordRule('abc!@#123')).toBe(true); // lower + special + numbers + + // Test all four types + expect(checkPasswordRule('abcABC123!@#')).toBe(true); // all types + }); +});