Une de mes recherches c'est d'avoir une distinction entre l'identité unique d'une personne et ses comptes. Je sais déjà gérer un flux d'identité, mais il faut faire la relation et la configuration avec les objets comptes (USER).
J'ai trouvé dans la mailing list un thread qui reprend ma problématique. Un utilisateur de Syncope propose une solution simple ou simplifiée dans laquelle:
On fabrique un objet de type AnyObjet depuis la console
On associe un workflow à cette création qui fabrique un objet USER
La logique ici est d'avoir une couche relativement "métier" qui évite de trop développer / étendre des classes de base de Syncope, comme le justifie l'auteur. Je suis d'accord avec lui, l'usage d'un workflow permet d'ajouter des étapes et des traitements dans un cadre relativement "métier", sans toucher en profondeur à la logique primaire.
L'auteur interpelle l'équipe de Syncope parce que l'objet USER créé ne fonctionne pas dans la console. Finalement Francesco Chicchiriccò propose une autre approche qui repose sur le gestionnaire de provisioning et qui pour lui semble plus conforme à l'esprit de Syncope.
Pour cela il faut étendre la classe qui gère le provisioning des objets AnyObject (comme nous l'avions déjà identifié), récupérer le Bean du provisioning des USER (via injection ?) et associer les deux objets, si nécessaire, par une relation.
Cette solution fait disparaitre le workflow, bien qu'il soit possiblement pris en compte par le gestionnaire de provisioning (comme nous l'avions déjà identifié). La solution répond au besoin de gérer une relation 1-1 ou 1-n entre un objet identité et des comptes.
Dans un vrai cas d'usage, l'identité apporte des données qui vont contraindre les accès, donc cela oblige à mettre cette logique "métier" au cœur du provisioning, dans une classe java que l'on aura produite pour le client. Ce qui est assez inconfortable quand la compétence projet n'est pas pérenne pour le client. Par exemple le client contracte avec un intégrateur, qui donne cette tâche à un novice, qui lui-même aura un cycle de vie vers de nouveau horizon pour profiter des opportunités ...
Je vais opérer comme le suggère le patron de TIRASA. Etendre la classe DefaultAnyObjectProvisioningManager, mais aussi déclarer une nouvelle configuration pour changer le Bean qui utilisera la classe étendue. De ce point de vue la documentation semble un peu dépassée. Ma classe étendue qui pour un objet PERSON va rechercher un objet USER et le cas échéant le créer:
public class PersonProvisioningManager extends DefaultAnyObjectProvisioningManager {
@Autowired
UserProvisioningManager userProvisioningManager;
@Autowired
protected UserDAO userDAO;
protected static final Logger LOG = LogManager.getLogger(PersonProvisioningManager.class);
public PersonProvisioningManager(AnyObjectWorkflowAdapter awfAdapter, PropagationManager propagationManager, PropagationTaskExecutor taskExecutor, AnyObjectDAO anyObjectDAO) {
super(awfAdapter, propagationManager, taskExecutor, anyObjectDAO);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public ProvisioningResult<String> create(
final AnyObjectCR anyObjectCR,
final Set<String> excludedResources,
final boolean nullPriorityAsync,
final String creator,
final String context){
if(!anyObjectCR.getType().equals("PERSON")){
LOG.info("pas un objet PERSON <> "+anyObjectCR.getType());
return super.create(anyObjectCR, excludedResources, nullPriorityAsync, creator, context);
}
LOG.info("Création d'une personne name="+anyObjectCR.getName());
WorkflowResult<String> created = awfAdapter.create(anyObjectCR, creator, context);
LOG.info("retour du workflow "+created.getResult());
//je sais ce qu'il raconte en cas de réussite ou échec ?
//on cherche un USER avec un personId == anyObjectCR.getName();
List<User> users = null;
boolean erreur = false;
try{
users = userDAO.findByDerAttrValue("personName==", anyObjectCR.getName(), false);
}catch(Exception ex){
LOG.error("Erreur recherche USER "+ex);
erreur = true;
}
if(!erreur && users != null && !users.isEmpty()){
//on trouve un des comptes
LOG.info("Déjà des comptes, mais conforme aux besoins métiers ?");
}else if(!erreur){
LOG.info("Pas comptes, mais répondre aux besoins métiers ?");
UserCR novo = new UserCR.Builder("/", "usr-"+anyObjectCR.getName())
.plainAttr(new Attr.Builder("personName").value(anyObjectCR.getName()).build()).build();
userProvisioningManager.create(novo, nullPriorityAsync, creator, context);
}
List<PropagationTaskInfo> taskInfos = propagationManager.getCreateTasks(
AnyTypeKind.ANY_OBJECT,
created.getResult(),
null,
created.getPropByRes(),
excludedResources);
LOG.info("Taches de propagation nb="+taskInfos.size());
for(PropagationTaskInfo taskInfo : taskInfos){
LOG.info("Tache de propagation key="+taskInfo.getKey());
}
PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, creator);
return new ProvisioningResult<>(created.getResult(), propagationReporter.getStatuses());
}
}
La classe de configuration qui surcharge le Bean par défaut:
@Configuration("PersonProvisioningConfiguration")
@EnableConfigurationProperties(ProvisioningProperties.class)
public class PersonProvisioningConfiguration {
@Bean
public AnyObjectProvisioningManager anyObjectProvisioningManager(
final AnyObjectWorkflowAdapter awfAdapter,
final PropagationManager propagationManager,
final PropagationTaskExecutor taskExecutor,
final AnyObjectDAO anyObjectDAO) {
return new PersonProvisioningManager(
awfAdapter,
propagationManager,
taskExecutor,
anyObjectDAO);
}
}
Un extrait du log qui prouve l'usage de la classe étendue dans le cas d'une création d'un objet PERSON:
2025-12-27 13:30:14.076 [operation.id=019b6000-958b-7ee8-94e8-e52abc56b6b0] INFO [Scheduler-8] org.apache.syncope.core.PersonProvisioningManager : Création d'une personne name=tata
2025-12-27 13:30:14.140 [operation.id=019b6000-958b-7ee8-94e8-e52abc56b6b0] INFO [Scheduler-8] org.apache.syncope.core.PersonProvisioningManager : retour du workflow 019b6000-a6bd-7eb9-a7b8-13f532b301c8
2025-12-27 13:30:14.259 [operation.id=019b6000-958b-7ee8-94e8-e52abc56b6b0] INFO [Scheduler-8] org.apache.syncope.core.PersonProvisioningManager : Pas comptes, mais répondre aux besoins métiers ?
2025-12-27 13:30:14.586 [operation.id=019b6000-958b-7ee8-94e8-e52abc56b6b0] INFO [Scheduler-8] org.apache.syncope.core.PersonProvisioningManager : Taches de propagation nb=0
Cette approche ne résout pas de manière élégante l'aspect "métier" qui va conditionner la ou les créations / associations de comptes et leurs autorisations, mais c'est un début dans la compréhension des travaux d'intégration.
Ce qui reste encore assez mystérieux c'est le passage par le workflow, alors qu'il n'est pas défini à ma connaissance, et la tâche de propagation. A suivre ...