【问题标题】:Oracle row level security by column value, not by user idOracle 行级安全性按列值,而不是按用户 ID
【发布时间】:2021-02-28 08:14:18
【问题描述】:

我正在学习 Oracle,我正在尝试将行级安全性添加到演示中 桌子。我可以成功运行第一个示例:

CREATE OR REPLACE FUNCTION get_emp_pred
    (schema_v IN VARCHAR2, tbl_v IN VARCHAR2) RETURN VARCHAR2 IS
    BEGIN
       RETURN 'upper(ename) = SYS_CONTEXT(''USERENV'', ''CURRENT_USER'') 
        OR upper(manager) = SYS_CONTEXT(''USERENV'', ''CURRENT_USER'')';
    END;

BEGIN
    SYS.DBMS_RLS.ADD_POLICY(
        object_schema   => 'hector',
        object_name     => 'emp',
        policy_name     => 'emp_vpd1',
        function_schema => 'vpdadmin',
        policy_function => 'get_emp_pred',
        statement_types => 'select'
    );
END;

在此示例中,每个用户都可以看到列“ename”或列“manager”所在的行 value 匹配他自己的用户名。现在我的问题是当我尝试过滤时 不是由用户在列中进行比较,而是针对某些任意列值(例如国家/地区或 薪水。例如,我想向 Bob 显示薪水 > 1000 的行,向 Anne 其中薪水

我尝试了这个函数和其他变体但没有结果:

CREATE OR REPLACE FUNCTION get_emp_pred
(schema_v IN VARCHAR2, tbl_v IN VARCHAR2) RETURN VARCHAR2 AS
    l_predicate VARCHAR2(100);
    BEGIN 
        IF 'BOB' = SYS_CONTEXT('USERENV', 'CURRENT_USER') THEN
            l_predicate := 'salary > 1000';
        ELSIF 'ANNE' = SYS_CONTEXT('USERENV', 'CURRENT_USER') THEN
            l_predicate := 'salary < 1000'; 
        ELSE 
            l_predicate := '1=1';
        END IF;
        RETURN l_predicate;
     END;

我想你不能在运行时评估这个条件,但如果我写一个 每个用户的策略和谓词都不起作用。例如这个 其政策适用于 Bob,其他适用于 Anne,等等:

CREATE OR REPLACE FUNCTION get_emp_pred_bob
    (schema_v IN VARCHAR2, tbl_v IN VARCHAR2) RETURN VARCHAR2 IS
    BEGIN 
        RETURN '''BOB'' = SYS_CONTEXT(''USERENV'', ''CURRENT_USER'') AND salary > 1000';
    END;

如果我只有 Bob 策略,它适用于 Bob 并为其他用户返回 0 行, 但是当我为 Anne 添加第二个策略时,每个人都得到 0 行,也许是因为 policyBob && policyAnne 总是返回 false。

我想知道这种过滤是否可行。

【问题讨论】:

    标签: sql oracle security


    【解决方案1】:

    您的第二个 get_emp_pred 函数没有达到您的预期,因为它是(默认)定义者的权限函数。这意味着在函数中,current_user 是所有者 VPDADMIN,而不是执行查询的用户。

    From the documentationcurrent_user 给出:

    权限当前处于活动状态的数据库用户的名称。这可能会在数据库会话期间发生变化……以反映任何活动定义者权限对象的所有者。当没有定义者的权限对象处于活动状态时,CURRENT_USER 返回与SESSION_USER 相同的值。

    因此,如果您在第二个函数中将 current_user 更改为 session_user,那么它应该会执行您想要的操作:

    CREATE OR REPLACE FUNCTION get_emp_pred
    (schema_v IN VARCHAR2, tbl_v IN VARCHAR2) RETURN VARCHAR2 AS
        l_predicate VARCHAR2(100);
        BEGIN 
            IF 'BOB' = SYS_CONTEXT('USERENV', 'SESSION_USER') THEN
                l_predicate := 'salary > 1000';
            ELSIF 'ANNE' = SYS_CONTEXT('USERENV', 'SESSION_USER') THEN
                l_predicate := 'salary < 1000'; 
            ELSE 
                l_predicate := '1=1';
            END IF;
            RETURN l_predicate;
         END;
    

    您的第一个函数不会受此影响,因为用户在谓词中作为查询的一部分进行评估,而不是在函数内。


    当我为 Anne 添加第二个策略时,每个人都得到 0 行,可能是因为 policyBob && policyAnne 返回总是 false。

    是的,这两个策略都被应用了,你最终得到了应用于查询的互斥谓词,所以现在行可以匹配了。


    您可以使用单个策略和函数来完成这项工作,该策略和函数返回一个谓词,该谓词将所有选项组合在一起;类似:

    RETURN q'[(SYS_CONTEXT('USERENV', 'CURRENT_USER') = 'BOB' AND salary > 1000)
           OR (SYS_CONTEXT('USERENV', 'CURRENT_USER') = 'ANNE' AND salary < 1000)
           OR (SYS_CONTEXT('USERENV', 'CURRENT_USER') NOT IN ('BOB', 'ANNE'))'];
    

    但是使用 PL/SQL 逻辑返回不同的谓词更简单——只要它不影响性能。具有单个固定复杂谓词的静态策略可能会表现得更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-15
      • 2018-06-24
      • 2019-04-08
      • 1970-01-01
      • 2023-03-22
      • 2021-11-01
      • 2011-05-08
      • 1970-01-01
      相关资源
      最近更新 更多