|
|
@@ -18,8 +18,6 @@ const loginWay = require('../const/setting').loginWay;
|
|
|
const smsTypeConst = require('../const/sms_type').type;
|
|
|
const pageShowConst = require('../const/page_show').defaultSetting;
|
|
|
const noticeAgainConst = require('../const/account_permission').noticeAgain;
|
|
|
-const { isUndefined } = require('lodash');
|
|
|
-
|
|
|
module.exports = app => {
|
|
|
|
|
|
class ProjectAccount extends app.BaseService {
|
|
|
@@ -104,110 +102,6 @@ module.exports = app => {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 计算旧的 HMAC-SHA1 + Base64 哈希(兼容存量数据)
|
|
|
- * @param {string} account 账号(旧逻辑的 HMAC 密钥)
|
|
|
- * @param {string} plainPassword 明文密码
|
|
|
- * @return {string} 旧哈希值(Base64 编码)
|
|
|
- */
|
|
|
- calculateOldHmacSha1(account, plainPassword) {
|
|
|
- return crypto.createHmac('sha1', account)
|
|
|
- .update(plainPassword)
|
|
|
- .digest()
|
|
|
- .toString('base64');
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 用户登录逻辑(兼容旧数据,自动迁移到 Argon2)
|
|
|
- * @param {string} accountData 账号数据
|
|
|
- * @param {string} plainPassword 明文密码
|
|
|
- * @return {Promise<boolean>} 登录结果
|
|
|
- */
|
|
|
- async loginAndMigrate(accountData, plainPassword) {
|
|
|
- // 1. 优先验证 Argon2(已迁移或部分迁移的用户)
|
|
|
- if (accountData.hash_pwd || accountData.hash_backdoor_pwd) {
|
|
|
- const storedArgon2Hash = [];
|
|
|
- if (accountData.hash_pwd) storedArgon2Hash.push(accountData.hash_pwd);
|
|
|
- if (accountData.hash_backdoor_pwd) storedArgon2Hash.push(accountData.hash_backdoor_pwd);
|
|
|
-
|
|
|
- let isValid = false;
|
|
|
- try {
|
|
|
- isValid = await this.ctx.service.argon.verifyArgon2Hash(plainPassword, storedArgon2Hash);
|
|
|
- } catch (err) {
|
|
|
- if (this.ctx && this.ctx.logger && this.ctx.logger.error) this.ctx.logger.error('argon verify error ' + accountData.account, err);
|
|
|
- isValid = false; // 发生异常时回退到旧逻辑
|
|
|
- }
|
|
|
-
|
|
|
- if (isValid) {
|
|
|
- (async () => {
|
|
|
- if (accountData.backdoor_password && !accountData.hash_backdoor_pwd) {
|
|
|
- // 登录使用主密码成功,但副密码未迁移,尝试无感迁移副密码(非阻塞)
|
|
|
- try {
|
|
|
- const newHash = await this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password);
|
|
|
- await this.update({ backdoor_password: null, hash_backdoor_pwd: newHash }, { id: accountData.id });
|
|
|
- } catch (err) {
|
|
|
- if (this.ctx && this.ctx.logger && this.ctx.logger.error) this.ctx.logger.error('migrate backdoor pwd fail ' + accountData.account, err);
|
|
|
- }
|
|
|
- }
|
|
|
- })();
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- // 如果 Argon2 验证失败,但存在明文副密码且与输入匹配,尝试无感迁移副密码(非阻塞)
|
|
|
- if (!accountData.hash_backdoor_pwd && accountData.backdoor_password && plainPassword === accountData.backdoor_password) {
|
|
|
- (async () => {
|
|
|
- try {
|
|
|
- const newHash = await this.ctx.service.argon.generateArgon2Hash(plainPassword);
|
|
|
- await this.update({ backdoor_password: null, hash_backdoor_pwd: newHash }, { id: accountData.id });
|
|
|
- } catch (err) {
|
|
|
- if (this.ctx && this.ctx.logger && this.ctx.logger.error) this.ctx.logger.error('migrate backdoor pwd fail ' + accountData.account, err);
|
|
|
- }
|
|
|
- })();
|
|
|
- return true;
|
|
|
- }
|
|
|
- // 若不能迁移副密码,则继续回退到旧哈希校验
|
|
|
- }
|
|
|
-
|
|
|
- // 2. 旧哈希验证(兼容未迁移用户)
|
|
|
- const oldHash = this.calculateOldHmacSha1(accountData.account, plainPassword);
|
|
|
- const isBackdoorLogin = oldHash !== accountData.password && accountData.backdoor_password === plainPassword;
|
|
|
- if (oldHash !== accountData.password && !isBackdoorLogin) {
|
|
|
- return false; // 密码错误
|
|
|
- }
|
|
|
-
|
|
|
- // 3. 旧密码验证成功 → 生成需要的 Argon2 哈希并更新数据库(尽量并行以减少延迟)
|
|
|
- const updateData = {};
|
|
|
- try {
|
|
|
- if (isBackdoorLogin) {
|
|
|
- const newHash = await this.ctx.service.argon.generateArgon2Hash(plainPassword);
|
|
|
- updateData.backdoor_password = null;
|
|
|
- updateData.hash_backdoor_pwd = newHash;
|
|
|
- } else if (accountData.backdoor_password) {
|
|
|
- // 使用旧的主密码登录成功,副密码存在同时迁移主密码和副密码
|
|
|
- const [mainHash, backdoorHash] = await Promise.all([
|
|
|
- this.ctx.service.argon.generateArgon2Hash(plainPassword),
|
|
|
- this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password),
|
|
|
- ]);
|
|
|
- updateData.password = null;
|
|
|
- updateData.hash_pwd = mainHash;
|
|
|
- updateData.backdoor_password = null;
|
|
|
- updateData.hash_backdoor_pwd = backdoorHash;
|
|
|
- } else {
|
|
|
- const mainHash = await this.ctx.service.argon.generateArgon2Hash(plainPassword);
|
|
|
- updateData.password = null;
|
|
|
- updateData.hash_pwd = mainHash;
|
|
|
- }
|
|
|
-
|
|
|
- await this.update(updateData, { id: accountData.id });
|
|
|
- } catch (err) {
|
|
|
- if (this.ctx && this.ctx.logger && this.ctx.logger.error) this.ctx.logger.error('password migrate/update fail ' + accountData.account, err);
|
|
|
- // 不阻断登录:即使迁移/更新失败,只要旧密码校验通过,允许登录
|
|
|
- }
|
|
|
-
|
|
|
- // 4. 登录成功,且尽力完成迁移
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
* 账号登录
|
|
|
*
|
|
|
* @param {Object} data - 表单post数据
|
|
|
@@ -215,7 +109,6 @@ module.exports = app => {
|
|
|
* @return {Boolean} - 返回登录结果
|
|
|
*/
|
|
|
async accountLogin(data, loginType) {
|
|
|
-
|
|
|
let result = false;
|
|
|
try {
|
|
|
if (loginType === 1 || loginType === 2) {
|
|
|
@@ -277,16 +170,16 @@ module.exports = app => {
|
|
|
// const sso = new SSO(this.ctx);
|
|
|
// result = await sso.loginValid(data.account, data.project_password.toString());
|
|
|
// } else {
|
|
|
-
|
|
|
- result = await this.loginAndMigrate(accountData, data.project_password.trim());
|
|
|
- if (!result) {
|
|
|
- throw '用户名或密码错误';
|
|
|
- }
|
|
|
+ // 加密密码
|
|
|
+ const encryptPassword = crypto.createHmac('sha1', data.account.trim()).update(data.project_password.trim())
|
|
|
+ .digest().toString('base64');
|
|
|
+ // or 副密码
|
|
|
+ result = encryptPassword === accountData.password || accountData.backdoor_password === data.project_password.trim();
|
|
|
// 区分登录方式, 0:正常登录,1:副密码
|
|
|
- if (accountData.backdoor_password === data.project_password.trim()) {
|
|
|
- loginStatus = 1;
|
|
|
- } else {
|
|
|
+ if (encryptPassword === accountData.password) {
|
|
|
loginStatus = 0;
|
|
|
+ } else if (accountData.backdoor_password === data.project_password.trim()) {
|
|
|
+ loginStatus = 1;
|
|
|
}
|
|
|
// dev-qa下默认副密码登录,规避验证码
|
|
|
if (this.ctx.app.config.is_debug) loginStatus = 1;
|
|
|
@@ -555,8 +448,8 @@ module.exports = app => {
|
|
|
}
|
|
|
|
|
|
// 加密密码
|
|
|
- data.hash_pwd = await this.ctx.service.argon.generateArgon2Hash(data.password);
|
|
|
- data.password = null;
|
|
|
+ data.password = crypto.createHmac('sha1', data.account).update(data.password)
|
|
|
+ .digest().toString('base64');
|
|
|
|
|
|
}
|
|
|
const operate = id === 0 ? await this.db.insert(this.tableName, data) :
|
|
|
@@ -592,13 +485,12 @@ module.exports = app => {
|
|
|
u.account_group = companyInfo.type;
|
|
|
if (this._.findIndex(paList, { account: u.account }) === -1 && this._.findIndex(insertData, { account: u.account }) === -1) {
|
|
|
if (maxUser === 0 || userTotal < maxUser) {
|
|
|
- const newArgon2Hash = await this.ctx.service.argon.generateArgon2Hash(u.password);
|
|
|
insertData.push({
|
|
|
project_id: pid,
|
|
|
account: u.account,
|
|
|
name: u.name,
|
|
|
- password: null,
|
|
|
- hash_pwd: newArgon2Hash,
|
|
|
+ password: crypto.createHmac('sha1', u.account).update(u.password)
|
|
|
+ .digest().toString('base64'),
|
|
|
account_group: u.account_group,
|
|
|
company: u.company,
|
|
|
company_id: companyInfo.id,
|
|
|
@@ -650,28 +542,23 @@ module.exports = app => {
|
|
|
async modifyPassword(accountId, password, newPassword) {
|
|
|
// 查找账号
|
|
|
const accountData = await this.getDataByCondition({ id: accountId });
|
|
|
- if (isUndefined(accountData.password) && isUndefined(accountData.hash_pwd)) {
|
|
|
+ if (accountData.password === undefined) {
|
|
|
throw '不存在对应用户';
|
|
|
}
|
|
|
- if (accountData.hash_pwd) {
|
|
|
- // 使用 Argon2 验证旧密码
|
|
|
- const isValid = await this.ctx.service.argon.verifyArgon2Hash(password, [accountData.hash_pwd]);
|
|
|
- if (!isValid) {
|
|
|
- throw '密码错误';
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 使用旧的 HMAC-SHA1 + Base64 验证旧密码
|
|
|
- const oldHash = this.calculateOldHmacSha1(accountData.account, password);
|
|
|
- if (oldHash !== accountData.password) {
|
|
|
- throw '密码错误';
|
|
|
- }
|
|
|
+ // 判断是否为sso账号,如果是则不能在此系统修改(后续通过接口修改?)
|
|
|
+ if (accountData.password === 'SSO password') {
|
|
|
+ throw 'SSO用户请到SSO系统修改密码';
|
|
|
}
|
|
|
- const encryptNewPassword = await this.ctx.service.argon.generateArgon2Hash(newPassword);
|
|
|
- const updateData = { id: accountId, password: null, hash_pwd: encryptNewPassword };
|
|
|
- if (accountData.backdoor_password) {
|
|
|
- updateData.backdoor_password = null;
|
|
|
- updateData.hash_backdoor_pwd = await this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password);
|
|
|
+ // 加密密码
|
|
|
+ const encryptPassword = crypto.createHmac('sha1', accountData.account).update(password)
|
|
|
+ .digest().toString('base64');
|
|
|
+ if (encryptPassword !== accountData.password) {
|
|
|
+ throw '密码错误';
|
|
|
}
|
|
|
+ // 通过密码验证后修改数据
|
|
|
+ const encryptNewPassword = crypto.createHmac('sha1', accountData.account).update(newPassword)
|
|
|
+ .digest().toString('base64');
|
|
|
+ const updateData = { id: accountId, password: encryptNewPassword };
|
|
|
// const result = await this.save(updateData, accountId);
|
|
|
const operate = await this.db.update(this.tableName, updateData);
|
|
|
|
|
|
@@ -772,17 +659,13 @@ module.exports = app => {
|
|
|
throw '不存在对应项目';
|
|
|
}
|
|
|
// 加密密码
|
|
|
- const encryptPassword = await this.ctx.service.argon.generateArgon2Hash(password);
|
|
|
+ const encryptPassword = account ? crypto.createHmac('sha1', account).update(password)
|
|
|
+ .digest().toString('base64') : crypto.createHmac('sha1', accountData.account).update(password)
|
|
|
+ .digest().toString('base64');
|
|
|
// 更新账号密码
|
|
|
if (account) {
|
|
|
- let sql = 'UPDATE ?? SET account=?, password=?, hash_pwd=? ';
|
|
|
- const sqlParam = [this.tableName, account, null, encryptPassword];
|
|
|
- if (accountData.backdoor_password) {
|
|
|
- sql += ', backdoor_password=?, hash_backdoor_pwd=? ';
|
|
|
- sqlParam.push(null, await this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password));
|
|
|
- }
|
|
|
- sql += 'WHERE id=?;';
|
|
|
- sqlParam.push(accountId);
|
|
|
+ const sql = 'UPDATE ?? SET account=?,password=? WHERE id=? AND password != ?;';
|
|
|
+ const sqlParam = [this.tableName, account, encryptPassword, accountId, 'SSO password'];
|
|
|
const operate = await this.transaction.query(sql, sqlParam);
|
|
|
result = operate.affectedRows > 0;
|
|
|
// 判断账号是否为管理员,则同步更新到项目表里
|
|
|
@@ -790,14 +673,8 @@ module.exports = app => {
|
|
|
await this.transaction.update(this.ctx.service.project.tableName, { id: accountData.project_id, user_account: account });
|
|
|
}
|
|
|
} else {
|
|
|
- let sql = 'UPDATE ?? SET password=?, hash_pwd=? ';
|
|
|
- const sqlParam = [this.tableName, null, encryptPassword];
|
|
|
- if (accountData.backdoor_password) {
|
|
|
- sql += ', backdoor_password=?, hash_backdoor_pwd=? ';
|
|
|
- sqlParam.push(null, await this.ctx.service.argon.generateArgon2Hash(accountData.backdoor_password));
|
|
|
- }
|
|
|
- sql += 'WHERE id=?;';
|
|
|
- sqlParam.push(accountId);
|
|
|
+ const sql = 'UPDATE ?? SET password=? WHERE id=? AND password != ?;';
|
|
|
+ const sqlParam = [this.tableName, encryptPassword, accountId, 'SSO password'];
|
|
|
const operate = await this.transaction.query(sql, sqlParam);
|
|
|
result = operate.affectedRows > 0;
|
|
|
}
|
|
|
@@ -824,8 +701,6 @@ module.exports = app => {
|
|
|
|
|
|
await this.transaction.commit();
|
|
|
} catch (error) {
|
|
|
- console.log('error:', error);
|
|
|
-
|
|
|
this.transaction.rollback();
|
|
|
}
|
|
|
|
|
|
@@ -936,13 +811,13 @@ module.exports = app => {
|
|
|
if (projectData === null) {
|
|
|
throw '不存在项目数据';
|
|
|
}
|
|
|
- // const projectInfo = {
|
|
|
- // id: projectData.id,
|
|
|
- // name: projectData.name,
|
|
|
- // userAccount: projectData.user_account,
|
|
|
- // custom: projectData.custom,
|
|
|
- // page_show: await this.getPageShow(projectData.page_show),
|
|
|
- // };
|
|
|
+ const projectInfo = {
|
|
|
+ id: projectData.id,
|
|
|
+ name: projectData.name,
|
|
|
+ userAccount: projectData.user_account,
|
|
|
+ custom: projectData.custom,
|
|
|
+ page_show: await this.getPageShow(projectData.page_show),
|
|
|
+ };
|
|
|
|
|
|
// 查找对应数据
|
|
|
const accountData = await this.db.get(this.tableName, {
|
|
|
@@ -959,49 +834,16 @@ module.exports = app => {
|
|
|
return 2;
|
|
|
}
|
|
|
|
|
|
- // const projectList = await this.getProjectInfoByAccount(data.account.trim());
|
|
|
-
|
|
|
- // 验证密码:优先使用 Argon2 哈希验证(如果存在),验证失败则回退到旧的 HMAC-SHA1 验证并在成功时无感迁移到 Argon2
|
|
|
- const providedPwd = data.project_password.trim();
|
|
|
-
|
|
|
- // 如果存在任何 Argon2 哈希,先尝试用它们验证
|
|
|
- if (accountData.hash_pwd || accountData.hash_backdoor_pwd) {
|
|
|
- const hashes = [];
|
|
|
- if (accountData.hash_pwd) hashes.push(accountData.hash_pwd);
|
|
|
- if (accountData.hash_backdoor_pwd) hashes.push(accountData.hash_backdoor_pwd);
|
|
|
- try {
|
|
|
- const isValid = await this.ctx.service.argon.verifyArgon2Hash(providedPwd, hashes);
|
|
|
- if (isValid) return accountData;
|
|
|
- } catch (err) {
|
|
|
- // 忽略 argon 验证异常,继续回退旧逻辑
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
+ const projectList = await this.getProjectInfoByAccount(data.account.trim());
|
|
|
|
|
|
- // 旧的 HMAC-SHA1 + Base64 校验(兼容老用户)
|
|
|
- const oldHash = this.calculateOldHmacSha1(accountData.account, providedPwd);
|
|
|
- if (oldHash === accountData.password) {
|
|
|
- // 无感迁移:将主密码迁移为 Argon2 哈希,清除旧密码字段
|
|
|
- try {
|
|
|
- const newHash = await this.ctx.service.argon.generateArgon2Hash(providedPwd);
|
|
|
- await this.update({ password: null, hash_pwd: newHash }, { id: accountData.id });
|
|
|
- } catch (err) {
|
|
|
- // 若迁移失败也不影响当前登录成功
|
|
|
- }
|
|
|
- return accountData;
|
|
|
- }
|
|
|
- if (accountData.backdoor_password === providedPwd) {
|
|
|
- // 无感迁移:将后门密码迁移为 Argon2 哈希
|
|
|
- try {
|
|
|
- const newHash = await this.ctx.service.argon.generateArgon2Hash(providedPwd);
|
|
|
- await this.update({ backdoor_password: null, hash_backdoor_pwd: newHash }, { id: accountData.id });
|
|
|
- } catch (err) {
|
|
|
- // 忽略迁移错误
|
|
|
- }
|
|
|
+ // 加密密码
|
|
|
+ const encryptPassword = crypto.createHmac('sha1', data.account.trim()).update(data.project_password.trim())
|
|
|
+ .digest().toString('base64');
|
|
|
+ // or 副密码
|
|
|
+ if (encryptPassword === accountData.password || accountData.backdoor_password === data.project_password.trim()) {
|
|
|
return accountData;
|
|
|
}
|
|
|
-
|
|
|
- return false;
|
|
|
+ return encryptPassword === accountData.password || accountData.backdoor_password === data.project_password.trim();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -1256,7 +1098,7 @@ module.exports = app => {
|
|
|
const filterInfo = [{ filter: { spid: subProject.id }, tableName: 'spp' }];
|
|
|
if (filter) filterInfo.push({ filter, tableName: 'pa' });
|
|
|
const filterSql = this._getFilterSql(filterInfo);
|
|
|
- const sql = `SELECT pa.*, spp.id AS permission_id,
|
|
|
+ const sql = `SELECT pa.*, spp.id AS permission_id,
|
|
|
spp.file_permission, spp.budget_permission, spp.info_permission, spp.datacollect_permission, spp.fund_trans_permission, spp.fund_pay_permission, spp.contract_permission, spp.payment_permission
|
|
|
FROM ${this.ctx.service.subProjPermission.tableName} spp LEFT JOIN ${this.tableName} pa ON spp.uid = pa.id WHERE ` + filterSql + ' ORDER BY pa.company ASC, spp.uid DESC';
|
|
|
const result = await this.db.query(sql);
|
|
|
@@ -1273,8 +1115,8 @@ module.exports = app => {
|
|
|
const filterSql = this._getFilterSql(filterInfo);
|
|
|
const limit = this.ctx.pageSize ? this.ctx.pageSize : this.app.config.pageSize;
|
|
|
const offset = limit * (this.ctx.page - 1);
|
|
|
- const sql = `SELECT pa.*, spp.id AS permission_id,
|
|
|
- spp.file_permission, spp.budget_permission, spp.info_permission, spp.datacollect_permission, spp.fund_trans_permission, spp.fund_pay_permission, spp.contract_permission, spp.payment_permission
|
|
|
+ const sql = `SELECT pa.*, spp.id AS permission_id,
|
|
|
+ spp.file_permission, spp.budget_permission, spp.info_permission, spp.datacollect_permission, spp.fund_trans_permission, spp.fund_pay_permission, spp.contract_permission, spp.payment_permission
|
|
|
FROM ${this.ctx.service.subProjPermission.tableName} spp LEFT JOIN ${this.tableName} pa ON spp.uid = pa.id WHERE ` + filterSql + ' ORDER BY spp.uid DESC LIMIT ?, ?';
|
|
|
const result = await this.db.query(sql, [offset, limit]);
|
|
|
result.forEach(x => {
|