【问题标题】:Database design - granting access to records数据库设计 - 授予对记录的访问权限
【发布时间】:2015-11-04 02:54:02
【问题描述】:

我正在设计一个限制对某些对象的访问的数据库。我和我的同事讨论了解决此问题的不同方法,主要候选人有:1)隐式访问和 2)显式访问

为了说明,我们假设有以下表格:

User (Id)
ObjectA (Id, ParentId) -- Where ParentId is an ObjectA
ObjectB (Id, ObjectAId)
UserObjectA (UserId, ObjectAId) -- Grants access to an ObjectA
UserObjectB (UserId, ObjectBId) -- Grants access to an ObjectB

隐式方法:

  1. 因为 ObjectA 作为 ObjectB 的包含实体,如果用户可以访问作为 ObjectB 容器的 ObjectA,那么即使在 UserObjectB 中没有这样的显式记录,他也可以访问包含的 ObjectB。
  2. 类似地,如果用户有权访问父 ObjectA,则他有权访问所有 ObjectA 后代,即使 UserObjectA 中没有此类记录。
  3. 此外,如果用户在任一访问授权表中都没有记录,则隐含地他可以访问 ObjectA 和 ObjectB 中的所有记录。

显式方法:

  1. 要访问 ObjectA 或 ObjectB 记录,用户必须分别在 UserObjectA 或 UserObjectB 中拥有记录。
  2. 没有记录就等于没有访问权限,句号。

隐式方法有两个好处:1)当用户隐式访问许多对象时节省空间,2)隐式访问所有对象的用户将可以访问将来添加的所有对象,而无需触发在创建对象时插入或处理 sprocs 中的插入。

显式方法的好处是查询更简单、可维护且性能更高。

最初,我们使用隐式方法运行。然而,在进入各种存储过程的实现之后,处理访问的逻辑变成了野兽,并且我们遇到了各种微妙之处,这使得这种方法比显式方法更容易出错。 (请注意,实际场景比简化的示例要复杂一些。)我发现自己不断实现递归 CTE 来确定访问权限,这不允许我(在考虑性能时)抽象出视图中的某些逻辑部分或内联 TVF。所以我必须在许多不同的存储过程中重复和调整容易出错的逻辑。如果有任何变化,我们将面临一项艰巨的维护任务。

那么,我们在使用这种隐式访问方法时犯了错误吗?我肯定会重新考虑,并希望任何有类似设计决策经验的人提供建议。

【问题讨论】:

    标签: database database-design


    【解决方案1】:

    如果您可以等待一个月,Postgres 9.5 将会发布并具有行安全性。如果你有 1000 万美元,甲骨文现在就有了。

    目前,或在其他数据库中,您可以模拟行安全性:

    1. 每个受保护的表都有一个“所有者”列。默认情况下,只有所有者可以选择、更新或删除该行。

    2. 每个“子”表也有一个所有者列,其中包含到父表的级联外键。因此,如果更改 parent.owner,那么这也会更改所有 children.owners

    3. 使用可更新的 CHECK OPTION 视图来加强安全性。

    4. 您需要从您的应用程序中设置 current_user。 Here's how 用于 pg + 弹簧

    在 Postgres 中:

    create schema protected;
    
    create table protected.foo (
      foo_id int primary key,
      bar text,
      owner name not null default_current user
    );
    
    create table protected.foo_children (
      foo_child_id int primary key,
      foo_id int not null references foo(food_id),
      owner name not null default current_user references foo(owner) on update cascade
    );
    

    现在一些 CHECK OPTION 视图 - 如果 postgres 使用 security_barrier:

    create view public.foo with (security_barrier) as 
      select
        *
      from protected.foo
      where
        owner = current_user
    WITH CHECK OPTION;
    
    create view public.foo_children with (security_barrier) as 
      select
        *
      from protected.foo_children
      where
        owner = current_user
    WITH CHECK OPTION;
    
    grant delete, insert, select, update on public.foo to some_users;
    grant delete, insert, select, update on public.foo_children to some_users;
    

    为了共享,您需要添加更多表格。重要的是您可以索引正确的列,这样就不会影响性能:

    create schema acl;
    
    create table acl.foo (
      foo_id int primary key references protected.foo(foo_id),
      grantee name not null,
      privilege char(1) not null
    );
    

    更新你的观点:

    create or update view public.foo with (security_barrier) as 
      select
        *
      from protected.foo
      where
        owner = current_user
        or exists ( select 1 from acl.foo where privilege in ('s','u','d') and grantee = current_user) );
    
     --add update trigger that checks for update privilege
    
     --add delete trigger that checks for delete privilege
    

    【讨论】:

    • 但我们每行需要不止一个“所有者”。 UserObjectX 表是多对多关系。 FWIW,我正在使用 SQL Server。
    猜你喜欢
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 2016-06-15
    • 1970-01-01
    • 1970-01-01
    • 2014-03-13
    • 1970-01-01
    相关资源
    最近更新 更多