fix #557: guide permission-driven VPD flow

This commit is contained in:
devmrko
2026-06-29 07:01:53 +09:00
parent 8dc77f87ab
commit fbe3d4682b
24 changed files with 1351 additions and 489 deletions

View File

@@ -87,6 +87,17 @@ public class BearerTokenService {
.toList();
}
public TokenContextView findTokenContextByPlainToken(String plainToken) {
BearerTokenRecord record = findByPlainToken(plainToken);
if (record == null) {
return null;
}
return findTokenContextOptions().stream()
.filter(context -> context.keyId() == record.keyId())
.findFirst()
.orElse(null);
}
public BearerTokenRecord findById(long keyId) {
return tokenMapper.findById(keyId);
}

View File

@@ -80,7 +80,10 @@ public class OrdsProbeService {
token = tokenService.findByPlainToken(command.bearerToken());
if (token == null) {
return auditAndReturn(command, ProbeResult.blocked(
ProbeStatus.TOKEN_NOT_FOUND, "TOKEN_NOT_FOUND", "토큰을 찾을 수 없습니다."));
ProbeStatus.TOKEN_NOT_FOUND,
"TOKEN_NOT_FOUND",
"현재 DB에 등록된 토큰이 아닙니다. 토큰 화면에서 이 환경의 새 토큰을 발급하세요."
));
}
} else {
token = tokenService.findById(command.tokenKeyId());

View File

@@ -20,6 +20,9 @@ public class ProbeErrorClassifier {
if (text.contains("ORA-00942") || text.contains("ORA-01031")) {
return ProbeStatus.OBJECT_NOT_ACCESSIBLE;
}
if (text.contains("ORA-28110") || text.contains("SQL Error Code 28110")) {
return ProbeStatus.VPD_FILTER_ERROR;
}
if (status != null && (status.value() == 401 || status.value() == 403)) {
return ProbeStatus.INVALID_TOKEN;
}

View File

@@ -96,6 +96,11 @@ public class VpdPolicyService {
@Transactional
public void saveFilterFunction(String functionOwnerValue, String functionNameValue, String filterPredicateValue) {
String functionName = requiredIdentifier(functionNameValue, "Function name");
if (DEFAULT_PERMISSION_FILTER_FUNCTION.equalsIgnoreCase(functionName)) {
throw new AppException("기본 동적 권한 필터 " + DEFAULT_PERMISSION_FILTER_FUNCTION
+ "는 이 화면에서 수정할 수 없습니다. 권한체계는 사용자·그룹·역할·권한 규칙 화면에서 변경하세요.");
}
String currentUser = jdbcTemplate.queryForObject("SELECT USER FROM dual", String.class);
String functionOwner = functionOwnerValue == null || functionOwnerValue.isBlank()
? currentUser
@@ -104,7 +109,6 @@ public class VpdPolicyService {
throw new AppException("Filter function 등록/수정은 현재 연결 사용자 스키마에만 가능합니다. 현재 사용자: "
+ currentUser + ", Function owner: " + functionOwner);
}
String functionName = requiredIdentifier(functionNameValue, "Function name");
String filterPredicate = filterPredicateValue == null ? "" : filterPredicateValue.trim();
if (filterPredicate.isBlank()) {
throw new AppException("Filter predicate는 필수입니다.");
@@ -113,6 +117,31 @@ public class VpdPolicyService {
clearCatalogCache();
}
@Transactional
public void createDefaultPermissionPolicy(String objectKey) {
String[] objectParts = objectKey == null ? new String[0] : objectKey.split("\\.", 2);
if (objectParts.length != 2) {
throw new AppException("보호할 객체 형식이 올바르지 않습니다: " + objectKey);
}
String functionKey = formOptions().defaultPermissionFunctionKey();
if (functionKey.isBlank()) {
throw new AppException("기본 동적 권한 필터 " + DEFAULT_PERMISSION_FILTER_FUNCTION
+ "가 설치되어 있지 않습니다. 운영 상태에서 동적 권한 필터 설치 여부를 확인한 뒤 다시 적용하세요.");
}
createPolicy(new VpdPolicyCreateCommand(
objectParts[0],
objectParts[1],
COMMON_POLICY_NAME,
functionKey,
null,
null,
"SELECT",
true,
false,
null
));
}
@Transactional
public void replacePolicy(String oldObjectKey, String oldPolicyName, VpdPolicyCreateCommand command) {
String[] objectParts = oldObjectKey == null ? new String[0] : oldObjectKey.split("\\.", 2);