fix #424: use dropdowns for VPD policy form

This commit is contained in:
devmrko
2026-06-25 17:03:44 +09:00
parent 1bcb6b6a43
commit 9a546b7997
9 changed files with 190 additions and 15 deletions

View File

@@ -0,0 +1,18 @@
package com.cloudhandson.vpdbackoffice.domain.vpd;
public record VpdFunctionOption(
String owner,
String packageName,
String functionName,
String objectType
) {
public String value() {
String packagePrefix = packageName == null || packageName.isBlank() ? "" : packageName + ".";
return owner + "." + packagePrefix + functionName;
}
public String label() {
return value() + " / " + objectType;
}
}

View File

@@ -4,6 +4,7 @@ public record VpdPolicyCreateCommand(
String objectOwner,
String objectName,
String policyName,
String functionKey,
String functionOwner,
String functionName,
String statementTypes,

View File

@@ -0,0 +1,11 @@
package com.cloudhandson.vpdbackoffice.domain.vpd;
import java.util.List;
public record VpdPolicyFormOptions(
List<String> policyNames,
List<String> owners,
List<VpdFunctionOption> functions,
List<String> statementTypes
) {
}

View File

@@ -1,5 +1,6 @@
package com.cloudhandson.vpdbackoffice.mapper;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdFunctionOption;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyView;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
@@ -10,6 +11,12 @@ public interface VpdPolicyMapper {
List<VpdPolicyView> findPolicies();
List<String> findPolicyNameOptions();
List<String> findOwnerOptions();
List<VpdFunctionOption> findFunctionOptions();
VpdPolicyView findPolicy(
@Param("objectOwner") String objectOwner,
@Param("objectName") String objectName,

View File

@@ -4,6 +4,7 @@ import com.cloudhandson.vpdbackoffice.domain.vpd.VpdFunctionSource;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyCreateCommand;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyDetail;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyExplanation;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyFormOptions;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyView;
import com.cloudhandson.vpdbackoffice.mapper.VpdPolicyMapper;
import java.util.List;
@@ -32,6 +33,24 @@ public class VpdPolicyService {
return mapper.findPolicies();
}
public VpdPolicyFormOptions formOptions() {
return new VpdPolicyFormOptions(
mapper.findPolicyNameOptions(),
mapper.findOwnerOptions(),
mapper.findFunctionOptions(),
List.of("SELECT", "INSERT", "UPDATE", "DELETE", "INDEX")
);
}
public VpdPolicyFormOptions emptyFormOptions() {
return new VpdPolicyFormOptions(
List.of(),
List.of(),
List.of(),
List.of("SELECT", "INSERT", "UPDATE", "DELETE", "INDEX")
);
}
public VpdFunctionSource findFunctionSource(String owner, String packageName, String functionName) {
String normalizedOwner = requiredIdentifier(owner, "Function owner");
String normalizedFunction = requiredIdentifier(functionName, "Function name");
@@ -59,12 +78,23 @@ public class VpdPolicyService {
String objectName = requiredIdentifier(command.objectName(), "Object name");
String policyName = requiredIdentifier(command.policyName(), "Policy name");
String currentUser = jdbcTemplate.queryForObject("SELECT USER FROM dual", String.class);
String functionOwner = command.functionOwner() == null || command.functionOwner().isBlank()
? currentUser
: requiredIdentifier(command.functionOwner(), "Function owner");
String functionName = command.functionName() == null || command.functionName().isBlank()
? generatedFunctionName(policyName)
: requiredIdentifier(command.functionName(), "Function name");
FunctionRef functionRef = parseFunctionRef(command.functionKey());
String functionOwner;
String packageName;
String functionName;
if (functionRef != null) {
functionOwner = functionRef.owner();
packageName = functionRef.packageName();
functionName = functionRef.functionName();
} else {
functionOwner = command.functionOwner() == null || command.functionOwner().isBlank()
? currentUser
: requiredIdentifier(command.functionOwner(), "Function owner");
packageName = null;
functionName = command.functionName() == null || command.functionName().isBlank()
? generatedFunctionName(policyName)
: requiredIdentifier(command.functionName(), "Function name");
}
String statementTypes = normalizeStatementTypes(command.statementTypes());
String filterPredicate = command.filterPredicate() == null ? "" : command.filterPredicate().trim();
@@ -95,7 +125,7 @@ public class VpdPolicyService {
objectName,
policyName,
functionOwner,
functionName,
packageName == null ? functionName : packageName + "." + functionName,
statementTypes);
}
@@ -271,6 +301,22 @@ public class VpdPolicyService {
return generated.length() > 128 ? generated.substring(0, 128) : generated;
}
private FunctionRef parseFunctionRef(String functionKey) {
if (functionKey == null || functionKey.isBlank()) {
return null;
}
String[] parts = functionKey.trim().toUpperCase(Locale.ROOT).split("\\.");
if (parts.length == 2) {
return new FunctionRef(requiredIdentifier(parts[0], "Function owner"), null,
requiredIdentifier(parts[1], "Function name"));
}
if (parts.length == 3) {
return new FunctionRef(requiredIdentifier(parts[0], "Function owner"),
requiredIdentifier(parts[1], "Package name"), requiredIdentifier(parts[2], "Function name"));
}
throw new AppException("Function 선택 값이 올바르지 않습니다: " + functionKey);
}
private String normalizeStatementTypes(String value) {
String raw = value == null || value.isBlank() ? "SELECT" : value;
List<String> statements = List.of(raw.split(",")).stream()
@@ -293,6 +339,9 @@ public class VpdPolicyService {
return value.replace("'", "''");
}
private record FunctionRef(String owner, String packageName, String functionName) {
}
private String policyFunctionArgument(VpdPolicyView policy) {
if (policy.packageName() == null || policy.packageName().isBlank()) {
return policy.functionName();

View File

@@ -29,11 +29,13 @@ public class VpdPolicyController {
try {
model.addAttribute("policies", vpdPolicyService.findPolicies());
model.addAttribute("objects", protectedObjectService.findEnabled());
model.addAttribute("formOptions", vpdPolicyService.formOptions());
} catch (DataAccessException exception) {
RuntimeErrorMessage message = RuntimeErrorMessages.dataAccess(exception);
model.addAttribute("runtimeError", message);
model.addAttribute("policies", List.of());
model.addAttribute("objects", List.of());
model.addAttribute("formOptions", vpdPolicyService.emptyFormOptions());
}
return "vpd-policies";
}
@@ -42,9 +44,10 @@ public class VpdPolicyController {
public String createPolicy(
@RequestParam String objectKey,
@RequestParam String policyName,
@RequestParam(required = false) String functionKey,
@RequestParam(required = false) String functionOwner,
@RequestParam(required = false) String functionName,
@RequestParam(defaultValue = "SELECT") String statementTypes,
@RequestParam(defaultValue = "SELECT") List<String> statementTypes,
@RequestParam(defaultValue = "false") boolean enabled,
@RequestParam(defaultValue = "false") boolean updateCheck,
@RequestParam(required = false) String filterPredicate,
@@ -59,9 +62,10 @@ public class VpdPolicyController {
objectParts[0],
objectParts[1],
policyName,
functionKey,
functionOwner,
functionName,
statementTypes,
String.join(",", statementTypes),
enabled,
updateCheck,
filterPredicate