package jnpf.dmsService; import jnpf.base.UserInfo; import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity; import jnpf.dmsEntity.permission.form.DmsPersonalFolderBorrowForm; import jnpf.dmsEntity.permission.form.DmsPersonalFolderDownloadForm; import jnpf.dmsEntity.permission.form.DmsPersonalFolderFavoriteForm; import jnpf.dmsEntity.permission.enums.DmsPermissionAction; import jnpf.dmsEntity.permission.enums.DmsResourceType; import jnpf.dmsEntity.permission.vo.DmsPersonalFolderAddResultVO; import jnpf.dmsEntity.permission.vo.DmsPersonalFolderFileVO; import jnpf.dmsEntity.permission.vo.DmsPersonalFolderVO; import jnpf.dmsEntity.print.form.DmsWorkflowPrintFilesGrantForm; import jnpf.dmsEntity.print.vo.DmsWorkflowPrintFilesGrantResultVO; import jnpf.dmsMapper.DmsPersonalFolderMapper; import jnpf.dmsPermission.DmsCurrentIdentity; import jnpf.dmsPermission.DmsPermissionError; import jnpf.dmsPermission.DmsPermissionException; import jnpf.dmsPermission.DmsPermissionExecutionContext; import jnpf.dmsPermission.DmsPermissionGuard; import jnpf.dmsPermission.DmsPermissionOperation; import jnpf.dmsPermission.DmsPermissionProperties; import org.junit.jupiter.api.Test; import org.mockito.InOrder; import org.mockito.ArgumentCaptor; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; class DmsPersonalFolderServiceTest { private static final UUID DOCUMENT_ID = UUID.fromString("018f0000-0000-7000-8000-000000000701"); private static final UUID FILE_VERSION_ID = UUID.fromString("018f0000-0000-7000-8000-000000000702"); private static final Date VALID_TO = new Date(4102444800000L); private static final Date LATER_VALID_TO = new Date(4102531200000L); @Test void exposesExactlyThreeImmutableFolderDefinitions() { Fixture fixture = new Fixture(); List folders = fixture.service.folders(); assertEquals(3, folders.size()); assertEquals("BORROW", folders.get(0).getCode()); assertEquals("借阅", folders.get(0).getName()); assertEquals("DOWNLOAD", folders.get(1).getCode()); assertEquals("下载", folders.get(1).getName()); assertEquals("FAVORITE", folders.get(2).getCode()); assertEquals("收藏", folders.get(2).getName()); verify(fixture.currentIdentity).requireUser(); } @Test void loadsFilesOnlyForTheAuthenticatedTenantAndUser() { Fixture fixture = new Fixture(); List expected = Collections.singletonList(new DmsPersonalFolderFileVO()); when(fixture.mapper.selectFiles("tenant-1", "user-1", "FAVORITE", "report", 80)) .thenReturn(expected); List result = fixture.service.files( " favorite ", " report ", 80); assertSame(expected, result); verify(fixture.mapper).selectFiles("tenant-1", "user-1", "FAVORITE", "report", 80); } @Test void rejectsFoldersOutsideTheFixedSet() { Fixture fixture = new Fixture(); assertThrows(DmsPermissionException.class, () -> fixture.service.files("custom", null, null)); } @Test void favoritesTheCurrentVersionAfterReadPermissionValidation() { Fixture fixture = new Fixture(); DmsPersonalFolderFavoriteForm form = new DmsPersonalFolderFavoriteForm(); form.setDocumentId(DOCUMENT_ID); form.setFileVersionId(FILE_VERSION_ID); form.setOperationId(UUID.fromString("018f0000-0000-7000-8000-000000000703")); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version(DOCUMENT_ID, FILE_VERSION_ID))); when(fixture.mapper.insertFavoriteSnapshot( "tenant-1", "user-1", DOCUMENT_ID, FILE_VERSION_ID)).thenReturn(1); when(fixture.permissionGuard.execute(any(), any())).thenAnswer(invocation -> { DmsPermissionOperation operation = invocation.getArgument(1); return operation.execute(); }); DmsPersonalFolderAddResultVO result = fixture.service.addFavoriteSnapshot(form); assertEquals(1, result.getRequestedCount()); assertEquals(1, result.getAddedCount()); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(DmsPermissionExecutionContext.class); verify(fixture.permissionGuard).execute(contextCaptor.capture(), any()); DmsPermissionExecutionContext context = contextCaptor.getValue(); assertEquals(DmsResourceType.FILE_VERSION, context.getResourceType()); assertEquals(FILE_VERSION_ID, context.getResourceId()); assertEquals(DmsPermissionAction.READ, context.getActionCode()); assertEquals(form.getOperationId(), context.getOperationId()); verify(fixture.mapper).insertFavoriteSnapshot( "tenant-1", "user-1", DOCUMENT_ID, FILE_VERSION_ID); } @Test void doesNotFavoriteTheFileWhenReadPermissionIsDenied() { Fixture fixture = new Fixture(); DmsPersonalFolderFavoriteForm form = new DmsPersonalFolderFavoriteForm(); form.setDocumentId(DOCUMENT_ID); form.setFileVersionId(FILE_VERSION_ID); form.setOperationId(UUID.fromString("018f0000-0000-7000-8000-000000000703")); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version(DOCUMENT_ID, FILE_VERSION_ID))); when(fixture.permissionGuard.execute(any(), any())).thenThrow( DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED, "当前用户没有所需的 READ 权限")); assertThrows(DmsPermissionException.class, () -> fixture.service.addFavoriteSnapshot(form)); verify(fixture.mapper, never()).insertFavoriteSnapshot( "tenant-1", "user-1", DOCUMENT_ID, FILE_VERSION_ID); } @Test void addsTheApprovedBorrowVersionSnapshotToTheTargetUsersFolder() { Fixture fixture = new Fixture(); DmsPersonalFolderBorrowForm form = borrowForm(DOCUMENT_ID, FILE_VERSION_ID); DmsFileVersionEntity version = version(DOCUMENT_ID, FILE_VERSION_ID); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version)); when(fixture.mapper.insertBorrowSnapshots( "tenant-1", "borrower-1", form.getFiles())).thenReturn(1); DmsPersonalFolderAddResultVO result = fixture.service.addBorrowSnapshots( form, "jnpf-flowable"); assertEquals(1, result.getRequestedCount()); assertEquals(1, result.getAddedCount()); verify(fixture.mapper).insertBorrowSnapshots( "tenant-1", "borrower-1", form.getFiles()); verify(fixture.permissionGrantService).ensureWorkflowBorrowReadPermissions( eq("tenant-1"), startsWith("personal-folder-borrow:"), eq("borrower-1"), eq(Collections.singletonList(FILE_VERSION_ID)), eq(VALID_TO), eq("jnpf-flowable"), eq(Collections.emptyList())); } @Test void addsDownloadSnapshotAndGrantsTimeLimitedDownloadPermission() { Fixture fixture = new Fixture(); DmsPersonalFolderDownloadForm form = new DmsPersonalFolderDownloadForm(); form.setUserId(" downloader-1 "); DmsPersonalFolderDownloadForm.FileRelation file = new DmsPersonalFolderDownloadForm.FileRelation(); file.setDocumentId(DOCUMENT_ID); file.setFileVersionId(FILE_VERSION_ID); file.setValidTo(VALID_TO); form.setFiles(Collections.singletonList(file)); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version(DOCUMENT_ID, FILE_VERSION_ID))); when(fixture.mapper.insertDownloadSnapshots( "tenant-1", "downloader-1", form.getFiles())).thenReturn(1); DmsPersonalFolderAddResultVO result = fixture.service.addDownloadSnapshots( form, "jnpf-flowable"); assertEquals(1, result.getRequestedCount()); assertEquals(1, result.getAddedCount()); verify(fixture.permissionGrantService).ensureWorkflowDownloadPermissions( eq("tenant-1"), startsWith("personal-folder-download:"), eq("downloader-1"), eq(Collections.singletonList(FILE_VERSION_ID)), eq(VALID_TO), eq("jnpf-flowable"), eq(Collections.emptyList())); verify(fixture.mapper).insertDownloadSnapshots( "tenant-1", "downloader-1", form.getFiles()); } @Test void downloadApplicationsHaveIndependentSourcesButRetriesReuseSource() { Fixture fixture = new Fixture(); DmsPersonalFolderDownloadForm form = new DmsPersonalFolderDownloadForm(); form.setUserId("downloader-1"); DmsPersonalFolderDownloadForm.FileRelation file = new DmsPersonalFolderDownloadForm.FileRelation(); file.setDocumentId(DOCUMENT_ID); file.setFileVersionId(FILE_VERSION_ID); file.setValidTo(VALID_TO); form.setFiles(Collections.singletonList(file)); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version(DOCUMENT_ID, FILE_VERSION_ID))); form.setRequestId("application-1"); fixture.service.addDownloadSnapshots(form, "jnpf-flowable"); fixture.service.addDownloadSnapshots(form, "jnpf-flowable"); form.setRequestId("application-2"); fixture.service.addDownloadSnapshots(form, "jnpf-flowable"); ArgumentCaptor sources = ArgumentCaptor.forClass(String.class); org.mockito.Mockito.verify(fixture.permissionGrantService, org.mockito.Mockito.times(3)) .ensureWorkflowDownloadPermissions(eq("tenant-1"), sources.capture(), eq("downloader-1"), eq(Collections.singletonList(FILE_VERSION_ID)), eq(VALID_TO), eq("jnpf-flowable"), eq(Collections.emptyList())); assertEquals(sources.getAllValues().get(0), sources.getAllValues().get(1)); org.junit.jupiter.api.Assertions.assertNotEquals( sources.getAllValues().get(0), sources.getAllValues().get(2)); } @Test void grantsPrintPermissionWithoutAddingAPersonalFolderItem() { Fixture fixture = new Fixture(); DmsWorkflowPrintFilesGrantForm form = new DmsWorkflowPrintFilesGrantForm(); form.setUserId(" printer-1 "); DmsWorkflowPrintFilesGrantForm.FileRelation file = new DmsWorkflowPrintFilesGrantForm.FileRelation(); file.setDocumentId(DOCUMENT_ID); file.setFileVersionId(FILE_VERSION_ID); file.setValidTo(VALID_TO); form.setFiles(Collections.singletonList(file)); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version(DOCUMENT_ID, FILE_VERSION_ID))); DmsWorkflowPrintFilesGrantResultVO result = fixture.service.grantPrintPermissions( form, "jnpf-flowable"); assertEquals(1, result.getRequestedCount()); assertEquals(1, result.getAuthorizedCount()); verify(fixture.permissionGrantService).ensureWorkflowPrintPermissions( eq("tenant-1"), startsWith("workflow-print-files:"), eq("printer-1"), eq(Collections.singletonList(FILE_VERSION_ID)), eq(VALID_TO), eq("jnpf-flowable"), eq(Collections.emptyList())); verify(fixture.mapper, never()).insertDownloadSnapshots( eq("tenant-1"), eq("printer-1"), eq(form.getFiles())); } @Test void collapsesExactDuplicatesButReportsTheOriginalRequestCount() { Fixture fixture = new Fixture(); DmsPersonalFolderBorrowForm form = borrowForm(DOCUMENT_ID, FILE_VERSION_ID); DmsPersonalFolderBorrowForm.FileRelation duplicate = relation(DOCUMENT_ID, FILE_VERSION_ID); form.setFiles(Arrays.asList(form.getFiles().get(0), duplicate)); DmsFileVersionEntity version = version(DOCUMENT_ID, FILE_VERSION_ID); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version)); when(fixture.mapper.insertBorrowSnapshots( "tenant-1", "borrower-1", Collections.singletonList(form.getFiles().get(0)))) .thenReturn(1); DmsPersonalFolderAddResultVO result = fixture.service.addBorrowSnapshots( form, "jnpf-flowable"); assertEquals(2, result.getRequestedCount()); assertEquals(1, result.getAddedCount()); verify(fixture.mapper).insertBorrowSnapshots( "tenant-1", "borrower-1", Collections.singletonList(form.getFiles().get(0))); } @Test void rejectsAFileVersionThatDoesNotBelongToTheSuppliedDocument() { Fixture fixture = new Fixture(); DmsPersonalFolderBorrowForm form = borrowForm(DOCUMENT_ID, FILE_VERSION_ID); UUID otherDocumentId = UUID.fromString("018f0000-0000-7000-8000-000000000703"); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version(otherDocumentId, FILE_VERSION_ID))); assertThrows(DmsPermissionException.class, () -> fixture.service.addBorrowSnapshots(form, "jnpf-flowable")); verify(fixture.mapper, never()).insertBorrowSnapshots( "tenant-1", "borrower-1", form.getFiles()); } @Test void rejectsMultipleVersionsOfTheSameDocument() { Fixture fixture = new Fixture(); UUID secondVersionId = UUID.fromString("018f0000-0000-7000-8000-000000000704"); DmsPersonalFolderBorrowForm form = borrowForm(DOCUMENT_ID, FILE_VERSION_ID); form.setFiles(Arrays.asList( form.getFiles().get(0), relation(DOCUMENT_ID, secondVersionId))); assertThrows(DmsPermissionException.class, () -> fixture.service.addBorrowSnapshots(form, "jnpf-flowable")); verify(fixture.mapper, never()).selectReadableVersions( "tenant-1", Arrays.asList(FILE_VERSION_ID, secondVersionId)); } @Test void rejectsAnExpiredBorrowValidityBeforeGrantingPermission() { Fixture fixture = new Fixture(); DmsPersonalFolderBorrowForm form = borrowForm(DOCUMENT_ID, FILE_VERSION_ID); form.getFiles().get(0).setValidTo(new Date(0)); assertThrows(DmsPermissionException.class, () -> fixture.service.addBorrowSnapshots(form, "jnpf-flowable")); verifyNoInteractions(fixture.permissionGrantService); } @Test void grantsPermanentReadPermissionWhenFileValidityIsNull() { Fixture fixture = new Fixture(); DmsPersonalFolderBorrowForm form = borrowForm(DOCUMENT_ID, FILE_VERSION_ID); form.getFiles().get(0).setValidTo(null); when(fixture.mapper.selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID))) .thenReturn(Collections.singletonList(version(DOCUMENT_ID, FILE_VERSION_ID))); when(fixture.mapper.insertBorrowSnapshots( "tenant-1", "borrower-1", form.getFiles())).thenReturn(1); DmsPersonalFolderAddResultVO result = fixture.service.addBorrowSnapshots( form, "jnpf-flowable"); assertEquals(1, result.getAddedCount()); verify(fixture.permissionGrantService).ensureWorkflowBorrowReadPermissions( eq("tenant-1"), startsWith("personal-folder-borrow:"), eq("borrower-1"), eq(Collections.singletonList(FILE_VERSION_ID)), eq(null), eq("jnpf-flowable"), eq(Collections.emptyList())); verify(fixture.mapper).insertBorrowSnapshots( "tenant-1", "borrower-1", form.getFiles()); } @Test void grantsEachFileUsingItsOwnValidityInDeterministicOrder() { Fixture fixture = new Fixture(); UUID secondDocumentId = UUID.fromString("018f0000-0000-7000-8000-000000000705"); UUID secondVersionId = UUID.fromString("018f0000-0000-7000-8000-000000000706"); DmsPersonalFolderBorrowForm form = new DmsPersonalFolderBorrowForm(); form.setUserId("borrower-1"); form.setFiles(Arrays.asList( relation(secondDocumentId, secondVersionId, LATER_VALID_TO), relation(DOCUMENT_ID, FILE_VERSION_ID, VALID_TO))); when(fixture.mapper.selectReadableVersions( "tenant-1", Arrays.asList(secondVersionId, FILE_VERSION_ID))) .thenReturn(Arrays.asList( version(secondDocumentId, secondVersionId), version(DOCUMENT_ID, FILE_VERSION_ID))); when(fixture.mapper.insertBorrowSnapshots( "tenant-1", "borrower-1", form.getFiles())).thenReturn(2); DmsPersonalFolderAddResultVO result = fixture.service.addBorrowSnapshots( form, "jnpf-flowable"); assertEquals(2, result.getAddedCount()); InOrder grants = inOrder(fixture.permissionGrantService); grants.verify(fixture.permissionGrantService).ensureWorkflowBorrowReadPermissions( eq("tenant-1"), startsWith("personal-folder-borrow:"), eq("borrower-1"), eq(Collections.singletonList(FILE_VERSION_ID)), eq(VALID_TO), eq("jnpf-flowable"), eq(Collections.emptyList())); grants.verify(fixture.permissionGrantService).ensureWorkflowBorrowReadPermissions( eq("tenant-1"), startsWith("personal-folder-borrow:"), eq("borrower-1"), eq(Collections.singletonList(secondVersionId)), eq(LATER_VALID_TO), eq("jnpf-flowable"), eq(Collections.emptyList())); } @Test void rejectsDifferentValiditiesForTheSameFileVersion() { Fixture fixture = new Fixture(); DmsPersonalFolderBorrowForm form = borrowForm(DOCUMENT_ID, FILE_VERSION_ID); form.setFiles(Arrays.asList( form.getFiles().get(0), relation(DOCUMENT_ID, FILE_VERSION_ID, LATER_VALID_TO))); assertThrows(DmsPermissionException.class, () -> fixture.service.addBorrowSnapshots(form, "jnpf-flowable")); verify(fixture.mapper, never()).selectReadableVersions( "tenant-1", Collections.singletonList(FILE_VERSION_ID)); verifyNoInteractions(fixture.permissionGrantService); } private static DmsPersonalFolderBorrowForm borrowForm( UUID documentId, UUID fileVersionId) { DmsPersonalFolderBorrowForm form = new DmsPersonalFolderBorrowForm(); form.setUserId(" borrower-1 "); form.setFiles(Collections.singletonList(relation(documentId, fileVersionId))); return form; } private static DmsPersonalFolderBorrowForm.FileRelation relation( UUID documentId, UUID fileVersionId) { return relation(documentId, fileVersionId, VALID_TO); } private static DmsPersonalFolderBorrowForm.FileRelation relation( UUID documentId, UUID fileVersionId, Date validTo) { DmsPersonalFolderBorrowForm.FileRelation relation = new DmsPersonalFolderBorrowForm.FileRelation(); relation.setDocumentId(documentId); relation.setFileVersionId(fileVersionId); relation.setValidTo(validTo); return relation; } private static DmsFileVersionEntity version(UUID documentId, UUID fileVersionId) { DmsFileVersionEntity version = new DmsFileVersionEntity(); version.setId(fileVersionId); version.setDocumentId(documentId); return version; } private static class Fixture { private final DmsCurrentIdentity currentIdentity = mock(DmsCurrentIdentity.class); private final DmsPermissionProperties properties = mock(DmsPermissionProperties.class); private final DmsPersonalFolderMapper mapper = mock(DmsPersonalFolderMapper.class); private final DmsPermissionGrantService permissionGrantService = mock(DmsPermissionGrantService.class); private final DmsPermissionGuard permissionGuard = mock(DmsPermissionGuard.class); private final UserInfo user = new UserInfo(); private final DmsPersonalFolderService service = new DmsPersonalFolderService(currentIdentity, properties, mapper, permissionGrantService, permissionGuard); private Fixture() { user.setUserId("user-1"); when(currentIdentity.requireUser()).thenReturn(user); when(currentIdentity.tenantId(user)).thenReturn("tenant-1"); when(properties.getDefaultPageSize()).thenReturn(50); when(properties.getMaxPageSize()).thenReturn(100); } } }