[ https://issues.apache.org/jira/browse/SYNCOPE-1386?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
DmitriyB. updated SYNCOPE-1386: ------------------------------- Attachment: confirm_pwd_reset_action.sh > Not committed managed objects can get into L2 cache. > ---------------------------------------------------- > > Key: SYNCOPE-1386 > URL: https://issues.apache.org/jira/browse/SYNCOPE-1386 > Project: Syncope > Issue Type: Bug > Components: core > Affects Versions: 2.0.8 > Reporter: DmitriyB. > Priority: Major > Attachments: confirm_pwd_reset_action.sh > > > Hi guys. I noticed the issue that leads to inconsitent data that comes in > response. > In Apache Syncope the Application Scoped Entity manager is used for all > operations with the database. Entity manager is created by appropriate Entity > Manager Factory that matches a particular domain. Thus, the scope of > Persistence Context is extended and also it is bound to a current thread. > Moreover, Entity Manager that is created by Entity Manager Factory is > Transactional. Thus any execution using entity manager without opened > transaction leads to exception like: > <pre> > java.lang.IllegalStateException: Could not find EntityManager for domain > dbrashevets > at > org.apache.syncope.core.persistence.jpa.dao.AbstractDAO.entityManager(AbstractDAO.java:41) > ~[syncope-core-persistence-jpa-2.0.8.jar:?] > at > org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO.findByUsername(JPAUserDAO.java:209) > ~[syncope-core-persistence-jpa-2.0.8.jar:?] > at sun.reflect.GeneratedMethodAccessor232.invoke(Unknown Source) ~[?:?] > at > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45005) > ~[?:1.8.0_151] > at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151] > at > org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) > ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE] > at > org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) > ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE] > at com.sun.proxy.$Proxy74.findByUsername(Unknown Source) ~[?:?] > </pre> > In Apache Syncope L2 cache is enabled by default. > syncope-core-persistence-jpa-2.0.8.jar!\domains.xml file has a property > <pre> > <entry key="openjpa.DataCache" value="true"/> > </pre> > If the transaction is opened, the entity, that is fetched via Entity Manager > with the method like > org.apache.syncope.core.persistence.api.dao.UserDAO#findByUsername, gets into > L1 cache and L2 cache. > Than retrieved JPA entity can be modified in the scope of an opened > transaction. And if an exception occurs transaction is rolled back. L1 cache > is being destroyed because Entity Manager is bound to a current thread, but > L2 cache can have this managed entity. > It means that furtherly going HTTP requests can retrieve this corrupted > entity from L2 cache. > > Here is the use-case how to reproduce this: > 1. Create user in Syncope > 2. Do a request password reset action and make sure that token that is used > for pwd reset action is generated and stored into database. > 3. Restart your application to be sure that L2 cache is empty. > 4. Confirm password reset action for this user and make sure that requested > password doesn't apply the password rules. In my case password is too short. > The exception like "InvalidUser:InvalidPassword: Password too short" should > be thrown. > 5. Request the user by username. The user that comes in HTTP Response doesn't > have "token" and "tokenExpireTime" attributes. But you may find "token" and > "tokenExpireTime" value in SyncopeUser table for this user. > Here is my explanation why it happens: > org.apache.syncope.core.workflow.java.AbstractUserWorkflowAdapter#confirmPasswordReset > removes token and tokenExpireTime values by triggering > org.apache.syncope.core.persistence.api.entity.user.User#removeToken. > And it happens with the entity that is in "managed" state. > Then org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO#doSave saves and > flushes the entity. I guess save+flush adds this managed user into L2 cache > as well. > And then managed user is checked on policies and the outcome is > "InvalidUser:InvalidPassword: Password too short" > In code > https://github.com/apache/syncope/blob/443f5a38ea45f15c092c41abb202f897c795c5f2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java#L397 > I also noticed that `entityManager().remove(merged);` is called. > What is the purpose why you do this? The transaction is rolled back and > changes are not added into database (even remove(merged) action doesn't work). > Maybe you need to detach the managed entity and also remove it from the L2 > cache if it is enabled? > <pre> > entityManager().detach(merged); > if (entityManager().getEntityManagerFactory().getCache() != null) { > entityManager().getEntityManagerFactory().getCache().evict(JPAUser.class, > merged.getKey()); > } > </pre> > Maybe you have other similar places in the code like above? > In our application, currently, I always clean the L2 cache when transaction > is rolled back in > org.apache.syncope.core.persistence.jpa.spring.DomainTransactionInterceptor. > Invoke method looks like this: > <pre> > @Override > public Object invoke(final MethodInvocation invocation) throws Throwable { > try { > return super.invoke(invocation); > } catch (Throwable e) { > EntityManagerFactory entityManagerFactory = > EntityManagerFactoryUtils.findEntityManagerFactory( > ApplicationContextProvider.getBeanFactory(), AuthContextUtils.getDomain()); > Cache l2Cache = entityManagerFactory.getCache(); > if (l2Cache != null) { > l2Cache.evictAll(); > } > LOG.debug("Error during {} invocation", invocation.getMethod(), e); > throw e; > } > } > </pre> > This is the guarantee for that correpted data won't come in response, but I'm > destroting the cache all the time when exception is thrown from one of > @Transactional methods. > I also noticed that ~ after 5 minutes left the L2 cache is gone. But I cannot > find any l2CacheTimeOut setting in Syncope. Do you have such properties > somehwere in configuration. > You can find an example in confirm_pwd_reset_action.sh script. You can run it > by executing the command: > "./request_user.sh | tee temp.log" > Here I'm trying to do confirm-password-reset action after 5 minutes of > waiting with the password that doesn't match the rules. And then I'm > requesting user by username. In response it comes without "token" and > "tokenExpireTime". -- This message was sent by Atlassian JIRA (v7.6.3#76005)