feat #466: add deny permission effect
This commit is contained in:
30
docs/design/466-permission-deny-priority/README.md
Normal file
30
docs/design/466-permission-deny-priority/README.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# 설계서: 역할/권한 모델에 deny, 우선순위, 충돌 해소 규칙 추가 (#466)
|
||||||
|
|
||||||
|
> **상태**: Approved
|
||||||
|
> **작성**: [AI] Architect · **최종수정**: 2026-06-25
|
||||||
|
> **추적성** — Redmine: #466 · 관련 ADR: 없음
|
||||||
|
> · 구현 파일: `cb_permission.permission_effect`, `PermissionService`, `PermissionMapper`, `cb_agent_doc_vpd_filter` · 테스트: SQLcl `27`, `mvn test`
|
||||||
|
|
||||||
|
## 1. 목적
|
||||||
|
|
||||||
|
역할이 여러 개 붙은 사용자에게 allow 권한과 deny 권한이 동시에 존재할 때 최종 접근 범위를 명확히 계산한다.
|
||||||
|
|
||||||
|
## 2. 범위
|
||||||
|
|
||||||
|
- 포함: permission 단위 `ALLOW/DENY` 효과 추가, deny 우선 VPD predicate 생성, UI 저장/목록 표시, 테스트.
|
||||||
|
- 제외: role 자체 우선순위 숫자, 승인 workflow, rule별 deny.
|
||||||
|
|
||||||
|
## 3. 인수조건
|
||||||
|
|
||||||
|
- [ ] `cb_permission.permission_effect`가 `ALLOW` 기본값으로 추가된다.
|
||||||
|
- [ ] deny permission은 같은 object/action의 allow 결과에서 제외된다.
|
||||||
|
- [ ] deny `ALL`이 있으면 최종 결과는 `1 = 0`이다.
|
||||||
|
- [ ] allow가 없으면 deny가 있어도 `1 = 0`이다.
|
||||||
|
- [ ] UI에서 권한 효과를 선택하고 목록에서 확인할 수 있다.
|
||||||
|
|
||||||
|
## 4. 설계 결정
|
||||||
|
|
||||||
|
- 효과는 `cb_permission` 단위로 둔다. 하나의 permission set이 allow인지 deny인지 명확히 구분한다.
|
||||||
|
- 최종 predicate는 `(<allow predicate>) AND NOT (<deny predicate>)`이다.
|
||||||
|
- allow `ALL`은 `1 = 1`, deny `ALL`은 즉시 `1 = 0`이다.
|
||||||
|
- deny만 있고 allow가 없으면 whitelist 기본값에 따라 `1 = 0`이다.
|
||||||
@@ -65,7 +65,8 @@ CREATE TABLE cb_permission (
|
|||||||
perm_id NUMBER PRIMARY KEY,
|
perm_id NUMBER PRIMARY KEY,
|
||||||
role_id NUMBER NOT NULL REFERENCES cb_app_role(role_id),
|
role_id NUMBER NOT NULL REFERENCES cb_app_role(role_id),
|
||||||
target_name VARCHAR2(128) NOT NULL,
|
target_name VARCHAR2(128) NOT NULL,
|
||||||
action_name VARCHAR2(30) NOT NULL
|
action_name VARCHAR2(30) NOT NULL,
|
||||||
|
permission_effect VARCHAR2(10) DEFAULT 'ALLOW' NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE cb_permission_rule (
|
CREATE TABLE cb_permission_rule (
|
||||||
@@ -106,9 +107,9 @@ INSERT INTO cb_user_role VALUES (101, 10);
|
|||||||
INSERT INTO cb_user_role VALUES (102, 20);
|
INSERT INTO cb_user_role VALUES (102, 20);
|
||||||
INSERT INTO cb_user_role VALUES (103, 30);
|
INSERT INTO cb_user_role VALUES (103, 30);
|
||||||
|
|
||||||
INSERT INTO cb_permission VALUES (100, 10, 'CB_V_SEARCH_DOCUMENTS', 'SELECT');
|
INSERT INTO cb_permission VALUES (100, 10, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'ALLOW');
|
||||||
INSERT INTO cb_permission VALUES (200, 20, 'CB_V_SEARCH_DOCUMENTS', 'SELECT');
|
INSERT INTO cb_permission VALUES (200, 20, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'ALLOW');
|
||||||
INSERT INTO cb_permission VALUES (300, 30, 'CB_V_SEARCH_DOCUMENTS', 'SELECT');
|
INSERT INTO cb_permission VALUES (300, 30, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'ALLOW');
|
||||||
|
|
||||||
INSERT INTO cb_permission_rule VALUES (1000, 100, 'MY_DEPT', NULL);
|
INSERT INTO cb_permission_rule VALUES (1000, 100, 'MY_DEPT', NULL);
|
||||||
INSERT INTO cb_permission_rule VALUES (2000, 200, 'SELF', NULL);
|
INSERT INTO cb_permission_rule VALUES (2000, 200, 'SELF', NULL);
|
||||||
@@ -256,7 +257,8 @@ AUTHID DEFINER
|
|||||||
AS
|
AS
|
||||||
v_user_id NUMBER;
|
v_user_id NUMBER;
|
||||||
v_target VARCHAR2(128);
|
v_target VARCHAR2(128);
|
||||||
v_predicate VARCHAR2(32767);
|
v_allow_predicate VARCHAR2(32767);
|
||||||
|
v_deny_predicate VARCHAR2(32767);
|
||||||
|
|
||||||
FUNCTION quote_literal(p_value IN VARCHAR2) RETURN VARCHAR2 IS
|
FUNCTION quote_literal(p_value IN VARCHAR2) RETURN VARCHAR2 IS
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -294,16 +296,22 @@ AS
|
|||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END;
|
END;
|
||||||
|
|
||||||
PROCEDURE append_or(p_clause IN VARCHAR2) IS
|
PROCEDURE append_or(p_effect IN VARCHAR2, p_clause IN VARCHAR2) IS
|
||||||
BEGIN
|
BEGIN
|
||||||
IF p_clause IS NULL THEN
|
IF p_clause IS NULL THEN
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF v_predicate IS NULL THEN
|
IF p_effect = 'DENY' THEN
|
||||||
v_predicate := '(' || p_clause || ')';
|
IF v_deny_predicate IS NULL THEN
|
||||||
|
v_deny_predicate := '(' || p_clause || ')';
|
||||||
|
ELSE
|
||||||
|
v_deny_predicate := v_deny_predicate || ' OR (' || p_clause || ')';
|
||||||
|
END IF;
|
||||||
|
ELSIF v_allow_predicate IS NULL THEN
|
||||||
|
v_allow_predicate := '(' || p_clause || ')';
|
||||||
ELSE
|
ELSE
|
||||||
v_predicate := v_predicate || ' OR (' || p_clause || ')';
|
v_allow_predicate := v_allow_predicate || ' OR (' || p_clause || ')';
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -323,6 +331,7 @@ BEGIN
|
|||||||
FOR r IN (
|
FOR r IN (
|
||||||
SELECT UPPER(TRIM(r.rule_type)) AS rule_type,
|
SELECT UPPER(TRIM(r.rule_type)) AS rule_type,
|
||||||
UPPER(TRIM(r.rule_column)) AS rule_column,
|
UPPER(TRIM(r.rule_column)) AS rule_column,
|
||||||
|
NVL(UPPER(TRIM(p.permission_effect)), 'ALLOW') AS permission_effect,
|
||||||
r.rule_value
|
r.rule_value
|
||||||
FROM cb_user_role ur
|
FROM cb_user_role ur
|
||||||
JOIN cb_permission p
|
JOIN cb_permission p
|
||||||
@@ -338,46 +347,56 @@ BEGIN
|
|||||||
v_column VARCHAR2(128);
|
v_column VARCHAR2(128);
|
||||||
BEGIN
|
BEGIN
|
||||||
IF r.rule_type = 'ALL' THEN
|
IF r.rule_type = 'ALL' THEN
|
||||||
RETURN '1 = 1';
|
IF r.permission_effect = 'DENY' THEN
|
||||||
|
RETURN '1 = 0';
|
||||||
|
END IF;
|
||||||
|
v_allow_predicate := '(1 = 1)';
|
||||||
ELSIF r.rule_type = 'MY_DEPT' THEN
|
ELSIF r.rule_type = 'MY_DEPT' THEN
|
||||||
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')');
|
append_or(r.permission_effect, v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')');
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type = 'SELF' THEN
|
ELSIF r.rule_type = 'SELF' THEN
|
||||||
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')');
|
append_or(r.permission_effect, v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')');
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type = 'DEPT' THEN
|
ELSIF r.rule_type = 'DEPT' THEN
|
||||||
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or(v_column || ' = ' || quote_literal(r.rule_value));
|
append_or(r.permission_effect, v_column || ' = ' || quote_literal(r.rule_value));
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type = 'EMP_NO' THEN
|
ELSIF r.rule_type = 'EMP_NO' THEN
|
||||||
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or(v_column || ' = ' || quote_literal(r.rule_value));
|
append_or(r.permission_effect, v_column || ' = ' || quote_literal(r.rule_value));
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type = '=' THEN
|
ELSIF r.rule_type = '=' THEN
|
||||||
v_column := safe_column(p_schema, p_object, r.rule_column);
|
v_column := safe_column(p_schema, p_object, r.rule_column);
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or('TO_CHAR(' || v_column || ') = ' || quote_literal(r.rule_value));
|
append_or(r.permission_effect, 'TO_CHAR(' || v_column || ') = ' || quote_literal(r.rule_value));
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type IN ('!=', '<>') THEN
|
ELSIF r.rule_type IN ('!=', '<>') THEN
|
||||||
v_column := safe_column(p_schema, p_object, r.rule_column);
|
v_column := safe_column(p_schema, p_object, r.rule_column);
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or('TO_CHAR(' || v_column || ') <> ' || quote_literal(r.rule_value));
|
append_or(r.permission_effect, 'TO_CHAR(' || v_column || ') <> ' || quote_literal(r.rule_value));
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
IF v_predicate IS NULL THEN
|
IF v_allow_predicate IS NULL THEN
|
||||||
RETURN '1 = 0';
|
RETURN '1 = 0';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
RETURN '(' || v_predicate || ')';
|
IF v_deny_predicate IS NULL THEN
|
||||||
|
IF v_allow_predicate = '(1 = 1)' THEN
|
||||||
|
RETURN '1 = 1';
|
||||||
|
END IF;
|
||||||
|
RETURN '(' || v_allow_predicate || ')';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN '((' || v_allow_predicate || ') AND NOT (' || v_deny_predicate || '))';
|
||||||
END;
|
END;
|
||||||
/
|
/
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,16 @@ EXCEPTION
|
|||||||
END;
|
END;
|
||||||
/
|
/
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
EXECUTE IMMEDIATE 'ALTER TABLE cb_permission ADD (permission_effect VARCHAR2(10) DEFAULT ''ALLOW'' NOT NULL)';
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
IF SQLCODE != -1430 THEN
|
||||||
|
RAISE;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
|
||||||
CREATE TABLE cb_protected_object (
|
CREATE TABLE cb_protected_object (
|
||||||
object_id NUMBER PRIMARY KEY,
|
object_id NUMBER PRIMARY KEY,
|
||||||
owner VARCHAR2(128) NOT NULL,
|
owner VARCHAR2(128) NOT NULL,
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ AS
|
|||||||
-- no matched stored row rule means no rows are visible.
|
-- no matched stored row rule means no rows are visible.
|
||||||
v_user_id NUMBER;
|
v_user_id NUMBER;
|
||||||
v_target VARCHAR2(128);
|
v_target VARCHAR2(128);
|
||||||
v_predicate VARCHAR2(32767);
|
v_allow_predicate VARCHAR2(32767);
|
||||||
|
v_deny_predicate VARCHAR2(32767);
|
||||||
|
|
||||||
FUNCTION quote_literal(p_value IN VARCHAR2) RETURN VARCHAR2 IS
|
FUNCTION quote_literal(p_value IN VARCHAR2) RETURN VARCHAR2 IS
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -60,16 +61,22 @@ AS
|
|||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END;
|
END;
|
||||||
|
|
||||||
PROCEDURE append_or(p_clause IN VARCHAR2) IS
|
PROCEDURE append_or(p_effect IN VARCHAR2, p_clause IN VARCHAR2) IS
|
||||||
BEGIN
|
BEGIN
|
||||||
IF p_clause IS NULL THEN
|
IF p_clause IS NULL THEN
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF v_predicate IS NULL THEN
|
IF p_effect = 'DENY' THEN
|
||||||
v_predicate := '(' || p_clause || ')';
|
IF v_deny_predicate IS NULL THEN
|
||||||
|
v_deny_predicate := '(' || p_clause || ')';
|
||||||
|
ELSE
|
||||||
|
v_deny_predicate := v_deny_predicate || ' OR (' || p_clause || ')';
|
||||||
|
END IF;
|
||||||
|
ELSIF v_allow_predicate IS NULL THEN
|
||||||
|
v_allow_predicate := '(' || p_clause || ')';
|
||||||
ELSE
|
ELSE
|
||||||
v_predicate := v_predicate || ' OR (' || p_clause || ')';
|
v_allow_predicate := v_allow_predicate || ' OR (' || p_clause || ')';
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -89,6 +96,7 @@ BEGIN
|
|||||||
FOR r IN (
|
FOR r IN (
|
||||||
SELECT UPPER(TRIM(r.rule_type)) AS rule_type,
|
SELECT UPPER(TRIM(r.rule_type)) AS rule_type,
|
||||||
UPPER(TRIM(r.rule_column)) AS rule_column,
|
UPPER(TRIM(r.rule_column)) AS rule_column,
|
||||||
|
NVL(UPPER(TRIM(p.permission_effect)), 'ALLOW') AS permission_effect,
|
||||||
r.rule_value
|
r.rule_value
|
||||||
FROM cb_user_role ur
|
FROM cb_user_role ur
|
||||||
JOIN cb_permission p
|
JOIN cb_permission p
|
||||||
@@ -104,48 +112,58 @@ BEGIN
|
|||||||
v_column VARCHAR2(128);
|
v_column VARCHAR2(128);
|
||||||
BEGIN
|
BEGIN
|
||||||
IF r.rule_type = 'ALL' THEN
|
IF r.rule_type = 'ALL' THEN
|
||||||
RETURN '1 = 1';
|
IF r.permission_effect = 'DENY' THEN
|
||||||
|
RETURN '1 = 0';
|
||||||
|
END IF;
|
||||||
|
v_allow_predicate := '(1 = 1)';
|
||||||
ELSIF r.rule_type = 'MY_DEPT' THEN
|
ELSIF r.rule_type = 'MY_DEPT' THEN
|
||||||
-- NULL rule_column keeps backward compatibility with the demo seed.
|
-- NULL rule_column keeps backward compatibility with the demo seed.
|
||||||
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')');
|
append_or(r.permission_effect, v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')');
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type = 'SELF' THEN
|
ELSIF r.rule_type = 'SELF' THEN
|
||||||
-- NULL rule_column keeps backward compatibility with the demo seed.
|
-- NULL rule_column keeps backward compatibility with the demo seed.
|
||||||
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')');
|
append_or(r.permission_effect, v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')');
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type = 'DEPT' THEN
|
ELSIF r.rule_type = 'DEPT' THEN
|
||||||
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or(v_column || ' = ' || quote_literal(r.rule_value));
|
append_or(r.permission_effect, v_column || ' = ' || quote_literal(r.rule_value));
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type = 'EMP_NO' THEN
|
ELSIF r.rule_type = 'EMP_NO' THEN
|
||||||
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or(v_column || ' = ' || quote_literal(r.rule_value));
|
append_or(r.permission_effect, v_column || ' = ' || quote_literal(r.rule_value));
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type = '=' THEN
|
ELSIF r.rule_type = '=' THEN
|
||||||
v_column := safe_column(p_schema, p_object, r.rule_column);
|
v_column := safe_column(p_schema, p_object, r.rule_column);
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or('TO_CHAR(' || v_column || ') = ' || quote_literal(r.rule_value));
|
append_or(r.permission_effect, 'TO_CHAR(' || v_column || ') = ' || quote_literal(r.rule_value));
|
||||||
END IF;
|
END IF;
|
||||||
ELSIF r.rule_type IN ('!=', '<>') THEN
|
ELSIF r.rule_type IN ('!=', '<>') THEN
|
||||||
v_column := safe_column(p_schema, p_object, r.rule_column);
|
v_column := safe_column(p_schema, p_object, r.rule_column);
|
||||||
IF v_column IS NOT NULL THEN
|
IF v_column IS NOT NULL THEN
|
||||||
append_or('TO_CHAR(' || v_column || ') <> ' || quote_literal(r.rule_value));
|
append_or(r.permission_effect, 'TO_CHAR(' || v_column || ') <> ' || quote_literal(r.rule_value));
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
IF v_predicate IS NULL THEN
|
IF v_allow_predicate IS NULL THEN
|
||||||
RETURN '1 = 0';
|
RETURN '1 = 0';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
RETURN '(' || v_predicate || ')';
|
IF v_deny_predicate IS NULL THEN
|
||||||
|
IF v_allow_predicate = '(1 = 1)' THEN
|
||||||
|
RETURN '1 = 1';
|
||||||
|
END IF;
|
||||||
|
RETURN '(' || v_allow_predicate || ')';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN '((' || v_allow_predicate || ') AND NOT (' || v_deny_predicate || '))';
|
||||||
END;
|
END;
|
||||||
/
|
/
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,12 @@ BEGIN
|
|||||||
INSERT INTO cb_app_user(user_id, user_name, employee_no, dept_code, can_read_contents, active)
|
INSERT INTO cb_app_user(user_id, user_name, employee_no, dept_code, can_read_contents, active)
|
||||||
VALUES (456110, 'rule_test_target_mismatch', 'E456110', 'QA', 'N', 'Y');
|
VALUES (456110, 'rule_test_target_mismatch', 'E456110', 'QA', 'N', 'Y');
|
||||||
|
|
||||||
|
INSERT INTO cb_app_user(user_id, user_name, employee_no, dept_code, can_read_contents, active)
|
||||||
|
VALUES (456111, 'rule_test_allow_deny', 'E456111', 'HR', 'N', 'Y');
|
||||||
|
|
||||||
|
INSERT INTO cb_app_user(user_id, user_name, employee_no, dept_code, can_read_contents, active)
|
||||||
|
VALUES (456112, 'rule_test_deny_only', 'E456112', 'HR', 'N', 'Y');
|
||||||
|
|
||||||
INSERT INTO cb_app_role(role_id, role_name) VALUES (456101, 'RULE_TEST_MY_DEPT');
|
INSERT INTO cb_app_role(role_id, role_name) VALUES (456101, 'RULE_TEST_MY_DEPT');
|
||||||
INSERT INTO cb_app_role(role_id, role_name) VALUES (456102, 'RULE_TEST_SELF');
|
INSERT INTO cb_app_role(role_id, role_name) VALUES (456102, 'RULE_TEST_SELF');
|
||||||
INSERT INTO cb_app_role(role_id, role_name) VALUES (456103, 'RULE_TEST_ALL');
|
INSERT INTO cb_app_role(role_id, role_name) VALUES (456103, 'RULE_TEST_ALL');
|
||||||
@@ -71,6 +77,8 @@ BEGIN
|
|||||||
INSERT INTO cb_app_role(role_id, role_name) VALUES (456108, 'RULE_TEST_OR_INJECTION');
|
INSERT INTO cb_app_role(role_id, role_name) VALUES (456108, 'RULE_TEST_OR_INJECTION');
|
||||||
INSERT INTO cb_app_role(role_id, role_name) VALUES (456109, 'RULE_TEST_COLUMN_INJECTION');
|
INSERT INTO cb_app_role(role_id, role_name) VALUES (456109, 'RULE_TEST_COLUMN_INJECTION');
|
||||||
INSERT INTO cb_app_role(role_id, role_name) VALUES (456110, 'RULE_TEST_TARGET_MISMATCH');
|
INSERT INTO cb_app_role(role_id, role_name) VALUES (456110, 'RULE_TEST_TARGET_MISMATCH');
|
||||||
|
INSERT INTO cb_app_role(role_id, role_name) VALUES (456111, 'RULE_TEST_ALLOW_DENY');
|
||||||
|
INSERT INTO cb_app_role(role_id, role_name) VALUES (456112, 'RULE_TEST_DENY_ONLY');
|
||||||
|
|
||||||
INSERT INTO cb_user_role(user_id, role_id) VALUES (456101, 456101);
|
INSERT INTO cb_user_role(user_id, role_id) VALUES (456101, 456101);
|
||||||
INSERT INTO cb_user_role(user_id, role_id) VALUES (456102, 456102);
|
INSERT INTO cb_user_role(user_id, role_id) VALUES (456102, 456102);
|
||||||
@@ -82,6 +90,8 @@ BEGIN
|
|||||||
INSERT INTO cb_user_role(user_id, role_id) VALUES (456108, 456108);
|
INSERT INTO cb_user_role(user_id, role_id) VALUES (456108, 456108);
|
||||||
INSERT INTO cb_user_role(user_id, role_id) VALUES (456109, 456109);
|
INSERT INTO cb_user_role(user_id, role_id) VALUES (456109, 456109);
|
||||||
INSERT INTO cb_user_role(user_id, role_id) VALUES (456110, 456110);
|
INSERT INTO cb_user_role(user_id, role_id) VALUES (456110, 456110);
|
||||||
|
INSERT INTO cb_user_role(user_id, role_id) VALUES (456111, 456111);
|
||||||
|
INSERT INTO cb_user_role(user_id, role_id) VALUES (456112, 456112);
|
||||||
|
|
||||||
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name)
|
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name)
|
||||||
VALUES (456101, 456101, 'CB_V_SEARCH_DOCUMENTS', 'SELECT');
|
VALUES (456101, 456101, 'CB_V_SEARCH_DOCUMENTS', 'SELECT');
|
||||||
@@ -113,6 +123,15 @@ BEGIN
|
|||||||
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name)
|
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name)
|
||||||
VALUES (456110, 456110, 'BOARD_POSTS', 'SELECT');
|
VALUES (456110, 456110, 'BOARD_POSTS', 'SELECT');
|
||||||
|
|
||||||
|
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name, permission_effect)
|
||||||
|
VALUES (456111, 456111, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'ALLOW');
|
||||||
|
|
||||||
|
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name, permission_effect)
|
||||||
|
VALUES (456112, 456111, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'DENY');
|
||||||
|
|
||||||
|
INSERT INTO cb_permission(perm_id, role_id, target_name, action_name, permission_effect)
|
||||||
|
VALUES (456113, 456112, 'CB_V_SEARCH_DOCUMENTS', 'SELECT', 'DENY');
|
||||||
|
|
||||||
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
|
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
|
||||||
VALUES (456101, 456101, NULL, 'MY_DEPT', NULL);
|
VALUES (456101, 456101, NULL, 'MY_DEPT', NULL);
|
||||||
|
|
||||||
@@ -143,6 +162,15 @@ BEGIN
|
|||||||
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
|
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
|
||||||
VALUES (456110, 456110, NULL, 'ALL', NULL);
|
VALUES (456110, 456110, NULL, 'ALL', NULL);
|
||||||
|
|
||||||
|
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
|
||||||
|
VALUES (456111, 456111, NULL, 'ALL', NULL);
|
||||||
|
|
||||||
|
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
|
||||||
|
VALUES (456112, 456112, 'DEPT_CODE', '=', 'HR');
|
||||||
|
|
||||||
|
INSERT INTO cb_permission_rule(rule_id, perm_id, rule_column, rule_type, rule_value)
|
||||||
|
VALUES (456113, 456113, 'DEPT_CODE', '=', 'HR');
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
END;
|
END;
|
||||||
/
|
/
|
||||||
@@ -229,6 +257,9 @@ BEGIN
|
|||||||
assert_equals('COLUMN_INJECTION_DENIES', 456109, '1 = 0');
|
assert_equals('COLUMN_INJECTION_DENIES', 456109, '1 = 0');
|
||||||
assert_equals_for_object('TARGET_MISMATCH_DENIES', 456110, 'CB_V_SEARCH_DOCUMENTS', '1 = 0');
|
assert_equals_for_object('TARGET_MISMATCH_DENIES', 456110, 'CB_V_SEARCH_DOCUMENTS', '1 = 0');
|
||||||
assert_equals_for_object('TARGET_MATCH_ALL_ALLOWS', 456110, 'BOARD_POSTS', '1 = 1');
|
assert_equals_for_object('TARGET_MATCH_ALL_ALLOWS', 456110, 'BOARD_POSTS', '1 = 1');
|
||||||
|
assert_contains('ALLOW_ALL_MINUS_DENY_DEPT', 456111, 'AND NOT');
|
||||||
|
assert_contains('ALLOW_ALL_MINUS_DENY_DEPT_DETAIL', 456111, 'TO_CHAR(DEPT_CODE) = ''HR''');
|
||||||
|
assert_equals('DENY_ONLY_DENIES', 456112, '1 = 0');
|
||||||
cb_agent_ctx_pkg.clear_user;
|
cb_agent_ctx_pkg.clear_user;
|
||||||
END;
|
END;
|
||||||
/
|
/
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public record PermissionSet(
|
|||||||
long roleId,
|
long roleId,
|
||||||
long objectId,
|
long objectId,
|
||||||
String action,
|
String action,
|
||||||
|
String permissionEffect,
|
||||||
List<PermissionRule> rules,
|
List<PermissionRule> rules,
|
||||||
List<String> visibleColumns
|
List<String> visibleColumns
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public record PermissionSetCommand(
|
|||||||
@Positive long roleId,
|
@Positive long roleId,
|
||||||
@Positive long objectId,
|
@Positive long objectId,
|
||||||
@NotBlank String action,
|
@NotBlank String action,
|
||||||
|
String permissionEffect,
|
||||||
@NotEmpty List<RuleCommand> rules,
|
@NotEmpty List<RuleCommand> rules,
|
||||||
List<String> visibleColumns
|
List<String> visibleColumns
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public record PermissionView(
|
|||||||
long objectId,
|
long objectId,
|
||||||
String objectName,
|
String objectName,
|
||||||
String action,
|
String action,
|
||||||
|
String permissionEffect,
|
||||||
String rules,
|
String rules,
|
||||||
String visibleColumns,
|
String visibleColumns,
|
||||||
String filterPreview,
|
String filterPreview,
|
||||||
|
|||||||
@@ -36,10 +36,14 @@ public interface PermissionMapper {
|
|||||||
void insertPermission(@Param("permissionId") long permissionId,
|
void insertPermission(@Param("permissionId") long permissionId,
|
||||||
@Param("roleId") long roleId,
|
@Param("roleId") long roleId,
|
||||||
@Param("objectId") long objectId,
|
@Param("objectId") long objectId,
|
||||||
@Param("action") String action);
|
@Param("action") String action,
|
||||||
|
@Param("permissionEffect") String permissionEffect);
|
||||||
|
|
||||||
void updatePermissionAction(@Param("permissionId") long permissionId, @Param("action") String action);
|
void updatePermissionAction(@Param("permissionId") long permissionId, @Param("action") String action);
|
||||||
|
|
||||||
|
void updatePermissionEffect(@Param("permissionId") long permissionId,
|
||||||
|
@Param("permissionEffect") String permissionEffect);
|
||||||
|
|
||||||
void deleteRules(@Param("permissionId") long permissionId);
|
void deleteRules(@Param("permissionId") long permissionId);
|
||||||
|
|
||||||
void insertRule(PermissionRule rule);
|
void insertRule(PermissionRule rule);
|
||||||
|
|||||||
@@ -56,9 +56,12 @@ public class BackofficeSchemaService {
|
|||||||
perm_id NUMBER PRIMARY KEY,
|
perm_id NUMBER PRIMARY KEY,
|
||||||
role_id NUMBER NOT NULL,
|
role_id NUMBER NOT NULL,
|
||||||
target_name VARCHAR2(128) NOT NULL,
|
target_name VARCHAR2(128) NOT NULL,
|
||||||
action_name VARCHAR2(30) DEFAULT 'SELECT' NOT NULL
|
action_name VARCHAR2(30) DEFAULT 'SELECT' NOT NULL,
|
||||||
|
permission_effect VARCHAR2(10) DEFAULT 'ALLOW' NOT NULL
|
||||||
)
|
)
|
||||||
""");
|
""");
|
||||||
|
addColumn(results, "cb_permission", "permission_effect",
|
||||||
|
"ALTER TABLE cb_permission ADD (permission_effect VARCHAR2(10) DEFAULT 'ALLOW' NOT NULL)");
|
||||||
createTable(results, "cb_permission_rule", """
|
createTable(results, "cb_permission_rule", """
|
||||||
CREATE TABLE cb_permission_rule (
|
CREATE TABLE cb_permission_rule (
|
||||||
rule_id NUMBER PRIMARY KEY,
|
rule_id NUMBER PRIMARY KEY,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class PermissionService {
|
|||||||
private static final Set<String> RULE_TYPES = Set.of("ALL", "=", "!=", "MY_DEPT", "SELF", "DEPT", "EMP_NO");
|
private static final Set<String> RULE_TYPES = Set.of("ALL", "=", "!=", "MY_DEPT", "SELF", "DEPT", "EMP_NO");
|
||||||
private static final Set<String> VALUE_REQUIRED_RULE_TYPES = Set.of("=", "!=", "DEPT", "EMP_NO");
|
private static final Set<String> VALUE_REQUIRED_RULE_TYPES = Set.of("=", "!=", "DEPT", "EMP_NO");
|
||||||
private static final Set<String> DEFAULT_COLUMN_RULE_TYPES = Set.of("MY_DEPT", "SELF", "DEPT", "EMP_NO");
|
private static final Set<String> DEFAULT_COLUMN_RULE_TYPES = Set.of("MY_DEPT", "SELF", "DEPT", "EMP_NO");
|
||||||
|
private static final Set<String> PERMISSION_EFFECTS = Set.of("ALLOW", "DENY");
|
||||||
|
|
||||||
private final PermissionMapper permissionMapper;
|
private final PermissionMapper permissionMapper;
|
||||||
private final ProtectedObjectService protectedObjectService;
|
private final ProtectedObjectService protectedObjectService;
|
||||||
@@ -69,6 +70,7 @@ public class PermissionService {
|
|||||||
if (!"SELECT".equalsIgnoreCase(command.action())) {
|
if (!"SELECT".equalsIgnoreCase(command.action())) {
|
||||||
throw new AppException("초기 구현에서는 SELECT 권한만 저장할 수 있습니다.");
|
throw new AppException("초기 구현에서는 SELECT 권한만 저장할 수 있습니다.");
|
||||||
}
|
}
|
||||||
|
String permissionEffect = normalizePermissionEffect(command.permissionEffect());
|
||||||
AppRole role = permissionMapper.findRole(command.roleId());
|
AppRole role = permissionMapper.findRole(command.roleId());
|
||||||
if (role == null) {
|
if (role == null) {
|
||||||
throw new AppException("역할을 찾을 수 없습니다.");
|
throw new AppException("역할을 찾을 수 없습니다.");
|
||||||
@@ -80,9 +82,10 @@ public class PermissionService {
|
|||||||
Long existingId = permissionMapper.findPermissionId(command.roleId(), command.objectId());
|
Long existingId = permissionMapper.findPermissionId(command.roleId(), command.objectId());
|
||||||
long permissionId = existingId == null ? permissionMapper.nextPermissionId() : existingId;
|
long permissionId = existingId == null ? permissionMapper.nextPermissionId() : existingId;
|
||||||
if (existingId == null) {
|
if (existingId == null) {
|
||||||
permissionMapper.insertPermission(permissionId, command.roleId(), command.objectId(), "SELECT");
|
permissionMapper.insertPermission(permissionId, command.roleId(), command.objectId(), "SELECT", permissionEffect);
|
||||||
} else {
|
} else {
|
||||||
permissionMapper.updatePermissionAction(permissionId, "SELECT");
|
permissionMapper.updatePermissionAction(permissionId, "SELECT");
|
||||||
|
permissionMapper.updatePermissionEffect(permissionId, permissionEffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
permissionMapper.deleteRules(permissionId);
|
permissionMapper.deleteRules(permissionId);
|
||||||
@@ -107,7 +110,7 @@ public class PermissionService {
|
|||||||
"PERMISSION_SAVED", null, command.objectId(), "SUCCESS", null, null,
|
"PERMISSION_SAVED", null, command.objectId(), "SUCCESS", null, null,
|
||||||
"roleId=" + command.roleId()
|
"roleId=" + command.roleId()
|
||||||
));
|
));
|
||||||
return new PermissionSet(permissionId, command.roleId(), command.objectId(), "SELECT", List.of(), List.of());
|
return new PermissionSet(permissionId, command.roleId(), command.objectId(), "SELECT", permissionEffect, List.of(), List.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -185,6 +188,14 @@ public class PermissionService {
|
|||||||
return clean(value).toUpperCase(Locale.ROOT);
|
return clean(value).toUpperCase(Locale.ROOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizePermissionEffect(String value) {
|
||||||
|
String effect = clean(value).isBlank() ? "ALLOW" : normalize(value);
|
||||||
|
if (!PERMISSION_EFFECTS.contains(effect)) {
|
||||||
|
throw new AppException("허용되지 않은 권한 효과입니다: " + effect);
|
||||||
|
}
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
private String normalizeNullable(String value) {
|
private String normalizeNullable(String value) {
|
||||||
String cleaned = clean(value);
|
String cleaned = clean(value);
|
||||||
return cleaned.isBlank() ? null : cleaned.toUpperCase(Locale.ROOT);
|
return cleaned.isBlank() ? null : cleaned.toUpperCase(Locale.ROOT);
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ public class PermissionController {
|
|||||||
public String save(
|
public String save(
|
||||||
@RequestParam long roleId,
|
@RequestParam long roleId,
|
||||||
@RequestParam String objectRef,
|
@RequestParam String objectRef,
|
||||||
|
@RequestParam(defaultValue = "ALLOW") String permissionEffect,
|
||||||
@RequestParam(required = false) List<String> ruleColumn,
|
@RequestParam(required = false) List<String> ruleColumn,
|
||||||
@RequestParam List<String> ruleType,
|
@RequestParam List<String> ruleType,
|
||||||
@RequestParam(required = false) List<String> ruleValue,
|
@RequestParam(required = false) List<String> ruleValue,
|
||||||
@@ -113,6 +114,7 @@ public class PermissionController {
|
|||||||
roleId,
|
roleId,
|
||||||
objectId,
|
objectId,
|
||||||
"SELECT",
|
"SELECT",
|
||||||
|
permissionEffect,
|
||||||
buildRules(ruleColumn, ruleType, ruleValue),
|
buildRules(ruleColumn, ruleType, ruleValue),
|
||||||
splitColumns(visibleColumns)
|
splitColumns(visibleColumns)
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
o.object_id,
|
o.object_id,
|
||||||
p.target_name AS object_name,
|
p.target_name AS object_name,
|
||||||
p.action_name AS action,
|
p.action_name AS action,
|
||||||
|
NVL(p.permission_effect, 'ALLOW') AS permission_effect,
|
||||||
LISTAGG(
|
LISTAGG(
|
||||||
NVL2(pr.rule_column, pr.rule_column || ' ', '') ||
|
NVL2(pr.rule_column, pr.rule_column || ' ', '') ||
|
||||||
pr.rule_type ||
|
pr.rule_type ||
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
JOIN cb_app_role r ON r.role_id = p.role_id
|
JOIN cb_app_role r ON r.role_id = p.role_id
|
||||||
LEFT JOIN cb_protected_object o ON o.object_name = p.target_name
|
LEFT JOIN cb_protected_object o ON o.object_name = p.target_name
|
||||||
LEFT JOIN cb_permission_rule pr ON pr.perm_id = p.perm_id
|
LEFT JOIN cb_permission_rule pr ON pr.perm_id = p.perm_id
|
||||||
GROUP BY p.perm_id, r.role_id, r.role_name, o.object_id, p.target_name, p.action_name
|
GROUP BY p.perm_id, r.role_id, r.role_name, o.object_id, p.target_name, p.action_name, p.permission_effect
|
||||||
ORDER BY r.role_name, p.target_name
|
ORDER BY r.role_name, p.target_name
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -104,7 +105,8 @@
|
|||||||
SELECT p.perm_id AS permission_id,
|
SELECT p.perm_id AS permission_id,
|
||||||
p.role_id,
|
p.role_id,
|
||||||
o.object_id,
|
o.object_id,
|
||||||
p.action_name AS action
|
p.action_name AS action,
|
||||||
|
NVL(p.permission_effect, 'ALLOW') AS permission_effect
|
||||||
FROM cb_permission p
|
FROM cb_permission p
|
||||||
JOIN cb_protected_object o ON o.object_name = p.target_name
|
JOIN cb_protected_object o ON o.object_name = p.target_name
|
||||||
WHERE p.role_id = #{roleId}
|
WHERE p.role_id = #{roleId}
|
||||||
@@ -120,8 +122,8 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insertPermission">
|
<insert id="insertPermission">
|
||||||
INSERT INTO cb_permission (perm_id, role_id, target_name, action_name)
|
INSERT INTO cb_permission (perm_id, role_id, target_name, action_name, permission_effect)
|
||||||
SELECT #{permissionId}, #{roleId}, object_name, #{action}
|
SELECT #{permissionId}, #{roleId}, object_name, #{action}, #{permissionEffect}
|
||||||
FROM cb_protected_object
|
FROM cb_protected_object
|
||||||
WHERE object_id = #{objectId}
|
WHERE object_id = #{objectId}
|
||||||
</insert>
|
</insert>
|
||||||
@@ -132,6 +134,12 @@
|
|||||||
WHERE perm_id = #{permissionId}
|
WHERE perm_id = #{permissionId}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<update id="updatePermissionEffect">
|
||||||
|
UPDATE cb_permission
|
||||||
|
SET permission_effect = #{permissionEffect}
|
||||||
|
WHERE perm_id = #{permissionId}
|
||||||
|
</update>
|
||||||
|
|
||||||
<delete id="deleteRules">
|
<delete id="deleteRules">
|
||||||
DELETE FROM cb_permission_rule
|
DELETE FROM cb_permission_rule
|
||||||
WHERE perm_id = #{permissionId}
|
WHERE perm_id = #{permissionId}
|
||||||
|
|||||||
@@ -37,6 +37,13 @@
|
|||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
<label>
|
||||||
|
권한 효과
|
||||||
|
<select class="form-select" name="permissionEffect" required>
|
||||||
|
<option value="ALLOW">허용</option>
|
||||||
|
<option value="DENY">거부</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
<div class="span-2">
|
<div class="span-2">
|
||||||
<div class="field-block-title">행 규칙</div>
|
<div class="field-block-title">행 규칙</div>
|
||||||
<div id="rowRuleList" class="rule-list">
|
<div id="rowRuleList" class="rule-list">
|
||||||
@@ -80,6 +87,7 @@
|
|||||||
<th>역할</th>
|
<th>역할</th>
|
||||||
<th>테이블/뷰</th>
|
<th>테이블/뷰</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
|
<th>Effect</th>
|
||||||
<th>행 규칙</th>
|
<th>행 규칙</th>
|
||||||
<th>적용 필터</th>
|
<th>적용 필터</th>
|
||||||
<th>NULL 제외 컬럼</th>
|
<th>NULL 제외 컬럼</th>
|
||||||
@@ -92,6 +100,11 @@
|
|||||||
<td th:text="${permission.roleName()}">HR_DEPT_ROLE</td>
|
<td th:text="${permission.roleName()}">HR_DEPT_ROLE</td>
|
||||||
<td th:text="${permission.objectName()}">CB_V_SEARCH_DOCUMENTS</td>
|
<td th:text="${permission.objectName()}">CB_V_SEARCH_DOCUMENTS</td>
|
||||||
<td th:text="${permission.action()}">SELECT</td>
|
<td th:text="${permission.action()}">SELECT</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge"
|
||||||
|
th:classappend="${permission.permissionEffect() == 'DENY'} ? ' text-bg-danger' : ' text-bg-success'"
|
||||||
|
th:text="${permission.permissionEffect()}">ALLOW</span>
|
||||||
|
</td>
|
||||||
<td th:text="${permission.rules()} ?: '-'">ALL</td>
|
<td th:text="${permission.rules()} ?: '-'">ALL</td>
|
||||||
<td><pre class="table-pre" th:text="${permission.filterPreview()} ?: '-'">ALL ROWS</pre></td>
|
<td><pre class="table-pre" th:text="${permission.filterPreview()} ?: '-'">ALL ROWS</pre></td>
|
||||||
<td th:text="${permission.visibleColumns()} ?: '-'">CONTENTS</td>
|
<td th:text="${permission.visibleColumns()} ?: '-'">CONTENTS</td>
|
||||||
@@ -104,7 +117,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr th:if="${#lists.isEmpty(permissions)}">
|
<tr th:if="${#lists.isEmpty(permissions)}">
|
||||||
<td colspan="8" class="text-muted">등록된 권한이 없습니다.</td>
|
<td colspan="9" class="text-muted">등록된 권한이 없습니다.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class PermissionServiceTest {
|
|||||||
10L,
|
10L,
|
||||||
1L,
|
1L,
|
||||||
"SELECT",
|
"SELECT",
|
||||||
|
"ALLOW",
|
||||||
List.of(new RuleCommand(null, "ALL", null), new RuleCommand("DEPT_CODE", "=", "APAC")),
|
List.of(new RuleCommand(null, "ALL", null), new RuleCommand("DEPT_CODE", "=", "APAC")),
|
||||||
List.of()
|
List.of()
|
||||||
);
|
);
|
||||||
@@ -67,6 +68,7 @@ class PermissionServiceTest {
|
|||||||
10L,
|
10L,
|
||||||
1L,
|
1L,
|
||||||
"SELECT",
|
"SELECT",
|
||||||
|
"ALLOW",
|
||||||
List.of(new RuleCommand("DEPT_CODE", "CUSTOM_PREDICATE", "1=1")),
|
List.of(new RuleCommand("DEPT_CODE", "CUSTOM_PREDICATE", "1=1")),
|
||||||
List.of()
|
List.of()
|
||||||
);
|
);
|
||||||
@@ -82,6 +84,7 @@ class PermissionServiceTest {
|
|||||||
10L,
|
10L,
|
||||||
1L,
|
1L,
|
||||||
"SELECT",
|
"SELECT",
|
||||||
|
"ALLOW",
|
||||||
List.of(new RuleCommand(null, "MY_DEPT", null), new RuleCommand(null, "SELF", null)),
|
List.of(new RuleCommand(null, "MY_DEPT", null), new RuleCommand(null, "SELF", null)),
|
||||||
List.of()
|
List.of()
|
||||||
);
|
);
|
||||||
@@ -103,6 +106,7 @@ class PermissionServiceTest {
|
|||||||
10L,
|
10L,
|
||||||
1L,
|
1L,
|
||||||
"SELECT",
|
"SELECT",
|
||||||
|
"ALLOW",
|
||||||
List.of(new RuleCommand(null, "DEPT", "HR"), new RuleCommand(null, "EMP_NO", "E2001")),
|
List.of(new RuleCommand(null, "DEPT", "HR"), new RuleCommand(null, "EMP_NO", "E2001")),
|
||||||
List.of()
|
List.of()
|
||||||
);
|
);
|
||||||
@@ -124,6 +128,7 @@ class PermissionServiceTest {
|
|||||||
10L,
|
10L,
|
||||||
1L,
|
1L,
|
||||||
"SELECT",
|
"SELECT",
|
||||||
|
"ALLOW",
|
||||||
List.of(new RuleCommand(null, "DEPT", "")),
|
List.of(new RuleCommand(null, "DEPT", "")),
|
||||||
List.of()
|
List.of()
|
||||||
);
|
);
|
||||||
@@ -133,6 +138,23 @@ class PermissionServiceTest {
|
|||||||
.hasMessageContaining("값이 필요");
|
.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
|
@Test
|
||||||
void disablesProtectedObjectWhenLastPermissionIsDeleted() {
|
void disablesProtectedObjectWhenLastPermissionIsDeleted() {
|
||||||
var mapper = new FakePermissionMapper();
|
var mapper = new FakePermissionMapper();
|
||||||
@@ -209,14 +231,22 @@ class PermissionServiceTest {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String insertedEffect;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void insertPermission(long permissionId, long roleId, long objectId, String action) {
|
public void insertPermission(long permissionId, long roleId, long objectId, String action, String permissionEffect) {
|
||||||
|
insertedEffect = permissionEffect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updatePermissionAction(long permissionId, String action) {
|
public void updatePermissionAction(long permissionId, String action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePermissionEffect(long permissionId, String permissionEffect) {
|
||||||
|
insertedEffect = permissionEffect;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteRules(long permissionId) {
|
public void deleteRules(long permissionId) {
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user