Hi Francesco, The case of Mr Bellini is very interesting, because in my view, informations came from both side: - the emails MX come from the IT university team (Group) groups['University A'].mx="@university_a.net" - the email identifier come from the Identity username (User) username="bellini" Suppose that a resource is joined with the group "University A" and "University B". So without human access to attributes, how can that information be propagated to emails servers scripts to produce 2 different mailboxes ? Where should I put this derived attribute: mail=username+mx, and when did it be evaluated for use in a provisioning process of the email resource ?
I'm sorry to insist, but if I had found more explanation on this use case in the documentation, I wouldn't have brought it up again. Best regards Stéphane POPOFF Le jeu. 15 janv. 2026 à 13:08, Francesco Chicchiriccò <[email protected]> a écrit : > > Hi, > I saw SYNCOPE-1944 but I had to fix the content. > > Let me repeat again that the purpose of Type Extension is not to copy Group's > attributes to members (Users or Any Objects) but to define additional > attributes that members will be able to hold, once a membership exists. > > With reference to the example given in > > https://syncope.apache.org/docs/4.0/reference-guide.html#type-extensions > > The user bellini has two email addresses, one for Membership with GROUP for > University A and one for Membership with GROUP for University B, but such > emails addresses had to be provided when creating or updating the user > bellini and are related to bellini itself, not the universities. > > The example is supposing that the Type Extensions for both university groups > are allowing the same email address for their members. > > > Besides this, in case you need to consider group attributes (not membership > attributes) for Resource Mapping, you can use the syntax as explained by > > https://syncope.apache.org/docs/4.0/reference-guide.html#mapping > > e.g. > > groups[groupName].schema > > Hope this clarifies. > Regards. > > On 15/01/26 08:45, Francesco Chicchiriccò wrote: > > Hi, > > by looking at the code, I think that derived attributes are not working > > correctly for memberships (as driven by group's type extension): please go > > ahead and open an issue on JIRA. > > > > Regarding plain attributes for memberships, instead, please consider that > > (a) Syncope will not store not return attributes with empty value and (b) > > it is up to the caller to provide such values when creating or updating the > > membership. > > > > Regards. > > > > On 14/01/26 12:51, Stéphane Popoff wrote: > >> 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 >
