package jnpf.dmsService;
|
|
import jnpf.dmsEntity.permission.entity.DmsAccessExclusionEntity;
|
import jnpf.dmsEntity.permission.entity.DmsPermissionResourceEntity;
|
import jnpf.dmsEntity.permission.enums.DmsResourceType;
|
import jnpf.dmsEntity.permission.enums.DmsSourceType;
|
import jnpf.dmsEntity.permission.form.AccessExclusionBatchForm;
|
import jnpf.dmsEntity.permission.form.AccessExclusionItemForm;
|
import jnpf.dmsEntity.permission.form.AccessExclusionRevokeForm;
|
import jnpf.dmsEntity.permission.vo.DmsAccessExclusionVO;
|
import jnpf.dmsEntity.permission.vo.DmsCursorPageVO;
|
import jnpf.dmsIntegration.audit.DmsAuditAdapter;
|
import jnpf.dmsMapper.DmsAccessExclusionMapper;
|
import jnpf.dmsMapper.DmsPermissionResourceMapper;
|
import jnpf.dmsPermission.DmsCurrentIdentity;
|
import jnpf.dmsPermission.DmsCursorCodec;
|
import jnpf.dmsPermission.DmsPermissionError;
|
import jnpf.dmsPermission.DmsPermissionException;
|
import jnpf.dmsPermission.DmsPermissionProperties;
|
import jnpf.dmsPermission.DmsPrincipalValidator;
|
import jnpf.dmsPermission.DmsUserDisplayNameResolver;
|
import org.junit.jupiter.api.Test;
|
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.assertFalse;
|
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.anyCollection;
|
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
class DmsAccessExclusionServiceTest {
|
private static final String TENANT_ID = "tenant-1";
|
private static final String ADMIN_ID = "admin-1";
|
private static final String USER_ID = "user-1";
|
private static final String SOURCE_ID = "exclude-command-1";
|
|
@Test
|
void createsWholeExclusionSetWithOneBatchInsert() {
|
Fixture fixture = new Fixture();
|
UUID documentId = UUID.randomUUID();
|
AccessExclusionBatchForm form = form(item(DmsResourceType.DOCUMENT, documentId, "reason"));
|
DmsAccessExclusionEntity created = exclusion(DmsResourceType.DOCUMENT, documentId, "reason");
|
when(fixture.exclusionMapper.selectBySource(TENANT_ID, "MANUAL", SOURCE_ID))
|
.thenReturn(Collections.emptyList(), Collections.singletonList(created));
|
when(fixture.resourceMapper.selectByTenantAndIds(TENANT_ID, Collections.singletonList(documentId)))
|
.thenReturn(Collections.singletonList(resource(DmsResourceType.DOCUMENT, documentId)));
|
when(fixture.exclusionMapper.insertBatch(any())).thenReturn(1);
|
|
fixture.service.create(form);
|
|
ArgumentCaptor<List<DmsAccessExclusionEntity>> captor = ArgumentCaptor.forClass(List.class);
|
verify(fixture.exclusionMapper).insertBatch(captor.capture());
|
assertEquals(1, captor.getValue().size());
|
DmsAccessExclusionEntity inserted = captor.getValue().get(0);
|
assertEquals("READ", inserted.getActionCode());
|
assertEquals(USER_ID, inserted.getUserId());
|
assertEquals(ADMIN_ID, inserted.getExcludedById());
|
verify(fixture.principalValidator).validateUsers(anyCollection());
|
verify(fixture.auditAdapter).dataChange("CREATE", "创建 DMS 用户读取排除",
|
"dms_access_exclusions", created.getId().toString(), "reason", null);
|
}
|
|
@Test
|
void returnsExistingSetForExactSourceRetry() {
|
Fixture fixture = new Fixture();
|
UUID documentId = UUID.randomUUID();
|
AccessExclusionBatchForm form = form(item(DmsResourceType.DOCUMENT, documentId, "reason"));
|
when(fixture.exclusionMapper.selectBySource(TENANT_ID, "MANUAL", SOURCE_ID))
|
.thenReturn(Collections.singletonList(exclusion(DmsResourceType.DOCUMENT, documentId, "reason")));
|
|
fixture.service.create(form);
|
|
verify(fixture.exclusionMapper, never()).insertBatch(any());
|
verify(fixture.resourceMapper, never()).selectByTenantAndIds(any(), any());
|
verify(fixture.principalValidator, never()).validateUsers(anyCollection());
|
}
|
|
@Test
|
void rejectsDifferentSetUsingSameSourceWithoutWriting() {
|
Fixture fixture = new Fixture();
|
UUID documentId = UUID.randomUUID();
|
AccessExclusionBatchForm form = form(item(DmsResourceType.DOCUMENT, documentId, "new reason"));
|
when(fixture.exclusionMapper.selectBySource(TENANT_ID, "MANUAL", SOURCE_ID))
|
.thenReturn(Collections.singletonList(exclusion(DmsResourceType.DOCUMENT, documentId,
|
"old reason")));
|
|
DmsPermissionException error = assertThrows(DmsPermissionException.class,
|
() -> fixture.service.create(form));
|
|
assertEquals(DmsPermissionError.SOURCE_CONFLICT, error.getError());
|
verify(fixture.exclusionMapper, never()).insertBatch(any());
|
}
|
|
@Test
|
void rejectsFolderExclusion() {
|
Fixture fixture = new Fixture();
|
|
DmsPermissionException error = assertThrows(DmsPermissionException.class,
|
() -> fixture.service.create(form(item(DmsResourceType.FOLDER, UUID.randomUUID(), "reason"))));
|
|
assertEquals(DmsPermissionError.INVALID_REQUEST, error.getError());
|
verify(fixture.exclusionMapper, never()).lockSource(any(), any(), any());
|
}
|
|
@Test
|
void revokesWholeSetInOneUpdate() {
|
Fixture fixture = new Fixture();
|
DmsAccessExclusionEntity first = exclusion(DmsResourceType.DOCUMENT, UUID.randomUUID(), "reason");
|
DmsAccessExclusionEntity second = exclusion(DmsResourceType.FILE_VERSION, UUID.randomUUID(), "reason");
|
List<UUID> ids = Arrays.asList(first.getId(), second.getId());
|
ids.sort(java.util.Comparator.comparing(UUID::toString));
|
when(fixture.exclusionMapper.selectByTenantAndIdsForUpdate(TENANT_ID, ids))
|
.thenReturn(Arrays.asList(first, second));
|
when(fixture.exclusionMapper.revokeBatch(TENANT_ID, ids, "USER", ADMIN_ID, "revoke"))
|
.thenReturn(2);
|
DmsAccessExclusionEntity revokedFirst = exclusion(
|
DmsResourceType.valueOf(first.getResourceType()), first.getResourceId(), "reason");
|
revokedFirst.setId(first.getId());
|
revokedFirst.setRevokedAt(new Date());
|
DmsAccessExclusionEntity revokedSecond = exclusion(
|
DmsResourceType.valueOf(second.getResourceType()), second.getResourceId(), "reason");
|
revokedSecond.setId(second.getId());
|
revokedSecond.setRevokedAt(new Date());
|
when(fixture.exclusionMapper.selectByTenantAndIds(TENANT_ID, ids))
|
.thenReturn(Arrays.asList(revokedFirst, revokedSecond));
|
AccessExclusionRevokeForm form = new AccessExclusionRevokeForm();
|
form.setExclusionIds(ids);
|
form.setReason("revoke");
|
|
fixture.service.revoke(form);
|
|
verify(fixture.exclusionMapper).revokeBatch(TENANT_ID, ids, "USER", ADMIN_ID, "revoke");
|
}
|
|
@Test
|
void rejectsSecondRevocationWithoutUpdating() {
|
Fixture fixture = new Fixture();
|
DmsAccessExclusionEntity existing = exclusion(DmsResourceType.DOCUMENT, UUID.randomUUID(), "reason");
|
existing.setRevokedAt(new Date());
|
List<UUID> ids = Collections.singletonList(existing.getId());
|
when(fixture.exclusionMapper.selectByTenantAndIdsForUpdate(TENANT_ID, ids))
|
.thenReturn(Collections.singletonList(existing));
|
AccessExclusionRevokeForm form = new AccessExclusionRevokeForm();
|
form.setExclusionIds(ids);
|
form.setReason("revoke again");
|
|
DmsPermissionException error = assertThrows(DmsPermissionException.class,
|
() -> fixture.service.revoke(form));
|
|
assertEquals(DmsPermissionError.EXCLUSION_ALREADY_REVOKED, error.getError());
|
verify(fixture.exclusionMapper, never()).revokeBatch(any(), any(), any(), any(), any());
|
}
|
|
@Test
|
void queriesExclusionsWithTenantScopedKeysetPage() {
|
Fixture fixture = new Fixture();
|
UUID resourceId = UUID.randomUUID();
|
DmsAccessExclusionEntity first = exclusion(DmsResourceType.DOCUMENT, resourceId, "first");
|
DmsAccessExclusionEntity second = exclusion(DmsResourceType.DOCUMENT, resourceId, "second");
|
when(fixture.exclusionMapper.selectPage(TENANT_ID, resourceId, USER_ID, null, null, 2))
|
.thenReturn(Arrays.asList(first, second));
|
when(fixture.userDisplayNameResolver.resolve(anyCollection()))
|
.thenReturn(Collections.singletonMap(USER_ID, "运营用户(yunying1)"));
|
|
DmsCursorPageVO<DmsAccessExclusionVO> result = fixture.service.page(
|
resourceId, " " + USER_ID + " ", null, 1);
|
|
assertEquals(1, result.getList().size());
|
assertEquals(first.getId(), result.getList().get(0).getId());
|
assertEquals("运营用户(yunying1)", result.getList().get(0).getUserName());
|
assertTrue(result.isHasMore());
|
assertFalse(result.getNextCursor() == null || result.getNextCursor().isEmpty());
|
verify(fixture.currentIdentity).requirePermissionAdministrator();
|
verify(fixture.userDisplayNameResolver).resolve(anyCollection());
|
}
|
|
private AccessExclusionBatchForm form(AccessExclusionItemForm item) {
|
AccessExclusionBatchForm result = new AccessExclusionBatchForm();
|
result.setSourceType(DmsSourceType.MANUAL);
|
result.setSourceId(SOURCE_ID);
|
result.setItems(Collections.singletonList(item));
|
return result;
|
}
|
|
private AccessExclusionItemForm item(DmsResourceType type, UUID id, String reason) {
|
AccessExclusionItemForm result = new AccessExclusionItemForm();
|
result.setResourceType(type);
|
result.setResourceId(id);
|
result.setUserId(USER_ID);
|
result.setExcludeReason(reason);
|
return result;
|
}
|
|
private DmsPermissionResourceEntity resource(DmsResourceType type, UUID id) {
|
DmsPermissionResourceEntity result = new DmsPermissionResourceEntity();
|
result.setId(id);
|
result.setResourceType(type.name());
|
return result;
|
}
|
|
private DmsAccessExclusionEntity exclusion(DmsResourceType type, UUID resourceId, String reason) {
|
DmsAccessExclusionEntity result = new DmsAccessExclusionEntity();
|
result.setId(UUID.randomUUID());
|
result.setTenantId(TENANT_ID);
|
result.setResourceType(type.name());
|
result.setResourceId(resourceId);
|
result.setUserId(USER_ID);
|
result.setActionCode("READ");
|
result.setSourceType("MANUAL");
|
result.setSourceId(SOURCE_ID);
|
result.setValidFrom(new Date(System.currentTimeMillis() - 60_000));
|
result.setExcludeReason(reason);
|
result.setExcludedByType("USER");
|
result.setExcludedById(ADMIN_ID);
|
result.setRevision(0L);
|
result.setCreatedAt(new Date());
|
result.setUpdatedAt(new Date());
|
return result;
|
}
|
|
private static class Fixture {
|
private final DmsCurrentIdentity currentIdentity = mock(DmsCurrentIdentity.class);
|
private final DmsPermissionProperties properties = new DmsPermissionProperties();
|
private final DmsPrincipalValidator principalValidator = mock(DmsPrincipalValidator.class);
|
private final DmsPermissionResourceMapper resourceMapper = mock(DmsPermissionResourceMapper.class);
|
private final DmsAccessExclusionMapper exclusionMapper = mock(DmsAccessExclusionMapper.class);
|
private final DmsCursorCodec cursorCodec = new DmsCursorCodec();
|
private final DmsAuditAdapter auditAdapter = mock(DmsAuditAdapter.class);
|
private final DmsUserDisplayNameResolver userDisplayNameResolver = mock(DmsUserDisplayNameResolver.class);
|
private final DmsAccessExclusionService service = new DmsAccessExclusionService(currentIdentity,
|
properties, principalValidator, resourceMapper, exclusionMapper, cursorCodec, auditAdapter,
|
userDisplayNameResolver);
|
|
private Fixture() {
|
when(currentIdentity.tenantId()).thenReturn(TENANT_ID);
|
when(currentIdentity.userId()).thenReturn(ADMIN_ID);
|
when(userDisplayNameResolver.resolve(anyCollection())).thenReturn(Collections.emptyMap());
|
}
|
}
|
}
|