Skip to content

[Feat] 권한 pre-filter 구성 - #55

Merged
kangcheolung merged 4 commits into
developfrom
feature/54
Jul 23, 2026
Merged

[Feat] 권한 pre-filter 구성#55
kangcheolung merged 4 commits into
developfrom
feature/54

Conversation

@kangcheolung

@kangcheolung kangcheolung commented Jul 23, 2026

Copy link
Copy Markdown
Member

🔍️ 작업 내용

Closes #54

✨ 상세 설명

배경

벡터 검색 실행 전에 사용자가 읽을 수 있는 문서 ID 목록을 미리 확보해, 검색 결과에 접근 불가 문서가 노출되지 않도록 한다.

변경 내용

DocumentRepository — 7-branch UNION 네이티브 쿼리 2종 추가

5가지 접근 경로(OWNER / PUBLIC / USER캐시 / ROLE live / DEPT live)를 UNION으로 합산해 한 번의 쿼리로 문서 ID를 반환한다.

메서드 설명
findReadableDocumentIds(userId) 전체 범위 — 7개 브랜치 UNION
findReadableDocumentIdsInCollection(userId, collectionId) 컬렉션 범위 — UNION을 서브쿼리로 감싸고 collection_documents로 교집합

AccessibleDocumentQueryService (신규)

collectionId null 여부로 두 쿼리를 분기하는 단일 진입점. 빈 목록은 그대로 반환해 호출 측(Issue 5)에서 벡터 검색을 skip하도록 한다.

🛠️ 추후 리팩토링 및 고도화 계획

  • Issue 5(POST /search API)에서 AccessibleDocumentQueryService를 SearchFacade에 연결 예정
  • 문서 수가 많아질 경우 UNION 쿼리 성능 검토 필요 (현재 status = 'INDEXED' + deleted_at IS NULL 필터로 범위 제한)

📸 스크린샷 (선택)

해당 없음

💬 리뷰 요구사항

  • 컬렉션 범위 쿼리에서 "UNION 전체를 서브쿼리로 감싸고 외부에서 IN 필터 적용" 방식을 사용했는데, 각 브랜치 안에서 바로 collection_documents를 조인하는 방식 대비 적절한지

Summary by CodeRabbit

  • 새로운 기능

    • 검색 전에 사용자가 읽을 수 있는 문서만 미리 선별합니다.
    • 전체 문서 검색과 컬렉션 범위 검색을 지원합니다.
    • 삭제되었거나 아직 색인되지 않은 문서, 만료되거나 무효화된 권한은 검색 결과에서 제외됩니다.
    • 접근 가능한 문서가 없으면 검색을 실행하지 않도록 처리합니다.
  • 문서화

    • 권한 사전 필터링 기준과 예외·제외 조건을 문서화했습니다.

kangcheolung and others added 3 commits July 23, 2026 11:35
…vice 구현 — F-SEARCH-04

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kangcheolung, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e91aa549-8a34-444d-ab14-72c672a0ed37

📥 Commits

Reviewing files that changed from the base of the PR and between b2dedec and 523032f.

📒 Files selected for processing (1)
  • docs/design/kangcheolung-#54-search-permission-pre-filter.md
📝 Walkthrough

Walkthrough

권한 조건을 UNION으로 결합한 문서 ID pre-filter 쿼리와 전체·컬렉션 범위 라우팅 서비스를 추가하고, 조회 결과 전달 및 빈 목록 처리를 단위 테스트와 설계 문서로 정리합니다.

Changes

권한 pre-filter

Layer / File(s) Summary
읽기 가능 문서 ID 쿼리
src/main/java/com/opensource/docgrid/domain/document/repository/DocumentRepository.java, docs/design/...
OWNER, PUBLIC, 사용자 캐시, ROLE, DEPARTMENT 권한 경로를 UNION으로 조회하며 INDEXED 및 soft delete 조건과 만료·무효화 조건을 적용합니다. 컬렉션 조회는 컬렉션 문서와 교집합을 구합니다.
검색 범위별 조회 라우팅
src/main/java/com/opensource/docgrid/domain/search/service/query/AccessibleDocumentQueryService.java, src/test/java/.../AccessibleDocumentQueryServiceTest.java
collectionId 유무에 따라 Repository 메서드를 선택하고, 반환 목록과 빈 목록 및 추가 호출 부재를 테스트합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AccessibleDocumentQueryService
  participant DocumentRepository
  participant Database
  AccessibleDocumentQueryService->>DocumentRepository: userId와 collectionId 기준 조회 위임
  DocumentRepository->>Database: 읽기 권한 UNION 쿼리 실행
  Database-->>DocumentRepository: 접근 가능한 문서 ID 목록 반환
  DocumentRepository-->>AccessibleDocumentQueryService: 문서 ID 목록 반환
Loading

Possibly related PRs

  • DocGrid/backend#19: pre-filter가 참조하는 사용자 문서 접근 캐시의 권한 부여·회수 상태를 갱신합니다.

Suggested labels: ✨ Feature

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive 핵심 구현은 [#54]와 맞지만, 요구된 권한 시나리오별 테스트 충족 여부는 요약만으로 확인되지 않습니다. 권한 시나리오별 테스트가 실제로 추가됐는지, no-permission 케이스까지 포함되는지 명시해 주세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed 문서, 저장소 쿼리, 서비스, 테스트가 모두 권한 pre-filter 목적에 직접 연결되어 있어 별도 범위 이탈이 보이지 않습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed 제목이 권한 pre-filter 추가라는 핵심 변경을 간결하게 요약합니다.
Description check ✅ Passed 템플릿의 필수 섹션을 모두 채웠고, 작업 내용·상세 설명·추후 계획·리뷰 요구사항이 포함됩니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/54

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/design/kangcheolung-`#54-search-permission-pre-filter.md:
- Around line 57-61: Specify the fenced code block language as text for the
findReadableDocumentIds flow diagram to resolve the MD040 lint violation,
without changing its contents.

In
`@src/test/java/com/opensource/docgrid/domain/search/service/query/AccessibleDocumentQueryServiceTest.java`:
- Around line 32-76: Replace the mock-based tests in
AccessibleDocumentQueryServiceTest with a `@DataJpaTest` integration test setup
that executes the native DocumentRepository queries against an actual test
database. Add fixtures and assertions covering OWNER, PUBLIC, and USER cache
expiration/invalidation, document and collection ROLE/DEPARTMENT permissions,
unauthorized documents, and collection-intersection filtering, including
empty-result cases; retain clear scenario-based test names and verify both
global and collection query methods.
- Around line 19-21: 클래스 수준 주석이 없어 AccessibleDocumentQueryServiceTest의 역할과 책임
범위가 드러나지 않습니다. `@DisplayName` 위 또는 클래스 선언부에 주석을 추가해 이 테스트 클래스가
AccessibleDocumentQueryService를 단위 테스트하며 테스트 대상과 범위를 명확히 설명하도록 하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 43137fd2-7ba4-4be6-8f49-ccbd9eabd1f8

📥 Commits

Reviewing files that changed from the base of the PR and between f68060d and b2dedec.

📒 Files selected for processing (4)
  • docs/design/kangcheolung-#54-search-permission-pre-filter.md
  • src/main/java/com/opensource/docgrid/domain/document/repository/DocumentRepository.java
  • src/main/java/com/opensource/docgrid/domain/search/service/query/AccessibleDocumentQueryService.java
  • src/test/java/com/opensource/docgrid/domain/search/service/query/AccessibleDocumentQueryServiceTest.java

Comment thread docs/design/kangcheolung-#54-search-permission-pre-filter.md Outdated
Comment on lines +19 to +21
@ExtendWith(MockitoExtension.class)
@DisplayName("AccessibleDocumentQueryService 단위 테스트")
class AccessibleDocumentQueryServiceTest {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

테스트 클래스의 역할과 경계를 클래스 주석으로 명시하세요.

@DisplayName은 클래스 수준 주석을 대체하지 않습니다.

수정 예시
+/**
+ * AccessibleDocumentQueryService의 저장소 호출 분기를 단위 테스트한다.
+ * 네이티브 권한 SQL 검증은 저장소 통합 테스트 범위다.
+ */
 `@ExtendWith`(MockitoExtension.class)
 `@DisplayName`("AccessibleDocumentQueryService 단위 테스트")
 class AccessibleDocumentQueryServiceTest {

As per coding guidelines, "Every newly created class, interface, or record must have a class-level comment describing its role, responsibility, and boundary."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ExtendWith(MockitoExtension.class)
@DisplayName("AccessibleDocumentQueryService 단위 테스트")
class AccessibleDocumentQueryServiceTest {
/**
* AccessibleDocumentQueryService의 저장소 호출 분기를 단위 테스트한다.
* 네이티브 권한 SQL 검증은 저장소 통합 테스트 범위다.
*/
`@ExtendWith`(MockitoExtension.class)
`@DisplayName`("AccessibleDocumentQueryService 단위 테스트")
class AccessibleDocumentQueryServiceTest {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/opensource/docgrid/domain/search/service/query/AccessibleDocumentQueryServiceTest.java`
around lines 19 - 21, 클래스 수준 주석이 없어 AccessibleDocumentQueryServiceTest의 역할과 책임
범위가 드러나지 않습니다. `@DisplayName` 위 또는 클래스 선언부에 주석을 추가해 이 테스트 클래스가
AccessibleDocumentQueryService를 단위 테스트하며 테스트 대상과 범위를 명확히 설명하도록 하세요.

Source: Coding guidelines

Comment on lines +32 to +76
@Test
@DisplayName("collectionId가 null이면 전체 범위 쿼리를 호출하고 결과를 반환한다")
void findReadableDocumentIds_withoutCollection_callsGlobalQuery() {
List<Long> expected = List.of(1L, 2L, 3L);
given(documentRepository.findReadableDocumentIds(USER_ID)).willReturn(expected);

List<Long> result = accessibleDocumentQueryService.findReadableDocumentIds(USER_ID, null);

assertThat(result).isEqualTo(expected);
then(documentRepository).should(times(1)).findReadableDocumentIds(USER_ID);
then(documentRepository).shouldHaveNoMoreInteractions();
}

@Test
@DisplayName("collectionId가 있으면 컬렉션 범위 쿼리를 호출하고 결과를 반환한다")
void findReadableDocumentIds_withCollection_callsCollectionQuery() {
List<Long> expected = List.of(2L, 3L);
given(documentRepository.findReadableDocumentIdsInCollection(USER_ID, COLLECTION_ID)).willReturn(expected);

List<Long> result = accessibleDocumentQueryService.findReadableDocumentIds(USER_ID, COLLECTION_ID);

assertThat(result).isEqualTo(expected);
then(documentRepository).should(times(1)).findReadableDocumentIdsInCollection(USER_ID, COLLECTION_ID);
then(documentRepository).shouldHaveNoMoreInteractions();
}

@Test
@DisplayName("접근 가능한 문서가 없으면 빈 목록을 반환한다")
void findReadableDocumentIds_noAccessible_returnsEmptyList() {
given(documentRepository.findReadableDocumentIds(USER_ID)).willReturn(List.of());

List<Long> result = accessibleDocumentQueryService.findReadableDocumentIds(USER_ID, null);

assertThat(result).isEmpty();
}

@Test
@DisplayName("컬렉션 범위에서 접근 가능한 문서가 없으면 빈 목록을 반환한다")
void findReadableDocumentIds_noAccessibleInCollection_returnsEmptyList() {
given(documentRepository.findReadableDocumentIdsInCollection(USER_ID, COLLECTION_ID)).willReturn(List.of());

List<Long> result = accessibleDocumentQueryService.findReadableDocumentIds(USER_ID, COLLECTION_ID);

assertThat(result).isEmpty();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

네이티브 권한 SQL을 검증하는 저장소 통합 테스트를 추가하세요.

현재 테스트는 DocumentRepository를 mock 처리해 라우팅만 확인합니다. 따라서 OWNER, PUBLIC, USER 캐시의 만료·무효화, 문서/컬렉션 ROLE·DEPARTMENT 권한, 무권한 및 컬렉션 교집합이 실제 SQL에서 올바르게 필터링되는지 검증하지 못합니다. @DataJpaTest로 각 권한 경로와 빈 결과를 실행 검증하세요.

As per path instructions, "src/test/**/*.java: 테스트 커버리지, 스프링 테스트 어노테이션, mock 사용법, 네이밍 규칙을 확인한다."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/opensource/docgrid/domain/search/service/query/AccessibleDocumentQueryServiceTest.java`
around lines 32 - 76, Replace the mock-based tests in
AccessibleDocumentQueryServiceTest with a `@DataJpaTest` integration test setup
that executes the native DocumentRepository queries against an actual test
database. Add fixtures and assertions covering OWNER, PUBLIC, and USER cache
expiration/invalidation, document and collection ROLE/DEPARTMENT permissions,
unauthorized documents, and collection-intersection filtering, including
empty-result cases; retain clear scenario-based test names and verify both
global and collection query methods.

Source: Path instructions

@kangcheolung kangcheolung changed the title Feature/54 [Feat] 권한 pre-filter 구성 Jul 23, 2026
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kangcheolung
kangcheolung merged commit a0982d4 into develop Jul 23, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 권한 pre-filter 구성

1 participant