Question

J'essaie de créer un système de contrôle d'accès.

Voici un exemple dépouillé de la table sur laquelle je tente de contrôler l'accès:

things table:
id   group_id   name
1    1          thing 1
2    1          thing 2
3    1          thing 3
4    1          thing 4
5    2          thing 5

Et la table de contrôle d'accès ressemble à ceci:

access table:
user_id  type   object_id  access
1        group  1          50
1        thing  1          10
1        thing  2          100

L'accès peut être accordé soit en spécifiant directement l'identifiant de la 'chose', soit accordé pour un groupe entier d'objets en spécifiant un identifiant de groupe. Dans l'exemple ci-dessus, l'utilisateur 1 a reçu un niveau d'accès de 50 pour le groupe 1, qui devrait s'appliquer sauf s'il existe d'autres règles accordant un accès plus spécifique à un élément individuel.

J'ai besoin d'une requête qui renvoie une liste d'éléments (les identifiants uniquement sont acceptables) ainsi que le niveau d'accès d'un utilisateur spécifique. Donc, en utilisant l'exemple ci-dessus, je voudrais quelque chose comme ceci pour l'ID utilisateur 1:

desired result:
thing_id   access
1          10
2          100
3          50   (things 3 and 4 have no specific access rule,
4          50   so this '50' is from the group rule)
5               (thing 5 has no rules at all, so although I 
                still want it in the output, there's no access
        level for it)

Le plus proche que je puisse trouver est le suivant:

SELECT * 
FROM things 
LEFT JOIN access ON 
    user_id = 1
    AND (
        (access.type = 'group' AND access.object_id = things.group_id)
        OR (access.type = 'thing' AND access.object_id = things.id)
    )

Mais cela retourne plusieurs lignes, alors que je n'en veux qu'une pour chaque ligne du tableau "objets". Je ne suis pas sûr de savoir comment arriver à une seule ligne pour chaque "chose", ou comment donner la priorité aux règles "de chose" par rapport aux règles de "groupe".

Si cela peut vous aider, la base de données que j'utilise est PostgreSQL.

N'hésitez pas à laisser un commentaire si des informations me manquent.

Merci d'avance!

Était-ce utile?

La solution

Je ne connais pas le dialecte SQL de Postgres, mais peut-être quelque chose comme:

select thing.*, coalesce ( ( select access
                             from   access
                             where  userid = 1
                             and    type = 'thing'
                             and    object_id = thing.id
                           ),
                           ( select access
                             from   access
                             where  userid = 1
                             and    type = 'group'
                             and    object_id = thing.group_id
                           )
                         )
from things

Incidemment, je n'aime pas le design. Je préférerais que la table d'accès soit divisée en deux:

thing_access (user_id, thing_id, access)
group_access (user_id, group_id, access)

Ma requête devient alors:

select thing.*, coalesce ( ( select access
                             from   thing_access
                             where  userid = 1
                             and    thing_id = thing.id
                           ),
                           ( select access
                             from   group_access
                             where  userid = 1
                             and    group_id = thing.group_id
                           )
                         )
from things

Je préfère cela car les clés étrangères peuvent maintenant être utilisées dans les tables d'accès.

Autres conseils

Je viens de lire un article hier soir à ce sujet. Il a quelques idées sur la façon de faire cela. Si vous ne pouvez pas utiliser le lien figurant dans le titre, essayez d'utiliser Google Scholar sur Limiter la divulgation d'informations à Hippocratic. Bases de données.

Bien qu'il existe plusieurs bonnes réponses, la plus efficace serait probablement la suivante:

SELECT things.id, things.group_id, things.name, max(access) 
FROM things 
LEFT JOIN access ON 
        user_id = 1 
        AND (
                (access.type = 'group' AND access.object_id = things.group_id)
                OR (access.type = 'thing' AND access.object_id = things.id)
        )
group by things.id, things.group_id, things.name

Qui utilise simplement le résumé ajouté à votre requête pour obtenir ce que vous recherchez.

Tony:

Ce n’est pas une mauvaise solution, je l’aime bien, semble fonctionner. Voici votre requête après quelques modifications mineures:

SELECT 
    things.*, 
    coalesce ( 
        (   SELECT access 
            FROM access 
            WHERE user_id = 1 
                AND type = 'thing' 
                AND object_id = things.id
        ), 
        (   SELECT access 
            FROM access 
            WHERE user_id = 1 
                AND type = 'group' 
                AND object_id = things.group_id 
        ) 
    ) AS access
    FROM things;

Et les résultats semblent corrects:

 id | group_id |  name   | access 
----+----------+---------+--------
  1 |        1 | thing 1 |     10
  2 |        1 | thing 2 |    100
  3 |        1 | thing 3 |     50
  4 |        1 | thing 4 |     50
  5 |        2 | thing 5 |       

Je prends complètement le fait que ce n’est pas un schéma idéal. Cependant, je suis coincé dans une certaine mesure.

Josef:

Votre solution est très similaire à celle avec laquelle je jouais, et mon instinct (tel qu’il est) me dit qu’il devrait être possible de le faire de cette façon. Malheureusement, cela ne donne pas des résultats complètement corrects:

 id | group_id |  name   | max 
----+----------+---------+-----
  1 |        1 | thing 1 |  50
  2 |        1 | thing 2 | 100
  3 |        1 | thing 3 |  50
  4 |        1 | thing 4 |  50
  5 |        2 | thing 5 |    

Le niveau d'accès pour 'chose 1' a pris la valeur d'accès 'groupe' supérieure, au lieu de la valeur d'accès plus spécifique 'chose' de 10, ce que je recherche après. Je ne pense pas qu'il soit possible de résoudre ce problème au sein d'un GROUP BY , mais si quelqu'un a des suggestions, je suis plus qu'heureux d'être prouvé qu'il est incorrect sur ce point.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top