fix: scope discovery update and delete by namespace (#6827) - #7007
Conversation
Add namespace_id predicates to the discovery update, updateSelective
and delete statements so that discovery configs can only be modified
within the caller's namespace. DiscoveryService#delete now validates
the namespace of the target discovery before processing, and the
DELETE /discovery/{discoveryId} endpoint requires a namespaceId
parameter and plugin edit/delete permissions.
Aias00
left a comment
There was a problem hiding this comment.
Review: Scope discovery update and delete by namespace (#6827)
Verdict: APPROVE
Analysis
A clean, end-to-end namespace scoping fix for discovery lifecycle operations.
Service / Mapper contract
DiscoveryMapper.delete(String)→delete(@Param("id") String id, @Param("namespaceId") String namespaceId).DiscoveryService.delete(String)→delete(String discoveryId, String namespaceId)(implDiscoveryServiceImplupdated).DiscoveryServiceImpl.deletenow loads theDiscoveryDOand rejects the call when it's missing or belongs to a different namespace:if (Objects.isNull(discoveryDO) || !Objects.equals(discoveryDO.getNamespaceId(), namespaceId)) { throw new ShenyuException("shenyu this discovery is not found in current namespace"); }
java.util.Objectsis imported (verified), andObjects.isNull/Objects.equalsare used consistently elsewhere in the class.
SQL scoping (discovery-sqlmap.xml)
delete→WHERE id = #{id} AND namespace_id = #{namespaceId}.updateandupdateSelectivealso gainedAND namespace_id = #{namespaceId}, preventing a cross-namespace UPDATE from silently touching the wrong row.
Internal callers updated (all three discoveryMapper.delete(...) call sites now pass the namespace):
DiscoveryServiceImpl.delete✓ProxySelectorServiceImpl.delete(passesdiscoveryDO.getNamespaceId()) ✓SelectorServiceImpl.unbindDiscovery(passesdiscoveryDO.getNamespaceId()) ✓
API hardening (DiscoveryController)
deletenow requires@RequestParam("namespaceId")validated via@Existed(provider = NamespaceMapper.class), plus@RequiresPermissions("system:plugin:delete");createOrUpdategets@RequiresPermissions("system:plugin:edit"). Both follow existing shenyu-admin conventions.
Tests: DiscoveryMapperTest (integration) covers scoped delete/update/updateSelective across two namespaces; DiscoveryServiceImplTest (unit, Mockito) covers the mismatch-namespace and not-found paths throwing ShenyuException without touching the mapper/processor. Good coverage of the new guard.
Conclusion
No leftover single-arg discoveryMapper.delete callers (the three in the codebase are all updated), the interface/impl signatures are consistent, and the SQL + service guard enforce namespace isolation. Approving.
Make sure that:
./mvnw clean install -Dmaven.javadoc.skip=true.Summary
Changes:
discovery-sqlmap.xml— addedAND namespace_id = #{namespaceId, jdbcType=VARCHAR}to the WHERE clauses ofupdate(:217),updateSelective(:248) anddelete(:254), so discovery mutations are scoped to the caller's namespace;deletenow takes(id, namespaceId)parameters.DiscoveryMapper.java:134—delete(String id)changed todelete(@Param("id") String id, @Param("namespaceId") String namespaceId).DiscoveryServiceImpl.java:192-206—deleteacceptsnamespaceIdand verifies the selectedDiscoveryDObelongs to the requested namespace before invoking the discovery processor, so cross-namespace ids produce no registry side effects (also fixes a potential NPE when the id does not exist); the mapper delete is then called with the scoped predicate.SelectorServiceImpl.java:332/ProxySelectorServiceImpl.java:184— internal cleanup paths passdiscoveryDO.getNamespaceId()(the DO is freshly loaded from DB, so the value is authoritative).DiscoveryController.java—DELETE /discovery/{discoveryId}now requires anamespaceIdrequest parameter (validated via@Existed(NamespaceMapper)); added@RequiresPermissions("system:plugin:edit")toinsertOrUpdateand@RequiresPermissions("system:plugin:delete")todelete, consistent with the Selector/Rule controllers.Test Cases:
DiscoveryMapperTest— H2 integration tests:delete/update/updateSelectivewith a mismatched namespace mutate 0 rows; with a matching namespace they mutate only the target row and leave the other namespace's row intact.DiscoveryServiceImplTest— delete succeeds in the matching namespace; throwsShenyuExceptionwith no processor/mapper side effects on namespace mismatch or when the discovery does not exist.Verification
./mvnw clean install -Dmaven.javadoc.skip=truepassed locally (JDK 21).DiscoveryMapperTest,DiscoveryServiceImplTest,SelectorServiceTest,ProxySelectorServiceTest).checkstyle:checkpassed.Note: the dashboard frontend (apache/shenyu-dashboard) currently calls
DELETE /discovery/{id}without anamespaceId; a follow-up in that repository is needed to pass the current namespace.close #6827
related pr: #596