[Developer] #619 focus POC on KB VPD permissions

This commit is contained in:
devmrko
2026-07-07 16:02:22 +09:00
parent ef944a6758
commit 2ef04e6ab2
18 changed files with 1238 additions and 167 deletions

View File

@@ -388,8 +388,8 @@ function syncRuleTypeHints(root = document) {
}
const type = typeSelect.value;
const valueRequired = ['=', '!=', 'DEPT', 'EMP_NO', 'TAG', 'STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL'].includes(type);
const columnOptional = ['ALL', 'MY_DEPT', 'SELF', 'DEPT', 'EMP_NO', 'TAG'].includes(type);
const valueRequired = ['=', '!=', 'DEPT', 'EMP_NO', 'TAG', 'STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL', 'STATIC_SQL'].includes(type);
const columnOptional = ['ALL', 'MY_DEPT', 'SELF', 'DEPT', 'EMP_NO', 'TAG', 'STATIC_SQL'].includes(type);
const placeholderByType = {
ALL: '값 불필요',
MY_DEPT: '값 불필요',
@@ -399,17 +399,30 @@ function syncRuleTypeHints(root = document) {
TAG: '예: SPRING_BOOT',
STAKEHOLDER_SELF: '예: 설계사',
STAKEHOLDER_CHANNEL: '예: 지점장',
TOKEN_SUBJECT: '값 불필요',
OWN_CONTRACT: '값 불필요',
CHANNEL_CONTRACT: '값 불필요',
OWN_CUSTOMER: '값 불필요',
CHANNEL_CUSTOMER: '값 불필요',
STATIC_SQL: "예: CONTRACT_STATUS = '정상'",
'=': '비교 값',
'!=': '비교 값'
};
columnSelect.required = !columnOptional;
columnSelect.disabled = ['ALL', 'MY_DEPT', 'SELF', 'DEPT', 'EMP_NO', 'TAG', 'STATIC_SQL'].includes(type);
valueInput.required = valueRequired;
valueInput.disabled = ['ALL', 'MY_DEPT', 'SELF'].includes(type);
valueInput.disabled = [
'ALL', 'MY_DEPT', 'SELF', 'TOKEN_SUBJECT', 'OWN_CONTRACT',
'CHANNEL_CONTRACT', 'OWN_CUSTOMER', 'CHANNEL_CUSTOMER'
].includes(type);
valueInput.placeholder = placeholderByType[type] || '비교 값';
if (valueInput.disabled) {
valueInput.value = '';
}
if (columnSelect.disabled) {
columnSelect.value = '';
}
});
}
@@ -700,17 +713,29 @@ function collectWizardRules(root) {
TAG: 'TECH_TAG',
STAKEHOLDER_SELF: '선택 컬럼',
STAKEHOLDER_CHANNEL: '선택 컬럼',
TOKEN_SUBJECT: '선택 컬럼',
OWN_CONTRACT: '선택 컬럼',
CHANNEL_CONTRACT: '선택 컬럼',
OWN_CUSTOMER: '선택 컬럼',
CHANNEL_CUSTOMER: '선택 컬럼',
STATIC_SQL: '',
ALL: ''
}[type] || '';
if (type === 'ALL') {
return 'ALL';
}
if (type === 'STATIC_SQL') {
return `정적 SQL: ${value}`;
}
if (['MY_DEPT', 'SELF'].includes(type)) {
return `${displayColumn} ${type}`;
}
if (['STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL'].includes(type)) {
return `${displayColumn} ${type} 역할=${value}`;
}
if (['TOKEN_SUBJECT', 'OWN_CONTRACT', 'CHANNEL_CONTRACT', 'OWN_CUSTOMER', 'CHANNEL_CUSTOMER'].includes(type)) {
return `${displayColumn} ${type}`;
}
if (type === 'TAG') {
return `${displayColumn} TAG ${value.toUpperCase()}`;
}
@@ -731,11 +756,20 @@ function collectWizardPredicates(root) {
TAG: 'TECH_TAG',
STAKEHOLDER_SELF: '선택 컬럼',
STAKEHOLDER_CHANNEL: '선택 컬럼',
TOKEN_SUBJECT: '선택 컬럼',
OWN_CONTRACT: '선택 컬럼',
CHANNEL_CONTRACT: '선택 컬럼',
OWN_CUSTOMER: '선택 컬럼',
CHANNEL_CUSTOMER: '선택 컬럼',
STATIC_SQL: '',
ALL: ''
}[type] || '';
if (type === 'ALL') {
return '1 = 1';
}
if (type === 'STATIC_SQL') {
return value;
}
if (type === 'MY_DEPT') {
return `${displayColumn} = SYS_CONTEXT('CB_AGENT_CTX', 'DEPT_CODE')`;
}
@@ -748,6 +782,21 @@ function collectWizardPredicates(root) {
if (type === 'STAKEHOLDER_CHANNEL') {
return `EXISTS (KB_CONTRACTS c: c.${displayColumn} = <현재행>.${displayColumn} AND TOKEN_ROLE = '${value}' AND c.FC_CHANNEL = TOKEN_CHANNEL)`;
}
if (type === 'TOKEN_SUBJECT') {
return `${displayColumn} = SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID')`;
}
if (type === 'OWN_CONTRACT') {
return `${displayColumn} = SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID')`;
}
if (type === 'CHANNEL_CONTRACT') {
return `${displayColumn} = SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_CHANNEL')`;
}
if (type === 'OWN_CUSTOMER') {
return `${displayColumn} IN (SELECT CUST_ID FROM KB_CONTRACTS WHERE FC_ID = SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID'))`;
}
if (type === 'CHANNEL_CUSTOMER') {
return `${displayColumn} IN (SELECT CUST_ID FROM KB_CONTRACTS WHERE FC_CHANNEL = SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_CHANNEL'))`;
}
if (type === 'DEPT' || type === 'EMP_NO') {
return `${displayColumn} = ${sqlLiteral(value)}`;
}
@@ -794,8 +843,8 @@ function updatePermissionWizardPreview(root = document) {
? `거부 규칙: ${ruleText}`
: `허용 규칙: ${ruleText}`;
const predicateText = effect === 'DENY'
? (rules.includes('ALL') ? 'DENY ALL: 1 = 0' : `DENY 후보: NOT (${predicates.join(' OR ') || '조건 없음'})`)
: `ALLOW 후보: ${predicates.join(' OR ') || '조건 없음'}`;
? (rules.includes('ALL') ? 'DENY ALL: 1 = 0' : `DENY 후보: NOT (${predicates.join(' AND ') || '조건 없음'})`)
: `ALLOW 후보: ${predicates.join(' AND ') || '조건 없음'}`;
const columnPolicy = visibleColumns
? `이 권한에서 원문 표시 허용: ${visibleColumns}`
: '마스킹 대상 컬럼은 기본 정책대로 NULL/마스킹 처리';
@@ -872,19 +921,25 @@ function permissionWizardRuleValidationMessage(wizard) {
if (values.some((rule) => rule.type === 'ALL') && values.length > 1) {
return 'ALL 규칙은 다른 조건 규칙과 함께 저장할 수 없습니다. ALL만 남기거나 ALL을 삭제하세요.';
}
const valueRequired = ['=', '!=', 'DEPT', 'EMP_NO', 'TAG', 'STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL'];
const valueRequired = ['=', '!=', 'DEPT', 'EMP_NO', 'TAG', 'STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL', 'STATIC_SQL'];
const seen = new Set();
for (const [index, rule] of values.entries()) {
const position = index + 1;
if (!rule.type) {
return `${position}번째 행 규칙 유형을 선택하세요.`;
}
if (['=', '!=', 'STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL'].includes(rule.type) && !rule.column) {
if ([
'=', '!=', 'STAKEHOLDER_SELF', 'STAKEHOLDER_CHANNEL', 'TOKEN_SUBJECT',
'OWN_CONTRACT', 'CHANNEL_CONTRACT', 'OWN_CUSTOMER', 'CHANNEL_CUSTOMER'
].includes(rule.type) && !rule.column) {
return `${position}번째 ${rule.type} 규칙에는 비교할 컬럼이 필요합니다.`;
}
if (valueRequired.includes(rule.type) && !rule.value) {
return `${position}번째 ${rule.type} 규칙의 값을 입력하세요.`;
}
if (rule.type === 'STATIC_SQL' && rule.column) {
return `${position}번째 정적 SQL 조건은 컬럼을 따로 선택하지 마세요.`;
}
if (rule.type === 'TAG' && rule.value && !/^[A-Za-z0-9_-]+$/.test(rule.value)) {
return `${position}번째 TAG 값은 영문·숫자, '_' 또는 '-'만 사용할 수 있습니다.`;
}