125 行
3.9 KiB
TypeScript
125 行
3.9 KiB
TypeScript
import {
|
|
TeamDataScopeService,
|
|
UserDataContext,
|
|
buildLeadDataScopeFilter,
|
|
assertLeadAccessPermission,
|
|
} from '../../../src/modules/team/service/data-scope';
|
|
|
|
describe('Point 52: 多租户团队成员细粒度数据隔离与销售管辖范围保护', () => {
|
|
let service: TeamDataScopeService;
|
|
|
|
beforeEach(() => {
|
|
service = new TeamDataScopeService();
|
|
});
|
|
|
|
describe('1. buildLeadDataScopeFilter (数据权限范围查询条件构建)', () => {
|
|
it('租户拥有者 (owner) 与管理员 (admin) 应具备租户级全量权限', () => {
|
|
const ownerCtx: UserDataContext = {
|
|
userId: 1,
|
|
tenantId: 'tenant-100',
|
|
role: 'owner',
|
|
};
|
|
const adminCtx: UserDataContext = {
|
|
userId: 2,
|
|
tenantId: 'tenant-100',
|
|
role: 'admin',
|
|
};
|
|
|
|
const ownerFilter = buildLeadDataScopeFilter(ownerCtx);
|
|
expect(ownerFilter.tenantId).toBe('tenant-100');
|
|
expect(ownerFilter.assignedUserId).toBeUndefined(); // 全员无限制
|
|
expect(ownerFilter.departmentId).toBeUndefined();
|
|
|
|
const adminFilter = buildLeadDataScopeFilter(adminCtx);
|
|
expect(adminFilter.tenantId).toBe('tenant-100');
|
|
expect(adminFilter.assignedUserId).toBeUndefined();
|
|
});
|
|
|
|
it('部门区域销售总监 (sales_manager) 应锁定为本部门管辖范围', () => {
|
|
const managerCtx: UserDataContext = {
|
|
userId: 10,
|
|
tenantId: 'tenant-100',
|
|
role: 'sales_manager',
|
|
departmentId: 'dept-middle-east',
|
|
};
|
|
|
|
const filter = buildLeadDataScopeFilter(managerCtx);
|
|
expect(filter.tenantId).toBe('tenant-100');
|
|
expect(filter.departmentId).toBe('dept-middle-east');
|
|
expect(filter.assignedUserId).toBeUndefined();
|
|
});
|
|
|
|
it('一线销售 (sales_rep/member) 必须严格隔离只能查看本人线索', () => {
|
|
const repCtx: UserDataContext = {
|
|
userId: 88,
|
|
tenantId: 'tenant-100',
|
|
role: 'sales_rep',
|
|
departmentId: 'dept-middle-east',
|
|
};
|
|
|
|
const filter = buildLeadDataScopeFilter(repCtx);
|
|
expect(filter.tenantId).toBe('tenant-100');
|
|
expect(filter.assignedUserId).toBe(88);
|
|
});
|
|
});
|
|
|
|
describe('2. assertLeadAccessPermission (单条线索详情读取防越权校验)', () => {
|
|
const mockLead = {
|
|
id: 999,
|
|
tenantId: 'tenant-100',
|
|
assignedUserId: 88,
|
|
departmentId: 'dept-middle-east',
|
|
};
|
|
|
|
it('允许线索归属人本人 (userId=88) 访问', () => {
|
|
const repCtx: UserDataContext = {
|
|
userId: 88,
|
|
tenantId: 'tenant-100',
|
|
role: 'sales_rep',
|
|
};
|
|
expect(() => assertLeadAccessPermission(repCtx, mockLead)).not.toThrow();
|
|
});
|
|
|
|
it('允许所属部门主管 (dept-middle-east) 与企业 Owner 跨权限访问', () => {
|
|
const managerCtx: UserDataContext = {
|
|
userId: 10,
|
|
tenantId: 'tenant-100',
|
|
role: 'sales_manager',
|
|
departmentId: 'dept-middle-east',
|
|
};
|
|
const ownerCtx: UserDataContext = {
|
|
userId: 1,
|
|
tenantId: 'tenant-100',
|
|
role: 'owner',
|
|
};
|
|
|
|
expect(() => assertLeadAccessPermission(managerCtx, mockLead)).not.toThrow();
|
|
expect(() => assertLeadAccessPermission(ownerCtx, mockLead)).not.toThrow();
|
|
});
|
|
|
|
it('同租户其他销售 (userId=89) 越权访问时应抛出 403 明确拒绝', () => {
|
|
const otherRepCtx: UserDataContext = {
|
|
userId: 89,
|
|
tenantId: 'tenant-100',
|
|
role: 'sales_rep',
|
|
};
|
|
|
|
expect(() => assertLeadAccessPermission(otherRepCtx, mockLead)).toThrow(
|
|
/无权访问该客户线索|Forbidden|权限不足/i
|
|
);
|
|
});
|
|
|
|
it('跨租户访问应坚决阻断', () => {
|
|
const crossTenantCtx: UserDataContext = {
|
|
userId: 1,
|
|
tenantId: 'tenant-200', // 跨租户
|
|
role: 'owner',
|
|
};
|
|
|
|
expect(() => assertLeadAccessPermission(crossTenantCtx, mockLead)).toThrow(
|
|
/无权访问该客户线索|Forbidden|权限不足/i
|
|
);
|
|
});
|
|
});
|
|
});
|