diff --git a/back/api/userManagement.ts b/back/api/userManagement.ts index 3799ad1c..3b008688 100644 --- a/back/api/userManagement.ts +++ b/back/api/userManagement.ts @@ -8,7 +8,7 @@ const route = Router(); // Middleware to check if user is admin const requireAdmin = (req: Request, res: Response, next: NextFunction) => { - if (req.user && (req.user as any).role === UserRole.admin) { + if (req.user && req.user.role === UserRole.admin) { return next(); } return res.status(403).send({ code: 403, message: '需要管理员权限' }); diff --git a/back/services/cron.ts b/back/services/cron.ts index 8616619b..c2f32597 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -46,7 +46,7 @@ export default class CronService { attributes: ['id', 'userId'], }); const unauthorized = crons.filter( - (cron) => cron.userId !== userId && cron.userId !== undefined + (cron) => cron.userId !== undefined && cron.userId !== userId ); if (unauthorized.length > 0) { throw new Error('无权限操作该定时任务'); diff --git a/back/services/env.ts b/back/services/env.ts index ea5590ce..fb91e824 100644 --- a/back/services/env.ts +++ b/back/services/env.ts @@ -36,7 +36,7 @@ export default class EnvService { attributes: ['id', 'userId'], }); const unauthorized = envs.filter( - (env) => env.userId !== userId && env.userId !== undefined + (env) => env.userId !== undefined && env.userId !== userId ); if (unauthorized.length > 0) { throw new Error('无权限操作该环境变量'); diff --git a/back/services/userManagement.ts b/back/services/userManagement.ts index 5653399a..60cdf594 100644 --- a/back/services/userManagement.ts +++ b/back/services/userManagement.ts @@ -2,11 +2,21 @@ import { Service, Inject } from 'typedi'; import winston from 'winston'; import { User, UserModel, UserRole, UserStatus } from '../data/user'; import { Op } from 'sequelize'; +import bcrypt from 'bcrypt'; @Service() export default class UserManagementService { constructor(@Inject('logger') private logger: winston.Logger) {} + private async hashPassword(password: string): Promise { + const saltRounds = 10; + return bcrypt.hash(password, saltRounds); + } + + private async verifyPassword(password: string, hash: string): Promise { + return bcrypt.compare(password, hash); + } + public async list(searchText?: string): Promise { let query: any = {}; if (searchText) { @@ -40,11 +50,15 @@ export default class UserManagementService { throw new Error('用户名已存在'); } - if (payload.password === 'admin') { - throw new Error('密码不能设置为admin'); + if (payload.password.length < 6) { + throw new Error('密码长度至少为6位'); } - const doc = await UserModel.create(payload); + // Hash the password before storing + const hashedPassword = await this.hashPassword(payload.password); + const userWithHashedPassword = { ...payload, password: hashedPassword }; + + const doc = await UserModel.create(userWithHashedPassword); return doc.get({ plain: true }); } @@ -58,8 +72,8 @@ export default class UserManagementService { throw new Error('用户不存在'); } - if (payload.password === 'admin') { - throw new Error('密码不能设置为admin'); + if (payload.password && payload.password.length < 6) { + throw new Error('密码长度至少为6位'); } // Check if username is being changed and if new username already exists @@ -70,7 +84,13 @@ export default class UserManagementService { } } - const [, [updated]] = await UserModel.update(payload, { + // Hash the password if it's being updated + const updatePayload = { ...payload }; + if (payload.password) { + updatePayload.password = await this.hashPassword(payload.password); + } + + const [, [updated]] = await UserModel.update(updatePayload, { where: { id: payload.id }, returning: true, }); @@ -91,7 +111,8 @@ export default class UserManagementService { return null; } - if (user.password !== password) { + const isPasswordValid = await this.verifyPassword(password, user.password); + if (!isPasswordValid) { return null; } diff --git a/package.json b/package.json index 8c2dbc9e..e3cd8f2b 100644 --- a/package.json +++ b/package.json @@ -54,12 +54,16 @@ } }, "dependencies": { + "@bufbuild/protobuf": "^2.10.0", "@grpc/grpc-js": "^1.14.0", "@grpc/proto-loader": "^0.8.0", + "@keyv/sqlite": "^4.0.1", "@otplib/preset-default": "^12.0.1", + "bcrypt": "^6.0.0", "body-parser": "^1.20.3", "celebrate": "^15.0.3", "chokidar": "^4.0.1", + "compression": "^1.7.4", "cors": "^2.8.5", "cron-parser": "^4.9.0", "cross-spawn": "^7.0.6", @@ -69,51 +73,49 @@ "express-jwt": "^8.4.1", "express-rate-limit": "^7.4.1", "express-urlrewrite": "^2.0.3", - "undici": "^7.9.0", + "helmet": "^8.1.0", "hpagent": "^1.2.0", "http-proxy-middleware": "^3.0.3", "iconv-lite": "^0.6.3", + "ip2region": "2.3.0", "js-yaml": "^4.1.0", "jsonwebtoken": "^9.0.2", + "keyv": "^5.2.3", "lodash": "^4.17.21", "multer": "1.4.5-lts.1", "node-schedule": "^2.1.0", "nodemailer": "^6.9.16", "p-queue-cjs": "7.3.4", - "@bufbuild/protobuf": "^2.10.0", + "proper-lockfile": "^4.1.2", "ps-tree": "^1.2.0", "reflect-metadata": "^0.2.2", + "request-ip": "3.3.0", "sequelize": "^6.37.5", "sockjs": "^0.3.24", "sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3", "toad-scheduler": "^3.0.1", "typedi": "^0.10.0", + "undici": "^7.9.0", "uuid": "^11.0.3", "winston": "^3.17.0", - "winston-daily-rotate-file": "^5.0.0", - "request-ip": "3.3.0", - "ip2region": "2.3.0", - "keyv": "^5.2.3", - "@keyv/sqlite": "^4.0.1", - "proper-lockfile": "^4.1.2", - "compression": "^1.7.4", - "helmet": "^8.1.0" + "winston-daily-rotate-file": "^5.0.0" }, "devDependencies": { - "moment": "2.30.1", "@ant-design/icons": "^5.0.1", "@ant-design/pro-layout": "6.38.22", - "@codemirror/view": "^6.34.1", "@codemirror/state": "^6.4.1", + "@codemirror/view": "^6.34.1", "@monaco-editor/react": "4.2.1", "@react-hook/resize-observer": "^2.0.2", - "react-router-dom": "6.26.1", + "@types/bcrypt": "^6.0.0", "@types/body-parser": "^1.19.2", + "@types/compression": "^1.7.2", "@types/cors": "^2.8.12", "@types/cross-spawn": "^6.0.2", "@types/express": "^4.17.13", "@types/express-jwt": "^6.0.4", "@types/file-saver": "2.0.2", + "@types/helmet": "^4.0.0", "@types/js-yaml": "^4.0.5", "@types/jsonwebtoken": "^8.5.8", "@types/lodash": "^4.14.185", @@ -121,17 +123,17 @@ "@types/node": "^17.0.21", "@types/node-schedule": "^1.3.2", "@types/nodemailer": "^6.4.4", + "@types/proper-lockfile": "^4.1.4", + "@types/ps-tree": "^1.1.6", "@types/qrcode.react": "^1.0.2", "@types/react": "^18.0.20", "@types/react-copy-to-clipboard": "^5.0.4", "@types/react-dom": "^18.0.6", + "@types/request-ip": "0.0.41", "@types/serve-handler": "^6.1.1", "@types/sockjs": "^0.3.33", "@types/sockjs-client": "^1.5.1", "@types/uuid": "^8.3.4", - "@types/request-ip": "0.0.41", - "@types/proper-lockfile": "^4.1.4", - "@types/ps-tree": "^1.1.6", "@uiw/codemirror-extensions-langs": "^4.21.9", "@uiw/react-codemirror": "^4.21.9", "@umijs/max": "^4.4.4", @@ -143,9 +145,9 @@ "axios": "^1.4.0", "compression-webpack-plugin": "9.2.0", "concurrently": "^7.0.0", - "react-hotkeys-hook": "^4.6.1", "file-saver": "2.0.2", "lint-staged": "^13.0.3", + "moment": "2.30.1", "monaco-editor": "0.33.0", "nodemon": "^3.0.1", "prettier": "^2.5.1", @@ -161,7 +163,9 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "18.3.1", + "react-hotkeys-hook": "^4.6.1", "react-intl-universal": "^2.12.0", + "react-router-dom": "6.26.1", "react-split-pane": "^0.1.92", "sockjs-client": "^1.6.0", "ts-node": "^10.9.2", @@ -169,8 +173,6 @@ "tslib": "^2.4.0", "typescript": "5.2.2", "vh-check": "^2.0.5", - "virtualizedtableforantd4": "1.3.0", - "@types/compression": "^1.7.2", - "@types/helmet": "^4.0.0" + "virtualizedtableforantd4": "1.3.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50cce37f..917e3c81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - overrides: sqlite3: git+https://github.com/whyour/node-sqlite3.git#v1.0.3 @@ -23,6 +19,9 @@ dependencies: '@otplib/preset-default': specifier: ^12.0.1 version: 12.0.1 + bcrypt: + specifier: ^6.0.0 + version: 6.0.0 body-parser: specifier: ^1.20.3 version: 1.20.3 @@ -160,6 +159,9 @@ devDependencies: '@react-hook/resize-observer': specifier: ^2.0.2 version: 2.0.2(react@18.3.1) + '@types/bcrypt': + specifier: ^6.0.0 + version: 6.0.0 '@types/body-parser': specifier: ^1.19.2 version: 1.19.5 @@ -3786,6 +3788,12 @@ packages: '@babel/types': 7.26.0 dev: true + /@types/bcrypt@6.0.0: + resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==} + dependencies: + '@types/node': 17.0.45 + dev: true + /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: @@ -5747,6 +5755,15 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + /bcrypt@6.0.0: + resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==} + engines: {node: '>= 18'} + requiresBuild: true + dependencies: + node-addon-api: 8.5.0 + node-gyp-build: 4.8.4 + dev: false + /before@0.0.1: resolution: {integrity: sha512-1J5SWbkoVJH9DTALN8igB4p+nPKZzPrJ/HomqBDLpfUvDXCdjdBmBUcH5McZfur0lftVssVU6BZug5NYh87zTw==} dev: true @@ -10212,6 +10229,11 @@ packages: resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} dev: false + /node-addon-api@8.5.0: + resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} + engines: {node: ^18 || ^20 || >= 21} + dev: false + /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -10238,6 +10260,11 @@ packages: formdata-polyfill: 4.0.10 dev: true + /node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + dev: false + /node-gyp@8.4.1: resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} engines: {node: '>= 10.12.0'} @@ -15213,3 +15240,7 @@ packages: - encoding - supports-color dev: false + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false