[ 
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)

Reply via email to