Hi Francesco,
I see an explanation about Type Extensions in the reference guide. I
created a group appAdmin with Type Extension [1] but when applying
the group to a user some attributes fail or don't appear [2].
My question is how the Type Extension attribute can be evaluated when
a group is associated with a user ? The case is the derived attribute:
login = "'adm'+personId".
Best regards,
Stéphane POPOFF
[1] {
"_class": "org.apache.syncope.common.lib.to.GroupTO",
"key": "019bbbf3-7b9f-7b6b-aa1c-6a210de2f615",
"type": "GROUP",
"realm": "/",
"name": "appAdmin",
"creator": "admin",
"creationDate": "2026-01-14T10:00:55.025528Z",
"creationContext": "REST",
"lastModifier": "admin",
"lastChangeDate": "2026-01-14T11:30:03.36464Z",
"lastChangeContext": "REST",
"dynRealms": [],
"status": null,
"auxClasses": [
"AdminGroup"
],
"plainAttrs": [
{
"schema": "projects",
"values": [
"projet1",
"projet2"
]
}
],
"derAttrs": [
{
"schema": "login",
"values": [
"adm-"
]
}
],
"resources": [
"admin"
],
"relationships": [],
"userOwner": null,
"groupOwner": null,
"udynMembershipCond": null,
"staticUserMembershipCount": 4,
"dynamicUserMembershipCount": 0,
"staticAnyObjectMembershipCount": 0,
"dynamicAnyObjectMembershipCount": 0,
"adynMembershipConds": {},
"typeExtensions": [
{
"anyType": "USER",
"auxClasses": [
"AdminGroup"
]
}
]
}
[2] {
"_class": "org.apache.syncope.common.lib.to.UserTO",
"key": "019bbbf4-232d-70e1-b940-1f091e4be34c",
"type": "USER",
"realm": "/",
"username": "tutu",
"creator": "admin",
"creationDate": "2026-01-14T10:01:37.940487Z",
"creationContext": "PULL Task 019bbbf1-7f0f-7935-b0c8-534be438ec42
'chargePersons'",
"lastModifier": "tutu",
"lastChangeDate": "2026-01-14T11:33:16.231655284Z",
"lastChangeContext": "REST",
"dynRealms": [],
"status": "active",
"auxClasses": [],
"plainAttrs": [
{
"schema": "nom",
"values": [
"nom de tutu"
]
},
{
"schema": "operationalUnit",
"values": [
"boulot"
]
},
{
"schema": "personId",
"values": [
"tutu"
]
},
{
"schema": "prenom",
"values": [
"prénom de tutu"
]
}
],
"derAttrs": [],
"resources": [
"admin"
],
"relationships": [],
"password": null,
"token": null,
"tokenExpireTime": null,
"lastLoginDate": "2026-01-14T11:32:54.613387809Z",
"changePwdDate": "2026-01-14T11:32:19.490419976Z",
"failedLogins": 0,
"securityQuestion": null,
"securityAnswer": null,
"suspended": false,
"mustChangePassword": false,
"memberships": [
{
"groupKey": "019bbbf3-7b9f-7b6b-aa1c-6a210de2f615",
"groupName": "appAdmin",
"plainAttrs": [], //where are projects values ?
"derAttrs": [
{
"schema": "login",
"values": [
"adm-" //should be adm-tutu
]
}
]
}
],
"dynMemberships": [],
"roles": [],
"dynRoles": [],
"linkedAccounts": [],
"delegatingDelegations": [],
"delegatedDelegations": []
}
Le ven. 9 janv. 2026 à 08:17, Francesco Chicchiriccò
<[email protected]> a écrit :
>
> 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
>