feat(密钥管理): 按数据权限分组隔离与筛选
- 后台密钥列表改为按 biz_shop_manage_group 分组:主管只看自己带的分组成员,超管看全量可按 group_id 筛选 - 行数据补「所属分组」名称(批量查询);列表接口带回分组筛选项 - 修复超管筛选不生效:前端筛选参数统一 snake_case(camel 被后端静默忽略) - 列表列/筛选项文案由「所属管理员」改为「分组」
This commit is contained in:
+59
-16
@@ -4,6 +4,8 @@ import com.nanri.aiimage.common.security.ShopCredentialCryptoService;
|
||||
import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
|
||||
import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
|
||||
import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
|
||||
import com.nanri.aiimage.modules.shopkey.model.dto.UserGroupRef;
|
||||
import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
|
||||
import com.nanri.aiimage.modules.usersecret.client.JikipProxyClient;
|
||||
import com.nanri.aiimage.modules.usersecret.mapper.UserApiSecretMapper;
|
||||
import com.nanri.aiimage.modules.usersecret.model.dto.AdminUserSecretQuery;
|
||||
@@ -32,6 +34,8 @@ class UserApiSecretServiceTest {
|
||||
private final JikipProxyClient jikipProxyClient = mock(JikipProxyClient.class);
|
||||
private final AdminUserMapper adminUserMapper = mock(AdminUserMapper.class);
|
||||
private final AdminAuthSupport adminAuthSupport = mock(AdminAuthSupport.class);
|
||||
private final com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper adminGroupMapper =
|
||||
mock(com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper.class);
|
||||
|
||||
private UserApiSecretService newService() {
|
||||
when(crypto.encrypt(anyString())).thenAnswer(inv -> "enc:" + inv.getArgument(0, String.class));
|
||||
@@ -41,7 +45,8 @@ class UserApiSecretServiceTest {
|
||||
});
|
||||
// 默认按主管(admin)判定;超管用例里单独改打桩。
|
||||
when(adminAuthSupport.currentRole(any())).thenReturn("admin");
|
||||
return new UserApiSecretService(mapper, crypto, checkService, jikipProxyClient, adminUserMapper, adminAuthSupport);
|
||||
return new UserApiSecretService(
|
||||
mapper, crypto, checkService, jikipProxyClient, adminUserMapper, adminAuthSupport, adminGroupMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -210,8 +215,6 @@ class UserApiSecretServiceTest {
|
||||
@Test
|
||||
void adminPageAggregatesOneRowPerUserWithProxyMasked() {
|
||||
UserApiSecretService service = newService();
|
||||
// 非限定查询:无 keyword 无组过滤时不触发 users 表圈定查询。
|
||||
when(adminUserMapper.selectList(any())).thenReturn(List.of());
|
||||
when(mapper.selectList(any())).thenReturn(List.of(
|
||||
row(1L, "similar-asin", "enc:sk-sa-1234", "passed"),
|
||||
row(1L, "appearance-patent", "enc:sk-ap-5678", "passed"),
|
||||
@@ -220,11 +223,11 @@ class UserApiSecretServiceTest {
|
||||
user.setId(1L);
|
||||
user.setUsername("张三");
|
||||
when(adminUserMapper.selectById(1L)).thenReturn(user);
|
||||
AdminUserEntity creator = new AdminUserEntity();
|
||||
creator.setId(99L);
|
||||
creator.setUsername("主管甲");
|
||||
when(adminUserMapper.selectById(99L)).thenReturn(creator);
|
||||
user.setCreatedById(99L);
|
||||
// 分组列:用户 1 属于「一组」
|
||||
UserGroupRef ref = new UserGroupRef();
|
||||
ref.setUserId(1L);
|
||||
ref.setGroupName("一组");
|
||||
when(adminGroupMapper.selectGroupNamesByUserIds(any())).thenReturn(List.of(ref));
|
||||
|
||||
AdminUserEntity operator = new AdminUserEntity();
|
||||
operator.setId(88L);
|
||||
@@ -234,8 +237,7 @@ class UserApiSecretServiceTest {
|
||||
assertThat(page.getItems()).hasSize(1);
|
||||
var rowVo = page.getItems().get(0);
|
||||
assertThat(rowVo.getUsername()).isEqualTo("张三");
|
||||
assertThat(rowVo.getCreatedById()).isEqualTo(99L);
|
||||
assertThat(rowVo.getCreatedByUsername()).isEqualTo("主管甲");
|
||||
assertThat(rowVo.getGroups()).containsExactly("一组");
|
||||
assertThat(rowVo.getStatus()).isEqualTo("failed");
|
||||
assertThat(rowVo.getSimilarAsin().getMasked()).isEqualTo("sk-s****1234");
|
||||
assertThat(rowVo.getProxy().getExists()).isTrue();
|
||||
@@ -243,20 +245,61 @@ class UserApiSecretServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminPageLocksAdminToOwnGroupUsers() {
|
||||
void adminPageLocksAdminToOwnLedGroups() {
|
||||
UserApiSecretService service = newService();
|
||||
// 主管无论传什么 createdById,都强制锁定为本组(created_by_id=88L)。
|
||||
AdminUserEntity operator = new AdminUserEntity();
|
||||
operator.setId(88L);
|
||||
when(adminUserMapper.selectList(any())).thenReturn(List.of());
|
||||
// 主管未带任何分组 → 直接空页,不查密钥表。
|
||||
when(adminGroupMapper.selectLedGroups(88L)).thenReturn(List.of());
|
||||
|
||||
service.adminPage(operator, new AdminUserSecretQuery());
|
||||
var page = service.adminPage(operator, new AdminUserSecretQuery());
|
||||
|
||||
// 主管模式:先圈定 users 表(组过滤),密钥表因无匹配行不再查询。
|
||||
verify(adminUserMapper).selectList(any());
|
||||
assertThat(page.getItems()).isEmpty();
|
||||
verify(mapper, never()).selectList(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminPageScopesToLedGroupMembers() {
|
||||
UserApiSecretService service = newService();
|
||||
AdminUserEntity operator = new AdminUserEntity();
|
||||
operator.setId(88L);
|
||||
ShopManageGroupEntity group = new ShopManageGroupEntity();
|
||||
group.setId(5L);
|
||||
group.setGroupName("一组");
|
||||
when(adminGroupMapper.selectLedGroups(88L)).thenReturn(List.of(group));
|
||||
when(adminGroupMapper.selectUserIdsByGroupIds(any())).thenReturn(List.of(1L));
|
||||
when(mapper.selectList(any())).thenReturn(List.of(row(1L, "similar-asin", "enc:sk-1", "passed")));
|
||||
AdminUserEntity user = new AdminUserEntity();
|
||||
user.setId(1L);
|
||||
user.setUsername("张三");
|
||||
when(adminUserMapper.selectById(1L)).thenReturn(user);
|
||||
when(adminGroupMapper.selectGroupNamesByUserIds(any())).thenReturn(List.of());
|
||||
|
||||
var page = service.adminPage(operator, new AdminUserSecretQuery());
|
||||
|
||||
assertThat(page.getItems()).hasSize(1);
|
||||
assertThat(page.getGroupOptions()).extracting(com.nanri.aiimage.modules.usersecret.model.vo.AdminUserSecretPageVo.GroupOptionVo::groupName)
|
||||
.containsExactly("一组");
|
||||
verify(adminGroupMapper).selectUserIdsByGroupIds(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminPageFiltersSuperAdminByGroupId() {
|
||||
UserApiSecretService service = newService();
|
||||
AdminUserEntity operator = new AdminUserEntity();
|
||||
operator.setId(88L);
|
||||
when(adminAuthSupport.currentRole(operator)).thenReturn("super_admin");
|
||||
when(adminGroupMapper.selectUserIdsByGroupIds(List.of(5L))).thenReturn(List.of());
|
||||
when(adminGroupMapper.selectAllGroups()).thenReturn(List.of());
|
||||
|
||||
AdminUserSecretQuery query = new AdminUserSecretQuery();
|
||||
query.setGroupId(5L);
|
||||
var page = service.adminPage(operator, query);
|
||||
|
||||
assertThat(page.getItems()).isEmpty();
|
||||
verify(adminGroupMapper).selectUserIdsByGroupIds(List.of(5L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminClearByUserDeletesAllRowsOfUser() {
|
||||
UserApiSecretService service = newService();
|
||||
|
||||
Reference in New Issue
Block a user