[ 
https://issues.apache.org/jira/browse/SYNCOPE-1386?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Francesco Chicchiriccò updated SYNCOPE-1386:
--------------------------------------------
    Description: 
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:

{code}
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) ~[?:?]
{code}

In Apache Syncope L2 cache is enabled by default. 
syncope-core-persistence-jpa-2.0.8.jar!\domains.xml file has a property 
{code}
<entry key="openjpa.DataCache" value="true"/>
{code}

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?
{code}
entityManager().detach(merged);
if (entityManager().getEntityManagerFactory().getCache() != null) {
entityManager().getEntityManagerFactory().getCache().evict(JPAUser.class, 
merged.getKey());
} 
{code}

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:
{code}
@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;
}
}
{code}

This is the guarantee for that corrected 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:
"./confirm_pwd_reset_action.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".

  was:
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:
"./confirm_pwd_reset_action.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".


> 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:
> {code}
> 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) ~[?:?]
> {code}
> In Apache Syncope L2 cache is enabled by default. 
> syncope-core-persistence-jpa-2.0.8.jar!\domains.xml file has a property 
> {code}
> <entry key="openjpa.DataCache" value="true"/>
> {code}
> 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?
> {code}
> entityManager().detach(merged);
> if (entityManager().getEntityManagerFactory().getCache() != null) {
> entityManager().getEntityManagerFactory().getCache().evict(JPAUser.class, 
> merged.getKey());
> } 
> {code}
> 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:
> {code}
> @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;
> }
> }
> {code}
> This is the guarantee for that corrected 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:
> "./confirm_pwd_reset_action.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