409 lines
14 KiB
JavaScript
409 lines
14 KiB
JavaScript
const axios = require('axios');
|
||
const colors = require('colors');
|
||
|
||
// 配置信息
|
||
const CONFIG = {
|
||
baseURL: 'http://localhost/dev-api',
|
||
timeout: 10000,
|
||
};
|
||
|
||
// 测试用户配置
|
||
const TEST_USERS = {
|
||
// 超级管理员
|
||
superAdmin: {
|
||
username: 'admin',
|
||
password: 'admin123',
|
||
description: '超级管理员',
|
||
expectedRole: 'superadmin',
|
||
},
|
||
// 物业维修管理员
|
||
propertyAdmin: {
|
||
username: 'zwc003', // 总务处科长舒刚
|
||
password: 'admin123',
|
||
description: '物业维修管理员',
|
||
expectedRole: 'property_maintenance_admin',
|
||
},
|
||
// 维修人员
|
||
maintenanceWorker: {
|
||
username: 'zwc005', // 总务处干事陆雄
|
||
password: 'admin123',
|
||
description: '维修人员',
|
||
expectedRole: 'maintenance_worker',
|
||
},
|
||
// 普通用户
|
||
normalUser: {
|
||
username: 'zkxy001', // 智控学院张三
|
||
password: 'admin123',
|
||
description: '普通用户(报修人)',
|
||
expectedRole: 'none',
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 登录获取Token
|
||
*/
|
||
async function login(username, password) {
|
||
const CryptoJS = require('crypto-js');
|
||
const NodeRSA = require('node-rsa');
|
||
|
||
// 生成32位随机AES密钥
|
||
function generateAESKey() {
|
||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||
let result = '';
|
||
for (let i = 0; i < 32; i++) {
|
||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// AES加密
|
||
function aesEncrypt(data, key) {
|
||
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
|
||
return CryptoJS.AES.encrypt(data, keyUtf8, {
|
||
mode: CryptoJS.mode.ECB,
|
||
padding: CryptoJS.pad.Pkcs7,
|
||
}).toString();
|
||
}
|
||
|
||
// RSA加密AES密钥
|
||
function rsaEncrypt(aesKey) {
|
||
const rsaKeyStr =
|
||
'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==';
|
||
const pemKey = `-----BEGIN PUBLIC KEY-----\n${rsaKeyStr}\n-----END PUBLIC KEY-----`;
|
||
const rsaKey = new NodeRSA(pemKey);
|
||
rsaKey.setOptions({ encryptionScheme: 'pkcs1' });
|
||
const aesKeyBase64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(aesKey));
|
||
return rsaKey.encrypt(aesKeyBase64, 'base64');
|
||
}
|
||
|
||
const aesKey = generateAESKey();
|
||
const loginData = {
|
||
tenantId: '000000',
|
||
username: username,
|
||
password: password,
|
||
clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||
grantType: 'password',
|
||
};
|
||
|
||
const encryptedData = aesEncrypt(JSON.stringify(loginData), aesKey);
|
||
const encryptedKey = rsaEncrypt(aesKey);
|
||
|
||
const headers = {
|
||
'Content-Type': 'application/json;charset=utf-8',
|
||
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||
'encrypt-key': encryptedKey,
|
||
referer: 'http://localhost/login?redirect=%252Findex',
|
||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||
isToken: 'false',
|
||
isEncrypt: 'true',
|
||
};
|
||
|
||
try {
|
||
const response = await axios.post(`${CONFIG.baseURL}/auth/login`, encryptedData, {
|
||
headers: headers,
|
||
timeout: CONFIG.timeout,
|
||
});
|
||
|
||
if (response.data.code === 200 && response.data.data?.access_token) {
|
||
return response.data.data.access_token;
|
||
} else {
|
||
throw new Error(response.data.msg || 'Login failed');
|
||
}
|
||
} catch (error) {
|
||
console.error(`登录失败 [${username}]:`, error.message);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 调用API接口
|
||
*/
|
||
async function callAPI(token, endpoint, method = 'GET', data = null) {
|
||
try {
|
||
const config = {
|
||
method: method.toLowerCase(),
|
||
url: `${CONFIG.baseURL}${endpoint}`,
|
||
headers: {
|
||
'Content-Type': 'application/json;charset=utf-8',
|
||
Authorization: `Bearer ${token}`,
|
||
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||
referer: 'http://localhost/login?redirect=%252Findex',
|
||
},
|
||
timeout: CONFIG.timeout,
|
||
};
|
||
|
||
if (data && method.toUpperCase() !== 'GET') {
|
||
config.data = data;
|
||
} else if (data && method.toUpperCase() === 'GET') {
|
||
config.params = data;
|
||
}
|
||
|
||
const response = await axios(config);
|
||
return {
|
||
success: true,
|
||
data: response.data,
|
||
status: response.status,
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
error: error.response?.data || error.message,
|
||
status: error.response?.status || 0,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户信息
|
||
*/
|
||
async function getUserInfo(token) {
|
||
return await callAPI(token, '/system/user/getInfo');
|
||
}
|
||
|
||
/**
|
||
* 获取报修工单列表
|
||
*/
|
||
async function getRepairOrders(token, params = {}) {
|
||
return await callAPI(token, '/pm/repairOrder/list', 'GET', params);
|
||
}
|
||
|
||
/**
|
||
* 获取指定工单详情
|
||
*/
|
||
async function getRepairOrderDetail(token, orderId) {
|
||
return await callAPI(token, `/pm/repairOrder/${orderId}`);
|
||
}
|
||
|
||
/**
|
||
* 测试数据权限
|
||
*/
|
||
async function testDataPermissions() {
|
||
console.log('🔐 报修工单数据权限测试'.yellow.bold);
|
||
console.log('='.repeat(80).gray);
|
||
|
||
const results = {};
|
||
|
||
for (const [userType, userConfig] of Object.entries(TEST_USERS)) {
|
||
console.log(`\n📋 测试用户:${userConfig.description} (${userConfig.username})`.cyan.bold);
|
||
console.log('-'.repeat(60).gray);
|
||
|
||
try {
|
||
// 1. 登录获取Token
|
||
console.log('🔑 正在登录...'.yellow);
|
||
const token = await login(userConfig.username, userConfig.password);
|
||
console.log('✅ 登录成功'.green);
|
||
|
||
// 2. 获取用户信息
|
||
console.log('👤 获取用户信息...'.yellow);
|
||
const userInfoResult = await getUserInfo(token);
|
||
if (userInfoResult.success) {
|
||
const userInfo = userInfoResult.data.data;
|
||
console.log(
|
||
`✅ 用户信息:${userInfo.user.nickName} | 角色:${userInfo.roles.join(', ')}`,
|
||
);
|
||
|
||
results[userType] = {
|
||
userConfig,
|
||
userInfo: userInfo,
|
||
token,
|
||
tests: {},
|
||
};
|
||
} else {
|
||
console.log('❌ 获取用户信息失败'.red);
|
||
continue;
|
||
}
|
||
|
||
// 3. 测试工单列表权限
|
||
console.log('📝 测试工单列表权限...'.yellow);
|
||
const listResult = await getRepairOrders(token);
|
||
if (listResult.success) {
|
||
const totalOrders = listResult.data.total || 0;
|
||
const orders = listResult.data.rows || [];
|
||
console.log(`✅ 可查看工单数量:${totalOrders} 个`);
|
||
|
||
results[userType].tests.listAccess = {
|
||
success: true,
|
||
totalOrders,
|
||
orders: orders.slice(0, 3), // 只保存前3个用于显示
|
||
};
|
||
|
||
// 显示可查看的工单概况
|
||
if (orders.length > 0) {
|
||
console.log(' 可查看的工单:');
|
||
orders.slice(0, 3).forEach((order) => {
|
||
console.log(
|
||
` - ${order.orderNo}: ${order.title} (报修人:${
|
||
order.reporterName
|
||
}, 维修员:${order.assigneeName || '未分派'})`,
|
||
);
|
||
});
|
||
if (orders.length > 3) {
|
||
console.log(` - 还有 ${orders.length - 3} 个工单...`);
|
||
}
|
||
}
|
||
} else {
|
||
console.log(`❌ 工单列表查询失败:${listResult.error.msg || listResult.error}`);
|
||
results[userType].tests.listAccess = {
|
||
success: false,
|
||
error: listResult.error,
|
||
};
|
||
}
|
||
|
||
// 4. 测试具体工单详情权限(测试工单ID: 1001, 1002, 1003)
|
||
console.log('🔍 测试工单详情权限...'.yellow);
|
||
const testOrderIds = [1001, 1002, 1003];
|
||
const detailResults = [];
|
||
|
||
for (const orderId of testOrderIds) {
|
||
const detailResult = await getRepairOrderDetail(token, orderId);
|
||
if (detailResult.success) {
|
||
const order = detailResult.data.data;
|
||
console.log(` ✅ 工单 ${orderId}: ${order.title} (有权限查看)`);
|
||
detailResults.push({
|
||
orderId,
|
||
success: true,
|
||
order: {
|
||
orderNo: order.orderNo,
|
||
title: order.title,
|
||
reporterName: order.reporterName,
|
||
assigneeName: order.assigneeName,
|
||
},
|
||
});
|
||
} else {
|
||
console.log(
|
||
` ❌ 工单 ${orderId}: 无权限查看 (${
|
||
detailResult.error.msg || detailResult.error
|
||
})`,
|
||
);
|
||
detailResults.push({
|
||
orderId,
|
||
success: false,
|
||
error: detailResult.error.msg || detailResult.error,
|
||
});
|
||
}
|
||
}
|
||
|
||
results[userType].tests.detailAccess = detailResults;
|
||
} catch (error) {
|
||
console.log(`❌ 测试失败:${error.message}`.red);
|
||
results[userType] = {
|
||
userConfig,
|
||
error: error.message,
|
||
};
|
||
}
|
||
}
|
||
|
||
// 输出测试总结
|
||
console.log('\n📊 数据权限测试总结'.cyan.bold);
|
||
console.log('='.repeat(80).gray);
|
||
|
||
for (const [userType, result] of Object.entries(results)) {
|
||
if (result.error) {
|
||
console.log(`❌ ${result.userConfig.description}: 测试失败 - ${result.error}`.red);
|
||
continue;
|
||
}
|
||
|
||
const listAccess = result.tests.listAccess;
|
||
const detailAccess = result.tests.detailAccess;
|
||
|
||
console.log(`\n👤 ${result.userConfig.description} (${result.userConfig.username}):`.cyan);
|
||
console.log(` 角色权限:${result.userInfo.roles.join(', ')}`);
|
||
console.log(
|
||
` 可查看工单:${listAccess.success ? listAccess.totalOrders + ' 个' : '查询失败'}`,
|
||
);
|
||
|
||
if (detailAccess) {
|
||
const accessibleDetails = detailAccess.filter((d) => d.success).length;
|
||
console.log(
|
||
` 详情权限:${accessibleDetails}/${detailAccess.length} 个工单可查看详情`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// 权限验证结果
|
||
console.log('\n🎯 权限验证结果'.green.bold);
|
||
console.log('-'.repeat(60).gray);
|
||
|
||
// 验证超级管理员权限
|
||
const superAdminResult = results.superAdmin;
|
||
if (superAdminResult && !superAdminResult.error) {
|
||
const canViewAll = superAdminResult.tests.listAccess.totalOrders >= 12; // 应该能看到所有工单
|
||
console.log(
|
||
`✅ 超级管理员权限:${canViewAll ? '✅ 正确' : '❌ 异常'} (可查看 ${
|
||
superAdminResult.tests.listAccess.totalOrders
|
||
} 个工单)`,
|
||
);
|
||
}
|
||
|
||
// 验证物业维修管理员权限
|
||
const propertyAdminResult = results.propertyAdmin;
|
||
if (propertyAdminResult && !propertyAdminResult.error) {
|
||
const canViewAll = propertyAdminResult.tests.listAccess.totalOrders >= 12; // 应该能看到所有工单
|
||
console.log(
|
||
`✅ 物业维修管理员权限:${canViewAll ? '✅ 正确' : '❌ 异常'} (可查看 ${
|
||
propertyAdminResult.tests.listAccess.totalOrders
|
||
} 个工单)`,
|
||
);
|
||
}
|
||
|
||
// 验证维修人员权限
|
||
const workerResult = results.maintenanceWorker;
|
||
if (workerResult && !workerResult.error) {
|
||
const limitedAccess = workerResult.tests.listAccess.totalOrders < 12; // 应该只能看到部分工单
|
||
console.log(
|
||
`✅ 维修人员权限:${limitedAccess ? '✅ 正确' : '❌ 异常'} (可查看 ${
|
||
workerResult.tests.listAccess.totalOrders
|
||
} 个工单)`,
|
||
);
|
||
}
|
||
|
||
// 验证普通用户权限
|
||
const normalUserResult = results.normalUser;
|
||
if (normalUserResult && !normalUserResult.error) {
|
||
const limitedAccess = normalUserResult.tests.listAccess.totalOrders <= 2; // 应该只能看到自己的工单
|
||
console.log(
|
||
`✅ 普通用户权限:${limitedAccess ? '✅ 正确' : '❌ 异常'} (可查看 ${
|
||
normalUserResult.tests.listAccess.totalOrders
|
||
} 个工单)`,
|
||
);
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* 主函数
|
||
*/
|
||
async function main() {
|
||
try {
|
||
console.log('🚀 开始数据权限测试...'.green.bold);
|
||
const results = await testDataPermissions();
|
||
|
||
console.log('\n✅ 数据权限测试完成!'.green.bold);
|
||
|
||
// 保存测试结果到文件(可选)
|
||
const fs = require('fs');
|
||
const reportPath = `data-permission-test-result-${
|
||
new Date().toISOString().split('T')[0]
|
||
}.json`;
|
||
fs.writeFileSync(reportPath, JSON.stringify(results, null, 2));
|
||
console.log(`📋 测试结果已保存到:${reportPath}`.gray);
|
||
} catch (error) {
|
||
console.error('❌ 测试执行失败:', error.message.red);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
// 运行测试
|
||
if (require.main === module) {
|
||
main();
|
||
}
|
||
|
||
module.exports = {
|
||
testDataPermissions,
|
||
login,
|
||
callAPI,
|
||
TEST_USERS,
|
||
};
|