From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master
---
jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsPermission/DmsPermissionGuardTest.java | 197 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 197 insertions(+), 0 deletions(-)
diff --git a/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsPermission/DmsPermissionGuardTest.java b/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsPermission/DmsPermissionGuardTest.java
new file mode 100644
index 0000000..c850c9a
--- /dev/null
+++ b/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsPermission/DmsPermissionGuardTest.java
@@ -0,0 +1,197 @@
+package jnpf.dmsPermission;
+
+import jnpf.base.UserInfo;
+import jnpf.dmsEntity.permission.entity.DmsFilePermissionEntity;
+import jnpf.dmsEntity.permission.entity.DmsPermissionUsageEntity;
+import jnpf.dmsEntity.permission.enums.DmsPermissionAction;
+import jnpf.dmsEntity.permission.enums.DmsResourceType;
+import jnpf.dmsEntity.permission.form.PermissionDecisionForm;
+import jnpf.dmsEntity.permission.model.DmsResourcePath;
+import jnpf.dmsEntity.permission.model.DmsSubjectSet;
+import jnpf.dmsEntity.permission.vo.DmsPermissionDecisionVO;
+import jnpf.dmsMapper.DmsFilePermissionMapper;
+import jnpf.dmsMapper.DmsPermissionUsageMapper;
+import jnpf.dmsService.DmsPermissionDecisionService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+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 DmsPermissionGuardTest {
+ private final DmsCurrentIdentity currentIdentity = mock(DmsCurrentIdentity.class);
+ private final DmsPermissionDecisionService decisionService = mock(DmsPermissionDecisionService.class);
+ private final DmsFilePermissionMapper permissionMapper = mock(DmsFilePermissionMapper.class);
+ private final DmsPermissionUsageMapper usageMapper = mock(DmsPermissionUsageMapper.class);
+ private final DmsResourceResolver resourceResolver = mock(DmsResourceResolver.class);
+ private final DmsSubjectResolver subjectResolver = mock(DmsSubjectResolver.class);
+ private final DmsPermissionGroupMembershipResolver groupResolver = mock(DmsPermissionGroupMembershipResolver.class);
+ private final DmsPermissionMatcher matcher = mock(DmsPermissionMatcher.class);
+ private final DmsAccessExclusionResolver exclusionResolver = mock(DmsAccessExclusionResolver.class);
+ private final DmsPermissionGuard guard = new DmsPermissionGuard(currentIdentity, decisionService,
+ permissionMapper, usageMapper, resourceResolver, subjectResolver, groupResolver, matcher,
+ exclusionResolver);
+ private final UUID permissionId = UUID.randomUUID();
+ private final UUID resourceId = UUID.randomUUID();
+ private final UserInfo user = new UserInfo();
+
+ @BeforeEach
+ void setUp() {
+ user.setTenantId("tenant-1");
+ user.setUserId("user-1");
+ when(currentIdentity.requireUser()).thenReturn(user);
+ when(currentIdentity.tenantId(user)).thenReturn("tenant-1");
+ when(subjectResolver.resolve(user)).thenReturn(mock(DmsSubjectSet.class));
+ when(resourceResolver.resolve(any(), any(), any())).thenReturn(mock(DmsResourcePath.class));
+ when(exclusionResolver.isExcluded(any(), any(), any())).thenReturn(false);
+ when(matcher.matches(any(), any(), any())).thenReturn(true);
+ DmsPermissionDecisionVO decision = new DmsPermissionDecisionVO();
+ decision.setAllowed(true);
+ decision.setMatchedPermissionId(permissionId);
+ when(decisionService.evaluate(any(PermissionDecisionForm.class))).thenReturn(decision);
+ }
+
+ @Test
+ void rejectsPermissionRevokedWhileWaitingForLock() {
+ DmsFilePermissionEntity permission = activePermission();
+ permission.setRevokedAt(new Date());
+ when(permissionMapper.selectByTenantAndIdForUpdate("tenant-1", permissionId)).thenReturn(permission);
+ DmsPermissionOperation<String> operation = mockOperation();
+
+ DmsPermissionException error = assertThrows(DmsPermissionException.class,
+ () -> guard.execute(context(), operation));
+
+ assertEquals(DmsPermissionError.PERMISSION_DENIED, error.getError());
+ verify(operation, never()).execute();
+ }
+
+ @Test
+ void rejectsPermissionExhaustedWhileWaitingForLock() {
+ DmsFilePermissionEntity permission = activePermission();
+ permission.setMaxUseCount(1L);
+ permission.setUsedCount(1L);
+ when(permissionMapper.selectByTenantAndIdForUpdate("tenant-1", permissionId)).thenReturn(permission);
+ DmsPermissionOperation<String> operation = mockOperation();
+
+ DmsPermissionException error = assertThrows(DmsPermissionException.class,
+ () -> guard.execute(context(), operation));
+
+ assertEquals(DmsPermissionError.PERMISSION_EXHAUSTED, error.getError());
+ verify(operation, never()).execute();
+ }
+
+ @Test
+ void recordsLimitedUseAfterBusinessOperationSucceeds() {
+ DmsFilePermissionEntity permission = activePermission();
+ permission.setMaxUseCount(2L);
+ when(permissionMapper.selectByTenantAndIdForUpdate("tenant-1", permissionId)).thenReturn(permission);
+ DmsPermissionOperation<String> operation = mockOperation();
+ when(operation.execute()).thenReturn("created");
+ DmsPermissionExecutionContext context = context();
+
+ String result = guard.execute(context, operation);
+
+ assertEquals("created", result);
+ org.mockito.ArgumentCaptor<DmsPermissionUsageEntity> captor =
+ org.mockito.ArgumentCaptor.forClass(DmsPermissionUsageEntity.class);
+ verify(usageMapper).insert(captor.capture());
+ DmsPermissionUsageEntity usage = captor.getValue();
+ assertEquals(permissionId, usage.getPermissionId());
+ assertEquals(resourceId, usage.getActualResourceId());
+ assertEquals(context.getOperationId(), usage.getOperationId());
+ assertEquals("READ", usage.getActionCode());
+ }
+
+ @Test
+ void replaysPriorUseWithoutExecutingBusinessOperationAgain() {
+ DmsPermissionExecutionContext context = context();
+ DmsPermissionUsageEntity prior = new DmsPermissionUsageEntity();
+ prior.setPermissionId(permissionId);
+ prior.setUserId("user-1");
+ prior.setActualResourceType("DOCUMENT");
+ prior.setActualResourceId(resourceId);
+ prior.setActionCode("READ");
+ prior.setOperationId(context.getOperationId());
+ when(usageMapper.selectByOperation("tenant-1", "user-1", context.getOperationId())).thenReturn(prior);
+ when(permissionMapper.selectByTenantAndId("tenant-1", permissionId)).thenReturn(activePermission());
+ DmsPermissionOperation<String> operation = mockOperation();
+ when(operation.replay()).thenReturn("replayed");
+
+ String result = guard.execute(context, operation);
+
+ assertEquals("replayed", result);
+ verify(operation, never()).execute();
+ verify(operation).replay();
+ verify(usageMapper, never()).insert(any(DmsPermissionUsageEntity.class));
+ }
+
+ @Test
+ void rejectsReadExcludedLimitedPermissionWithoutUsingPermission() {
+ DmsFilePermissionEntity permission = activePermission();
+ permission.setMaxUseCount(2L);
+ when(permissionMapper.selectByTenantAndIdForUpdate("tenant-1", permissionId)).thenReturn(permission);
+ when(exclusionResolver.isExcluded(any(), any(), any())).thenReturn(true);
+ DmsPermissionOperation<String> operation = mockOperation();
+
+ DmsPermissionException error = assertThrows(DmsPermissionException.class,
+ () -> guard.execute(context(), operation));
+
+ assertEquals(DmsPermissionError.PERMISSION_DENIED, error.getError());
+ verify(operation, never()).execute();
+ verify(usageMapper, never()).insert(any(DmsPermissionUsageEntity.class));
+ }
+
+ @Test
+ void rejectsIdempotentReplayAfterReadExclusionIsAdded() {
+ DmsPermissionExecutionContext context = context();
+ DmsPermissionUsageEntity prior = new DmsPermissionUsageEntity();
+ prior.setPermissionId(permissionId);
+ prior.setUserId("user-1");
+ prior.setActualResourceType("DOCUMENT");
+ prior.setActualResourceId(resourceId);
+ prior.setActionCode("READ");
+ prior.setOperationId(context.getOperationId());
+ when(usageMapper.selectByOperation("tenant-1", "user-1", context.getOperationId())).thenReturn(prior);
+ when(exclusionResolver.isExcluded(any(), any(), any())).thenReturn(true);
+ DmsPermissionOperation<String> operation = mockOperation();
+
+ DmsPermissionException error = assertThrows(DmsPermissionException.class,
+ () -> guard.execute(context, operation));
+
+ assertEquals(DmsPermissionError.PERMISSION_DENIED, error.getError());
+ verify(operation, never()).replay();
+ verify(permissionMapper, never()).selectByTenantAndId(any(), any());
+ }
+
+ @SuppressWarnings("unchecked")
+ private DmsPermissionOperation<String> mockOperation() {
+ return mock(DmsPermissionOperation.class);
+ }
+
+ private DmsPermissionExecutionContext context() {
+ DmsPermissionExecutionContext context = new DmsPermissionExecutionContext();
+ context.setResourceType(DmsResourceType.DOCUMENT);
+ context.setResourceId(resourceId);
+ context.setActionCode(DmsPermissionAction.READ);
+ context.setOperationId(UUID.randomUUID());
+ return context;
+ }
+
+ private DmsFilePermissionEntity activePermission() {
+ DmsFilePermissionEntity permission = new DmsFilePermissionEntity();
+ permission.setId(permissionId);
+ permission.setTenantId("tenant-1");
+ permission.setActionCode("READ");
+ permission.setValidFrom(new Date(System.currentTimeMillis() - 60_000));
+ permission.setUsedCount(0L);
+ return permission;
+ }
+}
--
Gitblit v1.8.0