package jnpf.dmsService; import jnpf.base.UserInfo; import jnpf.dmsEntity.permission.form.UserFolderPermissionTreeForm; import jnpf.dmsEntity.permission.model.DmsSubjectSet; import jnpf.dmsEntity.permission.model.DmsUserFolderPermissionTreeRow; import jnpf.dmsEntity.permission.vo.DmsCursorPageVO; import jnpf.dmsEntity.permission.vo.DmsUserFolderPermissionTreeNodeVO; import jnpf.dmsMapper.DmsUserFolderPermissionTreeMapper; import jnpf.dmsPermission.DmsCurrentIdentity; import jnpf.dmsPermission.DmsFileCenterCursorCodec; import jnpf.dmsPermission.DmsPermissionError; import jnpf.dmsPermission.DmsPermissionException; import jnpf.dmsPermission.DmsPermissionGroupMembershipResolver; import jnpf.dmsPermission.DmsPermissionProperties; import jnpf.dmsPermission.DmsUserFolderPermissionPrincipal; import jnpf.dmsPermission.DmsUserFolderPermissionTreeQuery; import jnpf.dmsPermission.DmsUserSubjectBatchResolver; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class DmsUserFolderPermissionTreeServiceTest { private final UUID parentId = UUID.fromString("018f0000-0000-7000-8000-000000000301"); private final UUID firstId = UUID.fromString("018f0000-0000-7000-8000-000000000302"); private final UUID secondId = UUID.fromString("018f0000-0000-7000-8000-000000000303"); private final DmsCurrentIdentity currentIdentity = mock(DmsCurrentIdentity.class); private final DmsPermissionProperties properties = properties(); private final DmsFileCenterCursorCodec cursorCodec = new DmsFileCenterCursorCodec(properties); private final DmsUserSubjectBatchResolver subjectResolver = mock(DmsUserSubjectBatchResolver.class); private final DmsPermissionGroupMembershipResolver groupResolver = mock(DmsPermissionGroupMembershipResolver.class); private final DmsUserFolderPermissionTreeMapper mapper = mock(DmsUserFolderPermissionTreeMapper.class); private final DmsUserFolderPermissionTreeService service = new DmsUserFolderPermissionTreeService( currentIdentity, properties, cursorCodec, subjectResolver, groupResolver, mapper); private final UserInfo actor = new UserInfo(); @Test void currentUserPrintScopeUsesAllPrincipalsWithoutAdministratorCheckOrTreePagination() { DmsSubjectSet subject = subject("admin-1"); subject.getDirectOrgIds().add("org"); subject.getAncestorOrgIds().add("parent-org"); subject.getPositionIds().add("position"); subject.getRoleIds().add("role"); subject.getPermissionGroupIds().add("group"); when(subjectResolver.resolve("tenant-1", Collections.singletonList("admin-1"))) .thenReturn(Collections.singletonList(subject)); when(mapper.selectPrintFolders(any())).thenReturn(Arrays.asList( row(firstId, "A", 1L, null, null, false), row(secondId, "B", 2L, null, null, false))); assertEquals(Arrays.asList(firstId, secondId), service.currentUserPrintFolderIds()); verify(currentIdentity, never()).requirePermissionAdministrator(); verify(mapper, never()).selectTree(any()); verify(groupResolver).resolveAll(Collections.singletonList(subject), 1000); ArgumentCaptor q = ArgumentCaptor.forClass(DmsUserFolderPermissionTreeQuery.class); verify(mapper).selectPrintFolders(q.capture()); assertEquals("tenant-1", q.getValue().getTenantId()); assertEquals(6, q.getValue().getPrincipals().size()); } @Test void personalWorkflowVersionsUseOnlyAuthenticatedUserAndIndependentQuery() { when(mapper.selectWorkflowPrintVersions("tenant-1", "admin-1")).thenReturn( Collections.singletonList(row(firstId, null, 0L, null, null, false))); assertEquals(Collections.singletonList(firstId), service.currentUserWorkflowPrintVersionIds()); verify(mapper).selectWorkflowPrintVersions("tenant-1", "admin-1"); verify(mapper, never()).selectPrintFolders(any()); verify(currentIdentity, never()).requirePermissionAdministrator(); verify(subjectResolver, never()).resolve(any(), any()); verify(groupResolver, never()).resolveAll(any(List.class), anyInt()); } @Test void currentUserScopeRejectsAnotherUsersResolvedSubjects() { when(subjectResolver.resolve("tenant-1", Collections.singletonList("admin-1"))) .thenReturn(Collections.singletonList(subject("other-user"))); assertThrows(DmsPermissionException.class, () -> service.currentUserPrintFolderIds()); verify(mapper, never()).selectPrintFolders(any()); } @BeforeEach void setUp() { actor.setUserId("admin-1"); actor.setTenantId("tenant-1"); when(currentIdentity.requireUser()).thenReturn(actor); when(currentIdentity.tenantId(actor)).thenReturn("tenant-1"); } @Test void returnsPermissionAnnotatedUnionTreeAndPaginatesByFolder() { DmsSubjectSet user2 = subject("user-2"); user2.getDirectOrgIds().add("org-direct"); user2.getAncestorOrgIds().addAll(Arrays.asList("org-direct", "org-parent")); user2.getPositionIds().add("position-1"); user2.getRoleIds().add("role-1"); user2.getPermissionGroupIds().add("permission-group-1"); DmsSubjectSet user1 = subject("user-1"); when(subjectResolver.resolve("tenant-1", Arrays.asList("user-2", "user-1"))) .thenReturn(Arrays.asList(user2, user1)); when(mapper.selectTree(any())).thenReturn(Arrays.asList( row(firstId, "A", 1024L, "user-1", "PRINT", true), row(firstId, "A", 1024L, "user-2", "READ", true), row(firstId, "A", 1024L, "user-1", "READ", true), row(secondId, "B", 2048L, null, null, false))); UserFolderPermissionTreeForm form = new UserFolderPermissionTreeForm(); form.setUserIds(Arrays.asList("user-2", " user-1 ", "user-2")); form.setParentId(parentId); form.setPageSize(1); DmsCursorPageVO result = service.tree(form); assertEquals(1, result.getList().size()); assertTrue(result.isHasMore()); assertNotNull(result.getNextCursor()); DmsUserFolderPermissionTreeNodeVO node = result.getList().get(0); assertEquals(firstId, node.getId()); assertTrue(node.isHasChildren()); assertEquals(Arrays.asList("user-2", "user-1"), node.getUserPermissions().stream() .map(value -> value.getUserId()).collect(Collectors.toList())); assertEquals(Collections.singletonList("READ"), node.getUserPermissions().get(0).getActions()); assertEquals(Arrays.asList("READ", "PRINT"), node.getUserPermissions().get(1).getActions()); String fingerprint = cursorCodec.fingerprint("user-2\nuser-1"); DmsFileCenterCursorCodec.Cursor cursor = cursorCodec.decode(result.getNextCursor(), "USER_FOLDER_PERMISSION_TREE", "tenant-1", "admin-1", parentId, fingerprint); assertEquals(1024L, cursor.getSortCode()); assertEquals(firstId, cursor.getId()); verify(groupResolver).resolveAll(Arrays.asList(user2, user1), 1000); ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(DmsUserFolderPermissionTreeQuery.class); verify(mapper).selectTree(queryCaptor.capture()); DmsUserFolderPermissionTreeQuery query = queryCaptor.getValue(); assertEquals(2, query.getLimit()); assertEquals(parentId, query.getParentId()); assertEquals("tenant-1", query.getTenantId()); assertTrue(hasPrincipal(query.getPrincipals(), "user-2", "USER", "user-2")); assertTrue(hasPrincipal(query.getPrincipals(), "user-2", "ORG_DIRECT", "org-direct")); assertTrue(hasPrincipal(query.getPrincipals(), "user-2", "ORG_ANCESTOR", "org-parent")); assertTrue(hasPrincipal(query.getPrincipals(), "user-2", "POSITION", "position-1")); assertTrue(hasPrincipal(query.getPrincipals(), "user-2", "ROLE", "role-1")); assertTrue(hasPrincipal(query.getPrincipals(), "user-2", "GROUP", "permission-group-1")); } @Test void keepsNavigationAncestorsWithoutClaimingPermissions() { DmsSubjectSet subject = subject("user-1"); when(subjectResolver.resolve("tenant-1", Collections.singletonList("user-1"))) .thenReturn(Collections.singletonList(subject)); when(mapper.selectTree(any())).thenReturn(Collections.singletonList( row(firstId, "Navigation", 1024L, null, null, true))); UserFolderPermissionTreeForm form = new UserFolderPermissionTreeForm(); form.setUserIds(Collections.singletonList("user-1")); DmsCursorPageVO result = service.tree(form); assertEquals(1, result.getList().size()); assertTrue(result.getList().get(0).getUserPermissions().isEmpty()); assertFalse(result.isHasMore()); } @Test void rejectsInvalidInputBeforeResolvingSubjects() { UserFolderPermissionTreeForm empty = new UserFolderPermissionTreeForm(); empty.setUserIds(Collections.emptyList()); assertThrows(DmsPermissionException.class, () -> service.tree(empty)); UserFolderPermissionTreeForm invalidPage = new UserFolderPermissionTreeForm(); invalidPage.setUserIds(Collections.singletonList("user-1")); invalidPage.setPageSize(101); assertThrows(DmsPermissionException.class, () -> service.tree(invalidPage)); verify(subjectResolver, never()).resolve(any(), any()); verify(groupResolver, never()).resolveAll(any(List.class), anyInt()); verify(mapper, never()).selectTree(any()); } @Test void rejectsNonAdministratorsBeforeReadingRequestContextOrCallingDownstreamServices() { doThrow(DmsPermissionException.of(DmsPermissionError.ADMIN_REQUIRED, "admin required")) .when(currentIdentity).requirePermissionAdministrator(); UserFolderPermissionTreeForm form = new UserFolderPermissionTreeForm(); form.setUserIds(Collections.singletonList("user-1")); DmsPermissionException error = assertThrows(DmsPermissionException.class, () -> service.tree(form)); assertEquals(DmsPermissionError.ADMIN_REQUIRED, error.getError()); verify(currentIdentity, never()).requireUser(); verify(subjectResolver, never()).resolve(any(), any()); verify(groupResolver, never()).resolveAll(any(List.class), anyInt()); verify(mapper, never()).selectTree(any()); } private DmsSubjectSet subject(String userId) { DmsSubjectSet value = new DmsSubjectSet(); value.setUserId(userId); return value; } private DmsUserFolderPermissionTreeRow row(UUID id, String name, long sortCode, String userId, String action, boolean hasChildren) { DmsUserFolderPermissionTreeRow value = new DmsUserFolderPermissionTreeRow(); value.setId(id); value.setParentId(parentId); value.setName(name); value.setSortCode(sortCode); value.setHasChildren(hasChildren); value.setUserId(userId); value.setActionCode(action); return value; } private boolean hasPrincipal(List values, String userId, String kind, String principalId) { return values.stream().anyMatch(value -> userId.equals(value.getUserId()) && kind.equals(value.getPrincipalKind()) && principalId.equals(value.getPrincipalId())); } private static DmsPermissionProperties properties() { DmsPermissionProperties value = new DmsPermissionProperties(); value.setDefaultPageSize(20); value.setMaxPageSize(100); value.setMaxUserFolderTreeUsers(50); value.setMaxPermissionGroupScan(1000); value.setCursorSigningSecret("0123456789abcdef0123456789abcdef"); return value; } }