Hello Francesco,
I'm really really sorry, I switched to another subject and was quite busy. It's 
always a pending task on my side, and I'll need to handle it in a very near 
future.

      De : Francesco Chicchiriccò <ilgro...@apache.org>
 À : user@syncope.apache.org 
 Envoyé le : Mardi 8 novembre 2016 9h22
 Objet : Re: SCIM & Syncope : OptimisticLockException on user groups membership 
update
   
 Hi Adrian,
 I was wondering if you made any progress on this.
 
 Regards.
 
 On 28/09/2016 08:02, Francesco Chicchiriccò wrote:
  
 
On 27/09/2016 17:24, Adrian Gonzalez wrote:
  
  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 suppose that the issue might come from the fact that you are essentially 
performing concurrent modifications to the same entity (e.g. user).
 
 
  i.e. 
  PUT http://localhost:9091/groups/2564b3a7-0f5d-424d-a4b3-a70f5d624d80 => 
gives a 200 PUT 
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) :  
 
 This NPE is quite strange: two possible causes come to my mind:
 
 1. there is some nasty transactional issue - even though I would have expected 
user.getMemberships() to be null rather than such collection containing a null 
membership
 
 2. there is some null element in user.getMemberships(), possibly coming by the 
fact that the first user modification is not committed yet
 
 In order to better investigate this behaviour I would suggest to debug the 
Syncope core application and to break in the findAllGroups() method to check if 
user.getMemberships() effectively contains null (and possibly non-null) 
elements.
 
 Regards.
 
 
  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(
                 
newMembershipPatch.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(
                 
newMembershipPatch.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
     
atorg.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]
     
atorg.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]
     
atorg.apache.commons.collections4.CollectionUtils.collect(CollectionUtils.java:1077)
 ~[commons-collections4-4.1.jar:4.1]
     
atorg.apache.commons.collections4.CollectionUtils.collect(CollectionUtils.java:1049)
 ~[commons-collections4-4.1.jar:4.1]
     
atorg.apache.syncope.core.persistence.jpa.dao.JPAUserDAO.findAllGroups(JPAUserDAO.java:495)~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
         
atorg.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) ~[?:?]
     
atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 ~[?:1.8.0_91]
     at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91]
     
atorg.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:280)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 ]       
atorg.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE--More--
     
atorg.apache.syncope.core.persistence.jpa.spring.DomainTransactionInterceptor.invoke(DomainTransactionInterceptor.java:64)~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.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) ~[?:?]
     
atorg.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl.createTasks(PropagationManagerImpl.java:345)~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
     
atorg.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl.getUpdateTasks(PropagationManagerImpl.java:275)~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
     
atorg.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl.getUserUpdateTasks(PropagationManagerImpl.java:204)~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
     
atorg.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--
     
atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
~[?:1.8.0_91]
     
atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 ~[?:1.8.0_91]
     at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91]
     
atorg.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:280)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
 ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]
         
atorg.apache.syncope.core.persistence.jpa.spring.DomainTransactionInterceptor.invoke(DomainTransactionInterceptor.java:64)~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.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) ~[?:?]
     
atorg.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager.update(DefaultUserProvisioningManager.java:123)~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
     
atorg.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--
     
atorg.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.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) ~[?:?]
     
atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 ~[?:1.8.0_91]
     at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91]
     
atorg.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
         
atorg.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:69)
 ~[spring-security-core-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
 ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.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]
     
atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
~[?:1.8.0_91]
     
atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 ~[?:1.8.0_91]
     at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91]
     
atorg.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180)
 ~[cxf-core-3.1.7.jar:3.1.7]
     
atorg.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]
     
atorg.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)
 ~[cxf-core-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)
 ~[cxf-core-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
 ~[cxf-core-3.1.7.jar:3.1.7]
         
atorg.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
 ~[cxf-core-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:254)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:180)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:299)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
     
atorg.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:276)
 ~[cxf-rt-transports-http-3.1.7.jar:3.1.7]
     
atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
 ~[catalina.jar:8.0.35]
     
atorg.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--
     
atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
 ~[catalina.jar:8.0.35]
     
atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.apache.syncope.core.spring.security.MustChangePasswordFilter.doFilter(MustChangePasswordFilter.java:77)
 ~[syncope-core-spring-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT]
     
atorg.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--
     
atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.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]
     
atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:215)
 ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
     
atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
     
atorg.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