Thanks Francesco !

I've modified my code, but now I'm getting NullPointerException (on the second 
PUT request on /Group endpoint).This error isn't generated when we execute the 
request serially.It's generated when HTTP request are executed in parallel.

i.e. 
PUT http://localhost:9091/groups/2564b3a7-0f5d-424d-a4b3-a70f5d624d80 => gives 
a 200PUT http://localhost:9091/groups/fad94db3-9245-449e-994d-b39245449e6a
=> gives a 500
My code is available in [1]
The stacktrace is available in [2]
The error in syncope is from JPAUserDAO (input is null) :public 
Collection<Group> findAllGroups(final User user) {
    return CollectionUtils.union(
            CollectionUtils.collect(user.getMemberships(), new 
Transformer<UMembership, Group>() {

                @Override
                public Group transform(final UMembership input) {
                    return input.getRightEnd();
                }
            }, new ArrayList<Group>()),
            findDynGroupMemberships(user));
}


[1] Here's exactly the code from the SCIM Provider side (calling Syncope REST 
API) :  
    // This is the method called on PUT for Group endpoint
    public Group update(String id, Group group) {

        // 1. General group update
        GroupTO existingGroupTO = getGroupTOByKey(id);
        List<UserTO> usersOfThisGroup = getUsersForGroup(id);
        GroupTO groupTO = groupConverter.fromScim(group);
        groupTO.setKey(existingGroupTO.getKey());
        GroupService groupService = 
syncopeClient.getService(GroupService.class);
        Response response;
        try {
            response = groupService.update(groupTO);
        } catch (SyncopeClientException ex) {
            throw syncopeExHandler.convertSyncopeGroupClientException(ex, 
group.getDisplayName(), ProvisioningOperation.UPDATING);
        }
        if (response == null || Response.Status.OK.getStatusCode() != 
response.getStatus()) {
            throw syncopeExHandler.convertSyncopeGroupErrorResponse(response, 
group.getDisplayName(),
                    ProvisioningOperation.UPDATING);
        }

        // 2. General membership update
        List<String> idsOfExistingUsers = usersOfThisGroup.stream().map(user -> 
user.getKey()).collect(Collectors.toList());
        List<String> idsOfNewUsers = group.getMembers().stream().map(member -> 
member.getValue()).collect(Collectors.toList());
        for (MemberRef memberRef : group.getMembers()) {
            if (!idsOfExistingUsers.contains(memberRef.getValue())) {
                addUserToGroup(groupTO, memberRef.getValue());
            }
        }
        for (String userId : idsOfExistingUsers) {
            if (!idsOfNewUsers.contains(userId)) {
                removeUserFromGroup(groupTO, userId);
            }
        }
        return getById(id);
    }

    private void addUserToGroup(GroupTO groupTO, String userId) {
        UserService userService = syncopeClient.getService(UserService.class);
        UserPatch userPatch = new UserPatch();
        userPatch.setKey(userId);
        userPatch.getMemberships().add(
                new 
MembershipPatch.Builder().operation(PatchOperation.ADD_REPLACE).group(groupTO.getKey()).build());
        try {
            userService.update(userPatch);
        } catch (SyncopeClientException e) {
            throw new SCIMException(String.format("User %s was not added to the 
group %s", userId, groupTO.getName()), e);
        }
    }

    private void removeUserFromGroup(GroupTO groupTO, String userId) {
        UserService userService = syncopeClient.getService(UserService.class);
        UserPatch userPatch = new UserPatch();
        userPatch.setKey(userId);
        userPatch.getMemberships().add(
                new 
MembershipPatch.Builder().operation(PatchOperation.DELETE).group(groupTO.getKey()).build());
        try {
            userService.update(userPatch);
        } catch (SyncopeClientException e) {
            throw new SCIMException(String.format("User %s was not removed from 
the group %s", userId, groupTO.getName()), e);
        }
    }
[2] Stacktrace Sorry for the super long stack :
15:05:47.623 ERROR org.apache.syncope.core.rest.cxf.RestServiceExceptionMapper 
- Exception thrown
java.lang.NullPointerException
    at 
org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO$2.transform(JPAUserDAO.java:500)
 ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO$2.transform(JPAUserDAO.java:496)
 ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.apache.commons.collections4.CollectionUtils.collect(CollectionUtils.java:1077)
 ~[commons-collections4-4.1.jar:4.1]
    at 
org.apache.commons.collections4.CollectionUtils.collect(CollectionUtils.java:1049)
 ~[commons-collections4-4.1.jar:4.1]
    at 
org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO.findAllGroups(JPAUserDAO.java:495)
 ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
        at 
org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO.findAllResources(JPAUserDAO.java:529)
 ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at sun.reflect.GeneratedMethodAccessor124.invoke(Unknown Source) ~[?:?]
    at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 ~[?:1.8.0_91]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91]
    at 
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:280)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
]       at 
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE--More--
    at 
org.apache.syncope.core.persistence.jpa.spring.DomainTransactionInterceptor.invoke(DomainTransactionInterceptor.java:64)
 ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at com.sun.proxy.$Proxy172.findAllResources(Unknown Source) ~[?:?]
    at 
org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl.createTasks(PropagationManagerImpl.java:345)
 ~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl.getUpdateTasks(PropagationManagerImpl.java:275)
 ~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl.getUserUpdateTasks(PropagationManagerImpl.java:204)
 ~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl.getUserUpdateTasks(PropagationManagerImpl.java:224)
 ~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
_91]    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
~[?:1.8.0--More--
    at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
~[?:1.8.0_91]
    at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 ~[?:1.8.0_91]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91]
    at 
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:280)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
        at 
org.apache.syncope.core.persistence.jpa.spring.DomainTransactionInterceptor.invoke(DomainTransactionInterceptor.java:64)
 ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at com.sun.proxy.$Proxy209.getUserUpdateTasks(Unknown Source) ~[?:?]
    at 
org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager.update(DefaultUserProvisioningManager.java:123)
 ~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager.update(DefaultUserProvisioningManager.java:57)
 ~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at org.apache.syncope.core.logic.UserLogic.doUpdate(UserLogic.java:232) 
~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at org.apache.syncope.core.logic.UserLogic.update(UserLogic.java:213) 
~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at org.apache.syncope.core.logic.UserLogic.update(UserLogic.java:68) 
~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
        at 
org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) 
~[spring-core-4.3.2.RELEASE.jar:4.3.2.RELEASE]0-SNAPSHOT.jar:2.0.0-SNAPSHOT]--More--
    at 
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.apache.syncope.core.logic.LogicInvocationHandler.around(LogicInvocationHandler.java:72)
 ~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at sun.reflect.GeneratedMethodAccessor133.invoke(Unknown Source) ~[?:?]
    at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 ~[?:1.8.0_91]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91]
    at 
org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
        at 
org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:69)
 ~[spring-security-core-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.apache.syncope.core.logic.UserLogic$$EnhancerBySpringCGLIB$$f6ff0886.update(<generated>)
 ~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
stractAnyService.java:159) 
~[syncope-core-rest-cxf-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]e--
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_91]
    at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
~[?:1.8.0_91]
    at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 ~[?:1.8.0_91]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91]
    at 
org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180)
 ~[cxf-core-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) 
~[cxf-core-3.1.7.jar:3.1.7]
    at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189) 
~[cxf-rt-frontend-jaxrs-3.1.7.jar:3.1.7]
    at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99) 
~[cxf-rt-frontend-jaxrs-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)
 ~[cxf-core-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)
 ~[cxf-core-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
 ~[cxf-core-3.1.7.jar:3.1.7]
        at 
org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
 ~[cxf-core-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:254)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:180)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:299)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
    at 
org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:276)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
    at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
 ~[catalina.jar:8.0.35]
    at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
 ~[catalina.jar:8.0.35]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
~[tomcat-websocket.jar:8.0.35]
icationFilterChain.java:240) 
~[catalina.jar:8.0.35]erChain.internalDoFilter(Appl--More--
    at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
 ~[catalina.jar:8.0.35]
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.apache.syncope.core.spring.security.MustChangePasswordFilter.doFilter(MustChangePasswordFilter.java:77)
 ~[syncope-core-spring-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
jar:4.1.3.RELEASE]ingframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
 ~[spring-security-web-4.1.3.RELEASE.--More--
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
.doFilter(RequestCacheAwareFilter.java:63) 
~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:215)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 
~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]http://localhost:9091/groups/fad94db3-9245-449e-994d-b39245449e


      De : Francesco Chicchiriccò <ilgro...@apache.org>
 À : user@syncope.apache.org 
 Envoyé le : Mardi 27 septembre 2016 15h20
 Objet : Re: SCIM & Syncope : OptimisticLockException on user groups membership 
update
   
 On 27/09/2016 15:00, Adrian Gonzalez wrote:
  
  Hello, 
  We're trying to build a POC on SCIM APIs on top of Syncope.  
 
 That's very good to hear: looks it is for SCIM 2.0, correct?
 Looking forward to take a look at it!
 
 
  Problem is when we're using some basic SCIM APIs to update the groups 
membership of a given user, we got a OptimisticLockException. 
  This is due to the fact that SCIM group membership can be updated only from 
the Group endpoint (not from the User endpoint). From 
https://tools.ietf.org/html/rfc7643#page-24 
  groups 
 A list of groups to which the user belongs,... 
 Since this attribute has a mutability of "readOnly", group membership changes 
MUST be applied via the 
 "Group" Resource (Section 4.2).  This attribute has a mutability of 
"readOnly". 
  So, we have the following scenario :  * we have a user1 member of 2 groups 
(group1 and group2).  * we want to remove the user1 from both groups from a UI 
console.  * the UI then needs to send 2 HTTP PUT on SCIM /Groups endpoint (one 
for each group).
     /PUT Groups/group1 
     /PUT Groups/group2 
  * we get a OptimisticLockException since both calls are made for a relation 
on the same user - because on the SCIM side for the Group endpoint, we must 
call    userService.update(userTO) to update a user <-> group relation. 
  i.e.          MembershipTO membershipTO = 
                 new MembershipTO.Builder().group(userTO.getKey(), 
"USER").group(groupTO.getKey(), groupTO.getName()).build(); 
         userTO.getMemberships().add(membershipTO); 
         try { 
             userService.update(userTO); 
         } catch (SyncopeClientException e) { 
             throw new SCIMException(String.format("User %s was not added to 
the group %s", userId, groupTO.getName()), e); 
         }
  
  Is there an API to update user or group membership without testing @Version 
field ? (i.e like a syncope REST API on top of a jpql update ?) 
  Perhaps we're not using the good API here (is there an API to handle 
membership from the group's side ?
  Do you see another possible solution (besides updating membership from the 
Group side/screen) ?  
 
 You are right about the fact that Syncope manages groups memberships by 
modifying users.
 
 I am a bit confused about your snippet above, so let me rephrase it a bit.
 Assuming your scenario:
 
  * we have a user1 member of 2 groups (group1 and group2).
  * we want to remove the user1 from both groups from a UI console.
  * the UI then needs to send 2 HTTP PUT on SCIM /Groups endpoint (one for each 
group).
     /PUT Groups/group1
     /PUT Groups/group2 
 
 the Group endpoint should perform as following:
 
 UserPatch userPatch = new UserPatch();
 userPatch.setKey("the key of user1");
 userPatch.getMemberships().add(new MembershipPatch.Builder().
         operation(PatchOperation.DELETE).
         group("the key of the group passed to the endpoint").
         build());
 userLogic.update(userPatch, true);
 
 As you can see, I am rather using the logic layer (e.g. UserLogic as done in 
UserServiceImpl) to interact with Syncope core, rather than the external 
service layer (e.g. how REST clients do).
 
 HTH
 Regards.
 -- 
Francesco Chicchiriccò

Tirasa - Open Source Excellence
http://www.tirasa.net/

Member at The Apache Software Foundation
Syncope, Cocoon, Olingo, CXF, OpenJPA, PonyMail
http://home.apache.org/~ilgrosso/ 

   

Reply via email to