Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -176,19 +176,19 @@ public UUID submit(MultipartFile file, PolicyOverrideRequest overrideRequest) {
*/
@Override
public UUID submit(MultipartFile file, PolicyOverrideRequest overrideRequest, TenantContext tenantContext) {
if (tenantContext == null) {
throw new IllegalArgumentException("tenant context is required");
}
PolicyOverrideRequest effectiveOverride = overrideRequest == null
? PolicyOverrideRequest.none()
: overrideRequest;
TenantContext effectiveTenant = tenantContext == null
? new TenantContext(TenantContext.DEMO_TENANT_ID, TenantContext.DEMO_SUBJECT_ID, java.util.Set.of())
: tenantContext;
validationService.validateOrThrow(file, effectiveOverride);

String contentHash = contentHash(file);
ConversionJob job = new ConversionJob(
UUID.randomUUID(),
effectiveTenant.tenantId(),
effectiveTenant.subjectId(),
tenantContext.tenantId(),
tenantContext.subjectId(),
sanitizeFilename(file.getOriginalFilename()),
file.getContentType(),
contentHash,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.clearfolio.viewer.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;

import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockMultipartFile;

import com.clearfolio.viewer.artifact.ArtifactStore;
import com.clearfolio.viewer.config.ConversionProperties;
import com.clearfolio.viewer.repository.ConversionJobRepository;

/**
* Verifies that the production tenant-aware submission boundary never invents
* demo authority when an authenticated tenant context is absent.
*/
class DefaultDocumentConversionServiceTenantAuthorityTest {

@Test
void tenantAwareSubmitRejectsMissingAuthorityBeforeAnyDocumentOrJobWork() {
ConversionJobRepository repository = mock(ConversionJobRepository.class);
DocumentValidationService validationService = mock(DocumentValidationService.class);
ConversionWorker worker = mock(ConversionWorker.class);
ArtifactStore artifactStore = mock(ArtifactStore.class);
DefaultDocumentConversionService service = new DefaultDocumentConversionService(
repository,
validationService,
worker,
artifactStore,
new ConversionProperties()
);
MockMultipartFile file = new MockMultipartFile(
"file",
"contract.docx",
"application/octet-stream",
"sensitive-document".getBytes(java.nio.charset.StandardCharsets.UTF_8)
);

IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> service.submit(file, PolicyOverrideRequest.none(), null)
);

assertEquals("tenant context is required", exception.getMessage());
verifyNoInteractions(validationService, repository, worker, artifactStore);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ void submitStoresTenantAndSubjectMetadataOnJob() {
}

@Test
void submitWithNullTenantContextFallsBackToDemoOwnership() {
void submitWithNullTenantContextFailsClosed() {
ConversionJobRepository repository = new InMemoryConversionJobRepository();
RecordingConversionWorker worker = new RecordingConversionWorker();
DocumentConversionService service = new DefaultDocumentConversionService(
Expand All @@ -277,11 +277,14 @@ void submitWithNullTenantContextFallsBackToDemoOwnership() {
"hello-viewer".getBytes()
);

UUID jobId = service.submit(file, PolicyOverrideRequest.none(), null);
IllegalArgumentException error = assertThrows(
IllegalArgumentException.class,
() -> service.submit(file, PolicyOverrideRequest.none(), null)
);

ConversionJob job = repository.findById(jobId).orElseThrow();
assertEquals(TenantContext.DEMO_TENANT_ID, job.getTenantId());
assertEquals(TenantContext.DEMO_SUBJECT_ID, job.getSubjectId());
assertEquals("tenant context is required", error.getMessage());
assertTrue(repository.findAll().isEmpty());
assertEquals(0, worker.enqueuedCount());
}

@Test
Expand Down
Loading