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
>

Reply via email to