【问题标题】:Allow some roles to update a column, and some others if it IS NULL允许某些角色更新列,如果它为空,则允许其他一些角色
【发布时间】:2013-04-01 22:09:34
【问题描述】:

我在表中有一个列A(类型int),在插入其他值时该列的数据不可用。我不想拆分表格,因为没有其他真正的理由这样做。我正在数据库级别实现权限分离。

只有特定用户(属于 A 类)才能随时修改column A。但是其他用户(在 B 类中,不一定与 A 类互斥)应该能够更新列,如果它的值尚未设置,即。如果是NULL.
我正在使用 PostgreSQL 9.2.4。

我怎样才能做到这一点?触发器?规则?还有什么?

【问题讨论】:

  • 案件并不完全清楚。 only certain users should be able to modify column A?这些用户是否应该能够修改整行或只是 A列? “其他用户”的同样问题..
  • 我已经编辑了问题以使其更清晰。请告诉我是否仍不清楚。谢谢。很抱歉这个问题的措辞不佳。

标签: postgresql database-design triggers privileges postgresql-9.2


【解决方案1】:

更新触发器应该可以工作。如果不是NULL,则需要检查旧值并中止更新。

【讨论】:

    【解决方案2】:

    例子:

    -- setup
    CREATE TABLE tbl (tbl_id serial PRIMARY KEY, value int);
    
    -- trigger function that always fails
    CREATE FUNCTION always_fail() RETURNS trigger as $$ begin raise exception 'value is not null'; end; $$ LANGUAGE plpgsql;
    -- create the trigger
    CREATE TRIGGER tbl_value_trigger BEFORE UPDATE ON tbl FOR EACH ROW WHEN (old.value is not null) EXECUTE PROCEDURE always_fail();
    
    -- test it
    INSERT INTO tbl(value) VALUES (NULL);
    INSERT INTO tbl(value) VALUES (NULL);
    UPDATE tbl SET value=2 WHERE tbl_id=1;
    UPDATE tbl SET value=3 WHERE tbl_id=1; -- fails
    UPDATE tbl SET value=2 WHERE tbl_id=2;
    UPDATE tbl SET value=3 WHERE tbl_id=2; -- fails
    
    -- cleanup
    DROP TABLE tbl; 
    DROP FUNCTION always_fail();
    

    注意事项:

    • 我必须创建一个触发函数 (CREATE FUNCTION),因为 CREATE TRIGGER 需要一个触发函数并且不接受普通函数,因此无法使用 CREATE TRIGGER ... EXECUTE PROCEDURE raise exception 'value not null'
    • 在每次更新之前评估触发器,即
      • INSERTS 没有被检查,所以你可以插入 NULLs
      • 检查发生在更新之前,因此可以中止
    • 我必须使用FOR EACH ROW,否则我将无法查看OLD.value

    【讨论】:

    • 虽然这在某种程度上有所帮助,但它无法区分“某些用户”和“其他用户”。 没有人可以再用value is not null更新一行,甚至超级用户也不行。
    • @ErwinBrandstetter 是正确的;我设法监督了这部分问题:-( 在这种情况下,current_user 应该以某种方式集成到触发器或函数中,具体取决于应该如何进行检查。
    • 我提供了另一个答案,应该会继续下去。但是为您已经拥有的内容+1。它比 Daniel Hilgarth 的一条草率的台词要好得多,不知何故设法收集了 3 个赞成票。
    【解决方案3】:

    为某些用户授予无条件更新权限,使他们成为具有该权限的角色的成员。

    create role the_role;
    grant update on table t to the_role;
    grant the_role to the_user;
    

    现在这个更新查询只有在当前用户是 the_role 的成员或者值为 null 时才有效:

    update t
    set A = 1
    where
        id = 1
        and (
            A is null
            or
            pg_has_role('the_role', 'usage')
        )
    

    Access privileges functions

    Database Roles

    【讨论】:

    • 为什么在这种情况下说触发器是无意义的?您的解决方案会起作用,但我必须在使用查询的任何地方实施检查。在服务器端做不是更好吗?但是在这里使用触发器有什么问题?我很好奇
    • @richy 我想我误解了你的问题。已编辑。
    【解决方案4】:

    只有特定用户才能修改 A 列
    ...
    如果...为NULL,其他用户应该能够更新该列。

    public 和其他不应该拥有特权的所有人撤消UPDATE(和DELETE ?!?)。

    REVOKE UPDATE ON TABLE tbl FROM public;
    REVOKE UPDATE ON TABLE tbl FROM ...
    

    创建一个(组)角色some_users,允许更新col_a(仅此而已):

    CREATE ROLE some_users;
    GRANT SELECT, UPDATE (col_a) ON TABLE tbl TO some_users;
    

    为什么是SELECTThe manual on GRANT:

    实际上,任何重要的UPDATE 命令都需要SELECT 特权也是如此,因为它必须引用表列来确定 要更新哪些行,和/或计算列的新值。

    这样,您就有了一个单点,您可以在其中分配列的权限。
    创建另一个(组)角色certain_users 可以做所有some_users 可以(加上更多):

    CREATE ROLE certain_users;
    GRANT some_users TO certain_users;
    

    根据需要将这些角色的成员资格授予用户角色:

    GRANT some_users TO lowly_user;
    GRANT certain_users TO chief_user;
    

    创建一个条件触发器,类似于 @Daniel provided,但使用 pg_has_role() 的另一个条件:

    CREATE TRIGGER tbl_value_trigger
    BEFORE UPDATE ON tbl
    FOR EACH ROW
    WHEN (OLD.col_a IS NOT NULL AND NOT pg_has_role('some_users', 'member'))
    EXECUTE PROCEDURE always_fail();
    

    使用触发功能:

    CREATE FUNCTION always_fail()
      RETURNS trigger
      LANGUAGE plpgsql AS
    $func$
    BEGIN
       -- To fail with exception, rolling back the whole transaction
       -- use this instead:
       -- RAISE EXCEPTION 'value is not null';
        
       -- Do nothing instead, letting the rest of the transaction commit.
       -- Requires BEFORE trigger.
       RAISE WARNING 'col_a IS NOT NULL. User % is too ambitious!', current_user;
       RETURN NULL;  -- effectively aborts UPDATE
    END
    $func$;
    

    现在:

    • 限制公众完全更新表格。
    • 允许some_userscertain_users 更新列col_a(仅此而已),
    • 仅来自certain_users 的更改通过一次col_a is NOT NULL

    注意超级用户自动成为任何组的成员。因此,NOT pg_has_role(...) 表单 启用 超级用户,而逆向逻辑可能会禁止超级用户更新列。

    【讨论】:

      猜你喜欢
      • 2011-02-16
      • 1970-01-01
      • 2016-10-19
      • 2019-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-23
      • 2018-09-24
      相关资源
      最近更新 更多