Hi,
I wouldn't say that Syncope is based on accounts, rather the opposite.
I would also designed differently your use case, based on Syncope features:
* no need for PERSON, just stick to USER
* define a Group for each application, where:
* the Type Extension for the Group contains all user attributes required by
the application
* the External Resource is assigned to the Group, not the single User, where
the mapping of such External Resource is based on both USER and Membership
attributes (as defined by the Type Extension)
Now, when a user needs to be assigned to an application, a membership will be
created and the given user will be propagated to the Resource assigned to the
Group for the membership.
No need for any code customization.
HTH
Regards.
On 08/01/26 12:42, Stéphane Popoff wrote:
Hi Francesco,
My english is also poor, so that explains some misunderstanding.
Basically one PERSON instance like 'titi' (personId="titi") may have
many accounts (User instance) like: 'usr-titi', 'adm-titi' ...
PERSON titi have their own lifecycle, managed by an external resource
like an HR System (see https://github.com/spopoff/persons).
So when an event of create / update / delete appears on 'titi', a
"business" logic should apply rules that modify accounts.
For example:
- When 'toto' is created, we creates an account 'usr-toto' with
minimal access to some applications
- When 'toto' is updated and this operationalUnit attribute equals
"SOC", we creates a new account 'adm-toto' with privilege access
- When 'toto' quit, is deleted, all the associate accounts are deleted
(based on the value 'toto' in the attribute personName)
Actually I put this "Business" logic (create /delete) in an extension
of DefaultAnyObjectProvisioningManager (see code below). But are there
the best implementations ?
Generally this "Business" logic takes place in a workflow, I also
extend DefaultAnyObjectWorkflowAdapter, but I found a problem with the
delete. It's also very deep in Syncope and not user friendly (pure
Java code).
My view on Syncope is that it's a good tool for synchronizing
resources and accounts, and also for authenticating those resources
and their accounts. However, its account-centric approach makes it
difficult to integrate an identity-oriented logic.
At this point in my thinking, I believe that part of the logic needs
to be put upstream of Syncope and APIs used to control access
management.
What did you think ?
Best regards,
Stéphane POPOFF
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){
//on teste si c'est un objet PERSON car alors on va peut-être
créer un compte d'accès primaire
if(!anyObjectCR.getType().equals("PERSON")){
LOG.info("pas un objet PERSON <> "+anyObjectCR.getType());
return super.create(anyObjectCR, excludedResources,
nullPriorityAsync, creator, context);
}
LOG.info("1 Création d'une personne name="+anyObjectCR.getName());
//on fait appel au workflow objet qui reconnait les PERSON
WorkflowResult<String> created =
awfAdapter.create(anyObjectCR, creator, context);
LOG.info("retour du workflow "+created.getResult());
//je sais pas 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("2 Déjà des comptes, mais conforme aux besoins métiers
?");
}else if(!erreur){
LOG.info("2 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);
}
//Je sais pas encore comment gérer cette partie
List<PropagationTaskInfo> taskInfos =
propagationManager.getCreateTasks(
AnyTypeKind.ANY_OBJECT,
created.getResult(),
null,
created.getPropByRes(),
excludedResources);
//en fait je retourne zéro pour l'instant
LOG.info("3 Taches de propagation nb="+taskInfos.size());
for(PropagationTaskInfo taskInfo : taskInfos){
LOG.info("4 Tache de propagation key="+taskInfo.getKey());
}
PropagationReporter propagationReporter =
taskExecutor.execute(taskInfos, nullPriorityAsync, creator);
return new ProvisioningResult<>(created.getResult(),
propagationReporter.getStatuses());
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public List<PropagationStatus> delete(
final String key,
final Set<String> excludedResources,
final boolean nullPriorityAsync,
final String eraser,
final String context) {
LOG.info("1 Suppression un objet clé="+key);
Optional<? extends AnyObject> anyObject = anyObjectDAO.findById(key);
boolean objDel = false;
if(anyObject.isEmpty() || !anyObject.isPresent()){
LOG.warn("Pas retrouvé l'objet clé="+key);
}else{
LOG.info("2 Suppression un objet type="+anyObject.get().getType()+
" avec name="+anyObject.get().getName());
objDel = true;
}
if(objDel &&
anyObject.get().getType().toString().equals("JPAAnyType[PERSON]")){
LOG.info("3 Suppression un objet PERSON dans provisioning name="+
anyObject.get().getName()+" key="+key);
List<User> users = null;
boolean erreur = false;
try{
users = userDAO.findByDerAttrValue("personName==",
anyObject.get().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("4 Trouver des comptes, on flingue");
for(User user : users){
userDAO.delete(user);
}
}
}
PropagationByResource<String> propByRes = new
PropagationByResource<>();
propByRes.set(ResourceOperation.DELETE,
anyObjectDAO.findAllResourceKeys(key));
// Note here that we can only notify about "delete", not any other
// task defined in workflow process definition: this because this
// information could only be available after awfAdapter.delete(), which
// will also effectively remove user from db, thus making virtually
// impossible by NotificationManager to fetch required any
object information
//En gros les informations / objet produits dans le workflow sont
inaccessibles car il va supprimer l'objet
//donc seuls les "ressources liées" sont prévenues
List<PropagationTaskInfo> taskInfos =
propagationManager.getDeleteTasks(
AnyTypeKind.ANY_OBJECT,
key,
propByRes,
null,
excludedResources);
PropagationReporter propagationReporter =
taskExecutor.execute(taskInfos, nullPriorityAsync, eraser);
awfAdapter.delete(key, eraser, context);
return propagationReporter.getStatuses();
}
}
Le jeu. 8 janv. 2026 à 08:50, Francesco Chicchiriccò
<[email protected]> a écrit :
Hi,
my French is not as good as it used to be, so I am not very able to follow :-)
Please try to explain your needs as clear as possible, in English, possibly in
terms of requirements.
So far, I have understood that:
1. you have defined a PERSON AnyType
2. you have mapped PERSON to one or more External Resources
3. (maybe?) multiple PERSON instances are in Relationship with a single USER
instance
4. there are events (REST calls to Syncope API? Console modifications?) that are triggering changes
on PERSON instances ("arrives first", "change OrganizationalUnit") or possibly
the related USER instance; such changes will propagate, through the mapping(s) provided, to the
configured External Resource(s)
What I am failing to understand so far:
1. why you should be modifying the PERSON or USER AnyType definition when the
events occurs: I would expect changes to PERSON or USER instances (by adding /
removing attributes, memberships or relationships for example) but not the
AnyType itself
2. whether your are encapsulating somehow the changes (maybe via a custom REST
endpoint) which is meant to affect both a USER and related PERSON instance(s)
Maybe you can just provide a couple of examples, using Syncope concepts as
AnyType, Relationships, instances, Resources, etc.
Regards.
On 07/01/26 11:30, Stéphane Popoff wrote:
Happy new year,
About my use case I made a short video
[https://sites.google.com/spopoff.net/integrationsyncope/democonnsync?usp=sharing]
to explain the need.
Best regards,
Stéphane POPOFF
Le mer. 31 déc. 2025 à 11:27, Stéphane Popoff <[email protected]> a écrit :
Hi Francesco,
I want to manage access (User account) by an identity object, that was
supported by an AnyObject PERSON and it's connector. So the life cycle of the
object PERSON conducts the need of access of the identity. In fact PERSON is
the the real digital identity, not as User in the Syncope's design, and User
only supports access.
Hope it's more clear.
Best regards,
Stéphane popoff
Le mer. 31 déc. 2025, 07:59, Francesco Chicchiriccò <[email protected]> a
écrit :
Hi,
I am not sure to understand your use case - or better, I am not sure to
understand why a change to the USER AnyType should be involved.
Can you please describe what you are trying to achieve from an abstract point
of view?
Regards.
On 30/12/25 19:31, Stéphane Popoff wrote:
Hello,
I have a use case where a flow of objects (JPAAnyType[PERSON]) may
produce change on accounts (User).
Beside the 2 flows I want to introduce a business logic of kind:
- If a person arrive first, we give it a basic access to RH system and
a basic access to the tool of this operationalUnit
- If a person changes this operationalUnit, previous access is closed,
newest is open (with or without overlay)
- well real time implementation of an IAM system ;-)
My question is where can I put this logic ? In the Provisioning
Manager or/and/xor in the Workflow ?
I did some tests and found that mixing operations across types
(JPAAnyType[PERSON] and User) in a basic workflow creates problems.
Best regards, happy new eve,
Stéphane POPOFF
--
Francesco Chicchiriccò
Tirasa - Open Source Excellence
http://www.tirasa.net/
Member at The Apache Software Foundation
Syncope, Cocoon, Olingo, CXF, OpenJPA
https://about.me/ilgrosso