package jnpf.dmsService;
|
|
import jnpf.base.UserInfo;
|
import jnpf.dmsEntity.permission.entity.DmsFilePermissionEntity;
|
import jnpf.dmsEntity.permission.enums.DmsPermissionAction;
|
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.dmsIntegration.audit.DmsAuditAdapter;
|
import jnpf.dmsPermission.DmsAccessExclusionResolver;
|
import jnpf.dmsPermission.DmsCurrentIdentity;
|
import jnpf.dmsPermission.DmsPermissionError;
|
import jnpf.dmsPermission.DmsPermissionException;
|
import jnpf.dmsPermission.DmsPermissionGroupMembershipResolver;
|
import jnpf.dmsPermission.DmsPermissionMatcher;
|
import jnpf.dmsPermission.DmsPermissionProperties;
|
import jnpf.dmsPermission.DmsResourceResolver;
|
import jnpf.dmsPermission.DmsSubjectResolver;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.UUID;
|
import java.util.stream.Collectors;
|
|
@Service
|
@RequiredArgsConstructor
|
public class DmsPermissionDecisionService {
|
private final DmsCurrentIdentity currentIdentity;
|
private final DmsSubjectResolver subjectResolver;
|
private final DmsResourceResolver resourceResolver;
|
private final DmsFilePermissionMapper permissionMapper;
|
private final DmsPermissionGroupMembershipResolver groupMembershipResolver;
|
private final DmsPermissionMatcher matcher;
|
private final DmsPermissionProperties properties;
|
private final DmsAuditAdapter auditAdapter;
|
private final DmsAccessExclusionResolver exclusionResolver;
|
|
@Transactional(readOnly = true)
|
public DmsPermissionDecisionVO evaluate(PermissionDecisionForm request) {
|
return evaluate(Collections.singletonList(request)).get(0);
|
}
|
|
@Transactional(readOnly = true)
|
public List<DmsPermissionDecisionVO> evaluate(List<PermissionDecisionForm> requests) {
|
if (requests == null || requests.isEmpty() || requests.size() > properties.getMaxDecisionBatchSize()) {
|
throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST,
|
"裁决批量大小必须在 1 到 " + properties.getMaxDecisionBatchSize() + " 之间");
|
}
|
UserInfo user = currentIdentity.requireUser();
|
String tenantId = currentIdentity.tenantId(user);
|
Map<UUID, DmsResourcePath> paths = resourceResolver.resolveAll(tenantId, requests);
|
List<DmsResourcePath> readPaths = requests.stream()
|
.filter(request -> request.getActionCode() == DmsPermissionAction.READ)
|
.map(request -> paths.get(request.getResourceId()))
|
.collect(Collectors.toList());
|
DmsAccessExclusionResolver.Snapshot exclusions = exclusionResolver.resolve(
|
tenantId, user.getUserId().trim(), readPaths);
|
|
List<PermissionDecisionForm> eligibleRequests = requests.stream()
|
.filter(request -> !isReadExcluded(request, paths.get(request.getResourceId()), exclusions))
|
.collect(Collectors.toList());
|
|
Map<String, List<DmsFilePermissionEntity>> candidatesByAction = new java.util.HashMap<>();
|
Set<String> permissionGroupIds = new LinkedHashSet<>();
|
DmsSubjectSet subjects = null;
|
if (!eligibleRequests.isEmpty()) {
|
subjects = subjectResolver.resolve(user);
|
LinkedHashSet<String> actionCodes = new LinkedHashSet<>();
|
LinkedHashSet<UUID> resourceIds = new LinkedHashSet<>();
|
for (PermissionDecisionForm request : eligibleRequests) {
|
actionCodes.add(request.getActionCode().name());
|
resourceIds.addAll(resourceResolver.candidateResourceIds(paths.get(request.getResourceId())));
|
}
|
List<DmsFilePermissionEntity> candidates = permissionMapper.selectCandidates(tenantId,
|
new ArrayList<>(actionCodes), new ArrayList<>(resourceIds));
|
for (DmsFilePermissionEntity candidate : candidates) {
|
candidatesByAction.computeIfAbsent(candidate.getActionCode(), key -> new ArrayList<>())
|
.add(candidate);
|
if ("GROUP".equals(candidate.getPrincipalType())) {
|
permissionGroupIds.add(candidate.getPrincipalId());
|
}
|
}
|
groupMembershipResolver.resolve(permissionGroupIds, subjects);
|
}
|
|
List<DmsPermissionDecisionVO> result = new ArrayList<>();
|
for (PermissionDecisionForm request : requests) {
|
DmsPermissionDecisionVO decision = denied(request);
|
DmsResourcePath path = paths.get(request.getResourceId());
|
if (isReadExcluded(request, path, exclusions)) {
|
decision.setReasonCode("USER_READ_EXCLUDED");
|
} else {
|
for (DmsFilePermissionEntity candidate : candidatesByAction.getOrDefault(
|
request.getActionCode().name(), Collections.<DmsFilePermissionEntity>emptyList())) {
|
if (matcher.matches(candidate, path, subjects)) {
|
decision.setAllowed(true);
|
decision.setReasonCode("ALLOWED");
|
decision.setMatchedPermissionId(candidate.getId());
|
if (candidate.getMaxUseCount() != null) {
|
decision.setRemainingUseCount(candidate.getMaxUseCount() - candidate.getUsedCount());
|
}
|
break;
|
}
|
}
|
}
|
auditAdapter.permissionDecision(decision.isAllowed(), request.getActionCode().name(),
|
request.getResourceId().toString(), "{\"reasonCode\":\"" + decision.getReasonCode() + "\"}");
|
result.add(decision);
|
}
|
return result;
|
}
|
|
private boolean isReadExcluded(PermissionDecisionForm request, DmsResourcePath path,
|
DmsAccessExclusionResolver.Snapshot exclusions) {
|
return request.getActionCode() == DmsPermissionAction.READ && exclusions.excludes(path);
|
}
|
|
public void requireAllowed(jnpf.dmsEntity.permission.enums.DmsResourceType type, UUID id,
|
jnpf.dmsEntity.permission.enums.DmsPermissionAction action) {
|
PermissionDecisionForm request = new PermissionDecisionForm();
|
request.setResourceType(type);
|
request.setResourceId(id);
|
request.setActionCode(action);
|
if (!evaluate(request).isAllowed()) {
|
throw DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED,
|
"当前用户没有所需的 " + action.name() + " 权限");
|
}
|
}
|
|
private DmsPermissionDecisionVO denied(PermissionDecisionForm request) {
|
DmsPermissionDecisionVO result = new DmsPermissionDecisionVO();
|
result.setResourceId(request.getResourceId());
|
result.setResourceType(request.getResourceType().name());
|
result.setActionCode(request.getActionCode().name());
|
result.setAllowed(false);
|
result.setReasonCode("NO_MATCHING_PERMISSION");
|
return result;
|
}
|
}
|