fix #424: build VPD filter from stored row rules
This commit is contained in:
@@ -254,66 +254,118 @@ CREATE OR REPLACE FUNCTION cb_agent_doc_vpd_filter(
|
|||||||
) RETURN VARCHAR2
|
) RETURN VARCHAR2
|
||||||
AUTHID DEFINER
|
AUTHID DEFINER
|
||||||
AS
|
AS
|
||||||
v_user_id VARCHAR2(30);
|
v_user_id NUMBER;
|
||||||
v_target_name VARCHAR2(128);
|
v_target VARCHAR2(128);
|
||||||
BEGIN
|
v_predicate VARCHAR2(32767);
|
||||||
v_user_id := SYS_CONTEXT('CB_AGENT_CTX', 'USER_ID');
|
|
||||||
|
|
||||||
IF v_user_id IS NULL THEN
|
FUNCTION quote_literal(p_value IN VARCHAR2) RETURN VARCHAR2 IS
|
||||||
|
BEGIN
|
||||||
|
RETURN '''' || REPLACE(NVL(p_value, ''), '''', '''''') || '''';
|
||||||
|
END;
|
||||||
|
|
||||||
|
FUNCTION safe_column(
|
||||||
|
p_owner IN VARCHAR2,
|
||||||
|
p_table_name IN VARCHAR2,
|
||||||
|
p_column_name IN VARCHAR2
|
||||||
|
) RETURN VARCHAR2 IS
|
||||||
|
v_column VARCHAR2(128);
|
||||||
|
v_count NUMBER;
|
||||||
|
BEGIN
|
||||||
|
IF p_column_name IS NULL THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
v_column := DBMS_ASSERT.SIMPLE_SQL_NAME(UPPER(TRIM(p_column_name)));
|
||||||
|
|
||||||
|
SELECT COUNT(*)
|
||||||
|
INTO v_count
|
||||||
|
FROM all_tab_columns
|
||||||
|
WHERE owner = UPPER(p_owner)
|
||||||
|
AND table_name = UPPER(p_table_name)
|
||||||
|
AND column_name = v_column;
|
||||||
|
|
||||||
|
IF v_count = 0 THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN v_column;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
|
||||||
|
PROCEDURE append_or(p_clause IN VARCHAR2) IS
|
||||||
|
BEGIN
|
||||||
|
IF p_clause IS NULL THEN
|
||||||
|
RETURN;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF v_predicate IS NULL THEN
|
||||||
|
v_predicate := '(' || p_clause || ')';
|
||||||
|
ELSE
|
||||||
|
v_predicate := v_predicate || ' OR (' || p_clause || ')';
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
BEGIN
|
||||||
|
BEGIN
|
||||||
|
v_user_id := TO_NUMBER(SYS_CONTEXT('CB_AGENT_CTX', 'USER_ID'));
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
RETURN '1 = 0';
|
||||||
|
END;
|
||||||
|
|
||||||
|
IF v_user_id IS NULL OR p_schema IS NULL OR p_object IS NULL THEN
|
||||||
RETURN '1 = 0';
|
RETURN '1 = 0';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
v_target_name := REPLACE(UPPER(p_object), '''', '''''');
|
v_target := UPPER(TRIM(p_object));
|
||||||
|
|
||||||
RETURN
|
FOR r IN (
|
||||||
'EXISTS (
|
SELECT UPPER(TRIM(r.rule_type)) AS rule_type,
|
||||||
SELECT 1
|
UPPER(TRIM(r.rule_column)) AS rule_column,
|
||||||
FROM admin.cb_user_role ur
|
r.rule_value
|
||||||
JOIN admin.cb_permission p
|
FROM cb_user_role ur
|
||||||
|
JOIN cb_permission p
|
||||||
ON p.role_id = ur.role_id
|
ON p.role_id = ur.role_id
|
||||||
JOIN admin.cb_permission_rule r
|
JOIN cb_permission_rule r
|
||||||
ON r.perm_id = p.perm_id
|
ON r.perm_id = p.perm_id
|
||||||
WHERE ur.user_id = TO_NUMBER(SYS_CONTEXT(''CB_AGENT_CTX'', ''USER_ID''))
|
WHERE ur.user_id = v_user_id
|
||||||
AND p.target_name = ''' || v_target_name || '''
|
AND p.target_name = v_target
|
||||||
AND p.action_name = ''SELECT''
|
AND p.action_name = 'SELECT'
|
||||||
AND (
|
ORDER BY r.rule_id
|
||||||
r.rule_type = ''ALL''
|
) LOOP
|
||||||
OR (
|
DECLARE
|
||||||
r.rule_type = ''MY_DEPT''
|
v_column VARCHAR2(128);
|
||||||
AND dept_code = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')
|
BEGIN
|
||||||
)
|
IF r.rule_type = 'ALL' THEN
|
||||||
OR (
|
RETURN '1 = 1';
|
||||||
r.rule_type = ''DEPT''
|
ELSIF r.rule_type = 'MY_DEPT' THEN
|
||||||
AND r.rule_value = dept_code
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
||||||
)
|
append_or(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')');
|
||||||
OR (
|
ELSIF r.rule_type = 'SELF' THEN
|
||||||
r.rule_type = ''SELF''
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
||||||
AND owner_emp_no = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')
|
append_or(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')');
|
||||||
)
|
ELSIF r.rule_type = 'DEPT' THEN
|
||||||
OR (
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
||||||
r.rule_type = ''EMP_NO''
|
append_or(v_column || ' = ' || quote_literal(r.rule_value));
|
||||||
AND r.rule_value = owner_emp_no
|
ELSIF r.rule_type = 'EMP_NO' THEN
|
||||||
)
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
||||||
OR (
|
append_or(v_column || ' = ' || quote_literal(r.rule_value));
|
||||||
r.rule_type = ''=''
|
ELSIF r.rule_type = '=' THEN
|
||||||
AND (
|
v_column := safe_column(p_schema, p_object, r.rule_column);
|
||||||
(r.rule_column = ''DOC_ID'' AND TO_CHAR(doc_id) = r.rule_value)
|
append_or('TO_CHAR(' || v_column || ') = ' || quote_literal(r.rule_value));
|
||||||
OR (r.rule_column = ''TITLE'' AND title = r.rule_value)
|
ELSIF r.rule_type IN ('!=', '<>') THEN
|
||||||
OR (r.rule_column = ''OWNER_EMP_NO'' AND owner_emp_no = r.rule_value)
|
v_column := safe_column(p_schema, p_object, r.rule_column);
|
||||||
OR (r.rule_column = ''DEPT_CODE'' AND dept_code = r.rule_value)
|
append_or('TO_CHAR(' || v_column || ') <> ' || quote_literal(r.rule_value));
|
||||||
)
|
END IF;
|
||||||
)
|
END;
|
||||||
OR (
|
END LOOP;
|
||||||
r.rule_type = ''!=''
|
|
||||||
AND (
|
IF v_predicate IS NULL THEN
|
||||||
(r.rule_column = ''DOC_ID'' AND TO_CHAR(doc_id) != r.rule_value)
|
RETURN '1 = 0';
|
||||||
OR (r.rule_column = ''TITLE'' AND title != r.rule_value)
|
END IF;
|
||||||
OR (r.rule_column = ''OWNER_EMP_NO'' AND owner_emp_no != r.rule_value)
|
|
||||||
OR (r.rule_column = ''DEPT_CODE'' AND dept_code != r.rule_value)
|
RETURN '(' || v_predicate || ')';
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)';
|
|
||||||
END;
|
END;
|
||||||
/
|
/
|
||||||
|
|
||||||
|
|||||||
138
sql/adb/26_agent_ords_security_dynamic_vpd_filter.sql
Normal file
138
sql/adb/26_agent_ords_security_dynamic_vpd_filter.sql
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 26_agent_ords_security_dynamic_vpd_filter.sql
|
||||||
|
-- Replaces the demo VPD filter with a whitelist predicate builder.
|
||||||
|
--
|
||||||
|
-- The function does not hardcode object names or rule values. It reads:
|
||||||
|
-- CB_AGENT_CTX.USER_ID -> CB_USER_ROLE -> CB_PERMISSION -> CB_PERMISSION_RULE
|
||||||
|
-- and builds a row predicate for the object passed by DBMS_RLS.
|
||||||
|
-- ============================================================
|
||||||
|
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||||
|
SET ECHO OFF
|
||||||
|
SET FEEDBACK ON
|
||||||
|
SET DEFINE OFF
|
||||||
|
|
||||||
|
PROMPT === Creating dynamic whitelist VPD filter ===
|
||||||
|
CREATE OR REPLACE FUNCTION cb_agent_doc_vpd_filter(
|
||||||
|
p_schema IN VARCHAR2,
|
||||||
|
p_object IN VARCHAR2
|
||||||
|
) RETURN VARCHAR2
|
||||||
|
AUTHID DEFINER
|
||||||
|
AS
|
||||||
|
v_user_id NUMBER;
|
||||||
|
v_target VARCHAR2(128);
|
||||||
|
v_predicate VARCHAR2(32767);
|
||||||
|
|
||||||
|
FUNCTION quote_literal(p_value IN VARCHAR2) RETURN VARCHAR2 IS
|
||||||
|
BEGIN
|
||||||
|
RETURN '''' || REPLACE(NVL(p_value, ''), '''', '''''') || '''';
|
||||||
|
END;
|
||||||
|
|
||||||
|
FUNCTION safe_column(
|
||||||
|
p_owner IN VARCHAR2,
|
||||||
|
p_table_name IN VARCHAR2,
|
||||||
|
p_column_name IN VARCHAR2
|
||||||
|
) RETURN VARCHAR2 IS
|
||||||
|
v_column VARCHAR2(128);
|
||||||
|
v_count NUMBER;
|
||||||
|
BEGIN
|
||||||
|
IF p_column_name IS NULL THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
v_column := DBMS_ASSERT.SIMPLE_SQL_NAME(UPPER(TRIM(p_column_name)));
|
||||||
|
|
||||||
|
SELECT COUNT(*)
|
||||||
|
INTO v_count
|
||||||
|
FROM all_tab_columns
|
||||||
|
WHERE owner = UPPER(p_owner)
|
||||||
|
AND table_name = UPPER(p_table_name)
|
||||||
|
AND column_name = v_column;
|
||||||
|
|
||||||
|
IF v_count = 0 THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN v_column;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
|
||||||
|
PROCEDURE append_or(p_clause IN VARCHAR2) IS
|
||||||
|
BEGIN
|
||||||
|
IF p_clause IS NULL THEN
|
||||||
|
RETURN;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF v_predicate IS NULL THEN
|
||||||
|
v_predicate := '(' || p_clause || ')';
|
||||||
|
ELSE
|
||||||
|
v_predicate := v_predicate || ' OR (' || p_clause || ')';
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
BEGIN
|
||||||
|
BEGIN
|
||||||
|
v_user_id := TO_NUMBER(SYS_CONTEXT('CB_AGENT_CTX', 'USER_ID'));
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
RETURN '1 = 0';
|
||||||
|
END;
|
||||||
|
|
||||||
|
IF v_user_id IS NULL OR p_schema IS NULL OR p_object IS NULL THEN
|
||||||
|
RETURN '1 = 0';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
v_target := UPPER(TRIM(p_object));
|
||||||
|
|
||||||
|
FOR r IN (
|
||||||
|
SELECT UPPER(TRIM(r.rule_type)) AS rule_type,
|
||||||
|
UPPER(TRIM(r.rule_column)) AS rule_column,
|
||||||
|
r.rule_value
|
||||||
|
FROM cb_user_role ur
|
||||||
|
JOIN cb_permission p
|
||||||
|
ON p.role_id = ur.role_id
|
||||||
|
JOIN cb_permission_rule r
|
||||||
|
ON r.perm_id = p.perm_id
|
||||||
|
WHERE ur.user_id = v_user_id
|
||||||
|
AND p.target_name = v_target
|
||||||
|
AND p.action_name = 'SELECT'
|
||||||
|
ORDER BY r.rule_id
|
||||||
|
) LOOP
|
||||||
|
DECLARE
|
||||||
|
v_column VARCHAR2(128);
|
||||||
|
BEGIN
|
||||||
|
IF r.rule_type = 'ALL' THEN
|
||||||
|
RETURN '1 = 1';
|
||||||
|
ELSIF r.rule_type = 'MY_DEPT' THEN
|
||||||
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
||||||
|
append_or(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''DEPT_CODE'')');
|
||||||
|
ELSIF r.rule_type = 'SELF' THEN
|
||||||
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
||||||
|
append_or(v_column || ' = SYS_CONTEXT(''CB_AGENT_CTX'', ''EMP_NO'')');
|
||||||
|
ELSIF r.rule_type = 'DEPT' THEN
|
||||||
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'DEPT_CODE'));
|
||||||
|
append_or(v_column || ' = ' || quote_literal(r.rule_value));
|
||||||
|
ELSIF r.rule_type = 'EMP_NO' THEN
|
||||||
|
v_column := safe_column(p_schema, p_object, NVL(r.rule_column, 'OWNER_EMP_NO'));
|
||||||
|
append_or(v_column || ' = ' || quote_literal(r.rule_value));
|
||||||
|
ELSIF r.rule_type = '=' THEN
|
||||||
|
v_column := safe_column(p_schema, p_object, r.rule_column);
|
||||||
|
append_or('TO_CHAR(' || v_column || ') = ' || quote_literal(r.rule_value));
|
||||||
|
ELSIF r.rule_type IN ('!=', '<>') THEN
|
||||||
|
v_column := safe_column(p_schema, p_object, r.rule_column);
|
||||||
|
append_or('TO_CHAR(' || v_column || ') <> ' || quote_literal(r.rule_value));
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
IF v_predicate IS NULL THEN
|
||||||
|
RETURN '1 = 0';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN '(' || v_predicate || ')';
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
|
||||||
|
SHOW ERRORS FUNCTION cb_agent_doc_vpd_filter
|
||||||
|
|
||||||
|
PROMPT === Dynamic whitelist VPD filter ready ===
|
||||||
Reference in New Issue
Block a user