Security improvements: Fix ownership checks, add password hashing with bcrypt

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-11-07 16:37:05 +00:00
co-authored by whyour
parent f355b4e441
commit b2b1777c6b
6 changed files with 88 additions and 34 deletions
+1 -1
View File
@@ -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: '需要管理员权限' });
+1 -1
View File
@@ -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('无权限操作该定时任务');
+1 -1
View File
@@ -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('无权限操作该环境变量');
+28 -7
View File
@@ -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<string> {
const saltRounds = 10;
return bcrypt.hash(password, saltRounds);
}
private async verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
public async list(searchText?: string): Promise<User[]> {
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;
}