You've already forked DataMate
fix(auth): harden confidential knowledge access checks and sensitivity filtering
This commit is contained in:
@@ -83,11 +83,13 @@ public class UserContextFilter implements GlobalFilter, Ordered {
|
||||
String userId = String.valueOf(claims.get("userId"));
|
||||
String username = claims.getSubject();
|
||||
List<String> roles = gatewayJwtUtils.getStringListClaim(claims, "roles");
|
||||
List<String> permissions = gatewayJwtUtils.getStringListClaim(claims, "permissions");
|
||||
|
||||
ServerHttpRequest mutatedRequest = request.mutate()
|
||||
.header("X-User-Id", userId)
|
||||
.header("X-User-Name", username)
|
||||
.header("X-User-Roles", String.join(",", roles))
|
||||
.header("X-User-Permissions", String.join(",", permissions))
|
||||
.build();
|
||||
return chain.filter(exchange.mutate().request(mutatedRequest).build());
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.datamate.datamanagement.application;
|
||||
|
||||
import com.datamate.common.auth.application.ResourceAccessService;
|
||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
||||
import com.datamate.common.infrastructure.exception.SystemErrorCode;
|
||||
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItemDirectory;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||
@@ -32,17 +34,19 @@ public class KnowledgeDirectoryApplicationService {
|
||||
private final KnowledgeItemDirectoryRepository knowledgeItemDirectoryRepository;
|
||||
private final KnowledgeItemRepository knowledgeItemRepository;
|
||||
private final KnowledgeSetRepository knowledgeSetRepository;
|
||||
private final ResourceAccessService resourceAccessService;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<KnowledgeItemDirectory> getKnowledgeDirectories(String setId, KnowledgeDirectoryQuery query) {
|
||||
BusinessAssert.notNull(query, CommonErrorCode.PARAM_ERROR);
|
||||
requireAccessibleKnowledgeSet(setId);
|
||||
query.setSetId(setId);
|
||||
return knowledgeItemDirectoryRepository.findByCriteria(query);
|
||||
}
|
||||
|
||||
public KnowledgeItemDirectory createKnowledgeDirectory(String setId, CreateKnowledgeDirectoryRequest request) {
|
||||
BusinessAssert.notNull(request, CommonErrorCode.PARAM_ERROR);
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||
|
||||
@@ -71,7 +75,7 @@ public class KnowledgeDirectoryApplicationService {
|
||||
}
|
||||
|
||||
public void deleteKnowledgeDirectory(String setId, String relativePath) {
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||
|
||||
@@ -88,6 +92,15 @@ public class KnowledgeDirectoryApplicationService {
|
||||
return knowledgeSet;
|
||||
}
|
||||
|
||||
private KnowledgeSet requireAccessibleKnowledgeSet(String setId) {
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(knowledgeSet.getSensitivity())) {
|
||||
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
return knowledgeSet;
|
||||
}
|
||||
|
||||
private boolean isReadOnlyStatus(KnowledgeStatusType status) {
|
||||
return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.datamate.datamanagement.application;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.datamate.common.auth.application.ResourceAccessService;
|
||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||
import com.datamate.common.infrastructure.exception.BusinessException;
|
||||
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
||||
@@ -12,11 +13,11 @@ import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
|
||||
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
||||
import com.datamate.datamanagement.domain.model.dataset.Dataset;
|
||||
import com.datamate.datamanagement.domain.model.dataset.DatasetFile;
|
||||
import com.datamate.datamanagement.domain.model.dataset.Tag;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||
import com.datamate.datamanagement.infrastructure.config.DataManagementProperties;
|
||||
import com.datamate.datamanagement.infrastructure.exception.DataManagementErrorCode;
|
||||
import com.datamate.datamanagement.infrastructure.persistence.mapper.TagMapper;
|
||||
import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetFileRepository;
|
||||
import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetRepository;
|
||||
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
|
||||
@@ -30,6 +31,7 @@ import com.datamate.datamanagement.interfaces.dto.KnowledgeItemResponse;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchQuery;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeManagementStatisticsResponse;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
|
||||
import com.datamate.datamanagement.interfaces.dto.ReplaceKnowledgeItemFileRequest;
|
||||
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeItemRequest;
|
||||
import com.datamate.datamanagement.interfaces.dto.UploadKnowledgeItemsRequest;
|
||||
@@ -56,12 +58,15 @@ import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 知识条目应用服务
|
||||
@@ -88,11 +93,11 @@ public class KnowledgeItemApplicationService {
|
||||
private final DatasetRepository datasetRepository;
|
||||
private final DatasetFileRepository datasetFileRepository;
|
||||
private final DataManagementProperties dataManagementProperties;
|
||||
private final TagMapper tagMapper;
|
||||
private final KnowledgeItemPreviewService knowledgeItemPreviewService;
|
||||
private final ResourceAccessService resourceAccessService;
|
||||
|
||||
public KnowledgeItem createKnowledgeItem(String setId, CreateKnowledgeItemRequest request) {
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||
|
||||
@@ -112,7 +117,7 @@ public class KnowledgeItemApplicationService {
|
||||
}
|
||||
|
||||
public List<KnowledgeItem> uploadKnowledgeItems(String setId, UploadKnowledgeItemsRequest request) {
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||
|
||||
@@ -176,7 +181,7 @@ public class KnowledgeItemApplicationService {
|
||||
}
|
||||
|
||||
public KnowledgeItem updateKnowledgeItem(String setId, String itemId, UpdateKnowledgeItemRequest request) {
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||
@@ -199,6 +204,7 @@ public class KnowledgeItemApplicationService {
|
||||
}
|
||||
|
||||
public void deleteKnowledgeItem(String setId, String itemId) {
|
||||
requireAccessibleKnowledgeSet(setId);
|
||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||
@@ -209,6 +215,7 @@ public class KnowledgeItemApplicationService {
|
||||
}
|
||||
|
||||
public void deleteKnowledgeItems(String setId, DeleteKnowledgeItemsRequest request) {
|
||||
requireAccessibleKnowledgeSet(setId);
|
||||
BusinessAssert.notNull(request, CommonErrorCode.PARAM_ERROR);
|
||||
List<String> ids = request.getIds();
|
||||
BusinessAssert.isTrue(CollectionUtils.isNotEmpty(ids), CommonErrorCode.PARAM_ERROR);
|
||||
@@ -231,6 +238,7 @@ public class KnowledgeItemApplicationService {
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public KnowledgeItem getKnowledgeItem(String setId, String itemId) {
|
||||
requireAccessibleKnowledgeSet(setId);
|
||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||
@@ -239,6 +247,7 @@ public class KnowledgeItemApplicationService {
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PagedResponse<KnowledgeItemResponse> getKnowledgeItems(String setId, KnowledgeItemPagingQuery query) {
|
||||
requireAccessibleKnowledgeSet(setId);
|
||||
query.setSetId(setId);
|
||||
IPage<KnowledgeItem> page = new Page<>(query.getPage(), query.getSize());
|
||||
page = knowledgeItemRepository.findByCriteria(page, query);
|
||||
@@ -248,19 +257,52 @@ public class KnowledgeItemApplicationService {
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public KnowledgeManagementStatisticsResponse getKnowledgeManagementStatistics() {
|
||||
boolean excludeConfidential = !resourceAccessService.canViewConfidential();
|
||||
String ownerFilterUserId = resourceAccessService.resolveOwnerFilterUserId();
|
||||
KnowledgeSetPagingQuery baseQuery = new KnowledgeSetPagingQuery();
|
||||
KnowledgeManagementStatisticsResponse response = new KnowledgeManagementStatisticsResponse();
|
||||
response.setTotalKnowledgeSets(knowledgeSetRepository.count());
|
||||
|
||||
long totalFiles = knowledgeItemRepository.countBySourceTypes(List.of(
|
||||
long totalSets = knowledgeSetRepository.countByCriteria(baseQuery, ownerFilterUserId, excludeConfidential);
|
||||
response.setTotalKnowledgeSets(totalSets);
|
||||
|
||||
List<String> accessibleSetIds = knowledgeSetRepository.listSetIdsByCriteria(baseQuery, ownerFilterUserId, excludeConfidential);
|
||||
List<KnowledgeSet> accessibleSets = knowledgeSetRepository.listByIds(accessibleSetIds);
|
||||
if (CollectionUtils.isEmpty(accessibleSets)) {
|
||||
response.setTotalFiles(0L);
|
||||
response.setTotalSize(0L);
|
||||
response.setTotalTags(0L);
|
||||
return response;
|
||||
}
|
||||
|
||||
List<String> normalizedSetIds = accessibleSets.stream()
|
||||
.map(KnowledgeSet::getId)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.toList();
|
||||
if (CollectionUtils.isEmpty(normalizedSetIds)) {
|
||||
response.setTotalFiles(0L);
|
||||
response.setTotalSize(0L);
|
||||
response.setTotalTags(0L);
|
||||
return response;
|
||||
}
|
||||
|
||||
long totalFiles = knowledgeItemRepository.countBySourceTypesAndSetIds(List.of(
|
||||
KnowledgeSourceType.DATASET_FILE,
|
||||
KnowledgeSourceType.FILE_UPLOAD
|
||||
));
|
||||
), normalizedSetIds);
|
||||
response.setTotalFiles(totalFiles);
|
||||
|
||||
long datasetFileSize = safeLong(knowledgeItemRepository.sumDatasetFileSize());
|
||||
long uploadFileSize = calculateUploadFileTotalSize();
|
||||
long datasetFileSize = safeLong(knowledgeItemRepository.sumDatasetFileSizeBySetIds(normalizedSetIds));
|
||||
long uploadFileSize = calculateUploadFileTotalSize(normalizedSetIds);
|
||||
response.setTotalSize(datasetFileSize + uploadFileSize);
|
||||
response.setTotalTags(safeLong(tagMapper.countKnowledgeSetTags()));
|
||||
|
||||
long totalTags = accessibleSets.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(set -> CollectionUtils.isEmpty(set.getTags()) ? Collections.<Tag>emptyList().stream() : set.getTags().stream())
|
||||
.map(tag -> StringUtils.trimToNull(tag == null ? null : tag.getName()))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(HashSet::new))
|
||||
.size();
|
||||
response.setTotalTags(totalTags);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -271,8 +313,9 @@ public class KnowledgeItemApplicationService {
|
||||
String keyword = StringUtils.trimToEmpty(query.getKeyword());
|
||||
BusinessAssert.isTrue(StringUtils.isNotBlank(keyword), CommonErrorCode.PARAM_ERROR);
|
||||
|
||||
boolean excludeConfidential = !resourceAccessService.canViewConfidential();
|
||||
IPage<KnowledgeItemSearchResponse> page = new Page<>(query.getPage(), query.getSize());
|
||||
IPage<KnowledgeItemSearchResponse> result = knowledgeItemRepository.searchFileItems(page, keyword);
|
||||
IPage<KnowledgeItemSearchResponse> result = knowledgeItemRepository.searchFileItems(page, keyword, excludeConfidential);
|
||||
List<KnowledgeItemSearchResponse> responses = result.getRecords()
|
||||
.stream()
|
||||
.map(this::normalizeSearchResponse)
|
||||
@@ -281,7 +324,7 @@ public class KnowledgeItemApplicationService {
|
||||
}
|
||||
|
||||
public List<KnowledgeItem> importKnowledgeItems(String setId, ImportKnowledgeItemsRequest request) {
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||
Dataset dataset = datasetRepository.getById(request.getDatasetId());
|
||||
@@ -318,7 +361,7 @@ public class KnowledgeItemApplicationService {
|
||||
@Transactional(readOnly = true)
|
||||
public void exportKnowledgeItems(String setId, HttpServletResponse response) {
|
||||
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||
List<KnowledgeItem> items = knowledgeItemRepository.findAllBySetId(setId);
|
||||
|
||||
response.setContentType(EXPORT_CONTENT_TYPE);
|
||||
@@ -347,6 +390,7 @@ public class KnowledgeItemApplicationService {
|
||||
@Transactional(readOnly = true)
|
||||
public void downloadKnowledgeItemFile(String setId, String itemId, HttpServletResponse response) {
|
||||
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
||||
requireAccessibleKnowledgeSet(setId);
|
||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||
@@ -380,6 +424,7 @@ public class KnowledgeItemApplicationService {
|
||||
@Transactional(readOnly = true)
|
||||
public void previewKnowledgeItemFile(String setId, String itemId, HttpServletResponse response) {
|
||||
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
||||
requireAccessibleKnowledgeSet(setId);
|
||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||
@@ -441,7 +486,7 @@ public class KnowledgeItemApplicationService {
|
||||
}
|
||||
|
||||
public KnowledgeItem replaceKnowledgeItemFile(String setId, String itemId, ReplaceKnowledgeItemFileRequest request) {
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||
@@ -655,8 +700,8 @@ public class KnowledgeItemApplicationService {
|
||||
return item;
|
||||
}
|
||||
|
||||
private long calculateUploadFileTotalSize() {
|
||||
List<KnowledgeItem> items = knowledgeItemRepository.findFileUploadItems();
|
||||
private long calculateUploadFileTotalSize(List<String> setIds) {
|
||||
List<KnowledgeItem> items = knowledgeItemRepository.findFileUploadItemsBySetIds(setIds);
|
||||
if (CollectionUtils.isEmpty(items)) {
|
||||
return 0L;
|
||||
}
|
||||
@@ -846,6 +891,18 @@ public class KnowledgeItemApplicationService {
|
||||
return knowledgeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前用户是否可访问指定知识集(含保密权限检查)
|
||||
*/
|
||||
private KnowledgeSet requireAccessibleKnowledgeSet(String setId) {
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(knowledgeSet.getSensitivity())) {
|
||||
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
return knowledgeSet;
|
||||
}
|
||||
|
||||
private String buildExportFileName(String setId) {
|
||||
return EXPORT_FILE_PREFIX + setId + "_" + LocalDateTime.now().format(EXPORT_TIME_FORMATTER) + EXPORT_FILE_SUFFIX;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package com.datamate.datamanagement.application;
|
||||
|
||||
import com.datamate.common.auth.application.ResourceAccessService;
|
||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
||||
import com.datamate.common.infrastructure.exception.SystemErrorCode;
|
||||
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
|
||||
import com.datamate.datamanagement.common.enums.KnowledgeItemPreviewStatus;
|
||||
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||
import com.datamate.datamanagement.infrastructure.config.DataManagementProperties;
|
||||
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
|
||||
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPreviewStatusResponse;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -38,8 +42,10 @@ public class KnowledgeItemPreviewService {
|
||||
private static final DateTimeFormatter PREVIEW_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
|
||||
private final KnowledgeItemRepository knowledgeItemRepository;
|
||||
private final KnowledgeSetRepository knowledgeSetRepository;
|
||||
private final DataManagementProperties dataManagementProperties;
|
||||
private final KnowledgeItemPreviewAsyncService knowledgeItemPreviewAsyncService;
|
||||
private final ResourceAccessService resourceAccessService;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public KnowledgeItemPreviewStatusResponse getPreviewStatus(String setId, String itemId) {
|
||||
@@ -138,6 +144,14 @@ public class KnowledgeItemPreviewService {
|
||||
private KnowledgeItem requireKnowledgeItem(String setId, String itemId) {
|
||||
BusinessAssert.isTrue(StringUtils.isNotBlank(setId), CommonErrorCode.PARAM_ERROR);
|
||||
BusinessAssert.isTrue(StringUtils.isNotBlank(itemId), CommonErrorCode.PARAM_ERROR);
|
||||
|
||||
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
||||
BusinessAssert.notNull(knowledgeSet, CommonErrorCode.PARAM_ERROR);
|
||||
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(knowledgeSet.getSensitivity())) {
|
||||
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
|
||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||
BusinessAssert.notNull(knowledgeItem, CommonErrorCode.PARAM_ERROR);
|
||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.datamate.common.auth.application.ResourceAccessService;
|
||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
||||
import com.datamate.common.infrastructure.exception.SystemErrorCode;
|
||||
import com.datamate.common.interfaces.PagedResponse;
|
||||
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
||||
import com.datamate.datamanagement.domain.model.dataset.Tag;
|
||||
@@ -46,9 +47,11 @@ public class KnowledgeSetApplicationService {
|
||||
public KnowledgeSet createKnowledgeSet(CreateKnowledgeSetRequest request) {
|
||||
BusinessAssert.isTrue(knowledgeSetRepository.findByName(request.getName()) == null,
|
||||
DataManagementErrorCode.KNOWLEDGE_SET_ALREADY_EXISTS);
|
||||
assertCanUseSensitivity(request.getSensitivity());
|
||||
|
||||
KnowledgeSet knowledgeSet = KnowledgeConverter.INSTANCE.convertToKnowledgeSet(request);
|
||||
knowledgeSet.setId(UUID.randomUUID().toString());
|
||||
knowledgeSet.setSensitivity(normalizeSensitivity(knowledgeSet.getSensitivity()));
|
||||
if (knowledgeSet.getStatus() == null) {
|
||||
knowledgeSet.setStatus(KnowledgeStatusType.DRAFT);
|
||||
}
|
||||
@@ -67,6 +70,7 @@ public class KnowledgeSetApplicationService {
|
||||
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
||||
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
||||
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
||||
assertConfidentialAccess(knowledgeSet);
|
||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||
|
||||
@@ -106,7 +110,8 @@ public class KnowledgeSetApplicationService {
|
||||
knowledgeSet.setSourceType(request.getSourceType());
|
||||
}
|
||||
if (request.getSensitivity() != null) {
|
||||
knowledgeSet.setSensitivity(request.getSensitivity());
|
||||
assertCanUseSensitivity(request.getSensitivity());
|
||||
knowledgeSet.setSensitivity(normalizeSensitivity(request.getSensitivity()));
|
||||
}
|
||||
if (request.getMetadata() != null) {
|
||||
knowledgeSet.setMetadata(request.getMetadata());
|
||||
@@ -123,6 +128,7 @@ public class KnowledgeSetApplicationService {
|
||||
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
||||
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
||||
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
||||
assertConfidentialAccess(knowledgeSet);
|
||||
knowledgeSetRepository.removeById(setId);
|
||||
}
|
||||
|
||||
@@ -131,6 +137,7 @@ public class KnowledgeSetApplicationService {
|
||||
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
||||
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
||||
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
||||
assertConfidentialAccess(knowledgeSet);
|
||||
return knowledgeSet;
|
||||
}
|
||||
|
||||
@@ -138,11 +145,33 @@ public class KnowledgeSetApplicationService {
|
||||
public PagedResponse<KnowledgeSetResponse> getKnowledgeSets(KnowledgeSetPagingQuery query) {
|
||||
IPage<KnowledgeSet> page = new Page<>(query.getPage(), query.getSize());
|
||||
String ownerFilterUserId = resourceAccessService.resolveOwnerFilterUserId();
|
||||
page = knowledgeSetRepository.findByCriteria(page, query, ownerFilterUserId);
|
||||
boolean excludeConfidential = !resourceAccessService.canViewConfidential();
|
||||
page = knowledgeSetRepository.findByCriteria(page, query, ownerFilterUserId, excludeConfidential);
|
||||
List<KnowledgeSetResponse> responses = KnowledgeConverter.INSTANCE.convertSetResponses(page.getRecords());
|
||||
return PagedResponse.of(responses, page.getCurrent(), page.getTotal(), page.getPages());
|
||||
}
|
||||
|
||||
private void assertConfidentialAccess(KnowledgeSet knowledgeSet) {
|
||||
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(knowledgeSet.getSensitivity())) {
|
||||
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertCanUseSensitivity(String sensitivity) {
|
||||
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(sensitivity)) {
|
||||
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeSensitivity(String sensitivity) {
|
||||
if (!StringUtils.hasText(sensitivity)) {
|
||||
return null;
|
||||
}
|
||||
return sensitivity.trim().toUpperCase();
|
||||
}
|
||||
|
||||
private boolean isReadOnlyStatus(KnowledgeStatusType status) {
|
||||
return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface KnowledgeItemMapper extends BaseMapper<KnowledgeItem> {
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT
|
||||
ki.id AS id,
|
||||
ki.set_id AS setId,
|
||||
@@ -34,19 +37,32 @@ public interface KnowledgeItemMapper extends BaseMapper<KnowledgeItem> {
|
||||
FROM t_dm_knowledge_items ki
|
||||
LEFT JOIN t_dm_knowledge_sets ks ON ki.set_id = ks.id
|
||||
LEFT JOIN t_dm_dataset_files df ON ki.source_file_id = df.id AND ki.source_type = 'DATASET_FILE'
|
||||
WHERE (ki.source_type = 'FILE_UPLOAD' AND (ki.source_file_id LIKE CONCAT('%', #{keyword}, '%')
|
||||
WHERE ((ki.source_type = 'FILE_UPLOAD' AND (ki.source_file_id LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR ki.relative_path LIKE CONCAT('%', #{keyword}, '%')))
|
||||
OR (ki.source_type = 'DATASET_FILE' AND (df.file_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR ki.relative_path LIKE CONCAT('%', #{keyword}, '%')))
|
||||
OR ki.relative_path LIKE CONCAT('%', #{keyword}, '%'))))
|
||||
<if test="excludeConfidential">
|
||||
AND (ks.sensitivity IS NULL OR UPPER(TRIM(ks.sensitivity)) != 'CONFIDENTIAL')
|
||||
</if>
|
||||
ORDER BY ki.created_at DESC
|
||||
</script>
|
||||
""")
|
||||
IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, @Param("keyword") String keyword);
|
||||
IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, @Param("keyword") String keyword,
|
||||
@Param("excludeConfidential") boolean excludeConfidential);
|
||||
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT COALESCE(SUM(df.file_size), 0)
|
||||
FROM t_dm_knowledge_items ki
|
||||
LEFT JOIN t_dm_dataset_files df ON ki.source_file_id = df.id
|
||||
WHERE ki.source_type = 'DATASET_FILE'
|
||||
<if test="setIds != null and setIds.size() > 0">
|
||||
AND ki.set_id IN
|
||||
<foreach collection="setIds" item="setId" open="(" separator="," close=")">
|
||||
#{setId}
|
||||
</foreach>
|
||||
</if>
|
||||
</script>
|
||||
""")
|
||||
Long sumDatasetFileSize();
|
||||
Long sumDatasetFileSizeBySetIds(@Param("setIds") List<String> setIds);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package com.datamate.datamanagement.infrastructure.persistence.repository;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.repository.IRepository;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -19,13 +19,13 @@ public interface KnowledgeItemRepository extends IRepository<KnowledgeItem> {
|
||||
|
||||
List<KnowledgeItem> findAllBySetId(String setId);
|
||||
|
||||
long countBySourceTypes(List<KnowledgeSourceType> sourceTypes);
|
||||
long countBySourceTypesAndSetIds(List<KnowledgeSourceType> sourceTypes, List<String> setIds);
|
||||
|
||||
List<KnowledgeItem> findFileUploadItems();
|
||||
List<KnowledgeItem> findFileUploadItemsBySetIds(List<String> setIds);
|
||||
|
||||
IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, String keyword);
|
||||
IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, String keyword, boolean excludeConfidential);
|
||||
|
||||
Long sumDatasetFileSize();
|
||||
Long sumDatasetFileSizeBySetIds(List<String> setIds);
|
||||
|
||||
boolean existsBySetIdAndRelativePath(String setId, String relativePath);
|
||||
|
||||
|
||||
@@ -5,11 +5,18 @@ import com.baomidou.mybatisplus.extension.repository.IRepository;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 知识集仓储接口
|
||||
*/
|
||||
public interface KnowledgeSetRepository extends IRepository<KnowledgeSet> {
|
||||
KnowledgeSet findByName(String name);
|
||||
|
||||
IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query, String createdBy);
|
||||
IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query, String createdBy,
|
||||
boolean excludeConfidential);
|
||||
|
||||
long countByCriteria(KnowledgeSetPagingQuery query, String createdBy, boolean excludeConfidential);
|
||||
|
||||
List<String> listSetIdsByCriteria(KnowledgeSetPagingQuery query, String createdBy, boolean excludeConfidential);
|
||||
}
|
||||
|
||||
@@ -61,26 +61,37 @@ public class KnowledgeItemRepositoryImpl extends CrudRepository<KnowledgeItemMap
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countBySourceTypes(List<KnowledgeSourceType> sourceTypes) {
|
||||
public long countBySourceTypesAndSetIds(List<KnowledgeSourceType> sourceTypes, List<String> setIds) {
|
||||
if (sourceTypes == null || sourceTypes.isEmpty() || setIds == null || setIds.isEmpty()) {
|
||||
return 0L;
|
||||
}
|
||||
return knowledgeItemMapper.selectCount(new LambdaQueryWrapper<KnowledgeItem>()
|
||||
.in(KnowledgeItem::getSourceType, sourceTypes));
|
||||
.in(KnowledgeItem::getSourceType, sourceTypes)
|
||||
.in(KnowledgeItem::getSetId, setIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KnowledgeItem> findFileUploadItems() {
|
||||
public List<KnowledgeItem> findFileUploadItemsBySetIds(List<String> setIds) {
|
||||
if (setIds == null || setIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return knowledgeItemMapper.selectList(new LambdaQueryWrapper<KnowledgeItem>()
|
||||
.eq(KnowledgeItem::getSourceType, KnowledgeSourceType.FILE_UPLOAD)
|
||||
.select(KnowledgeItem::getId, KnowledgeItem::getContent, KnowledgeItem::getSourceFileId));
|
||||
.in(KnowledgeItem::getSetId, setIds)
|
||||
.select(KnowledgeItem::getId, KnowledgeItem::getSetId, KnowledgeItem::getContent, KnowledgeItem::getSourceFileId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, String keyword) {
|
||||
return knowledgeItemMapper.searchFileItems(page, keyword);
|
||||
public IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, String keyword, boolean excludeConfidential) {
|
||||
return knowledgeItemMapper.searchFileItems(page, keyword, excludeConfidential);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long sumDatasetFileSize() {
|
||||
return knowledgeItemMapper.sumDatasetFileSize();
|
||||
public Long sumDatasetFileSizeBySetIds(List<String> setIds) {
|
||||
if (setIds == null || setIds.isEmpty()) {
|
||||
return 0L;
|
||||
}
|
||||
return knowledgeItemMapper.sumDatasetFileSizeBySetIds(setIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.datamate.datamanagement.infrastructure.persistence.repository.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.repository.CrudRepository;
|
||||
import com.datamate.common.auth.application.ResourceAccessService;
|
||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||
import com.datamate.datamanagement.infrastructure.persistence.mapper.KnowledgeSetMapper;
|
||||
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
|
||||
@@ -11,6 +12,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 知识集仓储实现类
|
||||
*/
|
||||
@@ -25,25 +29,62 @@ public class KnowledgeSetRepositoryImpl extends CrudRepository<KnowledgeSetMappe
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query, String createdBy) {
|
||||
public IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query, String createdBy,
|
||||
boolean excludeConfidential) {
|
||||
LambdaQueryWrapper<KnowledgeSet> wrapper = buildCriteriaWrapper(query, createdBy, excludeConfidential);
|
||||
wrapper.orderByDesc(KnowledgeSet::getCreatedAt);
|
||||
return knowledgeSetMapper.selectPage(page, wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByCriteria(KnowledgeSetPagingQuery query, String createdBy, boolean excludeConfidential) {
|
||||
return knowledgeSetMapper.selectCount(buildCriteriaWrapper(query, createdBy, excludeConfidential));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> listSetIdsByCriteria(KnowledgeSetPagingQuery query, String createdBy, boolean excludeConfidential) {
|
||||
LambdaQueryWrapper<KnowledgeSet> wrapper = buildCriteriaWrapper(query, createdBy, excludeConfidential)
|
||||
.select(KnowledgeSet::getId)
|
||||
.orderByDesc(KnowledgeSet::getCreatedAt);
|
||||
List<KnowledgeSet> sets = knowledgeSetMapper.selectList(wrapper);
|
||||
if (sets == null || sets.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return sets.stream().map(KnowledgeSet::getId).filter(StringUtils::isNotBlank).toList();
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<KnowledgeSet> buildCriteriaWrapper(KnowledgeSetPagingQuery query,
|
||||
String createdBy,
|
||||
boolean excludeConfidential) {
|
||||
KnowledgeSetPagingQuery safeQuery = query == null ? new KnowledgeSetPagingQuery() : query;
|
||||
|
||||
LambdaQueryWrapper<KnowledgeSet> wrapper = new LambdaQueryWrapper<KnowledgeSet>()
|
||||
.eq(query.getStatus() != null, KnowledgeSet::getStatus, query.getStatus())
|
||||
.eq(StringUtils.isNotBlank(query.getDomain()), KnowledgeSet::getDomain, query.getDomain())
|
||||
.eq(StringUtils.isNotBlank(query.getBusinessLine()), KnowledgeSet::getBusinessLine, query.getBusinessLine())
|
||||
.eq(StringUtils.isNotBlank(query.getOwner()), KnowledgeSet::getOwner, query.getOwner())
|
||||
.eq(StringUtils.isNotBlank(query.getSensitivity()), KnowledgeSet::getSensitivity, query.getSensitivity())
|
||||
.eq(query.getSourceType() != null, KnowledgeSet::getSourceType, query.getSourceType())
|
||||
.ge(query.getValidFrom() != null, KnowledgeSet::getValidFrom, query.getValidFrom())
|
||||
.le(query.getValidTo() != null, KnowledgeSet::getValidTo, query.getValidTo())
|
||||
.eq(safeQuery.getStatus() != null, KnowledgeSet::getStatus, safeQuery.getStatus())
|
||||
.eq(StringUtils.isNotBlank(safeQuery.getDomain()), KnowledgeSet::getDomain, safeQuery.getDomain())
|
||||
.eq(StringUtils.isNotBlank(safeQuery.getBusinessLine()), KnowledgeSet::getBusinessLine, safeQuery.getBusinessLine())
|
||||
.eq(StringUtils.isNotBlank(safeQuery.getOwner()), KnowledgeSet::getOwner, safeQuery.getOwner())
|
||||
.eq(safeQuery.getSourceType() != null, KnowledgeSet::getSourceType, safeQuery.getSourceType())
|
||||
.ge(safeQuery.getValidFrom() != null, KnowledgeSet::getValidFrom, safeQuery.getValidFrom())
|
||||
.le(safeQuery.getValidTo() != null, KnowledgeSet::getValidTo, safeQuery.getValidTo())
|
||||
.eq(StringUtils.isNotBlank(createdBy), KnowledgeSet::getCreatedBy, createdBy);
|
||||
|
||||
if (StringUtils.isNotBlank(query.getKeyword())) {
|
||||
wrapper.and(w -> w.like(KnowledgeSet::getName, query.getKeyword())
|
||||
.or()
|
||||
.like(KnowledgeSet::getDescription, query.getKeyword()));
|
||||
if (queryHasSensitivity(safeQuery)) {
|
||||
wrapper.apply("UPPER(TRIM(sensitivity)) = {0}", normalizeSensitivity(safeQuery.getSensitivity()));
|
||||
}
|
||||
|
||||
for (String tagName : query.getTags()) {
|
||||
if (excludeConfidential) {
|
||||
wrapper.and(w -> w.isNull(KnowledgeSet::getSensitivity)
|
||||
.or()
|
||||
.apply("UPPER(TRIM(sensitivity)) != {0}", ResourceAccessService.CONFIDENTIAL_SENSITIVITY));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(safeQuery.getKeyword())) {
|
||||
wrapper.and(w -> w.like(KnowledgeSet::getName, safeQuery.getKeyword())
|
||||
.or()
|
||||
.like(KnowledgeSet::getDescription, safeQuery.getKeyword()));
|
||||
}
|
||||
|
||||
for (String tagName : safeQuery.getTags()) {
|
||||
wrapper.and(w ->
|
||||
w.apply("tags IS NOT NULL " +
|
||||
"AND JSON_VALID(tags) = 1 " +
|
||||
@@ -52,7 +93,15 @@ public class KnowledgeSetRepositoryImpl extends CrudRepository<KnowledgeSetMappe
|
||||
);
|
||||
}
|
||||
|
||||
wrapper.orderByDesc(KnowledgeSet::getCreatedAt);
|
||||
return knowledgeSetMapper.selectPage(page, wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private boolean queryHasSensitivity(KnowledgeSetPagingQuery query) {
|
||||
String normalized = normalizeSensitivity(query.getSensitivity());
|
||||
return StringUtils.isNotBlank(normalized) && !"ALL".equals(normalized);
|
||||
}
|
||||
|
||||
private String normalizeSensitivity(String sensitivity) {
|
||||
return StringUtils.upperCase(StringUtils.trimToNull(sensitivity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,23 @@ import java.util.Objects;
|
||||
@Service
|
||||
public class ResourceAccessService {
|
||||
public static final String ADMIN_ROLE_CODE = "ROLE_ADMIN";
|
||||
public static final String VIEW_CONFIDENTIAL_PERMISSION = "knowledge:view-confidential";
|
||||
public static final String CONFIDENTIAL_SENSITIVITY = "CONFIDENTIAL";
|
||||
|
||||
public boolean isAdmin() {
|
||||
return RequestUserContextHolder.hasRole(ADMIN_ROLE_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前用户是否有权查看保密知识
|
||||
*/
|
||||
public boolean canViewConfidential() {
|
||||
if (isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
return RequestUserContextHolder.hasPermission(VIEW_CONFIDENTIAL_PERMISSION);
|
||||
}
|
||||
|
||||
public String getCurrentUserId() {
|
||||
return RequestUserContextHolder.getCurrentUserId();
|
||||
}
|
||||
|
||||
@@ -15,19 +15,21 @@ public class RequestUserContext {
|
||||
private final String userId;
|
||||
private final String username;
|
||||
private final List<String> roles;
|
||||
private final List<String> permissions;
|
||||
|
||||
private RequestUserContext(String userId, String username, List<String> roles) {
|
||||
private RequestUserContext(String userId, String username, List<String> roles, List<String> permissions) {
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.roles = roles == null ? Collections.emptyList() : List.copyOf(roles);
|
||||
this.permissions = permissions == null ? Collections.emptyList() : List.copyOf(permissions);
|
||||
}
|
||||
|
||||
public static RequestUserContext of(String userId, String username, List<String> roles) {
|
||||
return new RequestUserContext(userId, username, roles);
|
||||
public static RequestUserContext of(String userId, String username, List<String> roles, List<String> permissions) {
|
||||
return new RequestUserContext(userId, username, roles, permissions);
|
||||
}
|
||||
|
||||
public static RequestUserContext empty() {
|
||||
return new RequestUserContext(null, null, Collections.emptyList());
|
||||
return new RequestUserContext(null, null, Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public boolean hasRole(String roleCode) {
|
||||
@@ -36,5 +38,12 @@ public class RequestUserContext {
|
||||
}
|
||||
return roles.stream().anyMatch(role -> StringUtils.hasText(role) && Objects.equals(role.trim(), roleCode));
|
||||
}
|
||||
|
||||
public boolean hasPermission(String permissionCode) {
|
||||
if (!StringUtils.hasText(permissionCode)) {
|
||||
return false;
|
||||
}
|
||||
return permissions.stream().anyMatch(p -> StringUtils.hasText(p) && Objects.equals(p.trim(), permissionCode));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,19 @@ public final class RequestUserContextHolder {
|
||||
.anyMatch(role -> StringUtils.hasText(role) && roleCode.equalsIgnoreCase(role.trim()));
|
||||
}
|
||||
|
||||
public static List<String> getCurrentPermissions() {
|
||||
List<String> permissions = get().getPermissions();
|
||||
return permissions == null ? Collections.emptyList() : permissions;
|
||||
}
|
||||
|
||||
public static boolean hasPermission(String permissionCode) {
|
||||
if (!StringUtils.hasText(permissionCode)) {
|
||||
return false;
|
||||
}
|
||||
return getCurrentPermissions().stream()
|
||||
.anyMatch(p -> StringUtils.hasText(p) && permissionCode.equals(p.trim()));
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
USER_CONTEXT_HOLDER.remove();
|
||||
}
|
||||
|
||||
@@ -18,13 +18,15 @@ public class RequestUserContextInterceptor implements HandlerInterceptor {
|
||||
private static final String HEADER_USER_ID = "X-User-Id";
|
||||
private static final String HEADER_USER_NAME = "X-User-Name";
|
||||
private static final String HEADER_USER_ROLES = "X-User-Roles";
|
||||
private static final String HEADER_USER_PERMISSIONS = "X-User-Permissions";
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
String userId = normalizeValue(request.getHeader(HEADER_USER_ID));
|
||||
String username = normalizeValue(request.getHeader(HEADER_USER_NAME));
|
||||
List<String> roleCodes = parseRoleCodes(request.getHeader(HEADER_USER_ROLES));
|
||||
RequestUserContextHolder.set(RequestUserContext.of(userId, username, roleCodes));
|
||||
List<String> roleCodes = parseCommaSeparatedValues(request.getHeader(HEADER_USER_ROLES));
|
||||
List<String> permissionCodes = parseCommaSeparatedValues(request.getHeader(HEADER_USER_PERMISSIONS));
|
||||
RequestUserContextHolder.set(RequestUserContext.of(userId, username, roleCodes, permissionCodes));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,11 +42,11 @@ public class RequestUserContextInterceptor implements HandlerInterceptor {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
private List<String> parseRoleCodes(String roleHeader) {
|
||||
if (!StringUtils.hasText(roleHeader)) {
|
||||
private List<String> parseCommaSeparatedValues(String header) {
|
||||
if (!StringUtils.hasText(header)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(roleHeader.split(","))
|
||||
return Arrays.stream(header.split(","))
|
||||
.map(String::trim)
|
||||
.filter(StringUtils::hasText)
|
||||
.toList();
|
||||
|
||||
Reference in New Issue
Block a user