From 8cf8da6899053d7e6441eb2f8ff808a87e21465b Mon Sep 17 00:00:00 2001 From: let5sne Date: Sun, 12 Jul 2026 15:50:42 +0800 Subject: [PATCH] fix(personal): reject ambiguous org identities --- ...napshotEnterpriseKnowledgeAccessPolicy.java | 7 +++++-- ...hotEnterpriseKnowledgeAccessPolicyTest.java | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/personal/service/OrgSnapshotEnterpriseKnowledgeAccessPolicy.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/personal/service/OrgSnapshotEnterpriseKnowledgeAccessPolicy.java index 33fc2be4..dfe4e8f3 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/personal/service/OrgSnapshotEnterpriseKnowledgeAccessPolicy.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/personal/service/OrgSnapshotEnterpriseKnowledgeAccessPolicy.java @@ -79,12 +79,15 @@ public class OrgSnapshotEnterpriseKnowledgeAccessPolicy implements EnterpriseKno AND position_name IS NOT NULL AND position_name <> '' ORDER BY snapshot_date DESC, id ASC - LIMIT 1 + LIMIT 2 """, (rs, rowNum) -> new OrganizationIdentity( trimmed(rs.getString("project_code")), trimmed(rs.getString("position_name")), 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 authorizedFragmentIds(String tenantId, OrganizationIdentity identity) { diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/personal/OrgSnapshotEnterpriseKnowledgeAccessPolicyTest.java b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/personal/OrgSnapshotEnterpriseKnowledgeAccessPolicyTest.java index 445633d5..895582b3 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/personal/OrgSnapshotEnterpriseKnowledgeAccessPolicyTest.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/personal/OrgSnapshotEnterpriseKnowledgeAccessPolicyTest.java @@ -90,6 +90,20 @@ class OrgSnapshotEnterpriseKnowledgeAccessPolicyTest { 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 void aclQueryEnforcesTenantProjectPositionAndTenantScopes() { RecordingJdbcTemplate jdbc = fixture(); @@ -190,6 +204,7 @@ class OrgSnapshotEnterpriseKnowledgeAccessPolicyTest { private String phone; private String expectedTenant; private Map organization; + private List> organizations; private List fragmentIds = List.of(); private boolean fail; @@ -207,6 +222,9 @@ class OrgSnapshotEnterpriseKnowledgeAccessPolicyTest { return phone == null ? List.of() : mapRows(rowMapper, List.of(Map.of("phonenumber", phone))); } 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)); } if (sql.contains("FROM aihr_knowledge_acl")) {