fix(personal): reject ambiguous org identities

This commit is contained in:
2026-07-12 15:50:42 +08:00
parent 2161353247
commit 8cf8da6899
2 changed files with 23 additions and 2 deletions
@@ -79,12 +79,15 @@ public class OrgSnapshotEnterpriseKnowledgeAccessPolicy implements EnterpriseKno
AND position_name IS NOT NULL AND position_name IS NOT NULL
AND position_name <> '' AND position_name <> ''
ORDER BY snapshot_date DESC, id ASC ORDER BY snapshot_date DESC, id ASC
LIMIT 1 LIMIT 2
""", (rs, rowNum) -> new OrganizationIdentity( """, (rs, rowNum) -> new OrganizationIdentity(
trimmed(rs.getString("project_code")), trimmed(rs.getString("project_code")),
trimmed(rs.getString("position_name")), trimmed(rs.getString("position_name")),
trimmed(rs.getString("position_level"))), tenantId, phone); trimmed(rs.getString("position_level"))), tenantId, phone);
return rows.stream().filter(OrganizationIdentity::valid).findFirst(); if (rows.size() != 1 || !rows.get(0).valid()) {
return Optional.empty();
}
return Optional.of(rows.get(0));
} }
private List<Long> authorizedFragmentIds(String tenantId, OrganizationIdentity identity) { private List<Long> authorizedFragmentIds(String tenantId, OrganizationIdentity identity) {
@@ -90,6 +90,20 @@ class OrgSnapshotEnterpriseKnowledgeAccessPolicyTest {
assertTrue(jdbc.sql.stream().anyMatch(value -> value.contains("employment_status = 'active'"))); assertTrue(jdbc.sql.stream().anyMatch(value -> value.contains("employment_status = 'active'")));
} }
@Test
void duplicateActivePhoneIdentitiesFailClosedBeforeAclLookup() {
RecordingJdbcTemplate jdbc = fixture();
jdbc.organizations = List.of(
Map.of("project_code", "PRJ-FCW", "position_name", "物业管家", "position_level", "一线"),
Map.of("project_code", "PRJ-YSF", "position_name", "物业管家", "position_level", "一线")
);
jdbc.fragmentIds = List.of(100101L);
assertTrue(new OrgSnapshotEnterpriseKnowledgeAccessPolicy(jdbc)
.authorize(new PersonalOwner("000000", 103L, null)).isEmpty());
assertFalse(jdbc.sql.stream().anyMatch(value -> value.contains("FROM aihr_knowledge_acl")));
}
@Test @Test
void aclQueryEnforcesTenantProjectPositionAndTenantScopes() { void aclQueryEnforcesTenantProjectPositionAndTenantScopes() {
RecordingJdbcTemplate jdbc = fixture(); RecordingJdbcTemplate jdbc = fixture();
@@ -190,6 +204,7 @@ class OrgSnapshotEnterpriseKnowledgeAccessPolicyTest {
private String phone; private String phone;
private String expectedTenant; private String expectedTenant;
private Map<String, String> organization; private Map<String, String> organization;
private List<Map<String, String>> organizations;
private List<Long> fragmentIds = List.of(); private List<Long> fragmentIds = List.of();
private boolean fail; private boolean fail;
@@ -207,6 +222,9 @@ class OrgSnapshotEnterpriseKnowledgeAccessPolicyTest {
return phone == null ? List.of() : mapRows(rowMapper, List.of(Map.of("phonenumber", phone))); return phone == null ? List.of() : mapRows(rowMapper, List.of(Map.of("phonenumber", phone)));
} }
if (sql.contains("FROM aihr_org_snapshot")) { if (sql.contains("FROM aihr_org_snapshot")) {
if (organizations != null) {
return mapRows(rowMapper, new ArrayList<>(organizations));
}
return organization == null ? List.of() : mapRows(rowMapper, List.of(organization)); return organization == null ? List.of() : mapRows(rowMapper, List.of(organization));
} }
if (sql.contains("FROM aihr_knowledge_acl")) { if (sql.contains("FROM aihr_knowledge_acl")) {