Dans ma pratique de l'IAM j'ai besoin d'une source d'identité qui va être le point commun de toutes les demandes d'accès. Syncope de base repose sur objet USER qui doit être le réceptacle de tous les attributs amonts et aval, ce qui conduit à une confusion et une surcharge d'attribut, entre ce qui est:
De l'identité personnelle
nom, patronyme, diplomes, famille ...
De l'identité professionnelle
emploie, qualification, formations, projets, sites, management, habilitations ...
Des attributs de compte
secrets, activation, positionnement, autorisations ...
J'ai donc sur la base d'un petit service REST, développé en Spring Boot, définit une source d'identité, basée sur les attributs suivants dans un premier temps:
personId identifiant unique d'un individu dans le temps et persistant quelque soit le cycle de vie
firstname un prénom
lastname un nom
operationalUnit une unité opérationnelle (pour faire très très simple ;-)
La notion de schémas de Syncope hérite largement du monde LDAP donc des attributs simple avec quelque caractéristiques (Schemas). Puis un regroupement des attributs, comme dans les classes LDAP (AnyTypeClasses) et enfin la consommation de ces regroupements dans des objets (AnyType).
Ce qui n'est pas aussi clair qu'avec LDAP, c'est la distinction entre les attributs obligatoires et optionnels d'un classe (AnyTypeClasses), n'est pas vraiment évidente, au moment de faire le mapping des attributs sur la ressource. Nous y reviendrons plus loin.
la classe personne et ses attributs
l'objet PERSON et sa classe personne associée (non visible)
Si on examine bien les classe d'attributs on voit que firstname est déjà présent dans "minimal user" et qu'en conséquence dans la classe "presonne" j'ai créé nom et prenom. C'est un des problèmes de confusion que l'on obtient quand un seul objet porte l'identité et les accès. Et si je veux utiliser "minimal user" simplement pour firstname je me retrouve avec les contraintes des autres attributs comme fullname et userId qui doivent être unique et donc ne peuvent pas être nuls si je ne les alimentent pas. Donc une AnyTypeClasses c'est un jeu d'attribut cohérents.
Un connecteur REST repose sur cette logique (mélange de verbe et de hiérarchie des accès via les Path). Mon implémentation est très simple:
un serveur accessible par une url de base (http://localhost/48080)
un Path persons
des accès simplement en GET
/persons pour le test du connecteur
/persons/ pour lire tout le contenu
/persons/{personId} pour un seul membre
Cette logique doit être décrite dans des scripts groovy:
En premier le schéma qui aide à la validation des associations (extrait de SchemaScript.groovy)
orgAttrsInfo = new HashSet<AttributeInfo>();orgAttrsInfo.add(idAIB.build());orgAttrsInfo.add(AttributeInfoBuilder.build("personId", String.class));orgAttrsInfo.add(AttributeInfoBuilder.build("prenom", String.class));orgAttrsInfo.add(AttributeInfoBuilder.build("nom", String.class));orgAttrsInfo.add(AttributeInfoBuilder.build("operationalUnit", String.class));Le test (extrait de TestScript.groovy)
WebClient webClient = client;ObjectMapper mapper = new ObjectMapper();La fonction de recherche (extrait de SearchScript.groovy)
def buildConnectorObject(node) { return [ personId:node.get("personId").textValue(), operationalUnit:node.get("operationalUnit").textValue(), prenom:node.get("firstname").textValue(), nom:node.get("lastname").textValue(), key:node.get("personId").textValue(), __NAME__:node.get("personId").textValue(), __UID__:node.get("personId").textValue() ];}Si on analyse ce qui précèdent on voit que mon schéma reprend celui de l'objet PERSON, même si ce n'est pas celui du service REST persons. Il provisionne des attributs suppémentaires __NAME___ et __UID__ qui sont des incontournables dans le monde connId, parce que des fonctions getName() et getUid() peuvent se retrouver embarqués.
Le fournisseur d'identité est composé de deux parties inter dépendantes
En premier un connecteur qui définit la technologie de transport depuis un serveur de connexion, où trouver les scripts (ce qui conditionne largement la suite), les opérations supportées
En second la ressource qui définit le mapping des attributs entre un objet Syncope (=PERSON) et un objet ressource (=__PERSON__ voir les scripts groovy)
Comme précisé avant je peux pas utiliser une classe (AnyTypeClasses) auxiliaire pour mapper les attributs firstname et surname
Dans le menu de la ressource person on trouve:
Explorer la ressource de voir le contenu remonter par l'ensemble.
Mais pour charger la base de données Syncope, il faut définir une tâche de traction (Pull) qui persistera les données collectées
La tâche de traction définit les paramètres de réconciliation de la base depuis la source et aussi la périodicité de cette tâche.
Des actions sont associables et qui seront largement à prendre en compte dans la logique d'intégration.
Au final nous avons des objets PERSON alimentés depuis une source externe et qui pourra servir de base pour des demandes d'accès produisant des comptes.
En conclusion de nos travaux je trouve que Syncope est basé sur une logique circulation de données, un peu en toile d'araignée, ce qui était une critique des outils de synchronisation (avant l'IAM). La logique de gestion par les identités et leur cycle de vie n'est pas vraiment au coeur du système.
L'étape suivante sera de voir comment on va alimenter les objets USER liés à une objet PERSON car ils sont incontounables dans Syncope, en particulier dans le module EndUser
Une première idée serait d'utiliser une action de propagation qui permet d'avoir la main sur la base de donnée et donc d'inscrire des objets.
Comme cette gestion est très dépendante du métier il faudrait pouvoir au maximum externaliser les règles et les gérer dans un cycle de vie des identités. A voir s'il est possible de raccrocher le moteur de workflow sur les objets PERSON ?
La réponse n'est pas simple, visiblement il doit exister un moteur de workflow par défaut, mais qui repose sur quoi ? La notion de Logic pourrait intervenir avec ce moteur, en effet on retrouve toutes les actions élémentaires qui s'appliquent à un changement d'état (voir la classe AnyObjectLogic), mais cela suggère un workflow de cycle de vie de données synchronisée.
D'une manière plus évidente l'extension Flowable met en œuvre un moteur de workflow indépendant et intégré dans Syncope. Le seul problème c'est qu'il est complètement dédier à l'objet USER et que le chargeur de workflow ne prend qu'un seul diagramme qui ne réagira que pour cet objet.
Pourtant quand on examine la classe qui gère le provisioning des objets (hors USER et GROUP), il est possible d'introduire un workflow spécifique et de l'utiliser dans un appel (ligne 82), comme ce qui est montré dans le schéma du provisioning. Mais comment je déclare ce nouveau diagramme et le charge ?
Cette étude me renforce dans l'idée que Syncope est en premier un outil de synchronisation comme était Novell Identity Manager à son époque.