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