mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-28 11:25:00 +00:00
fix(search): align portal visibility for private skills (#323)
* feat(ci): add AI-powered release notes generation - Add GitHub Models integration for automated release notes - Support bilingual (EN) release notes with highlights extraction - Fallback to conventional commit grouping when LLM unavailable - Trigger on tag push or manual workflow dispatch - Zero configuration: uses GitHub Models (gpt-4o-mini) by default * chore: pin action versions and update gitignore - Pin checkout and setup-deno to commit hashes matching project convention - Add .playwright-mcp/ and .mcp.json to gitignore * fix(search): keep private skills out of portal discovery - remove super admin expanded visibility from portal search - exclude all private skills from portal search results - keep namespace-only skills visible only to namespace members - reserve private skill access for my-skills and future admin surfaces * test(search): update test to reflect portal visibility policy - rename test from platformWideAccessShouldBypass... to platformWideAccessShouldNotBypass... - verify that platformWideAccess flag does not grant extra visibility in portal search - verify PRIVATE skills are excluded from search SQL - aligns test with new requirement: portal search uses standard visibility rules * test(search): update app service test for portal visibility policy - rename test from search_shouldGrantPlatformWideAccessToSuperAdmin to search_shouldNotGrantPlatformWideAccessToSuperAdminInPortal - verify super admin does not get platformWideAccess in portal search - aligns with requirement: portal uses standard visibility, admin features reserved for dedicated interface
This commit is contained in:
parent
c03790a11e
commit
080be28eef
4 changed files with 10 additions and 21 deletions
|
|
@ -122,10 +122,8 @@ public class SkillSearchAppService {
|
|||
}
|
||||
|
||||
private boolean hasPlatformWideReadAccess(Set<String> platformRoles) {
|
||||
if (platformRoles == null || platformRoles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return platformRoles.contains("SUPER_ADMIN");
|
||||
// Super admins should use a dedicated admin interface, not the public portal
|
||||
return false;
|
||||
}
|
||||
|
||||
private SearchResponse searchVisibleSkills(
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ class SkillSearchAppServiceTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void search_shouldGrantPlatformWideAccessToSuperAdmin() {
|
||||
void search_shouldNotGrantPlatformWideAccessToSuperAdminInPortal() {
|
||||
when(searchQueryService.search(any()))
|
||||
.thenReturn(new SearchResult(List.of(), 0, 0, 20));
|
||||
when(rbacService.getUserRoleCodes("admin-1")).thenReturn(Set.of("SUPER_ADMIN", "USER"));
|
||||
|
|
@ -228,7 +228,7 @@ class SkillSearchAppServiceTest {
|
|||
|
||||
SearchVisibilityScope scope = captor.getValue().visibilityScope();
|
||||
assertEquals("admin-1", scope.userId());
|
||||
assertEquals(true, scope.platformWideAccess());
|
||||
assertEquals(false, scope.platformWideAccess());
|
||||
}
|
||||
|
||||
private void setField(Object target, String fieldName, Object value) {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
Set<Long> adminNamespaceIds = query.visibilityScope().adminNamespaceIds().isEmpty()
|
||||
? Set.of(-1L)
|
||||
: query.visibilityScope().adminNamespaceIds();
|
||||
boolean platformWideAccess = query.visibilityScope().platformWideAccess();
|
||||
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("SELECT d.skill_id ");
|
||||
|
|
@ -118,9 +117,6 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
sql.append("AND (d.visibility = 'PUBLIC' ");
|
||||
if (query.visibilityScope().userId() != null) {
|
||||
sql.append("OR (d.visibility = 'NAMESPACE_ONLY' AND d.namespace_id IN :memberNamespaceIds) ");
|
||||
sql.append("OR (d.visibility = 'NAMESPACE_ONLY' AND :platformWideAccess = TRUE) ");
|
||||
sql.append("OR (d.visibility = 'PRIVATE' AND (d.namespace_id IN :adminNamespaceIds OR d.owner_id = :userId)) ");
|
||||
sql.append("OR (d.visibility = 'PRIVATE' AND :platformWideAccess = TRUE) ");
|
||||
}
|
||||
sql.append(") ");
|
||||
|
||||
|
|
@ -131,7 +127,6 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
sql.append("AND (n.status <> 'ARCHIVED' ");
|
||||
if (query.visibilityScope().userId() != null) {
|
||||
sql.append("OR d.namespace_id IN :memberNamespaceIds ");
|
||||
sql.append("OR :platformWideAccess = TRUE ");
|
||||
}
|
||||
sql.append(") ");
|
||||
|
||||
|
|
@ -196,8 +191,6 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
if (query.visibilityScope().userId() != null) {
|
||||
nativeQuery.setParameter("memberNamespaceIds", memberNamespaceIds);
|
||||
nativeQuery.setParameter("adminNamespaceIds", adminNamespaceIds);
|
||||
nativeQuery.setParameter("platformWideAccess", platformWideAccess);
|
||||
nativeQuery.setParameter("userId", query.visibilityScope().userId());
|
||||
}
|
||||
|
||||
if (query.namespaceId() != null) {
|
||||
|
|
@ -243,8 +236,6 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
if (query.visibilityScope().userId() != null) {
|
||||
countQuery.setParameter("memberNamespaceIds", memberNamespaceIds);
|
||||
countQuery.setParameter("adminNamespaceIds", adminNamespaceIds);
|
||||
countQuery.setParameter("platformWideAccess", platformWideAccess);
|
||||
countQuery.setParameter("userId", query.visibilityScope().userId());
|
||||
}
|
||||
|
||||
if (query.namespaceId() != null) {
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ class PostgresFullTextQueryServiceTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void platformWideAccessShouldBypassNamespaceVisibilityRestrictions() {
|
||||
void platformWideAccessShouldNotBypassVisibilityInPortalSearch() {
|
||||
EntityManager entityManager = mock(EntityManager.class);
|
||||
Query nativeQuery = mock(Query.class);
|
||||
Query countQuery = mock(Query.class);
|
||||
|
|
@ -418,12 +418,12 @@ class PostgresFullTextQueryServiceTest {
|
|||
|
||||
ArgumentCaptor<String> sqlCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(entityManager, org.mockito.Mockito.times(2)).createNativeQuery(sqlCaptor.capture());
|
||||
// Portal search should not include platformWideAccess bypass logic
|
||||
assertThat(sqlCaptor.getAllValues().getFirst())
|
||||
.contains("OR (d.visibility = 'NAMESPACE_ONLY' AND :platformWideAccess = TRUE)")
|
||||
.contains("OR (d.visibility = 'PRIVATE' AND :platformWideAccess = TRUE)")
|
||||
.contains("OR :platformWideAccess = TRUE");
|
||||
verify(nativeQuery).setParameter("platformWideAccess", true);
|
||||
verify(countQuery).setParameter("platformWideAccess", true);
|
||||
.doesNotContain("platformWideAccess")
|
||||
.doesNotContain("PRIVATE");
|
||||
verify(nativeQuery, never()).setParameter("platformWideAccess", true);
|
||||
verify(countQuery, never()).setParameter("platformWideAccess", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue