2017-01-21 4 views
1

Celle-ci va être difficile à expliquer et donc un peu longue. Mes excuses pour ça.Organisation hiérarchique multi-locataire en base de données avec héritage

Je prévois un système basé sur DB qui va prendre en charge la multi-location hiérarchique avec l'héritage des capacités et des privilèges. Une structure schématique serait quelque chose comme ce qui suit:

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

Voici les chiffres dénotant chaque nœud dans la hiérarchie sont définies pour des raisons de simplicité et que rien ne doit être déduit de leur part.

Une seconde dimension serait donnée par les différents types d'utilisateurs, chaque type définissant un ensemble de privilèges (par exemple, le type d'accès aux différents types d'objets manipulés par le système).

Chaque utilisateur sera assigné à un type d'utilisateur spécifique et à un locataire spécifique (c'est-à-dire un nœud dans le diagramme ci-dessus). Un utilisateur affecté à un nœud aura uniquement une visibilité (si les privilèges requis sont affectés au type d'utilisateur concerné) aux objets du même locataire et à tous les locataires dont il est membre.

Une troisième dimension va être donnée par le fait que des modifications peuvent être apportées aux privilèges par défaut assignés aux types d'utilisateurs. Par exemple, un type d'utilisateur Administrateur peut créer de nouveaux utilisateurs sauf dans la sous-branche [221] (et vers le bas). Cette exception sera définie comme un "masque" chez le locataire [221].

Ainsi, chaque fois qu'un administrateur se connecte dans le système dont le locataire est, par exemple, [2212], les privilèges qu'il reçoit est la super-position des privilèges:

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]     

À titre d'exemple, supposons qu'il y sont 5 types d'objets différents, à savoir O1, O2, O3, O4 et O5, et les privilèges sont "N" (aucun), "R" (lire) et "W" (lecture/écriture). Par défaut, le type d'utilisateur administrateur aura les privilèges "W" sur tous les types d'objets.

Maintenant, nous définissons le privilège "R" sur les types d'objet O3 et O5 au noeud [22], et le privilège "N" sur le type d'objet O5 au niveau [221]. En conséquence, un administrateur affecté au noeud (locataire) [2212] sera inhérant les privilèges suivants:

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 

maintenant, et en espérant que le tableau est clair, trois défis doivent être traités:

  1. Lorsqu'un utilisateur se connecte dans le système au noeud (locataire) X, tous les sous-nœuds de besoin X à recueillir pour identifier les objets sur lesquels l'utilisateur sera (potentiel) avoir une visibilité et une action droite,

  2. Lorsqu'un utilisateur se connecte au système, ses privilèges doivent être évalué en regardant vers le haut jusqu'au nœud racine,

  3. L'approche présentée est-elle raisonnable pour une implémentation DB (SQL Server)?

En dehors de ces trois questions, "1" peut être simplement résolu en utilisant CTE pour implémenter une récursivité. Nous sommes alors laissés avec les questions 2 et 3.

Vos commentaires.Les commentaires et suggestions seront grandement appréciés.

Répondre

1

Si vous normalisez votre table de privilège à quelque chose comme (admin_id, org_id, obj, perm) au lieu de (admin_id, org_id, o1,o2,o3,o4,o5), cela peut être fait avec une requête qui obtient tous les ancêtres d'un org id et des références croisées cette liste avec la table des autorisations pour l'administrateur que vous êtes à la recherche et obtient l'autorisation pour le noeud le plus proche de celui qui est connecté.

configuration 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'; 

Après la configuration de test, une option se présente comme suit:

/* 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 

résultats sans pivot:

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

Si vous avez besoin du résultat être une ligne, vous pouvez faire pivoter les résultats de la requête précédente:

/* 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 

résultats avec pivot:

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

SqlZim, je vous remercie de la réponse claire et complète, et le lien que vous avez inclus à la page "test". Outre (ce que vous appelez) la normalisation de la table des privilèges, mettriez-vous en œuvre des choses si c'était le cas? – FDavidov

+0

@FDavidov J'implémenterais probablement les choses de cette façon ... Cela dépend de la taille de la hiérarchie, de la fréquence à laquelle elle changera, de la fréquence à laquelle elle sera utilisée et de la manière dont ces opérations seront simultanées. Si cela devait être une table à usage intensif de taille significative, j'évaluerais en utilisant l'exemple ci-dessus ** liste d'adjacence ** avec le ** kimball helper table ** pour aider avec les requêtes. Je recommande de vérifier cela: [Louis Davidson - Présentations - Comment optimiser une hiérarchie dans SQL Server - Présentation et Code de démonstration] (http://www.drsql.org/Pages/Presentations.aspx) – SqlZim

+0

Merci SqlZim pour votre retour d'information. En fait, je ne m'attends pas à ce que cette structure hiérarchique croisse au-dessus de quelques centaines d'éléments (probablement avec, disons, 4 niveaux de hiérarchie et plusieurs nœuds horizontalement, c'est-à-dire beaucoup plus gros que la hauteur). Le système n'est pas destiné à être consulté par des millions de personnes, mais plutôt quelques centaines à quelques milliers, et le processus ci-dessus est invoqué à LOGIN seulement. Donc je pense que ça devrait aller. N'êtes-vous pas d'accord? – FDavidov