Cet article décrit le mécanisme que l'on peut mettre en place dans OpenIDM (et les produits équivalents, Forgerock / Ping IDM), afin de pouvoir enrichier le modèle de rôles, incluant des informations de contexte ou de périmètre.

Contexte métier

Le modèle traditionnel d'affectation de rôles dans un outil d'IGA (Identity Governance and Administration) repose souvent sur le lien entre un utilisateur (une identité) et un rôle applicatif.

La limite de ce modèle est rapidement atteinte dans des secteurs tels que le retail (domaine que je connais), dans lequel la notion de rôle métier (ex : Chef de Rayon), doit souvent être complété par le type rayon / le périmètre sur lequel le rôle va s'appliquer.

Une solution simple consiste à démultiplier les rôles en fonction du contexte, mais ceci peut vite conduire à une inflation du nombre de rôles.

Il n'est pas rare d'avoir des centaines de départements / services / rayons, à mettre en regard des métiers (quelques centaines également) et des sites (quelques centaines). Dans un modèle standard, on aurait facilement des dizaines de milliers de rôles à gérer.

Avec la mise en place de la flexibilité, on peut avoir des utilisateurs qui travaillent sur plusieurs sites, et qui auront des responsabilités différentes sur chaque site.

Exemple réel :

  • Directrice ressources Humaines au siège
  • Responsable ressources Humaines sur le magasin M1
  • Responsable ressources Humaines sur le magasin M2
  • Responsable ressources Humaines sur le magasin M3

Autre exemple, lié au département / rayon :

  • Conseiller de vente / ELEC PLOMBERIE
  • Conseiller de vente / SANITAIRE
  • Conseiller de vente / CONFORT ENERGIE

Il est donc essentiel de pouvoir gérer des profils applicatifs de manière souple, qui prenne en compte la complexité du métier, et donc gérer des aspects multi-dimensionnels lors de l'affectation d'un rôle applicatif.

 Mise en place dans OpenIDM

Le lien entre un utilisateur et un rôle est stocké dans un objet relation, déclaré dans l'attribut roles de l'objet user, en tant que relation, pointant sur la collection de ressource managed/role :

"roles" : {
                        "description" : "",
                        "title" : "Provisioning Roles",
                        "viewable" : true,
                        "searchable" : false,
                        "userEditable" : false,
                        "policies" : [ ],
                        "returnByDefault" : false,
                        "minLength" : null,
                        "pattern" : "",
                        "type" : "array",
                        "items" : {
                            "type" : "relationship",
                            "reverseRelationship" : true,
                            "reversePropertyName" : "members",
                            "validate" : true,
                            "properties" : {
                                "_ref" : {
                                    "type" : "string"
                                },
                                "_refProperties" : {
                                    "type" : "object",
                                    "properties" : {
                                        "_id" : {
                                            "type" : "string",
                                            "label" : ""
                                        },
                                        "_grantType" : {
                                            "type" : "string",
                                            "label" : "Grant Type"
                                        }
                                    }
                                }
                            },
                            "resourceCollection" : [
                                {
                                    "path" : "managed/role",
                                    "label" : "Role",
                                    "query" : {
                                        "queryFilter" : "true",
                                        "fields" : [
                                            "name"
                                        ],
                                        "sortKeys" : [
                                            "name"
                                        ]
                                    }
                                }
                            ]
                        }
                    },

On constate que l'attribut roles est de type Array, constitué de relationship. Et la relation peut avoir des propriétés (attribut _refProperties), ce qui permet d'enrichir la relation.

On peut donc utiliser cette fonctionnalité pour ajouter des éléments sur la relation entre l'utilisateur et le rôle.

Il est possible d'enrichir la définition en précisant les attributs que l'on veut gérer. Par exemple :

  "_refProperties" : {
   "type" : "object",
   "properties" : {
      "_businessUnits" : {
         "type" : "array",
         "items" : {
            "type" : "string"
         },
         "label" : "Business Units"
       },
      "_departments" : {
         "type" : "array",
         "items" : {
           "type" : "string"
         },
         "label" : "Departments"
       },
       "_grantType" : {
         "type" : "string",
         "label" : "Grant Type"
       }
    }
  }

Lors de l'assignation d'un nouveau membre au rôle, on peut alors préciser les valeurs :

curl --header 'Content-Type: application/json' --request PATCH \
  --data '[ { "operation": "add", "field": "/members/-", "value": { "_ref" : "managed/user/9c427f53-c08f-40da-bb53-3163c19b3d7d", "_refProperties": { "_departments": ["Sales","Finance","IT"] , "_businessUnits" : ["France","Italie","Portugal"] } } } ]' http://openidm.example.com:8080/openidm/managed/role/209940f1-8534-4673-8d08-f31bbc57e1e8

Note : l'ajout de propriétés n'est possible qu'en ajoutant des membres au rôle, et pas dans le cas de l'ajout d'un rôle à un utilisateur.

Quand on interroge l'utilisateur, on peut alors avoir ses rôles et les éléments de contexte associés :

curl -s --header 'Content-Type: application/json' --request GET 'http://openidm.example.com:8080/openidm/managed/user/9c427f53-c08f-40da-bb53-3163c19b3d7d/roles?_queryFilter=true&_fields='
{
  "result": [
    {
      "_id": "209940f1-8534-4673-8d08-f31bbc57e1e8",
      "_rev": "4",
      "_ref": "managed/role/209940f1-8534-4673-8d08-f31bbc57e1e8",
      "_refProperties": {
        "_departments": [
          "Sales",
          "Finance",
          "IT"
        ],
        "_businessUnits": [
          "France",
          "Italie",
          "Portugal"
        ],
        "_id": "2c2837d5-db7b-44d2-83da-790171519f52",
        "_rev": "0"
      },
      "name": "BUS-DEPT-BU",
      "description": "Role par departement",
      "rolecode": "bus-dept-bu"
    }
  ],
  "resultCount": 1,
  "pagedResultsCookie": null,
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}

La table relationship utilise un modèle générique, elle stocke donc les propriétés de l'objet dans un objet JSON, dont la structure n'est pas prédéfinie.

CREATE TABLE IF NOT EXISTS openidm.relationships (
  id BIGSERIAL NOT NULL,
  objecttypes_id BIGINT NOT NULL,
  objectid VARCHAR(255) NOT NULL,
  rev VARCHAR(38) NOT NULL,
  fullobject JSON,
  PRIMARY KEY (id) );

De fait, il est optionnel de déclarer les propriétés supplémentaires dans l'attribut _refProperties.

Par exemple,

curl --header 'Content-Type: application/json' --request PATCH \
--data '[
  {
    "operation": "add",
    "field": "/members/-",
    "value": {
      "_ref": "managed/user/1c8b43a9-218c-4c2d-8320-e0ffaa757198",
      "_refProperties": {
        "_departments": [
          "Sales",
          "Finance"
        ],
        "_businessUnits": [
          "France",
          "Italie"
        ],
        "context": {
          "countries": [
            "FR",
            "IT"
          ],
          "model": "Referenced",
          "sites": [
            "FRA001",
            "FRA002",
            "IT222"
          ]
        }
      }
    }
  }
]
 \
' http://openidm.example.com:8080/openidm/managed/role/209940f1-8534-4673-8d08-f31bbc57e1e8

Lorsqu'on interroge OpenIDM pour récupérer l'utilisateur, on a bien la liste des rôles et des méta-données (le contexte du rôle) :

curl -s --insecure --header 'Content-Type: application/json' --request GET 'http://openidm.id-num.fr:8080/openidm/managed/user/1c8b43a9-218c-4c2d-8320-e0ffaa757198/roles?_queryFilter=true'

{
  "result": [
    {
      "_ref": "managed/role/2f777cac-a0bf-4f21-81d3-a43003cc618c",
      "_refProperties": {
        "_grantType": "conditional",
        "_id": "4d9ea585-d4a1-4a5c-be8d-52e54aae332e",
        "_rev": "0"
      }
    },
    {
      "_ref": "managed/role/209940f1-8534-4673-8d08-f31bbc57e1e8",
      "_refProperties": {
        "_departments": [
          "Sales",
          "Finance"
        ],
        "_businessUnits": [
          "France",
          "Italie",
          "BM-IT"
        ],
        "context": {
          "countries": [
            "FR",
            "IT"
          ],
          "model": "Referenced",
          "sites": [
            "FRA001",
            "FRA002",
            "IT222"
          ]
        }
        "_id": "5380ea05-ccc0-49d9-a2fb-be4afe0b0dc2",
        "_rev": "0"
      }
    }
  ],
  "resultCount": 2,
  "pagedResultsCookie": null,
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}

Limitations

Comme vu précédemment, ce fonctionnement n'est possible que lorsqu'on ajoute un membre à un rôle.

De plus, l'interface graphique ne permet pas de gérer nativement cette mécanique. Il est donc nécessaire d'utiliser les appels API pour réaliser cette affectation.

Cependant ce mécanisme permet de s'affranchir de limitations du modèle traditionnel. Pour que les applications puissent utiliser ces informations, il faut ensuite descendre ces données dans l'annuaire qui sert au SSO, ou exposer ces données via OpenIDM (soit en direct, soit en développant un endpoint spécifique pour ce besoin).

Previous Post Next Post