diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillController.java index 6d582cae..3e480060 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillController.java @@ -70,12 +70,10 @@ public class SkillController extends BaseApiController { @PathVariable String namespace, @PathVariable String slug, @RequestAttribute(value = "userId", required = false) String userId, - @RequestAttribute(value = "userNsRoles", required = false) Map userNsRoles, - @RequestAttribute(value = "platformRoles", required = false) java.util.Set platformRoles) { + @RequestAttribute(value = "userNsRoles", required = false) Map userNsRoles) { SkillQueryService.SkillDetailDTO detail = skillQueryService.getSkillDetail( - namespace, slug, userId, userNsRoles != null ? userNsRoles : Map.of(), - platformRoles != null ? platformRoles : java.util.Set.of()); + namespace, slug, userId, userNsRoles != null ? userNsRoles : Map.of()); SkillDetailResponse response = new SkillDetailResponse( detail.id(), diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/SkillControllerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/SkillControllerTest.java index 800c6a7a..e47da366 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/SkillControllerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/SkillControllerTest.java @@ -149,8 +149,7 @@ class SkillControllerTest { eq("team"), eq("demo"), eq((String) null), - eq(Map.of()), - org.mockito.ArgumentMatchers.>any())) + eq(Map.of()))) .thenReturn(new SkillQueryService.SkillDetailDTO( 1L, "demo", @@ -197,8 +196,7 @@ class SkillControllerTest { eq("team"), eq("demo"), eq((String) null), - eq(Map.of()), - org.mockito.ArgumentMatchers.>any())) + eq(Map.of()))) .thenThrow(new DomainForbiddenException("error.namespace.archived", "team")); mockMvc.perform(get("/api/web/skills/team/demo")) diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillQueryService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillQueryService.java index 2f01d5fd..8bf88767 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillQueryService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillQueryService.java @@ -150,28 +150,15 @@ public class SkillQueryService { String skillSlug, String currentUserId, Map userNsRoles) { - return getSkillDetail(namespaceSlug, skillSlug, currentUserId, userNsRoles, Set.of()); - } - - public SkillDetailDTO getSkillDetail( - String namespaceSlug, - String skillSlug, - String currentUserId, - Map userNsRoles, - Set platformRoles) { - Namespace namespace = findNamespace(namespaceSlug); Skill skill = resolveVisibleSkill(namespace.getId(), skillSlug, currentUserId); - // Archived namespace: only members (or super admins) may view if (namespace.getStatus() == com.iflytek.skillhub.domain.namespace.NamespaceStatus.ARCHIVED - && !isNamespaceMember(namespace.getId(), currentUserId, userNsRoles) - && !isSuperAdmin(platformRoles)) { + && !isNamespaceMember(namespace.getId(), currentUserId, userNsRoles)) { throw new DomainForbiddenException("error.namespace.archived", namespaceSlug); } - // Visibility check - if (!visibilityChecker.canAccess(skill, currentUserId, userNsRoles, platformRoles)) { + if (!visibilityChecker.canAccess(skill, currentUserId, userNsRoles)) { throw new DomainForbiddenException("error.skill.access.denied", skillSlug); } @@ -203,7 +190,7 @@ public class SkillQueryService { skill.getNamespaceId(), skill.getCreatedAt(), skill.getUpdatedAt(), - canManageRestrictedSkill(skill, currentUserId, userNsRoles, platformRoles), + canManageRestrictedSkill(skill, currentUserId, userNsRoles), canSubmitPromotion(namespace, skill, publishedVersion, currentUserId, userNsRoles), headlineVersion == null || "PUBLISHED".equals(headlineVersion.status()), currentUserId == null || !Objects.equals(skill.getOwnerId(), currentUserId), @@ -215,6 +202,15 @@ public class SkillQueryService { ); } + public SkillDetailDTO getSkillDetail( + String namespaceSlug, + String skillSlug, + String currentUserId, + Map userNsRoles, + Set platformRoles) { + return getSkillDetail(namespaceSlug, skillSlug, currentUserId, userNsRoles); + } + /** * Lists skills within a namespace after filtering out records the caller is * not allowed to discover. @@ -647,13 +643,6 @@ public class SkillQueryService { } private boolean canManageRestrictedSkill(Skill skill, String currentUserId, Map userNsRoles) { - return canManageRestrictedSkill(skill, currentUserId, userNsRoles, Set.of()); - } - - private boolean canManageRestrictedSkill(Skill skill, String currentUserId, Map userNsRoles, Set platformRoles) { - if (platformRoles != null && platformRoles.contains("SUPER_ADMIN")) { - return true; - } if (currentUserId == null) { return false; } @@ -695,10 +684,6 @@ public class SkillQueryService { return currentUserId != null && userNsRoles.containsKey(namespaceId); } - private boolean isSuperAdmin(Set platformRoles) { - return platformRoles != null && platformRoles.contains("SUPER_ADMIN"); - } - private String resolveOwnerPreviewReviewComment(SkillLifecycleProjectionService.VersionProjection ownerPreviewVersion) { if (ownerPreviewVersion == null || !"REJECTED".equals(ownerPreviewVersion.status())) { return null; diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillQueryServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillQueryServiceTest.java index ab0b4aba..7ee683ac 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillQueryServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillQueryServiceTest.java @@ -771,6 +771,56 @@ class SkillQueryServiceTest { assertFalse(result.canSubmitPromotion()); } + @Test + void testGetSkillDetail_ShouldNotGrantLifecyclePermissionToSuperAdminInPortal() throws Exception { + String namespaceSlug = "test-ns"; + String skillSlug = "test-skill"; + String userId = "super-1"; + Map userNsRoles = Map.of(1L, NamespaceRole.MEMBER); + + Namespace namespace = new Namespace(namespaceSlug, "Test NS", "owner-1"); + setId(namespace, 1L); + Skill skill = new Skill(1L, skillSlug, "owner-1", SkillVisibility.PUBLIC); + setId(skill, 1L); + skill.setStatus(SkillStatus.ACTIVE); + skill.setLatestVersionId(11L); + + SkillVersion published = new SkillVersion(1L, "1.0.0", "owner-1"); + setId(published, 11L); + published.setStatus(SkillVersionStatus.PUBLISHED); + + when(namespaceRepository.findBySlug(namespaceSlug)).thenReturn(Optional.of(namespace)); + when(skillRepository.findByNamespaceIdAndSlug(1L, skillSlug)).thenReturn(List.of(skill)); + when(skillVersionRepository.findById(11L)).thenReturn(Optional.of(published)); + + SkillQueryService.SkillDetailDTO result = service.getSkillDetail( + namespaceSlug, skillSlug, userId, userNsRoles, Set.of("SUPER_ADMIN")); + + assertFalse(result.canManageLifecycle()); + assertFalse(result.canSubmitPromotion()); + assertEquals("PUBLISHED", result.resolutionMode()); + } + + @Test + void testGetSkillDetail_ShouldNotGrantPrivateVisibilityToSuperAdminInPortal() throws Exception { + String namespaceSlug = "test-ns"; + String skillSlug = "test-skill"; + String userId = "super-1"; + + Namespace namespace = new Namespace(namespaceSlug, "Test NS", "owner-1"); + setId(namespace, 1L); + Skill skill = new Skill(1L, skillSlug, "owner-1", SkillVisibility.PRIVATE); + setId(skill, 1L); + skill.setStatus(SkillStatus.ACTIVE); + skill.setLatestVersionId(11L); + + when(namespaceRepository.findBySlug(namespaceSlug)).thenReturn(Optional.of(namespace)); + when(skillRepository.findByNamespaceIdAndSlug(1L, skillSlug)).thenReturn(List.of(skill)); + + assertThrows(DomainForbiddenException.class, () -> + service.getSkillDetail(namespaceSlug, skillSlug, userId, Map.of(), Set.of("SUPER_ADMIN"))); + } + @Test void testGetSkillDetail_ShouldPreferPendingVersionForOwnerPreview() throws Exception { String namespaceSlug = "test-ns";