Repository navigation
🛡️ Sentinel: [CRITICAL] AdminController의 누락된 인증 체계를 추가합니다. #609
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
60ff6da
44664fb
ff95350
d8d28b7
3a5311e
92bc12e
8602233
456607f
a5296cd
d5d1ba2
d726af7
d6a299e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,53 @@ | ||
| package com.clearfolio.viewer.controller; | ||
|
|
||
| import static org.mockito.ArgumentMatchers.any; | ||
| import static org.mockito.ArgumentMatchers.anyString; | ||
| import static org.mockito.Mockito.mock; | ||
| import static org.mockito.Mockito.when; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Set; | ||
| import java.util.UUID; | ||
|
|
||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.http.HttpHeaders; | ||
| import org.springframework.test.web.reactive.server.WebTestClient; | ||
|
|
||
| import com.clearfolio.viewer.auth.TenantAccessService; | ||
| import com.clearfolio.viewer.auth.TenantContext; | ||
| import com.clearfolio.viewer.auth.TenantPermissions; | ||
| import com.clearfolio.viewer.model.ConversionJob; | ||
| import com.clearfolio.viewer.service.DocumentConversionService; | ||
| import com.clearfolio.viewer.service.RetryDeadLetterResult; | ||
|
|
||
| class AdminControllerTest { | ||
|
|
||
| private DocumentConversionService conversionService; | ||
| private TenantAccessService tenantAccessService; | ||
| private WebTestClient webTestClient; | ||
| private AdminController controller; | ||
|
|
||
| @BeforeEach | ||
| void setUp() { | ||
| conversionService = mock(DocumentConversionService.class); | ||
| controller = new AdminController(conversionService); | ||
| tenantAccessService = mock(TenantAccessService.class); | ||
| controller = new AdminController(conversionService, tenantAccessService); | ||
| webTestClient = WebTestClient.bindToController(controller) | ||
| .controllerAdvice(new ApiExceptionHandler()) | ||
| .build(); | ||
| } | ||
|
|
||
| private void mockAuth() { | ||
| TenantContext ctx = new TenantContext("tenant-1", "subject-1", Set.of(TenantPermissions.ADMIN_READ, TenantPermissions.ADMIN_WRITE)); | ||
| when(tenantAccessService.require(any(HttpHeaders.class), anyString())).thenReturn(ctx); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '1,180p' src/test/java/com/clearfolio/viewer/controller/AdminControllerTest.java
sed -n '1,130p' src/main/java/com/clearfolio/viewer/controller/AdminController.javaRepository: ContextualWisdomLab/clearfolio Length of output: 10044 엔드포인트별 권한 계약을 검증하십시오.
각 테스트에서 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| @Test | ||
| void getAllJobsReturnsAllJobsWhenNoFilterProvided() { | ||
| ConversionJob job1 = new ConversionJob(UUID.randomUUID(), "a.pdf", "application/pdf", "hash-a", 100L); | ||
| ConversionJob job2 = new ConversionJob(UUID.randomUUID(), "b.pdf", "application/pdf", "hash-b", 100L); | ||
| mockAuth(); | ||
| ConversionJob job1 = new ConversionJob(UUID.randomUUID(), "tenant-1", "subject-1", "a.pdf", "application/pdf", "hash-a", 100L, 3); | ||
| ConversionJob job2 = new ConversionJob(UUID.randomUUID(), "tenant-1", "subject-1", "b.pdf", "application/pdf", "hash-b", 100L, 3); | ||
| when(conversionService.getAllJobs()).thenReturn(Arrays.asList(job1, job2)); | ||
|
|
||
| webTestClient.get() | ||
|
|
@@ -45,11 +60,28 @@ void getAllJobsReturnsAllJobsWhenNoFilterProvided() { | |
| .jsonPath("$.jobs[1].fileName").isEqualTo("b.pdf"); | ||
| } | ||
|
|
||
| @Test | ||
| void getAllJobsFiltersByTenantId() { | ||
| mockAuth(); | ||
| ConversionJob job1 = new ConversionJob(UUID.randomUUID(), "tenant-1", "subject-1", "a.pdf", "application/pdf", "hash-a", 100L, 3); | ||
| ConversionJob job2 = new ConversionJob(UUID.randomUUID(), "tenant-2", "subject-1", "b.pdf", "application/pdf", "hash-b", 100L, 3); | ||
| when(conversionService.getAllJobs()).thenReturn(Arrays.asList(job1, job2)); | ||
|
|
||
| webTestClient.get() | ||
| .uri("/api/v1/admin/convert/jobs") | ||
| .exchange() | ||
| .expectStatus().isOk() | ||
| .expectBody() | ||
| .jsonPath("$.jobs.length()").isEqualTo(1) | ||
| .jsonPath("$.jobs[0].fileName").isEqualTo("a.pdf"); | ||
| } | ||
|
|
||
| @Test | ||
| void getAllJobsFiltersByDeadLetteredTrue() { | ||
| ConversionJob job1 = new ConversionJob(UUID.randomUUID(), "a.pdf", "application/pdf", "hash-a", 100L); | ||
| mockAuth(); | ||
| ConversionJob job1 = new ConversionJob(UUID.randomUUID(), "tenant-1", "subject-1", "a.pdf", "application/pdf", "hash-a", 100L, 3); | ||
| job1.markDeadLettered("failed"); | ||
| ConversionJob job2 = new ConversionJob(UUID.randomUUID(), "b.pdf", "application/pdf", "hash-b", 100L); | ||
| ConversionJob job2 = new ConversionJob(UUID.randomUUID(), "tenant-1", "subject-1", "b.pdf", "application/pdf", "hash-b", 100L, 3); | ||
|
|
||
| when(conversionService.getAllJobs()).thenReturn(Arrays.asList(job1, job2)); | ||
|
|
||
|
|
@@ -64,9 +96,10 @@ void getAllJobsFiltersByDeadLetteredTrue() { | |
|
|
||
| @Test | ||
| void getAllJobsFiltersByDeadLetteredFalse() { | ||
| ConversionJob job1 = new ConversionJob(UUID.randomUUID(), "a.pdf", "application/pdf", "hash-a", 100L); | ||
| mockAuth(); | ||
| ConversionJob job1 = new ConversionJob(UUID.randomUUID(), "tenant-1", "subject-1", "a.pdf", "application/pdf", "hash-a", 100L, 3); | ||
| job1.markDeadLettered("failed"); | ||
| ConversionJob job2 = new ConversionJob(UUID.randomUUID(), "b.pdf", "application/pdf", "hash-b", 100L); | ||
| ConversionJob job2 = new ConversionJob(UUID.randomUUID(), "tenant-1", "subject-1", "b.pdf", "application/pdf", "hash-b", 100L, 3); | ||
|
|
||
| when(conversionService.getAllJobs()).thenReturn(Arrays.asList(job1, job2)); | ||
|
|
||
|
|
@@ -81,17 +114,34 @@ void getAllJobsFiltersByDeadLetteredFalse() { | |
|
|
||
| @Test | ||
| void deleteJobReturnsNoContent() { | ||
| mockAuth(); | ||
| UUID jobId = UUID.randomUUID(); | ||
| when(conversionService.deleteJob(any(UUID.class), any(TenantContext.class))).thenReturn(true); | ||
|
|
||
| webTestClient.delete() | ||
| .uri("/api/v1/admin/convert/jobs/" + jobId) | ||
| .exchange() | ||
| .expectStatus().isNoContent(); | ||
| } | ||
|
|
||
| @Test | ||
| void deleteJobReturnsNotFoundWhenWrongTenant() { | ||
| mockAuth(); | ||
| UUID jobId = UUID.randomUUID(); | ||
| when(conversionService.deleteJob(any(UUID.class), any(TenantContext.class))).thenReturn(false); | ||
|
|
||
| webTestClient.delete() | ||
| .uri("/api/v1/admin/convert/jobs/" + jobId) | ||
| .exchange() | ||
| .expectStatus().isNotFound(); | ||
| } | ||
|
|
||
| @Test | ||
| void retryDeadLetteredReturnsAcceptedWhenAccepted() { | ||
| mockAuth(); | ||
| UUID jobId = UUID.randomUUID(); | ||
| ConversionJob job = new ConversionJob(jobId, "tenant-1", "subject-1", "a.pdf", "application/pdf", "hash", 100L, 3); | ||
| when(conversionService.getJob(jobId)).thenReturn(java.util.Optional.of(job)); | ||
| when(conversionService.retryDeadLettered(jobId, "admin")).thenReturn(RetryDeadLetterResult.ACCEPTED); | ||
|
|
||
| webTestClient.post() | ||
|
|
@@ -102,8 +152,24 @@ void retryDeadLetteredReturnsAcceptedWhenAccepted() { | |
|
|
||
| @Test | ||
| void retryDeadLetteredReturnsNotFoundWhenNotFound() { | ||
| mockAuth(); | ||
| UUID jobId = UUID.randomUUID(); | ||
| when(conversionService.getJob(jobId)).thenReturn(java.util.Optional.empty()); | ||
|
|
||
| webTestClient.post() | ||
| .uri("/api/v1/admin/convert/jobs/" + jobId + "/retry") | ||
| .exchange() | ||
| .expectStatus().isNotFound(); | ||
| } | ||
|
|
||
| @Test | ||
| void retryDeadLetteredReturnsNotFoundWhenWrongTenant() { | ||
| mockAuth(); | ||
| UUID jobId = UUID.randomUUID(); | ||
| when(conversionService.retryDeadLettered(jobId, "admin")).thenReturn(RetryDeadLetterResult.NOT_FOUND); | ||
| ConversionJob job = new ConversionJob(jobId, "tenant-2", "subject-1", "a.pdf", "application/pdf", "hash", 100L, 3); | ||
| when(conversionService.getJob(jobId)).thenReturn(java.util.Optional.of(job)); | ||
| org.mockito.Mockito.doThrow(new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND)) | ||
| .when(tenantAccessService).requireSameTenant(any(TenantContext.class), any(ConversionJob.class)); | ||
|
|
||
| webTestClient.post() | ||
| .uri("/api/v1/admin/convert/jobs/" + jobId + "/retry") | ||
|
|
@@ -113,7 +179,10 @@ void retryDeadLetteredReturnsNotFoundWhenNotFound() { | |
|
|
||
| @Test | ||
| void retryDeadLetteredReturnsConflictWhenNotEligible() { | ||
| mockAuth(); | ||
| UUID jobId = UUID.randomUUID(); | ||
| ConversionJob job = new ConversionJob(jobId, "tenant-1", "subject-1", "a.pdf", "application/pdf", "hash", 100L, 3); | ||
| when(conversionService.getJob(jobId)).thenReturn(java.util.Optional.of(job)); | ||
| when(conversionService.retryDeadLettered(jobId, "admin")).thenReturn(RetryDeadLetterResult.NOT_ELIGIBLE); | ||
|
|
||
| webTestClient.post() | ||
|
|
||
There was a problem hiding this comment.
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
🔎 Supported by static analysis
🏁 Script executed:
Repository: ContextualWisdomLab/clearfolio
Length of output: 5664
🏁 Script executed:
Repository: ContextualWisdomLab/clearfolio
Length of output: 20012
🤖 get_repo_knowledge executed:
get_repo_knowledge ContextualWisdomLab/clearfolio /tmp/coderabbit-repo-knowledge/contextualwisdomlab-clearfolio-2deecf52/conventionsLength of output: 7273
🏁 Script executed:
Repository: ContextualWisdomLab/clearfolio
Length of output: 20011
실제 날짜를 기록하십시오.
Markdown은
$(date +%Y-%m-%d)를 실행하지 않습니다. 이 제목은 셸 치환 문자열 그대로 표시됩니다. 기존 항목이 사용하는YYYY-MM-DD형식에 맞춰 실제 기록 날짜로 교체하십시오.🤖 Prompt for AI Agents