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
>

Reply via email to