Utilisation d'attributs Json dans Forgerock DS

Problématique

Il arrive des cas où l'utilisation des attributs "simples", qu'ils soient mono ou multi-valués, n'est pas satisfaisante par rapport aux besoins des utilisateurs.

Prenons le cas suivant : on veut gérer des délégations pour X types de profils et chacun avec des rôles spécifiques.

Soit :

  • délégation pour une société S1, avec les rôles RS1, RS2, RS3
  • délégation pour une société S2, avec les rôles RS1, RS3
  • délégation pour un utilisateur U1, avec les rôles RU1, RU2

Si on utilise un attribut de type chaîne de caractères, on peut s'en sortir avec des concaténations et des caractères de séparation. Par exemple :

type|identifiant du type|role1-role2-role3

Dans ce cas on doit déjà gérer 2 types de séparateurs :le "|" pour les différents éléments, et le "-" pour les rôles.

Dans notre exemple, ça donnerait :

  • societe|S1|RS1-RS2-RS3
  • societe|S2|RS1-RS3
  • utilisateur|U1|RU1-RU2

Par contre pour les recherches, ça devient plus compliqué, sauf à pré-calculer un filtre, en jouant avec les séparateurs.

Attribut Json

Depuis la version 5.5 (au moins), Forgerock permet d'utiliser une syntaxe Json dans les attributs, ce qui permet de gérer des données structurées à l'intérieur d'un attribut.

Déclaration dans le schéma

L'ajout d'un attribut de type JSON se fait de manière traditionnelle, en utilisant une syntaxe spécifique (1.3.6.1.4.1.36733.2.1.3.1).

On ajoute ensuite les attributs dans le schéma :

attributeTypes: ( 1.3.6.1.4.1.18472.2.101 NAME 'delegations' SYNTAX 1.3.6.1.4.1.36733.2.1.3.1 EQUALITY caseIgnoreJsonQueryMatch X-ORIGIN 'Mes tests json' )
attributeTypes: ( 1.3.6.1.4.1.18472.2.102 NAME 'jsonId' SYNTAX 1.3.6.1.4.1.36733.2.1.3.1 EQUALITY caseIgnoreJsonIdMatch SINGLE-VALUE X-ORIGIN 'Mes tests json' )

On doit également préciser le type de règle d'égalité qui sera utilisée. Il en existe 4 pré-définies :

  • caseIgnoreJsonQueryMatch : 2 objets sont identiques si tous leurs champs sont les mêmes, à la casse près
  • caseExactJsonQueryMatch : 2 objets sont identiques si tous leurs champs sont les mêmes, avec la même casse de caractères
  • caseIgnoreJsonIdMatch : 2 objets sont identiques si leur champ "_id" est identique, quelle que soit la casse de caractères
  • caseExactJsonIdMatch : 2 objets sont identiques si leur champ "_id" est identique, avec la même casse de caractères.

Quelques exemples pour illustrer les cas. En supposant que j'ai 2 entrées dans l'annuaire LDAP, avec :

  1. {"_id":"json","type":"user","value":"user123"}
  2. {"_id":"Json","type":"User","value":"USER123"}
  3. {"_id":"json","type":"user","value":"user345"}
  4. {"_id":"Json","type":"User","value":"USER345"}

Selon les cas :

  • avec la règle caseIgnoreJsonQueryMatch, les objets 1 et 2, et les objets 3 et 4 sont identiques
  • avec la règle caseExactJsonQueryMatch, il n'y a pas de correspondance
  • avec la règle caseIgnoreJsonIdMatch, les 4 objets sont identiques (ils ont le même _id)
  • avec la règle caseExactJsonIdMatch, les objets 1 et 3 sont considérés identiques, ainsi que 2 et 4

Indexation

Lorsqu'on indexe un attribut JSON, le comportement par défaut de Forgerock DS est de maintenir des clés pour chaque champ JSON. De ce fait, si l'attribut JSON contient de nombreux champs différents (et qui ne font pas l'objet de recherches) on peut choisir de n'indexer que ces champs. Dans ce cas, il faudra définir soi-même une règle (de type json-equality-matching-rule).

Si on utilise une règle de comparaison basée sur l'ID uniquement, c'est ce champ qui sera indexé, et pas les autres.

Recherche

La syntaxe du filtre de recherche est la même que celle utilisée pour les API REST Forgerock (communément appelées CREST = Common REST).

Quelques exemples de filtres, sur la base de nos attributs delegations et jsonId déclarés précédemment :

delegations: {"type":"societe","attributions":[{"id":"societe1","roles":["role-soc1","role-soc2","role-soc3"]},{"id":"societe2","roles":["role-soc4"]}]}
delegations: {"type":"utilisateur","attributions":[{"id":"user1","roles":["role-usr1","role-usr2"]},{"id":"user2","roles":["role-usr4"]}]}
delegations: {"type":"org","attributions":[{"id":"org1","roles":["role-org1","role-org2","role-org4"]}] }
jsonId: {"_id":"json","type":"user","value":"user123"}
jsonId: {"_id":"Json","type":"User","value":"USER123"}
jsonId: {"_id":"json","type":"user","value":"user345"}
jsonId: {"_id":"Json","type":"User","value":"USER345"}
  • (delegations=*) : va renvoyer toutes les entrées qui ont un attribut delegations
  • (jsonid=_id eq 'json') va renvoyer les entrées qui ont la valeur 'json' dans le champ "_id" de l'attribut
  • (delegations=type eq 'utilisateur' or type eq 'societe') : va renvoyer les objets ayant l'une ou l'autre valeur dans le champ type

Limitations sur les filtres de recherche

Il n'est pas possible de faire une recherche dans un tableau qui contient plusieurs objets (attributions dans notre exemple).

(delegations=id eq "user2") 

ne ramène rien (on n'y accède pas directement, il faudrait passer par attributions->id).

Par contre, si on a une structure dont l'un des attributs est un tableau, qui ne contient que des valeurs, cela fonctionne.

Si on a des attributs comme ceci :

delegations: {"type":"utilisateur","id":"user1","roles":["role-usr1","role-usr2"]}
delegations: {"type":"utilisateur","id":"user2","roles":["role-usr4","role-usr2"]}
delegations: {"type":"societe","id":"societe1","roles":["role-soc1","role-soc2","role-soc3"]}
delegations: {"type":"societe","id":"societe2","roles":["role-soc4"]}
delegations: {"type":"org","id":"org2","roles":["role-org1","role-org2","role-org4"]}

On peut bien faire des recherches de type :

ldapsearch -b ou=people,dc=example,dc=com '(delegations=id eq "user2")'

Si on veut éviter des collisions entre les différents types, on peut combiner plusieurs recherches. Si elles portent sur plusieurs éléments de l'attribut json, il faut utiliser le filtre adéquat. Par exemple :

(delegations=id eq "2" and type eq "societe")

La recherche dans un tableau fonctionne également. On peut ainsi rechercher tout les objets LDAP qui ont un role "role-usr4" :

(delegations=roles eq "role-usr4")

Idem pour les objets avec un type "societe" et un rôle "role-soc1" :

(delegations=type eq "societe" and roles eq "role-soc1")
Catégorie

Ajouter un commentaire

Plain text

  • Aucune balise HTML autorisée.
  • Les adresses de pages web et les adresses courriel se transforment en liens automatiquement.
  • Les lignes et les paragraphes vont à la ligne automatiquement.