数据库设计 - 授予对记录的访问权限

时间:2015-11-03 16:38:36

标签: database database-design

我正在设计一个限制对某些对象的访问的数据库。我的同事和我已经讨论了不同的方法来解决这个问题,主要候选人是: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,那么即使没有这样的显式记录,他也可以访问包含的ObjectB。在UserObjectB中。
  2. 同样,如果用户有权访问父ObjectA,那么即使UserObjectA中没有此类记录,他也可以访问所有ObjectA后代。
  3. 此外,如果用户在访问授权表中没有记录,则隐式地他可以访问ObjectA和ObjectB中的所有记录。

明确方法:

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

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

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

最初,我们使用隐式方法运行。然而,在进入各种sprocs的实现之后,处理访问的逻辑变成了一种野兽,我们遇到了各种各样的细微之处,使得这种方法比显式方法更容易出错。 (请注意,真实场景比简化示例稍微复杂一些。)我发现自己不断实现递归CTE来确定访问权限,这使我(在考虑性能时)不允许在视图中抽象出某些逻辑部分或内联TVF。因此,我必须在许多不同的sprocs中重复和调整容易出错的逻辑。如果有任何改变,我们手上就会有一项大的维护任务。

那么,我们是否犯了这种隐式访问方法的错误?我肯定会有第二个想法,并会感谢任何有类似设计决策经验的人的建议。

1 个答案:

答案 0 :(得分:1)

如果你可以等一个月,Postgres 9.5将会出来并拥有Row Security。如果你有1000万美元的话,Oracle现在就拥有它。

现在,或者在其他dbs中,您可以模仿行安全性:

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

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

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

  4. 您需要从应用程序中设置current_user。 Here's how for pg + spring

  5. 在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