【问题标题】:Hierarchical multi-tenant organization in database with inheritance具有继承性的数据库中的分层多租户组织
【发布时间】:2019-05-03 02:08:54
【问题描述】:

我正在计划一个基于 DB 的系统,该系统将支持分层多租户以及功能和权限的继承。示意图结构如下所示:

             [0]
            /   \
           /     \
        [1]       [2]
       /         /   \
      /         /     \
  [11]      [21]       [22]
           /    \          \
          /      \          \
     [211]        [212]      [221]
                            /     \       
                           /       \      
                     [2211]         [2212] 

此处设置表示层次结构中每个节点的数字只是为了简单起见,不应从中推断出任何内容。

第二个维度将由不同类型的用户给出,每种类型定义一组权限(例如,对系统处理的不同类型对象的访问类型)。

每个用户都将被分配给一个特定的用户类型和一个特定的租户(即上图中的一个节点)。分配给节点的用户将仅具有可见性(如果分配了所需的权限 到相关的用户类型)到同一租户内的对象以及用户所属的租户下的所有租户。

第三个维度将由可以对分配给用户类型的默认权限进行修改的事实给出。例如,管理员用户类型可以创建新用户,但在子分支 [221](和向下)除外。此异常将被定义为租户 [221] 的“掩码”。

因此,每当管理员登录其租户为 [2212] 的系统时,他获得的权限就是以下权限的叠加:

 Privileges defined at [2212] overriding the
 privileges defined at [221]  overriding the
 privileges defined at [22]   overriding the
 privileges defined at [2]    overriding the
 privileges defined at [0]                   

例如,假设有 5 种不同的对象类型,分别是 O1、O2、O3、O4 和 O5,权限分别为“N”(无)、“R”(读取)和“W”(读取/写)。默认情况下,管理员用户类型将对所有对象类型拥有“W”权限。

现在,我们在节点 [22] 的对象类型 O3 和 O5 上定义特权“R”,在级别 [221] 的对象类型 O5 上定义“N”特权。因此,分配给节点(租户)[2212] 的管理员将继承以下权限:

Object Type    O1      O2      O3      O4      O5
=================================================
From [0]       W       W       W       W       W
From [2]       _       _       _       _       _
From [22]      _       _       R       _       R
From [221]     _       _       _       _       N
From [2212]    _       _       _       _       _
------------------------------------------------
Result ===>    W       W       R       W       N

现在,需要应对三个挑战:

  1. 当用户在节点(租户)X登录系统时,需要收集X的所有子节点以识别用户将(潜在)对其具有可见性和操作权的对象,

  2. 当用户登录系统时,需要通过向上直到根节点来评估他的权限,

  3. 所提出的方法对于 DB 实现 (SQL Server) 是否合理?

在这三个问题中,“1”可以通过使用CTE实现递归来简单解决。然后剩下问题 2 和 3。我应该如何解决这个问题?

【问题讨论】:

    标签: sql sql-server inheritance multi-tenant


    【解决方案1】:

    如果您将您的权限表标准化为 (admin_id, org_id, obj, perm) 而不是 (admin_id, org_id, o1,o2,o3,o4,o5),这可以通过一个查询来完成正在查找并获得最接近正在登录的节点的权限。

    rextester 设置:http://rextester.com/MZRF65032

    create table org(id int not null primary key, parentid int null);
    insert into org values 
       (0,null)
      ,(1,0),(11,1)
      ,(2,0),(21,2),(211,21),(212,21)
      ,(22,2),(221,22),(2211,221),(2212,221);
    
    create table objects (obj char(2));
    insert into objects values ('O1'),('O2'),('O3'),('O4'),('O5');
    
    create table admin (id int not null primary key, name varchar(32));
    insert into admin values (1,'Zim');
    
    create table permissions(
        admin_id int not null
      , org_id int not null
      , obj char(2)
      , perm char(1)
      );
    insert into permissions (admin_id, org_id, obj, perm)
              select 1,  0,  obj, 'W' from objects
    union all select 1, 22, 'O3', 'R'
    union all select 1,221, 'O5', 'R'
    union all select 1,221, 'O5', 'N';
    

    测试设置后,一个选项如下所示:

    /* without pivot */
    ;with cte as (
    select
            p.parentid
          , p.id
          , step=0
      from org p
      where p.id = 2212
    union all
    select
           c.parentid
         , c.id
         , step=p.step+1
      from org as c
        inner join cte p on p.parentid = c.id
    )
    select o.obj, x.perm
      from objects o
        cross apply (
          select top 1 
              perm.perm
            from cte
              inner join permissions perm on cte.id = perm.org_id 
                and perm.admin_id = 1
            where perm.obj = o.obj
            order by step
        ) as x
    

    没有基准的结果:

    +-----+------+
    | obj | perm |
    +-----+------+
    | O1  | W    |
    | O2  | W    |
    | O3  | R    |
    | O4  | W    |
    | O5  | R    |
    +-----+------+
    

    如果您需要结果为一行,您可以旋转上一个查询的结果:

    /* with pivot */
    ;with cte as (
    select
          p.parentid
        , p.id
        , step=0
      from org p
      where p.id = 2212
    union all
    select 
         c.parentid
        , c.id
        , step=p.step+1
      from org as c
        inner join cte p on p.parentid = c.id
    )
    select o.obj, x.perm
      from objects o
        cross apply (
          select top 1 
              perm.perm
            from cte
              inner join permissions perm on cte.id = perm.org_id 
                and perm.admin_id = 1
            where perm.obj = o.obj
            order by step
        ) as x
        pivot (min(perm) for [obj] in ([o1],[o2],[o3],[o4],[o5])) as p
    

    带有枢轴的结果:

    +----+----+----+----+----+
    | o1 | o2 | o3 | o4 | o5 |
    +----+----+----+----+----+
    | W  | W  | R  | W  | R  |
    +----+----+----+----+----+
    

    【讨论】:

    • SqlZim,感谢您提供清晰完整的答案,以及您包含到“测试”页面的链接。除了(你所说的)特权表的规范化之外,如果你不得不这样做,你会实现吗?
    • @FDavidov 我可能会以这种方式实现......这取决于层次结构将获得多大,它会多久改变一次,它的使用频率以及这些操作的并发程度将会。如果这将是一个很大的重用表,我将使用上面的示例 adjacency list 以及 kimball 帮助表 来帮助查询。我建议检查一下:Louis Davidson - Presentations - How to Optimize a Hierarchy In SQL Server - Presentations & Demo Code
    • 感谢 SqlZim 的反馈。事实上,我不希望这种层次结构超过几百个元素(很可能有 4 级层次结构和水平的几个节点,换句话说,比高度要胖得多)。该系统并非旨在供数百万人访问,而是由数百到数千人访问,并且仅在登录时调用上述过程。所以我觉得应该没问题。你不同意吗?
    • @FDavidov 我同意这一点。
    猜你喜欢
    • 2018-01-31
    • 1970-01-01
    • 2015-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-06
    • 1970-01-01
    相关资源
    最近更新 更多