Consolidate data access control backoffice updates

This commit is contained in:
devmrko
2026-07-13 23:06:23 +09:00
parent 403298d474
commit e18b30feab
181 changed files with 11571 additions and 954 deletions

View File

@@ -1,12 +1,15 @@
package com.cloudhandson.vpdbackoffice.web;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyCreateCommand;
import com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyView;
import com.cloudhandson.vpdbackoffice.service.AppException;
import com.cloudhandson.vpdbackoffice.service.VpdPolicyService;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@@ -17,6 +20,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
public class VpdPolicyController {
private static final Logger log = LoggerFactory.getLogger(VpdPolicyController.class);
private final VpdPolicyService vpdPolicyService;
public VpdPolicyController(VpdPolicyService vpdPolicyService) {
@@ -28,7 +32,7 @@ public class VpdPolicyController {
@RequestParam(required = false) String schemaOwner,
Model model
) {
populatePolicyModel(schemaOwner, model);
populatePolicyModel(schemaOwner, false, model);
return "vpd-policies";
}
@@ -37,41 +41,95 @@ public class VpdPolicyController {
@RequestParam(required = false) String schemaOwner,
Model model
) {
populatePolicyModel(schemaOwner, model);
populatePolicyModel(schemaOwner, true, model);
return "vpd-filter-policies";
}
private void populatePolicyModel(String schemaOwner, Model model) {
/** Read-only operational view of the default dynamic permission filter. */
@GetMapping("/vpd-filter-runtime")
public String filterRuntime(Model model) {
try {
List<VpdPolicyView> policies = vpdPolicyService.findPolicies().stream()
.filter(VpdPolicyView::permissionSystemDefault)
.toList();
model.addAttribute("policies", policies);
if (!policies.isEmpty()) {
VpdPolicyView filter = policies.get(0);
model.addAttribute("source", vpdPolicyService.findFunctionSource(
filter.functionOwner(), filter.packageName(), filter.functionName()));
}
} catch (DataAccessException exception) {
RuntimeErrorMessage message = RuntimeErrorMessages.dataAccess(exception);
model.addAttribute("runtimeError", message);
model.addAttribute("policies", List.of());
} catch (AppException exception) {
model.addAttribute("errorMessage", exception.getMessage());
model.addAttribute("policies", List.of());
}
return "vpd-filter-runtime";
}
private void populatePolicyModel(String schemaOwner, boolean includeFilterEditor, Model model) {
try {
long started = System.nanoTime();
String selectedSchemaOwner = schemaOwner == null ? "" : schemaOwner.trim().toUpperCase();
List<com.cloudhandson.vpdbackoffice.domain.vpd.VpdPolicyView> policies = vpdPolicyService.findPolicies();
long policiesAt = System.nanoTime();
Map<String, String> policyDescriptions = new LinkedHashMap<>();
policies.forEach(policy -> policyDescriptions.put(
policy.objectDisplayName() + "|" + policy.policyName(),
vpdPolicyService.findPolicyDescription(policy.objectOwner(), policy.objectName(), policy.policyName())
));
if (!includeFilterEditor) {
policyDescriptions.putAll(vpdPolicyService.findPolicyDescriptionMap());
policies.forEach(policy -> policyDescriptions.put(
policy.objectDisplayName() + "|" + policy.policyName(),
policyDescriptions.getOrDefault(
policy.objectDisplayName() + "|" + policy.policyName(),
policy.objectDisplayName() + "에 요청마다 현재 권한체계의 행 접근 조건을 적용하는 "
+ policy.policyName() + " policy입니다."
)
));
}
long policyDescriptionsAt = System.nanoTime();
model.addAttribute("policies", policies);
model.addAttribute("policyDescriptions", policyDescriptions);
model.addAttribute("vpdTargets", vpdPolicyService.findVpdTargets(selectedSchemaOwner));
model.addAttribute("vpdTargets", includeFilterEditor
? List.of()
: vpdPolicyService.findVpdTargets(selectedSchemaOwner));
long targetsAt = System.nanoTime();
model.addAttribute("selectedSchemaOwner", selectedSchemaOwner);
var formOptions = vpdPolicyService.formOptions();
long formOptionsAt = System.nanoTime();
Map<String, String> filterDescriptions = new LinkedHashMap<>();
formOptions.functions().forEach(function -> filterDescriptions.put(
function.owner() + "|" + function.functionName(),
vpdPolicyService.findFilterDescription(function.owner(), function.functionName())
));
filterDescriptions.putAll(vpdPolicyService.findFilterDescriptionMap());
policies.forEach(policy -> filterDescriptions.putIfAbsent(
policy.functionOwner() + "|" + policy.functionName(),
vpdPolicyService.findFilterDescription(policy.functionOwner(), policy.functionName())
defaultFilterDescription(policy.functionName())
));
if (includeFilterEditor) {
formOptions.functions().forEach(function -> filterDescriptions.putIfAbsent(
function.owner() + "|" + function.functionName(),
defaultFilterDescription(function.functionName())
));
}
long filterDescriptionsAt = System.nanoTime();
Map<String, String> filterPredicates = new LinkedHashMap<>();
formOptions.functions().forEach(function -> filterPredicates.put(
function.owner() + "|" + function.functionName(),
vpdPolicyService.findFilterPredicate(function.owner(), function.packageName(), function.functionName())
));
if (includeFilterEditor) {
formOptions.functions().forEach(function -> filterPredicates.put(
function.owner() + "|" + function.functionName(),
vpdPolicyService.findFilterPredicate(function.owner(), function.packageName(), function.functionName())
));
}
long predicatesAt = System.nanoTime();
model.addAttribute("formOptions", formOptions);
model.addAttribute("filterDescriptions", filterDescriptions);
model.addAttribute("filterPredicates", filterPredicates);
log.info("vpd page timings: editor={} policies={}ms policyDescriptions={}ms targets={}ms formOptions={}ms filterDescriptions={}ms predicates={}ms total={}ms",
includeFilterEditor,
elapsedMillis(started, policiesAt),
elapsedMillis(policiesAt, policyDescriptionsAt),
elapsedMillis(policyDescriptionsAt, targetsAt),
elapsedMillis(targetsAt, formOptionsAt),
elapsedMillis(formOptionsAt, filterDescriptionsAt),
elapsedMillis(filterDescriptionsAt, predicatesAt),
elapsedMillis(started, predicatesAt));
} catch (DataAccessException exception) {
RuntimeErrorMessage message = RuntimeErrorMessages.dataAccess(exception);
model.addAttribute("runtimeError", message);
@@ -94,6 +152,16 @@ public class VpdPolicyController {
}
}
private static String defaultFilterDescription(String functionName) {
return "CB_AGENT_DOC_VPD_FILTER".equalsIgnoreCase(functionName)
? "사용자·그룹·역할·TAG 권한을 동적으로 합쳐 VPD predicate를 반환합니다."
: "이 Filter function이 반환하는 predicate로 조회 행을 제한합니다.";
}
private static long elapsedMillis(long started, long finished) {
return (finished - started) / 1_000_000;
}
@PostMapping("/vpd-policies")
public String createPolicy(
@RequestParam String objectKey,