459 lines
13 KiB
Java
459 lines
13 KiB
Java
package com.cloudhandson.vpdbackoffice.service;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
|
|
import com.cloudhandson.vpdbackoffice.domain.audit.AuditEvent;
|
|
import com.cloudhandson.vpdbackoffice.domain.permission.AppRole;
|
|
import com.cloudhandson.vpdbackoffice.domain.permission.PermissionRule;
|
|
import com.cloudhandson.vpdbackoffice.domain.permission.PermissionSet;
|
|
import com.cloudhandson.vpdbackoffice.domain.permission.PermissionSetCommand;
|
|
import com.cloudhandson.vpdbackoffice.domain.permission.PermissionView;
|
|
import com.cloudhandson.vpdbackoffice.domain.permission.RuleCommand;
|
|
import com.cloudhandson.vpdbackoffice.domain.protectedobject.ProtectedColumn;
|
|
import com.cloudhandson.vpdbackoffice.domain.protectedobject.ProtectedObject;
|
|
import com.cloudhandson.vpdbackoffice.mapper.AuditMapper;
|
|
import com.cloudhandson.vpdbackoffice.mapper.PermissionMapper;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
class PermissionServiceTest {
|
|
|
|
private PermissionMapper permissionMapper;
|
|
private ProtectedObjectService protectedObjectService;
|
|
private PermissionService permissionService;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
permissionMapper = new FakePermissionMapper();
|
|
AuditService auditService = new AuditService(new NoopAuditMapper());
|
|
protectedObjectService = new ProtectedObjectService(null, auditService) {
|
|
@Override
|
|
public ProtectedObject assertEnabled(long objectId) {
|
|
return new ProtectedObject(1L, "ADMIN", "CB_V_SEARCH_DOCUMENTS", "cb-agent-security/vpd/documents", "Y");
|
|
}
|
|
|
|
@Override
|
|
public List<ProtectedColumn> findColumns(long objectId) {
|
|
return List.of(
|
|
new ProtectedColumn(1L, 1L, "DEPT_CODE", "N", null, "INTERNAL", "NONE"),
|
|
new ProtectedColumn(2L, 1L, "OWNER_EMP_NO", "N", null, "INTERNAL", "NONE"),
|
|
new ProtectedColumn(3L, 1L, "TECH_TAG", "N", null, "PUBLIC", "NONE")
|
|
);
|
|
}
|
|
};
|
|
permissionService = new PermissionService(permissionMapper, protectedObjectService, auditService);
|
|
}
|
|
|
|
@Test
|
|
void rejectsAllRuleMixedWithSpecificRule() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand(null, "ALL", null), new RuleCommand("DEPT_CODE", "=", "APAC")),
|
|
List.of()
|
|
);
|
|
|
|
assertThatThrownBy(() -> permissionService.savePermissionSet(command))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("ALL");
|
|
}
|
|
|
|
@Test
|
|
void rejectsCustomPredicateRule() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand("DEPT_CODE", "CUSTOM_PREDICATE", "1=1")),
|
|
List.of()
|
|
);
|
|
|
|
assertThatThrownBy(() -> permissionService.savePermissionSet(command))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("허용되지 않은");
|
|
}
|
|
|
|
@Test
|
|
void allowsDefaultColumnsForContextRules() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand(null, "MY_DEPT", null), new RuleCommand(null, "SELF", null)),
|
|
List.of()
|
|
);
|
|
|
|
permissionService.savePermissionSet(command);
|
|
|
|
FakePermissionMapper mapper = (FakePermissionMapper) permissionMapper;
|
|
assertThat(mapper.insertedRules)
|
|
.extracting(PermissionRule::ruleType)
|
|
.containsExactly("MY_DEPT", "SELF");
|
|
assertThat(mapper.insertedRules)
|
|
.extracting(PermissionRule::ruleColumn)
|
|
.containsExactly(null, null);
|
|
}
|
|
|
|
@Test
|
|
void acceptsDeptAndEmpNoRulesWithDefaultColumnsAndValues() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand(null, "DEPT", "HR"), new RuleCommand(null, "EMP_NO", "E2001")),
|
|
List.of()
|
|
);
|
|
|
|
permissionService.savePermissionSet(command);
|
|
|
|
FakePermissionMapper mapper = (FakePermissionMapper) permissionMapper;
|
|
assertThat(mapper.insertedRules)
|
|
.extracting(PermissionRule::ruleType)
|
|
.containsExactly("DEPT", "EMP_NO");
|
|
assertThat(mapper.insertedRules)
|
|
.extracting(PermissionRule::ruleValue)
|
|
.containsExactly("HR", "E2001");
|
|
}
|
|
|
|
@Test
|
|
void rejectsDeptRuleWithoutValue() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand(null, "DEPT", "")),
|
|
List.of()
|
|
);
|
|
|
|
assertThatThrownBy(() -> permissionService.savePermissionSet(command))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("값이 필요");
|
|
}
|
|
|
|
@Test
|
|
void acceptsMultipleTagRulesAsOrCandidates() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(
|
|
new RuleCommand(null, "TAG", "spring_boot"),
|
|
new RuleCommand(null, "TAG", "oracle_vpd")
|
|
),
|
|
List.of()
|
|
);
|
|
|
|
permissionService.savePermissionSet(command);
|
|
|
|
FakePermissionMapper mapper = (FakePermissionMapper) permissionMapper;
|
|
assertThat(mapper.insertedRules)
|
|
.extracting(PermissionRule::ruleType)
|
|
.containsExactly("TAG", "TAG");
|
|
assertThat(mapper.insertedRules)
|
|
.extracting(PermissionRule::ruleColumn)
|
|
.containsExactly(null, null);
|
|
assertThat(mapper.insertedRules)
|
|
.extracting(PermissionRule::ruleValue)
|
|
.containsExactly("SPRING_BOOT", "ORACLE_VPD");
|
|
}
|
|
|
|
@Test
|
|
void acceptsStakeholderContextRulesWithAnExplicitObjectColumn() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(
|
|
new RuleCommand("DEPT_CODE", "STAKEHOLDER_SELF", "설계사"),
|
|
new RuleCommand("OWNER_EMP_NO", "STAKEHOLDER_CHANNEL", "지점장")
|
|
),
|
|
List.of()
|
|
);
|
|
|
|
permissionService.savePermissionSet(command);
|
|
|
|
FakePermissionMapper mapper = (FakePermissionMapper) permissionMapper;
|
|
assertThat(mapper.insertedRules)
|
|
.extracting(PermissionRule::ruleType)
|
|
.containsExactly("STAKEHOLDER_SELF", "STAKEHOLDER_CHANNEL");
|
|
}
|
|
|
|
@Test
|
|
void rejectsStakeholderContextRuleWithoutAnObjectColumn() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand(null, "STAKEHOLDER_SELF", null)),
|
|
List.of()
|
|
);
|
|
|
|
assertThatThrownBy(() -> permissionService.savePermissionSet(command))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("컬럼이 필요");
|
|
}
|
|
|
|
@Test
|
|
void rejectsStakeholderContextRuleWithoutExpectedRole() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand("DEPT_CODE", "STAKEHOLDER_SELF", null)),
|
|
List.of()
|
|
);
|
|
|
|
assertThatThrownBy(() -> permissionService.savePermissionSet(command))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("값이 필요");
|
|
}
|
|
|
|
@Test
|
|
void acceptsMultipleVisibleColumns() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand("DEPT_CODE", "=", "HR")),
|
|
List.of("DEPT_CODE", "OWNER_EMP_NO")
|
|
);
|
|
|
|
permissionService.savePermissionSet(command);
|
|
|
|
FakePermissionMapper mapper = (FakePermissionMapper) permissionMapper;
|
|
assertThat(mapper.insertedVisibleColumns).containsExactly("DEPT_CODE", "OWNER_EMP_NO");
|
|
}
|
|
|
|
@Test
|
|
void rejectsTagRuleWithoutValue() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand(null, "TAG", "")),
|
|
List.of()
|
|
);
|
|
|
|
assertThatThrownBy(() -> permissionService.savePermissionSet(command))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("값이 필요");
|
|
}
|
|
|
|
@Test
|
|
void rejectsUnsafeTagValue() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"ALLOW",
|
|
List.of(new RuleCommand(null, "TAG", "SPRING' OR '1'='1")),
|
|
List.of()
|
|
);
|
|
|
|
assertThatThrownBy(() -> permissionService.savePermissionSet(command))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("영문 대문자");
|
|
}
|
|
|
|
@Test
|
|
void acceptsDenyPermissionEffect() {
|
|
var command = new PermissionSetCommand(
|
|
10L,
|
|
1L,
|
|
"SELECT",
|
|
"DENY",
|
|
List.of(new RuleCommand("DEPT_CODE", "=", "HR")),
|
|
List.of()
|
|
);
|
|
|
|
permissionService.savePermissionSet(command);
|
|
|
|
FakePermissionMapper mapper = (FakePermissionMapper) permissionMapper;
|
|
assertThat(mapper.insertedEffect).isEqualTo("DENY");
|
|
}
|
|
|
|
@Test
|
|
void disablesProtectedObjectWhenLastPermissionIsDeleted() {
|
|
var mapper = new FakePermissionMapper();
|
|
boolean[] disabled = {false};
|
|
AuditService auditService = new AuditService(new NoopAuditMapper());
|
|
ProtectedObjectService objectService = new ProtectedObjectService(null, auditService) {
|
|
@Override
|
|
public void disableObject(long objectId) {
|
|
disabled[0] = objectId == 1L;
|
|
}
|
|
};
|
|
PermissionService service = new PermissionService(mapper, objectService, auditService);
|
|
|
|
service.deletePermission(1000L);
|
|
|
|
assertThat(disabled[0]).isTrue();
|
|
}
|
|
|
|
@Test
|
|
void rejectsPermissionDeleteWithoutImpactConfirmation() {
|
|
assertThatThrownBy(() -> permissionService.deletePermission(1000L, false))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("영향 확인");
|
|
}
|
|
|
|
@Test
|
|
void blocksRoleDeleteWhenDependenciesRemain() {
|
|
var mapper = new FakePermissionMapper();
|
|
mapper.userRoleCount = 1;
|
|
PermissionService service = new PermissionService(mapper, protectedObjectService, new AuditService(new NoopAuditMapper()));
|
|
|
|
assertThatThrownBy(() -> service.deleteRole(10L, true))
|
|
.isInstanceOf(AppException.class)
|
|
.hasMessageContaining("먼저 해제");
|
|
}
|
|
|
|
private static class NoopAuditMapper implements AuditMapper {
|
|
@Override
|
|
public void insert(AuditEvent event) {
|
|
}
|
|
}
|
|
|
|
private static class FakePermissionMapper implements PermissionMapper {
|
|
private final List<PermissionRule> insertedRules = new ArrayList<>();
|
|
private final List<String> insertedVisibleColumns = new ArrayList<>();
|
|
|
|
@Override
|
|
public List<AppRole> findRoles() {
|
|
return List.of(new AppRole(10L, "HR_DEPT_ROLE", null, "INTERNAL"));
|
|
}
|
|
|
|
@Override
|
|
public AppRole findRole(long roleId) {
|
|
return roleId == 10L ? new AppRole(10L, "HR_DEPT_ROLE", null, "INTERNAL") : null;
|
|
}
|
|
|
|
@Override
|
|
public long nextRoleId() {
|
|
return 40L;
|
|
}
|
|
|
|
@Override
|
|
public void insertRole(long roleId, String roleName, String description, String maxSensitivityLevel) {
|
|
}
|
|
|
|
@Override
|
|
public int updateRoleMaxSensitivity(long roleId, String maxSensitivityLevel) {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public int deleteRole(long roleId) {
|
|
return 1;
|
|
}
|
|
|
|
private int userRoleCount;
|
|
private int groupRoleCount;
|
|
private int permissionCountByRole;
|
|
|
|
@Override
|
|
public int countUserRolesByRoleId(long roleId) {
|
|
return userRoleCount;
|
|
}
|
|
|
|
@Override
|
|
public int countGroupRolesByRoleId(long roleId) {
|
|
return groupRoleCount;
|
|
}
|
|
|
|
@Override
|
|
public int countPermissionsByRoleId(long roleId) {
|
|
return permissionCountByRole;
|
|
}
|
|
|
|
@Override
|
|
public List<PermissionView> findPermissionViews() {
|
|
return List.of();
|
|
}
|
|
|
|
@Override
|
|
public PermissionSet findPermissionSet(long roleId, long objectId) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Long findPermissionId(long roleId, long objectId) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Long findObjectIdByPermissionId(long permissionId) {
|
|
return permissionId == 1000L ? 1L : null;
|
|
}
|
|
|
|
@Override
|
|
public int countPermissionsByObjectId(long objectId) {
|
|
return 0;
|
|
}
|
|
|
|
private String insertedEffect;
|
|
|
|
@Override
|
|
public void insertPermission(long permissionId, long roleId, long objectId, String action, String permissionEffect) {
|
|
insertedEffect = permissionEffect;
|
|
}
|
|
|
|
@Override
|
|
public void updatePermissionAction(long permissionId, String action) {
|
|
}
|
|
|
|
@Override
|
|
public void updatePermissionEffect(long permissionId, String permissionEffect) {
|
|
insertedEffect = permissionEffect;
|
|
}
|
|
|
|
@Override
|
|
public void deleteRules(long permissionId) {
|
|
}
|
|
|
|
@Override
|
|
public void insertRule(PermissionRule rule) {
|
|
insertedRules.add(rule);
|
|
}
|
|
|
|
@Override
|
|
public void deleteVisibleColumns(long permissionId) {
|
|
}
|
|
|
|
@Override
|
|
public void insertVisibleColumn(long permissionId, String columnName) {
|
|
insertedVisibleColumns.add(columnName);
|
|
}
|
|
|
|
@Override
|
|
public int deletePermission(long permissionId) {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public long nextPermissionId() {
|
|
return 1000L;
|
|
}
|
|
|
|
@Override
|
|
public long nextRuleId() {
|
|
return 10000L;
|
|
}
|
|
}
|
|
}
|