syncope-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "DmitriyB. (JIRA)" <j...@apache.org>
Subject [jira] [Updated] (SYNCOPE-1386) Not committed managed objects can get into L2 cache.
Date Fri, 19 Oct 2018 17:59:00 GMT

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

DmitriyB. 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}
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:java}
<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.

In our application that is based on Syncope 2.0.8, 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:java}
@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 destroying the
cache all the time when exception is thrown from one of @Transactional methods.

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:
{code:java}
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:java}
<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.

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:java}
@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 destroying 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}
> 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:java}
> <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.
> In our application that is based on Syncope 2.0.8, 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:java}
> @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 destroying
the cache all the time when exception is thrown from one of @Transactional methods.
> 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)

Mime
View raw message