diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/VisibilityChecker.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/VisibilityChecker.java index a57ba4e7..16980af1 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/VisibilityChecker.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/VisibilityChecker.java @@ -7,6 +7,9 @@ import java.util.Map; public class VisibilityChecker { public boolean canAccess(Skill skill, String currentUserId, Map userNamespaceRoles) { + if (skill.isHidden()) { + return isOwner(skill, currentUserId) || isAdminOrAbove(userNamespaceRoles.get(skill.getNamespaceId())); + } if (skill.getLatestVersionId() == null) { return isOwner(skill, currentUserId); } diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/VisibilityCheckerTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/VisibilityCheckerTest.java index 46d8601b..1ffe5c8f 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/VisibilityCheckerTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/VisibilityCheckerTest.java @@ -15,6 +15,7 @@ class VisibilityCheckerTest { private Skill namespaceOnlySkill; private Skill privateSkill; private Skill unpublishedPublicSkill; + private Skill hiddenPublicSkill; private static final Long NAMESPACE_ID = 1L; private static final String OWNER_ID = "user-100"; @@ -33,6 +34,9 @@ class VisibilityCheckerTest { privateSkill = new Skill(NAMESPACE_ID, "private-skill", OWNER_ID, SkillVisibility.PRIVATE); privateSkill.setLatestVersionId(12L); unpublishedPublicSkill = new Skill(NAMESPACE_ID, "draft-public-skill", OWNER_ID, SkillVisibility.PUBLIC); + hiddenPublicSkill = new Skill(NAMESPACE_ID, "hidden-public-skill", OWNER_ID, SkillVisibility.PUBLIC); + hiddenPublicSkill.setLatestVersionId(13L); + hiddenPublicSkill.setHidden(true); } @Test @@ -129,4 +133,29 @@ class VisibilityCheckerTest { boolean canAccess = checker.canAccess(unpublishedPublicSkill, OWNER_ID, Map.of()); assertTrue(canAccess); } + + @Test + void testHiddenSkillNotAccessibleByAnonymous() { + boolean canAccess = checker.canAccess(hiddenPublicSkill, null, Map.of()); + assertFalse(canAccess); + } + + @Test + void testHiddenSkillNotAccessibleByOtherUser() { + boolean canAccess = checker.canAccess(hiddenPublicSkill, OTHER_USER_ID, Map.of()); + assertFalse(canAccess); + } + + @Test + void testHiddenSkillAccessibleByOwner() { + boolean canAccess = checker.canAccess(hiddenPublicSkill, OWNER_ID, Map.of()); + assertTrue(canAccess); + } + + @Test + void testHiddenSkillAccessibleByNamespaceAdmin() { + Map roles = Map.of(NAMESPACE_ID, NamespaceRole.ADMIN); + boolean canAccess = checker.canAccess(hiddenPublicSkill, ADMIN_USER_ID, roles); + assertTrue(canAccess); + } } 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 37e43fb9..4b56ff9b 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 @@ -257,6 +257,35 @@ class SkillQueryServiceTest { assertEquals("own-skill", result.getContent().get(0).getSlug()); } + @Test + void testListSkillsByNamespace_ShouldHideHiddenSkillsFromRegularUsers() throws Exception { + String namespaceSlug = "test-ns"; + String userId = "user-100"; + Map userNsRoles = Map.of(); + Pageable pageable = PageRequest.of(0, 10); + + Namespace namespace = new Namespace(namespaceSlug, "Test NS", "user-1"); + setId(namespace, 1L); + Skill visibleSkill = new Skill(1L, "visible-skill", "user-200", SkillVisibility.PUBLIC); + setId(visibleSkill, 1L); + visibleSkill.setLatestVersionId(11L); + Skill hiddenSkill = new Skill(1L, "hidden-skill", "user-300", SkillVisibility.PUBLIC); + setId(hiddenSkill, 2L); + hiddenSkill.setLatestVersionId(12L); + hiddenSkill.setHidden(true); + + when(namespaceRepository.findBySlug(namespaceSlug)).thenReturn(Optional.of(namespace)); + when(skillRepository.findByNamespaceIdAndStatus(1L, SkillStatus.ACTIVE)) + .thenReturn(List.of(visibleSkill, hiddenSkill)); + when(visibilityChecker.canAccess(visibleSkill, userId, userNsRoles)).thenReturn(true); + when(visibilityChecker.canAccess(hiddenSkill, userId, userNsRoles)).thenReturn(false); + + Page result = service.listSkillsByNamespace(namespaceSlug, userId, userNsRoles, pageable); + + assertEquals(1, result.getTotalElements()); + assertEquals("visible-skill", result.getContent().get(0).getSlug()); + } + @Test void testListFiles() throws Exception { // Arrange