You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jl...@apache.org on 2015/10/31 00:14:05 UTC
tomee git commit: TOMEE-1648: The password cipher is called each time
the datasource is built even if we already have the clear password
Repository: tomee
Updated Branches:
refs/heads/master 34c4cc7a2 -> 436089b65
TOMEE-1648: The password cipher is called each time the datasource is built even if we already have the clear password
Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/436089b6
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/436089b6
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/436089b6
Branch: refs/heads/master
Commit: 436089b658e239757e0a51b49be01974ca339627
Parents: 34c4cc7
Author: Jean-Louis Monteiro <jl...@apache.org>
Authored: Fri Oct 30 16:13:56 2015 -0700
Committer: Jean-Louis Monteiro <jl...@apache.org>
Committed: Fri Oct 30 16:13:56 2015 -0700
----------------------------------------------------------------------
.../resource/jdbc/dbcp/BasicDataSource.java | 20 +-
.../jdbc/dbcp/BasicManagedDataSource.java | 23 ++-
.../jdbc/CipheredPasswordDataSourceTest.java | 193 +++++++++++++++++++
3 files changed, 233 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
index ffb5bba..995221c 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
@@ -55,6 +55,10 @@ public class BasicDataSource extends org.apache.commons.dbcp2.BasicDataSource im
* not ciphered. The {@link org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
*/
private String passwordCipher;
+
+ // keep tracking the user configured password in case we need it to be decrypted again
+ private String initialPassword;
+
private JMXBasicDataSource jmxDs;
private CommonDataSource delegate;
private String name;
@@ -128,6 +132,18 @@ public class BasicDataSource extends org.apache.commons.dbcp2.BasicDataSource im
}
}
+ @Override
+ public void setPassword(final String password) {
+ final ReentrantLock l = lock;
+ l.lock();
+ try {
+ // keep the encrypted value if it's encrypted
+ this.initialPassword = password;
+ super.setPassword(password);
+ } finally {
+ l.unlock();
+ }
+ }
public String getUserName() {
final ReentrantLock l = lock;
@@ -226,7 +242,9 @@ public class BasicDataSource extends org.apache.commons.dbcp2.BasicDataSource im
// check password codec if available
if (null != passwordCipher && !"PlainText".equals(passwordCipher)) {
final PasswordCipher cipher = PasswordCipherFactory.getPasswordCipher(passwordCipher);
- final String plainPwd = cipher.decrypt(getPassword().toCharArray());
+
+ // always use the initial encrypted value
+ final String plainPwd = cipher.decrypt(initialPassword.toCharArray());
// override previous password value
super.setPassword(plainPwd);
http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
index ec604b0..0846f21 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
@@ -54,6 +54,10 @@ public class BasicManagedDataSource extends org.apache.commons.dbcp2.managed.Bas
* not ciphered. The {@link org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
*/
private String passwordCipher;
+
+ // keep tracking the user configured password in case we need it to be decrypted again
+ private String initialPassword;
+
private JMXBasicDataSource jmxDs;
public BasicManagedDataSource(final String name) {
@@ -85,7 +89,7 @@ public class BasicManagedDataSource extends org.apache.commons.dbcp2.managed.Bas
private void setJndiXaDataSource(final String xaDataSource) {
setXaDataSourceInstance( // proxy cause we don't know if this datasource was created before or not the delegate
- XADataSourceResource.proxy(getDriverClassLoader() != null ? getDriverClassLoader() : Thread.currentThread().getContextClassLoader(), xaDataSource));
+ XADataSourceResource.proxy(getDriverClassLoader() != null ? getDriverClassLoader() : Thread.currentThread().getContextClassLoader(), xaDataSource));
if (getTransactionManager() == null) {
setTransactionManager(OpenEJB.getTransactionManager());
@@ -133,6 +137,19 @@ public class BasicManagedDataSource extends org.apache.commons.dbcp2.managed.Bas
}
}
+ @Override
+ public void setPassword(final String password) {
+ final ReentrantLock l = lock;
+ l.lock();
+ try {
+ // keep the encrypted value if it's encrypted
+ this.initialPassword = password;
+ super.setPassword(password);
+ } finally {
+ l.unlock();
+ }
+ }
+
public String getUserName() {
final ReentrantLock l = lock;
l.lock();
@@ -229,7 +246,9 @@ public class BasicManagedDataSource extends org.apache.commons.dbcp2.managed.Bas
// check password codec if available
if (null != passwordCipher) {
final PasswordCipher cipher = PasswordCipherFactory.getPasswordCipher(passwordCipher);
- final String plainPwd = cipher.decrypt(getPassword().toCharArray());
+
+ // always use the initial encrypted value
+ final String plainPwd = cipher.decrypt(initialPassword.toCharArray());
// override previous password value
super.setPassword(plainPwd);
http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
new file mode 100644
index 0000000..0f1224f
--- /dev/null
+++ b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.resource.jdbc;
+
+import org.apache.commons.dbcp.PoolableConnection;
+import org.apache.openejb.cipher.PasswordCipher;
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.jee.SingletonBean;
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.resource.jdbc.dbcp.BasicManagedDataSource;
+import org.apache.openejb.testing.Configuration;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.util.Strings;
+import org.apache.openejb.util.reflection.Reflections;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.annotation.Resource;
+import javax.ejb.EJB;
+import javax.ejb.EJBContext;
+import javax.ejb.LocalBean;
+import javax.ejb.Singleton;
+import javax.ejb.Startup;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Statement;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(ApplicationComposer.class)
+public class CipheredPasswordDataSourceTest {
+ private static final String URL = "jdbc:fake://sthg:3306";
+ private static final String USER = "pete";
+ private static final String ENCRYPTED_PASSWORD = "This is the encrypted value.";
+
+ @EJB
+ private Persister persistManager;
+
+ @Resource
+ private DataSource ds;
+
+ @Configuration
+ public Properties config() {
+ final Properties p = new Properties();
+ p.put("openejb.jdbc.datasource-creator", "dbcp");
+
+ p.put("managed", "new://Resource?type=DataSource");
+ p.put("managed.JdbcDriver", FakeDriver.class.getName());
+ p.put("managed.JdbcUrl", URL);
+ p.put("managed.UserName", USER);
+ p.put("managed.Password", ENCRYPTED_PASSWORD);
+ p.put("managed.PasswordCipher", EmptyPasswordCipher.class.getName());
+ p.put("managed.JtaManaged", "true");
+ p.put("managed.initialSize", "10");
+ p.put("managed.maxActive", "10");
+ p.put("managed.maxIdle", "10");
+ p.put("managed.minIdle", "10");
+ p.put("managed.maxWait", "200");
+ p.put("managed.defaultAutoCommit", "false");
+ p.put("managed.defaultReadOnly", "false");
+ p.put("managed.testOnBorrow", "true");
+ p.put("managed.testOnReturn", "true");
+ p.put("managed.testWhileIdle", "true");
+
+ return p;
+ }
+
+ @Module
+ public EjbJar app() throws Exception {
+ return new EjbJar()
+ .enterpriseBean(new SingletonBean(Persister.class).localBean());
+
+ }
+
+ @Before
+ public void createDatasource(){
+ // This is to make sure TomEE created the data source.
+ persistManager.save();
+ }
+
+ @Test
+ public void rebuild() {
+ // because of the exception at startup, the pool creating aborted
+ // before fixing this bug, the password was already decrypted, but the data source field
+ // wasn't yet initialized, so this.data source == null
+ // but the next time we try to initialize it, it tries to decrypt and already decrypted password
+
+ try {
+ ds.getConnection();
+
+ } catch (final SQLException e) {
+ System.out.println(e.getMessage());
+ // success - it throws the SQLException from the connect
+ // but it does not fail with the IllegalArgumentException from the Password Cipher
+ }
+ }
+
+ public static class EmptyPasswordCipher implements PasswordCipher {
+
+ @Override
+ public char[] encrypt(String plainPassword) {
+ throw new RuntimeException("Should never be called in this test.");
+ }
+
+ @Override
+ public String decrypt(char[] encryptedPassword) {
+ System.out.println(String.format(">>> Decrypt password '%s'", new String(encryptedPassword)));
+
+ // we want to know if the password as already been called
+ if (encryptedPassword.length == 0) {
+ throw new IllegalArgumentException("Can only decrypt a non empty string.");
+ }
+
+ return "";
+ }
+ }
+
+ public static class FakeDriver implements java.sql.Driver {
+ public boolean acceptsURL(final String url) throws SQLException {
+ return false;
+ }
+
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ return null;
+ }
+
+ public Connection connect(final String url, final Properties info) throws SQLException {
+ throw new SQLException("Pete said it first fails here!");
+ }
+
+ public int getMajorVersion() {
+ return 0;
+ }
+
+ public int getMinorVersion() {
+ return 0;
+ }
+
+ public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) throws SQLException {
+ return new DriverPropertyInfo[0];
+ }
+
+ public boolean jdbcCompliant() {
+ return false;
+ }
+ }
+
+ @LocalBean
+ @Singleton
+ @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+ public static class Persister {
+
+ @Resource(name = "managed")
+ private DataSource ds;
+
+ public void save() {
+ try {
+ ds.getConnection();
+
+ } catch (SQLException e) {
+ System.err.println("Generated SQL error > " + e.getMessage());
+ }
+ }
+ }
+
+
+}
Re: Fwd: tomee git commit: TOMEE-1648: The password cipher is called
each time the datasource is built even if we already have the clear password
Posted by Jean-Louis Monteiro <jl...@tomitribe.com>.
oh that is all perfect feedback
--
Jean-Louis Monteiro
http://twitter.com/jlouismonteiro
http://www.tomitribe.com
On Mon, Nov 2, 2015 at 11:36 AM, Romain Manni-Bucau <rm...@gmail.com>
wrote:
> Ok just checked and we didnt expose the setPassword to JMX for dbcp so we
> are fine, sorry for the noise!
>
>
> Romain Manni-Bucau
> @rmannibucau <https://twitter.com/rmannibucau> | Blog
> <http://rmannibucau.wordpress.com> | Github <
> https://github.com/rmannibucau> |
> LinkedIn <https://www.linkedin.com/in/rmannibucau> | Tomitriber
> <http://www.tomitribe.com>
>
> 2015-10-30 17:39 GMT-07:00 Jean-Louis Monteiro <jl...@tomitribe.com>:
>
> > Sure. Maybe the fix can be improved. Let's discuss
> > Le 30 oct. 2015 17:37, "Romain Manni-Bucau" <rm...@gmail.com> a
> > écrit :
> >
> > > It prevents to reset the pwd at runtime if i read it correctly. It
> should
> > > be fixed since it is used at least through jmx.
> > > ---------- Message transféré ----------
> > > De : <jl...@apache.org>
> > > Date : 30 oct. 2015 16:14
> > > Objet : tomee git commit: TOMEE-1648: The password cipher is called
> each
> > > time the datasource is built even if we already have the clear password
> > > À : <co...@tomee.apache.org>
> > > Cc :
> > >
> > > Repository: tomee
> > > Updated Branches:
> > > refs/heads/master 34c4cc7a2 -> 436089b65
> > >
> > >
> > > TOMEE-1648: The password cipher is called each time the datasource is
> > built
> > > even if we already have the clear password
> > >
> > >
> > > Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
> > > Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/436089b6
> > > Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/436089b6
> > > Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/436089b6
> > >
> > > Branch: refs/heads/master
> > > Commit: 436089b658e239757e0a51b49be01974ca339627
> > > Parents: 34c4cc7
> > > Author: Jean-Louis Monteiro <jl...@apache.org>
> > > Authored: Fri Oct 30 16:13:56 2015 -0700
> > > Committer: Jean-Louis Monteiro <jl...@apache.org>
> > > Committed: Fri Oct 30 16:13:56 2015 -0700
> > >
> > > ----------------------------------------------------------------------
> > > .../resource/jdbc/dbcp/BasicDataSource.java | 20 +-
> > > .../jdbc/dbcp/BasicManagedDataSource.java | 23 ++-
> > > .../jdbc/CipheredPasswordDataSourceTest.java | 193
> > +++++++++++++++++++
> > > 3 files changed, 233 insertions(+), 3 deletions(-)
> > > ----------------------------------------------------------------------
> > >
> > >
> > >
> > >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > > ----------------------------------------------------------------------
> > > diff --git
> > >
> > >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > >
> > >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > > index ffb5bba..995221c 100644
> > > ---
> > >
> > >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > > +++
> > >
> > >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > > @@ -55,6 +55,10 @@ public class BasicDataSource extends
> > > org.apache.commons.dbcp2.BasicDataSource im
> > > * not ciphered. The {@link
> > > org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
> > > */
> > > private String passwordCipher;
> > > +
> > > + // keep tracking the user configured password in case we need it
> to
> > be
> > > decrypted again
> > > + private String initialPassword;
> > > +
> > > private JMXBasicDataSource jmxDs;
> > > private CommonDataSource delegate;
> > > private String name;
> > > @@ -128,6 +132,18 @@ public class BasicDataSource extends
> > > org.apache.commons.dbcp2.BasicDataSource im
> > > }
> > > }
> > >
> > > + @Override
> > > + public void setPassword(final String password) {
> > > + final ReentrantLock l = lock;
> > > + l.lock();
> > > + try {
> > > + // keep the encrypted value if it's encrypted
> > > + this.initialPassword = password;
> > > + super.setPassword(password);
> > > + } finally {
> > > + l.unlock();
> > > + }
> > > + }
> > >
> > > public String getUserName() {
> > > final ReentrantLock l = lock;
> > > @@ -226,7 +242,9 @@ public class BasicDataSource extends
> > > org.apache.commons.dbcp2.BasicDataSource im
> > > // check password codec if available
> > > if (null != passwordCipher &&
> > > !"PlainText".equals(passwordCipher)) {
> > > final PasswordCipher cipher =
> > > PasswordCipherFactory.getPasswordCipher(passwordCipher);
> > > - final String plainPwd =
> > > cipher.decrypt(getPassword().toCharArray());
> > > +
> > > + // always use the initial encrypted value
> > > + final String plainPwd =
> > > cipher.decrypt(initialPassword.toCharArray());
> > >
> > > // override previous password value
> > > super.setPassword(plainPwd);
> > >
> > >
> > >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > > ----------------------------------------------------------------------
> > > diff --git
> > >
> > >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > >
> > >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > > index ec604b0..0846f21 100644
> > > ---
> > >
> > >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > > +++
> > >
> > >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > > @@ -54,6 +54,10 @@ public class BasicManagedDataSource extends
> > > org.apache.commons.dbcp2.managed.Bas
> > > * not ciphered. The {@link
> > > org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
> > > */
> > > private String passwordCipher;
> > > +
> > > + // keep tracking the user configured password in case we need it
> to
> > be
> > > decrypted again
> > > + private String initialPassword;
> > > +
> > > private JMXBasicDataSource jmxDs;
> > >
> > > public BasicManagedDataSource(final String name) {
> > > @@ -85,7 +89,7 @@ public class BasicManagedDataSource extends
> > > org.apache.commons.dbcp2.managed.Bas
> > >
> > > private void setJndiXaDataSource(final String xaDataSource) {
> > > setXaDataSourceInstance( // proxy cause we don't know if this
> > > datasource was created before or not the delegate
> > > - XADataSourceResource.proxy(getDriverClassLoader() != null
> ?
> > > getDriverClassLoader() :
> Thread.currentThread().getContextClassLoader(),
> > > xaDataSource));
> > > + XADataSourceResource.proxy(getDriverClassLoader() !=
> > null
> > > ? getDriverClassLoader() :
> > Thread.currentThread().getContextClassLoader(),
> > > xaDataSource));
> > >
> > > if (getTransactionManager() == null) {
> > > setTransactionManager(OpenEJB.getTransactionManager());
> > > @@ -133,6 +137,19 @@ public class BasicManagedDataSource extends
> > > org.apache.commons.dbcp2.managed.Bas
> > > }
> > > }
> > >
> > > + @Override
> > > + public void setPassword(final String password) {
> > > + final ReentrantLock l = lock;
> > > + l.lock();
> > > + try {
> > > + // keep the encrypted value if it's encrypted
> > > + this.initialPassword = password;
> > > + super.setPassword(password);
> > > + } finally {
> > > + l.unlock();
> > > + }
> > > + }
> > > +
> > > public String getUserName() {
> > > final ReentrantLock l = lock;
> > > l.lock();
> > > @@ -229,7 +246,9 @@ public class BasicManagedDataSource extends
> > > org.apache.commons.dbcp2.managed.Bas
> > > // check password codec if available
> > > if (null != passwordCipher) {
> > > final PasswordCipher cipher =
> > > PasswordCipherFactory.getPasswordCipher(passwordCipher);
> > > - final String plainPwd =
> > > cipher.decrypt(getPassword().toCharArray());
> > > +
> > > + // always use the initial encrypted value
> > > + final String plainPwd =
> > > cipher.decrypt(initialPassword.toCharArray());
> > >
> > > // override previous password value
> > > super.setPassword(plainPwd);
> > >
> > >
> > >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > > ----------------------------------------------------------------------
> > > diff --git
> > >
> > >
> >
> a/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > >
> > >
> >
> b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > > new file mode 100644
> > > index 0000000..0f1224f
> > > --- /dev/null
> > > +++
> > >
> > >
> >
> b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > > @@ -0,0 +1,193 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one or more
> > > + * contributor license agreements. See the NOTICE file distributed
> with
> > > + * this work for additional information regarding copyright ownership.
> > > + * The ASF licenses this file to You under the Apache License, Version
> > 2.0
> > > + * (the "License"); you may not use this file except in compliance
> with
> > > + * the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing, software
> > > + * distributed under the License is distributed on an "AS IS" BASIS,
> > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > > implied.
> > > + * See the License for the specific language governing permissions and
> > > + * limitations under the License.
> > > + */
> > > +package org.apache.openejb.resource.jdbc;
> > > +
> > > +import org.apache.commons.dbcp.PoolableConnection;
> > > +import org.apache.openejb.cipher.PasswordCipher;
> > > +import org.apache.openejb.jee.EjbJar;
> > > +import org.apache.openejb.jee.SingletonBean;
> > > +import org.apache.openejb.junit.ApplicationComposer;
> > > +import org.apache.openejb.resource.jdbc.dbcp.BasicManagedDataSource;
> > > +import org.apache.openejb.testing.Configuration;
> > > +import org.apache.openejb.testing.Module;
> > > +import org.apache.openejb.util.Strings;
> > > +import org.apache.openejb.util.reflection.Reflections;
> > > +import org.junit.Before;
> > > +import org.junit.BeforeClass;
> > > +import org.junit.Test;
> > > +import org.junit.runner.RunWith;
> > > +
> > > +import javax.annotation.Resource;
> > > +import javax.ejb.EJB;
> > > +import javax.ejb.EJBContext;
> > > +import javax.ejb.LocalBean;
> > > +import javax.ejb.Singleton;
> > > +import javax.ejb.Startup;
> > > +import javax.ejb.TransactionAttribute;
> > > +import javax.ejb.TransactionAttributeType;
> > > +import javax.sql.DataSource;
> > > +import java.sql.Connection;
> > > +import java.sql.DriverManager;
> > > +import java.sql.DriverPropertyInfo;
> > > +import java.sql.ResultSet;
> > > +import java.sql.SQLException;
> > > +import java.sql.SQLFeatureNotSupportedException;
> > > +import java.sql.Statement;
> > > +import java.util.Properties;
> > > +import java.util.logging.Logger;
> > > +
> > > +import static org.junit.Assert.assertFalse;
> > > +import static org.junit.Assert.assertTrue;
> > > +
> > > +@RunWith(ApplicationComposer.class)
> > > +public class CipheredPasswordDataSourceTest {
> > > + private static final String URL = "jdbc:fake://sthg:3306";
> > > + private static final String USER = "pete";
> > > + private static final String ENCRYPTED_PASSWORD = "This is the
> > > encrypted value.";
> > > +
> > > + @EJB
> > > + private Persister persistManager;
> > > +
> > > + @Resource
> > > + private DataSource ds;
> > > +
> > > + @Configuration
> > > + public Properties config() {
> > > + final Properties p = new Properties();
> > > + p.put("openejb.jdbc.datasource-creator", "dbcp");
> > > +
> > > + p.put("managed", "new://Resource?type=DataSource");
> > > + p.put("managed.JdbcDriver", FakeDriver.class.getName());
> > > + p.put("managed.JdbcUrl", URL);
> > > + p.put("managed.UserName", USER);
> > > + p.put("managed.Password", ENCRYPTED_PASSWORD);
> > > + p.put("managed.PasswordCipher",
> > > EmptyPasswordCipher.class.getName());
> > > + p.put("managed.JtaManaged", "true");
> > > + p.put("managed.initialSize", "10");
> > > + p.put("managed.maxActive", "10");
> > > + p.put("managed.maxIdle", "10");
> > > + p.put("managed.minIdle", "10");
> > > + p.put("managed.maxWait", "200");
> > > + p.put("managed.defaultAutoCommit", "false");
> > > + p.put("managed.defaultReadOnly", "false");
> > > + p.put("managed.testOnBorrow", "true");
> > > + p.put("managed.testOnReturn", "true");
> > > + p.put("managed.testWhileIdle", "true");
> > > +
> > > + return p;
> > > + }
> > > +
> > > + @Module
> > > + public EjbJar app() throws Exception {
> > > + return new EjbJar()
> > > + .enterpriseBean(new
> > > SingletonBean(Persister.class).localBean());
> > > +
> > > + }
> > > +
> > > + @Before
> > > + public void createDatasource(){
> > > + // This is to make sure TomEE created the data source.
> > > + persistManager.save();
> > > + }
> > > +
> > > + @Test
> > > + public void rebuild() {
> > > + // because of the exception at startup, the pool creating
> > aborted
> > > + // before fixing this bug, the password was already decrypted,
> > but
> > > the data source field
> > > + // wasn't yet initialized, so this.data source == null
> > > + // but the next time we try to initialize it, it tries to
> > decrypt
> > > and already decrypted password
> > > +
> > > + try {
> > > + ds.getConnection();
> > > +
> > > + } catch (final SQLException e) {
> > > + System.out.println(e.getMessage());
> > > + // success - it throws the SQLException from the connect
> > > + // but it does not fail with the IllegalArgumentException
> > from
> > > the Password Cipher
> > > + }
> > > + }
> > > +
> > > + public static class EmptyPasswordCipher implements PasswordCipher
> {
> > > +
> > > + @Override
> > > + public char[] encrypt(String plainPassword) {
> > > + throw new RuntimeException("Should never be called in this
> > > test.");
> > > + }
> > > +
> > > + @Override
> > > + public String decrypt(char[] encryptedPassword) {
> > > + System.out.println(String.format(">>> Decrypt password
> > '%s'",
> > > new String(encryptedPassword)));
> > > +
> > > + // we want to know if the password as already been called
> > > + if (encryptedPassword.length == 0) {
> > > + throw new IllegalArgumentException("Can only decrypt a
> > non
> > > empty string.");
> > > + }
> > > +
> > > + return "";
> > > + }
> > > + }
> > > +
> > > + public static class FakeDriver implements java.sql.Driver {
> > > + public boolean acceptsURL(final String url) throws
> SQLException
> > {
> > > + return false;
> > > + }
> > > +
> > > + public Logger getParentLogger() throws
> > > SQLFeatureNotSupportedException {
> > > + return null;
> > > + }
> > > +
> > > + public Connection connect(final String url, final Properties
> > info)
> > > throws SQLException {
> > > + throw new SQLException("Pete said it first fails here!");
> > > + }
> > > +
> > > + public int getMajorVersion() {
> > > + return 0;
> > > + }
> > > +
> > > + public int getMinorVersion() {
> > > + return 0;
> > > + }
> > > +
> > > + public DriverPropertyInfo[] getPropertyInfo(final String url,
> > > final Properties info) throws SQLException {
> > > + return new DriverPropertyInfo[0];
> > > + }
> > > +
> > > + public boolean jdbcCompliant() {
> > > + return false;
> > > + }
> > > + }
> > > +
> > > + @LocalBean
> > > + @Singleton
> > > + @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
> > > + public static class Persister {
> > > +
> > > + @Resource(name = "managed")
> > > + private DataSource ds;
> > > +
> > > + public void save() {
> > > + try {
> > > + ds.getConnection();
> > > +
> > > + } catch (SQLException e) {
> > > + System.err.println("Generated SQL error > " +
> > > e.getMessage());
> > > + }
> > > + }
> > > + }
> > > +
> > > +
> > > +}
> > >
> >
>
Re: Fwd: tomee git commit: TOMEE-1648: The password cipher is called
each time the datasource is built even if we already have the clear password
Posted by Romain Manni-Bucau <rm...@gmail.com>.
Ok just checked and we didnt expose the setPassword to JMX for dbcp so we
are fine, sorry for the noise!
Romain Manni-Bucau
@rmannibucau <https://twitter.com/rmannibucau> | Blog
<http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> |
LinkedIn <https://www.linkedin.com/in/rmannibucau> | Tomitriber
<http://www.tomitribe.com>
2015-10-30 17:39 GMT-07:00 Jean-Louis Monteiro <jl...@tomitribe.com>:
> Sure. Maybe the fix can be improved. Let's discuss
> Le 30 oct. 2015 17:37, "Romain Manni-Bucau" <rm...@gmail.com> a
> écrit :
>
> > It prevents to reset the pwd at runtime if i read it correctly. It should
> > be fixed since it is used at least through jmx.
> > ---------- Message transféré ----------
> > De : <jl...@apache.org>
> > Date : 30 oct. 2015 16:14
> > Objet : tomee git commit: TOMEE-1648: The password cipher is called each
> > time the datasource is built even if we already have the clear password
> > À : <co...@tomee.apache.org>
> > Cc :
> >
> > Repository: tomee
> > Updated Branches:
> > refs/heads/master 34c4cc7a2 -> 436089b65
> >
> >
> > TOMEE-1648: The password cipher is called each time the datasource is
> built
> > even if we already have the clear password
> >
> >
> > Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
> > Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/436089b6
> > Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/436089b6
> > Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/436089b6
> >
> > Branch: refs/heads/master
> > Commit: 436089b658e239757e0a51b49be01974ca339627
> > Parents: 34c4cc7
> > Author: Jean-Louis Monteiro <jl...@apache.org>
> > Authored: Fri Oct 30 16:13:56 2015 -0700
> > Committer: Jean-Louis Monteiro <jl...@apache.org>
> > Committed: Fri Oct 30 16:13:56 2015 -0700
> >
> > ----------------------------------------------------------------------
> > .../resource/jdbc/dbcp/BasicDataSource.java | 20 +-
> > .../jdbc/dbcp/BasicManagedDataSource.java | 23 ++-
> > .../jdbc/CipheredPasswordDataSourceTest.java | 193
> +++++++++++++++++++
> > 3 files changed, 233 insertions(+), 3 deletions(-)
> > ----------------------------------------------------------------------
> >
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > ----------------------------------------------------------------------
> > diff --git
> >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > index ffb5bba..995221c 100644
> > ---
> >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > +++
> >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > @@ -55,6 +55,10 @@ public class BasicDataSource extends
> > org.apache.commons.dbcp2.BasicDataSource im
> > * not ciphered. The {@link
> > org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
> > */
> > private String passwordCipher;
> > +
> > + // keep tracking the user configured password in case we need it to
> be
> > decrypted again
> > + private String initialPassword;
> > +
> > private JMXBasicDataSource jmxDs;
> > private CommonDataSource delegate;
> > private String name;
> > @@ -128,6 +132,18 @@ public class BasicDataSource extends
> > org.apache.commons.dbcp2.BasicDataSource im
> > }
> > }
> >
> > + @Override
> > + public void setPassword(final String password) {
> > + final ReentrantLock l = lock;
> > + l.lock();
> > + try {
> > + // keep the encrypted value if it's encrypted
> > + this.initialPassword = password;
> > + super.setPassword(password);
> > + } finally {
> > + l.unlock();
> > + }
> > + }
> >
> > public String getUserName() {
> > final ReentrantLock l = lock;
> > @@ -226,7 +242,9 @@ public class BasicDataSource extends
> > org.apache.commons.dbcp2.BasicDataSource im
> > // check password codec if available
> > if (null != passwordCipher &&
> > !"PlainText".equals(passwordCipher)) {
> > final PasswordCipher cipher =
> > PasswordCipherFactory.getPasswordCipher(passwordCipher);
> > - final String plainPwd =
> > cipher.decrypt(getPassword().toCharArray());
> > +
> > + // always use the initial encrypted value
> > + final String plainPwd =
> > cipher.decrypt(initialPassword.toCharArray());
> >
> > // override previous password value
> > super.setPassword(plainPwd);
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > ----------------------------------------------------------------------
> > diff --git
> >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > index ec604b0..0846f21 100644
> > ---
> >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > +++
> >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > @@ -54,6 +54,10 @@ public class BasicManagedDataSource extends
> > org.apache.commons.dbcp2.managed.Bas
> > * not ciphered. The {@link
> > org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
> > */
> > private String passwordCipher;
> > +
> > + // keep tracking the user configured password in case we need it to
> be
> > decrypted again
> > + private String initialPassword;
> > +
> > private JMXBasicDataSource jmxDs;
> >
> > public BasicManagedDataSource(final String name) {
> > @@ -85,7 +89,7 @@ public class BasicManagedDataSource extends
> > org.apache.commons.dbcp2.managed.Bas
> >
> > private void setJndiXaDataSource(final String xaDataSource) {
> > setXaDataSourceInstance( // proxy cause we don't know if this
> > datasource was created before or not the delegate
> > - XADataSourceResource.proxy(getDriverClassLoader() != null ?
> > getDriverClassLoader() : Thread.currentThread().getContextClassLoader(),
> > xaDataSource));
> > + XADataSourceResource.proxy(getDriverClassLoader() !=
> null
> > ? getDriverClassLoader() :
> Thread.currentThread().getContextClassLoader(),
> > xaDataSource));
> >
> > if (getTransactionManager() == null) {
> > setTransactionManager(OpenEJB.getTransactionManager());
> > @@ -133,6 +137,19 @@ public class BasicManagedDataSource extends
> > org.apache.commons.dbcp2.managed.Bas
> > }
> > }
> >
> > + @Override
> > + public void setPassword(final String password) {
> > + final ReentrantLock l = lock;
> > + l.lock();
> > + try {
> > + // keep the encrypted value if it's encrypted
> > + this.initialPassword = password;
> > + super.setPassword(password);
> > + } finally {
> > + l.unlock();
> > + }
> > + }
> > +
> > public String getUserName() {
> > final ReentrantLock l = lock;
> > l.lock();
> > @@ -229,7 +246,9 @@ public class BasicManagedDataSource extends
> > org.apache.commons.dbcp2.managed.Bas
> > // check password codec if available
> > if (null != passwordCipher) {
> > final PasswordCipher cipher =
> > PasswordCipherFactory.getPasswordCipher(passwordCipher);
> > - final String plainPwd =
> > cipher.decrypt(getPassword().toCharArray());
> > +
> > + // always use the initial encrypted value
> > + final String plainPwd =
> > cipher.decrypt(initialPassword.toCharArray());
> >
> > // override previous password value
> > super.setPassword(plainPwd);
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > ----------------------------------------------------------------------
> > diff --git
> >
> >
> a/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> >
> >
> b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > new file mode 100644
> > index 0000000..0f1224f
> > --- /dev/null
> > +++
> >
> >
> b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > @@ -0,0 +1,193 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one or more
> > + * contributor license agreements. See the NOTICE file distributed with
> > + * this work for additional information regarding copyright ownership.
> > + * The ASF licenses this file to You under the Apache License, Version
> 2.0
> > + * (the "License"); you may not use this file except in compliance with
> > + * the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +package org.apache.openejb.resource.jdbc;
> > +
> > +import org.apache.commons.dbcp.PoolableConnection;
> > +import org.apache.openejb.cipher.PasswordCipher;
> > +import org.apache.openejb.jee.EjbJar;
> > +import org.apache.openejb.jee.SingletonBean;
> > +import org.apache.openejb.junit.ApplicationComposer;
> > +import org.apache.openejb.resource.jdbc.dbcp.BasicManagedDataSource;
> > +import org.apache.openejb.testing.Configuration;
> > +import org.apache.openejb.testing.Module;
> > +import org.apache.openejb.util.Strings;
> > +import org.apache.openejb.util.reflection.Reflections;
> > +import org.junit.Before;
> > +import org.junit.BeforeClass;
> > +import org.junit.Test;
> > +import org.junit.runner.RunWith;
> > +
> > +import javax.annotation.Resource;
> > +import javax.ejb.EJB;
> > +import javax.ejb.EJBContext;
> > +import javax.ejb.LocalBean;
> > +import javax.ejb.Singleton;
> > +import javax.ejb.Startup;
> > +import javax.ejb.TransactionAttribute;
> > +import javax.ejb.TransactionAttributeType;
> > +import javax.sql.DataSource;
> > +import java.sql.Connection;
> > +import java.sql.DriverManager;
> > +import java.sql.DriverPropertyInfo;
> > +import java.sql.ResultSet;
> > +import java.sql.SQLException;
> > +import java.sql.SQLFeatureNotSupportedException;
> > +import java.sql.Statement;
> > +import java.util.Properties;
> > +import java.util.logging.Logger;
> > +
> > +import static org.junit.Assert.assertFalse;
> > +import static org.junit.Assert.assertTrue;
> > +
> > +@RunWith(ApplicationComposer.class)
> > +public class CipheredPasswordDataSourceTest {
> > + private static final String URL = "jdbc:fake://sthg:3306";
> > + private static final String USER = "pete";
> > + private static final String ENCRYPTED_PASSWORD = "This is the
> > encrypted value.";
> > +
> > + @EJB
> > + private Persister persistManager;
> > +
> > + @Resource
> > + private DataSource ds;
> > +
> > + @Configuration
> > + public Properties config() {
> > + final Properties p = new Properties();
> > + p.put("openejb.jdbc.datasource-creator", "dbcp");
> > +
> > + p.put("managed", "new://Resource?type=DataSource");
> > + p.put("managed.JdbcDriver", FakeDriver.class.getName());
> > + p.put("managed.JdbcUrl", URL);
> > + p.put("managed.UserName", USER);
> > + p.put("managed.Password", ENCRYPTED_PASSWORD);
> > + p.put("managed.PasswordCipher",
> > EmptyPasswordCipher.class.getName());
> > + p.put("managed.JtaManaged", "true");
> > + p.put("managed.initialSize", "10");
> > + p.put("managed.maxActive", "10");
> > + p.put("managed.maxIdle", "10");
> > + p.put("managed.minIdle", "10");
> > + p.put("managed.maxWait", "200");
> > + p.put("managed.defaultAutoCommit", "false");
> > + p.put("managed.defaultReadOnly", "false");
> > + p.put("managed.testOnBorrow", "true");
> > + p.put("managed.testOnReturn", "true");
> > + p.put("managed.testWhileIdle", "true");
> > +
> > + return p;
> > + }
> > +
> > + @Module
> > + public EjbJar app() throws Exception {
> > + return new EjbJar()
> > + .enterpriseBean(new
> > SingletonBean(Persister.class).localBean());
> > +
> > + }
> > +
> > + @Before
> > + public void createDatasource(){
> > + // This is to make sure TomEE created the data source.
> > + persistManager.save();
> > + }
> > +
> > + @Test
> > + public void rebuild() {
> > + // because of the exception at startup, the pool creating
> aborted
> > + // before fixing this bug, the password was already decrypted,
> but
> > the data source field
> > + // wasn't yet initialized, so this.data source == null
> > + // but the next time we try to initialize it, it tries to
> decrypt
> > and already decrypted password
> > +
> > + try {
> > + ds.getConnection();
> > +
> > + } catch (final SQLException e) {
> > + System.out.println(e.getMessage());
> > + // success - it throws the SQLException from the connect
> > + // but it does not fail with the IllegalArgumentException
> from
> > the Password Cipher
> > + }
> > + }
> > +
> > + public static class EmptyPasswordCipher implements PasswordCipher {
> > +
> > + @Override
> > + public char[] encrypt(String plainPassword) {
> > + throw new RuntimeException("Should never be called in this
> > test.");
> > + }
> > +
> > + @Override
> > + public String decrypt(char[] encryptedPassword) {
> > + System.out.println(String.format(">>> Decrypt password
> '%s'",
> > new String(encryptedPassword)));
> > +
> > + // we want to know if the password as already been called
> > + if (encryptedPassword.length == 0) {
> > + throw new IllegalArgumentException("Can only decrypt a
> non
> > empty string.");
> > + }
> > +
> > + return "";
> > + }
> > + }
> > +
> > + public static class FakeDriver implements java.sql.Driver {
> > + public boolean acceptsURL(final String url) throws SQLException
> {
> > + return false;
> > + }
> > +
> > + public Logger getParentLogger() throws
> > SQLFeatureNotSupportedException {
> > + return null;
> > + }
> > +
> > + public Connection connect(final String url, final Properties
> info)
> > throws SQLException {
> > + throw new SQLException("Pete said it first fails here!");
> > + }
> > +
> > + public int getMajorVersion() {
> > + return 0;
> > + }
> > +
> > + public int getMinorVersion() {
> > + return 0;
> > + }
> > +
> > + public DriverPropertyInfo[] getPropertyInfo(final String url,
> > final Properties info) throws SQLException {
> > + return new DriverPropertyInfo[0];
> > + }
> > +
> > + public boolean jdbcCompliant() {
> > + return false;
> > + }
> > + }
> > +
> > + @LocalBean
> > + @Singleton
> > + @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
> > + public static class Persister {
> > +
> > + @Resource(name = "managed")
> > + private DataSource ds;
> > +
> > + public void save() {
> > + try {
> > + ds.getConnection();
> > +
> > + } catch (SQLException e) {
> > + System.err.println("Generated SQL error > " +
> > e.getMessage());
> > + }
> > + }
> > + }
> > +
> > +
> > +}
> >
>
Re: Fwd: tomee git commit: TOMEE-1648: The password cipher is called
each time the datasource is built even if we already have the clear password
Posted by Jean-Louis Monteiro <jl...@tomitribe.com>.
Sure. Maybe the fix can be improved. Let's discuss
Le 30 oct. 2015 17:37, "Romain Manni-Bucau" <rm...@gmail.com> a
écrit :
> It prevents to reset the pwd at runtime if i read it correctly. It should
> be fixed since it is used at least through jmx.
> ---------- Message transféré ----------
> De : <jl...@apache.org>
> Date : 30 oct. 2015 16:14
> Objet : tomee git commit: TOMEE-1648: The password cipher is called each
> time the datasource is built even if we already have the clear password
> À : <co...@tomee.apache.org>
> Cc :
>
> Repository: tomee
> Updated Branches:
> refs/heads/master 34c4cc7a2 -> 436089b65
>
>
> TOMEE-1648: The password cipher is called each time the datasource is built
> even if we already have the clear password
>
>
> Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
> Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/436089b6
> Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/436089b6
> Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/436089b6
>
> Branch: refs/heads/master
> Commit: 436089b658e239757e0a51b49be01974ca339627
> Parents: 34c4cc7
> Author: Jean-Louis Monteiro <jl...@apache.org>
> Authored: Fri Oct 30 16:13:56 2015 -0700
> Committer: Jean-Louis Monteiro <jl...@apache.org>
> Committed: Fri Oct 30 16:13:56 2015 -0700
>
> ----------------------------------------------------------------------
> .../resource/jdbc/dbcp/BasicDataSource.java | 20 +-
> .../jdbc/dbcp/BasicManagedDataSource.java | 23 ++-
> .../jdbc/CipheredPasswordDataSourceTest.java | 193 +++++++++++++++++++
> 3 files changed, 233 insertions(+), 3 deletions(-)
> ----------------------------------------------------------------------
>
>
>
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> ----------------------------------------------------------------------
> diff --git
>
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
>
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> index ffb5bba..995221c 100644
> ---
>
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> +++
>
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> @@ -55,6 +55,10 @@ public class BasicDataSource extends
> org.apache.commons.dbcp2.BasicDataSource im
> * not ciphered. The {@link
> org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
> */
> private String passwordCipher;
> +
> + // keep tracking the user configured password in case we need it to be
> decrypted again
> + private String initialPassword;
> +
> private JMXBasicDataSource jmxDs;
> private CommonDataSource delegate;
> private String name;
> @@ -128,6 +132,18 @@ public class BasicDataSource extends
> org.apache.commons.dbcp2.BasicDataSource im
> }
> }
>
> + @Override
> + public void setPassword(final String password) {
> + final ReentrantLock l = lock;
> + l.lock();
> + try {
> + // keep the encrypted value if it's encrypted
> + this.initialPassword = password;
> + super.setPassword(password);
> + } finally {
> + l.unlock();
> + }
> + }
>
> public String getUserName() {
> final ReentrantLock l = lock;
> @@ -226,7 +242,9 @@ public class BasicDataSource extends
> org.apache.commons.dbcp2.BasicDataSource im
> // check password codec if available
> if (null != passwordCipher &&
> !"PlainText".equals(passwordCipher)) {
> final PasswordCipher cipher =
> PasswordCipherFactory.getPasswordCipher(passwordCipher);
> - final String plainPwd =
> cipher.decrypt(getPassword().toCharArray());
> +
> + // always use the initial encrypted value
> + final String plainPwd =
> cipher.decrypt(initialPassword.toCharArray());
>
> // override previous password value
> super.setPassword(plainPwd);
>
>
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> ----------------------------------------------------------------------
> diff --git
>
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
>
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> index ec604b0..0846f21 100644
> ---
>
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> +++
>
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> @@ -54,6 +54,10 @@ public class BasicManagedDataSource extends
> org.apache.commons.dbcp2.managed.Bas
> * not ciphered. The {@link
> org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
> */
> private String passwordCipher;
> +
> + // keep tracking the user configured password in case we need it to be
> decrypted again
> + private String initialPassword;
> +
> private JMXBasicDataSource jmxDs;
>
> public BasicManagedDataSource(final String name) {
> @@ -85,7 +89,7 @@ public class BasicManagedDataSource extends
> org.apache.commons.dbcp2.managed.Bas
>
> private void setJndiXaDataSource(final String xaDataSource) {
> setXaDataSourceInstance( // proxy cause we don't know if this
> datasource was created before or not the delegate
> - XADataSourceResource.proxy(getDriverClassLoader() != null ?
> getDriverClassLoader() : Thread.currentThread().getContextClassLoader(),
> xaDataSource));
> + XADataSourceResource.proxy(getDriverClassLoader() != null
> ? getDriverClassLoader() : Thread.currentThread().getContextClassLoader(),
> xaDataSource));
>
> if (getTransactionManager() == null) {
> setTransactionManager(OpenEJB.getTransactionManager());
> @@ -133,6 +137,19 @@ public class BasicManagedDataSource extends
> org.apache.commons.dbcp2.managed.Bas
> }
> }
>
> + @Override
> + public void setPassword(final String password) {
> + final ReentrantLock l = lock;
> + l.lock();
> + try {
> + // keep the encrypted value if it's encrypted
> + this.initialPassword = password;
> + super.setPassword(password);
> + } finally {
> + l.unlock();
> + }
> + }
> +
> public String getUserName() {
> final ReentrantLock l = lock;
> l.lock();
> @@ -229,7 +246,9 @@ public class BasicManagedDataSource extends
> org.apache.commons.dbcp2.managed.Bas
> // check password codec if available
> if (null != passwordCipher) {
> final PasswordCipher cipher =
> PasswordCipherFactory.getPasswordCipher(passwordCipher);
> - final String plainPwd =
> cipher.decrypt(getPassword().toCharArray());
> +
> + // always use the initial encrypted value
> + final String plainPwd =
> cipher.decrypt(initialPassword.toCharArray());
>
> // override previous password value
> super.setPassword(plainPwd);
>
>
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> ----------------------------------------------------------------------
> diff --git
>
> a/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
>
> b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> new file mode 100644
> index 0000000..0f1224f
> --- /dev/null
> +++
>
> b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> @@ -0,0 +1,193 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements. See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You under the Apache License, Version 2.0
> + * (the "License"); you may not use this file except in compliance with
> + * the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +package org.apache.openejb.resource.jdbc;
> +
> +import org.apache.commons.dbcp.PoolableConnection;
> +import org.apache.openejb.cipher.PasswordCipher;
> +import org.apache.openejb.jee.EjbJar;
> +import org.apache.openejb.jee.SingletonBean;
> +import org.apache.openejb.junit.ApplicationComposer;
> +import org.apache.openejb.resource.jdbc.dbcp.BasicManagedDataSource;
> +import org.apache.openejb.testing.Configuration;
> +import org.apache.openejb.testing.Module;
> +import org.apache.openejb.util.Strings;
> +import org.apache.openejb.util.reflection.Reflections;
> +import org.junit.Before;
> +import org.junit.BeforeClass;
> +import org.junit.Test;
> +import org.junit.runner.RunWith;
> +
> +import javax.annotation.Resource;
> +import javax.ejb.EJB;
> +import javax.ejb.EJBContext;
> +import javax.ejb.LocalBean;
> +import javax.ejb.Singleton;
> +import javax.ejb.Startup;
> +import javax.ejb.TransactionAttribute;
> +import javax.ejb.TransactionAttributeType;
> +import javax.sql.DataSource;
> +import java.sql.Connection;
> +import java.sql.DriverManager;
> +import java.sql.DriverPropertyInfo;
> +import java.sql.ResultSet;
> +import java.sql.SQLException;
> +import java.sql.SQLFeatureNotSupportedException;
> +import java.sql.Statement;
> +import java.util.Properties;
> +import java.util.logging.Logger;
> +
> +import static org.junit.Assert.assertFalse;
> +import static org.junit.Assert.assertTrue;
> +
> +@RunWith(ApplicationComposer.class)
> +public class CipheredPasswordDataSourceTest {
> + private static final String URL = "jdbc:fake://sthg:3306";
> + private static final String USER = "pete";
> + private static final String ENCRYPTED_PASSWORD = "This is the
> encrypted value.";
> +
> + @EJB
> + private Persister persistManager;
> +
> + @Resource
> + private DataSource ds;
> +
> + @Configuration
> + public Properties config() {
> + final Properties p = new Properties();
> + p.put("openejb.jdbc.datasource-creator", "dbcp");
> +
> + p.put("managed", "new://Resource?type=DataSource");
> + p.put("managed.JdbcDriver", FakeDriver.class.getName());
> + p.put("managed.JdbcUrl", URL);
> + p.put("managed.UserName", USER);
> + p.put("managed.Password", ENCRYPTED_PASSWORD);
> + p.put("managed.PasswordCipher",
> EmptyPasswordCipher.class.getName());
> + p.put("managed.JtaManaged", "true");
> + p.put("managed.initialSize", "10");
> + p.put("managed.maxActive", "10");
> + p.put("managed.maxIdle", "10");
> + p.put("managed.minIdle", "10");
> + p.put("managed.maxWait", "200");
> + p.put("managed.defaultAutoCommit", "false");
> + p.put("managed.defaultReadOnly", "false");
> + p.put("managed.testOnBorrow", "true");
> + p.put("managed.testOnReturn", "true");
> + p.put("managed.testWhileIdle", "true");
> +
> + return p;
> + }
> +
> + @Module
> + public EjbJar app() throws Exception {
> + return new EjbJar()
> + .enterpriseBean(new
> SingletonBean(Persister.class).localBean());
> +
> + }
> +
> + @Before
> + public void createDatasource(){
> + // This is to make sure TomEE created the data source.
> + persistManager.save();
> + }
> +
> + @Test
> + public void rebuild() {
> + // because of the exception at startup, the pool creating aborted
> + // before fixing this bug, the password was already decrypted, but
> the data source field
> + // wasn't yet initialized, so this.data source == null
> + // but the next time we try to initialize it, it tries to decrypt
> and already decrypted password
> +
> + try {
> + ds.getConnection();
> +
> + } catch (final SQLException e) {
> + System.out.println(e.getMessage());
> + // success - it throws the SQLException from the connect
> + // but it does not fail with the IllegalArgumentException from
> the Password Cipher
> + }
> + }
> +
> + public static class EmptyPasswordCipher implements PasswordCipher {
> +
> + @Override
> + public char[] encrypt(String plainPassword) {
> + throw new RuntimeException("Should never be called in this
> test.");
> + }
> +
> + @Override
> + public String decrypt(char[] encryptedPassword) {
> + System.out.println(String.format(">>> Decrypt password '%s'",
> new String(encryptedPassword)));
> +
> + // we want to know if the password as already been called
> + if (encryptedPassword.length == 0) {
> + throw new IllegalArgumentException("Can only decrypt a non
> empty string.");
> + }
> +
> + return "";
> + }
> + }
> +
> + public static class FakeDriver implements java.sql.Driver {
> + public boolean acceptsURL(final String url) throws SQLException {
> + return false;
> + }
> +
> + public Logger getParentLogger() throws
> SQLFeatureNotSupportedException {
> + return null;
> + }
> +
> + public Connection connect(final String url, final Properties info)
> throws SQLException {
> + throw new SQLException("Pete said it first fails here!");
> + }
> +
> + public int getMajorVersion() {
> + return 0;
> + }
> +
> + public int getMinorVersion() {
> + return 0;
> + }
> +
> + public DriverPropertyInfo[] getPropertyInfo(final String url,
> final Properties info) throws SQLException {
> + return new DriverPropertyInfo[0];
> + }
> +
> + public boolean jdbcCompliant() {
> + return false;
> + }
> + }
> +
> + @LocalBean
> + @Singleton
> + @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
> + public static class Persister {
> +
> + @Resource(name = "managed")
> + private DataSource ds;
> +
> + public void save() {
> + try {
> + ds.getConnection();
> +
> + } catch (SQLException e) {
> + System.err.println("Generated SQL error > " +
> e.getMessage());
> + }
> + }
> + }
> +
> +
> +}
>
Fwd: tomee git commit: TOMEE-1648: The password cipher is called each
time the datasource is built even if we already have the clear password
Posted by Romain Manni-Bucau <rm...@gmail.com>.
It prevents to reset the pwd at runtime if i read it correctly. It should
be fixed since it is used at least through jmx.
---------- Message transféré ----------
De : <jl...@apache.org>
Date : 30 oct. 2015 16:14
Objet : tomee git commit: TOMEE-1648: The password cipher is called each
time the datasource is built even if we already have the clear password
À : <co...@tomee.apache.org>
Cc :
Repository: tomee
Updated Branches:
refs/heads/master 34c4cc7a2 -> 436089b65
TOMEE-1648: The password cipher is called each time the datasource is built
even if we already have the clear password
Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/436089b6
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/436089b6
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/436089b6
Branch: refs/heads/master
Commit: 436089b658e239757e0a51b49be01974ca339627
Parents: 34c4cc7
Author: Jean-Louis Monteiro <jl...@apache.org>
Authored: Fri Oct 30 16:13:56 2015 -0700
Committer: Jean-Louis Monteiro <jl...@apache.org>
Committed: Fri Oct 30 16:13:56 2015 -0700
----------------------------------------------------------------------
.../resource/jdbc/dbcp/BasicDataSource.java | 20 +-
.../jdbc/dbcp/BasicManagedDataSource.java | 23 ++-
.../jdbc/CipheredPasswordDataSourceTest.java | 193 +++++++++++++++++++
3 files changed, 233 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
----------------------------------------------------------------------
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
index ffb5bba..995221c 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
@@ -55,6 +55,10 @@ public class BasicDataSource extends
org.apache.commons.dbcp2.BasicDataSource im
* not ciphered. The {@link
org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
*/
private String passwordCipher;
+
+ // keep tracking the user configured password in case we need it to be
decrypted again
+ private String initialPassword;
+
private JMXBasicDataSource jmxDs;
private CommonDataSource delegate;
private String name;
@@ -128,6 +132,18 @@ public class BasicDataSource extends
org.apache.commons.dbcp2.BasicDataSource im
}
}
+ @Override
+ public void setPassword(final String password) {
+ final ReentrantLock l = lock;
+ l.lock();
+ try {
+ // keep the encrypted value if it's encrypted
+ this.initialPassword = password;
+ super.setPassword(password);
+ } finally {
+ l.unlock();
+ }
+ }
public String getUserName() {
final ReentrantLock l = lock;
@@ -226,7 +242,9 @@ public class BasicDataSource extends
org.apache.commons.dbcp2.BasicDataSource im
// check password codec if available
if (null != passwordCipher &&
!"PlainText".equals(passwordCipher)) {
final PasswordCipher cipher =
PasswordCipherFactory.getPasswordCipher(passwordCipher);
- final String plainPwd =
cipher.decrypt(getPassword().toCharArray());
+
+ // always use the initial encrypted value
+ final String plainPwd =
cipher.decrypt(initialPassword.toCharArray());
// override previous password value
super.setPassword(plainPwd);
http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
----------------------------------------------------------------------
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
index ec604b0..0846f21 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
@@ -54,6 +54,10 @@ public class BasicManagedDataSource extends
org.apache.commons.dbcp2.managed.Bas
* not ciphered. The {@link
org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
*/
private String passwordCipher;
+
+ // keep tracking the user configured password in case we need it to be
decrypted again
+ private String initialPassword;
+
private JMXBasicDataSource jmxDs;
public BasicManagedDataSource(final String name) {
@@ -85,7 +89,7 @@ public class BasicManagedDataSource extends
org.apache.commons.dbcp2.managed.Bas
private void setJndiXaDataSource(final String xaDataSource) {
setXaDataSourceInstance( // proxy cause we don't know if this
datasource was created before or not the delegate
- XADataSourceResource.proxy(getDriverClassLoader() != null ?
getDriverClassLoader() : Thread.currentThread().getContextClassLoader(),
xaDataSource));
+ XADataSourceResource.proxy(getDriverClassLoader() != null
? getDriverClassLoader() : Thread.currentThread().getContextClassLoader(),
xaDataSource));
if (getTransactionManager() == null) {
setTransactionManager(OpenEJB.getTransactionManager());
@@ -133,6 +137,19 @@ public class BasicManagedDataSource extends
org.apache.commons.dbcp2.managed.Bas
}
}
+ @Override
+ public void setPassword(final String password) {
+ final ReentrantLock l = lock;
+ l.lock();
+ try {
+ // keep the encrypted value if it's encrypted
+ this.initialPassword = password;
+ super.setPassword(password);
+ } finally {
+ l.unlock();
+ }
+ }
+
public String getUserName() {
final ReentrantLock l = lock;
l.lock();
@@ -229,7 +246,9 @@ public class BasicManagedDataSource extends
org.apache.commons.dbcp2.managed.Bas
// check password codec if available
if (null != passwordCipher) {
final PasswordCipher cipher =
PasswordCipherFactory.getPasswordCipher(passwordCipher);
- final String plainPwd =
cipher.decrypt(getPassword().toCharArray());
+
+ // always use the initial encrypted value
+ final String plainPwd =
cipher.decrypt(initialPassword.toCharArray());
// override previous password value
super.setPassword(plainPwd);
http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
----------------------------------------------------------------------
diff --git
a/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
new file mode 100644
index 0000000..0f1224f
--- /dev/null
+++
b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.resource.jdbc;
+
+import org.apache.commons.dbcp.PoolableConnection;
+import org.apache.openejb.cipher.PasswordCipher;
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.jee.SingletonBean;
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.resource.jdbc.dbcp.BasicManagedDataSource;
+import org.apache.openejb.testing.Configuration;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.util.Strings;
+import org.apache.openejb.util.reflection.Reflections;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.annotation.Resource;
+import javax.ejb.EJB;
+import javax.ejb.EJBContext;
+import javax.ejb.LocalBean;
+import javax.ejb.Singleton;
+import javax.ejb.Startup;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Statement;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(ApplicationComposer.class)
+public class CipheredPasswordDataSourceTest {
+ private static final String URL = "jdbc:fake://sthg:3306";
+ private static final String USER = "pete";
+ private static final String ENCRYPTED_PASSWORD = "This is the
encrypted value.";
+
+ @EJB
+ private Persister persistManager;
+
+ @Resource
+ private DataSource ds;
+
+ @Configuration
+ public Properties config() {
+ final Properties p = new Properties();
+ p.put("openejb.jdbc.datasource-creator", "dbcp");
+
+ p.put("managed", "new://Resource?type=DataSource");
+ p.put("managed.JdbcDriver", FakeDriver.class.getName());
+ p.put("managed.JdbcUrl", URL);
+ p.put("managed.UserName", USER);
+ p.put("managed.Password", ENCRYPTED_PASSWORD);
+ p.put("managed.PasswordCipher",
EmptyPasswordCipher.class.getName());
+ p.put("managed.JtaManaged", "true");
+ p.put("managed.initialSize", "10");
+ p.put("managed.maxActive", "10");
+ p.put("managed.maxIdle", "10");
+ p.put("managed.minIdle", "10");
+ p.put("managed.maxWait", "200");
+ p.put("managed.defaultAutoCommit", "false");
+ p.put("managed.defaultReadOnly", "false");
+ p.put("managed.testOnBorrow", "true");
+ p.put("managed.testOnReturn", "true");
+ p.put("managed.testWhileIdle", "true");
+
+ return p;
+ }
+
+ @Module
+ public EjbJar app() throws Exception {
+ return new EjbJar()
+ .enterpriseBean(new
SingletonBean(Persister.class).localBean());
+
+ }
+
+ @Before
+ public void createDatasource(){
+ // This is to make sure TomEE created the data source.
+ persistManager.save();
+ }
+
+ @Test
+ public void rebuild() {
+ // because of the exception at startup, the pool creating aborted
+ // before fixing this bug, the password was already decrypted, but
the data source field
+ // wasn't yet initialized, so this.data source == null
+ // but the next time we try to initialize it, it tries to decrypt
and already decrypted password
+
+ try {
+ ds.getConnection();
+
+ } catch (final SQLException e) {
+ System.out.println(e.getMessage());
+ // success - it throws the SQLException from the connect
+ // but it does not fail with the IllegalArgumentException from
the Password Cipher
+ }
+ }
+
+ public static class EmptyPasswordCipher implements PasswordCipher {
+
+ @Override
+ public char[] encrypt(String plainPassword) {
+ throw new RuntimeException("Should never be called in this
test.");
+ }
+
+ @Override
+ public String decrypt(char[] encryptedPassword) {
+ System.out.println(String.format(">>> Decrypt password '%s'",
new String(encryptedPassword)));
+
+ // we want to know if the password as already been called
+ if (encryptedPassword.length == 0) {
+ throw new IllegalArgumentException("Can only decrypt a non
empty string.");
+ }
+
+ return "";
+ }
+ }
+
+ public static class FakeDriver implements java.sql.Driver {
+ public boolean acceptsURL(final String url) throws SQLException {
+ return false;
+ }
+
+ public Logger getParentLogger() throws
SQLFeatureNotSupportedException {
+ return null;
+ }
+
+ public Connection connect(final String url, final Properties info)
throws SQLException {
+ throw new SQLException("Pete said it first fails here!");
+ }
+
+ public int getMajorVersion() {
+ return 0;
+ }
+
+ public int getMinorVersion() {
+ return 0;
+ }
+
+ public DriverPropertyInfo[] getPropertyInfo(final String url,
final Properties info) throws SQLException {
+ return new DriverPropertyInfo[0];
+ }
+
+ public boolean jdbcCompliant() {
+ return false;
+ }
+ }
+
+ @LocalBean
+ @Singleton
+ @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+ public static class Persister {
+
+ @Resource(name = "managed")
+ private DataSource ds;
+
+ public void save() {
+ try {
+ ds.getConnection();
+
+ } catch (SQLException e) {
+ System.err.println("Generated SQL error > " +
e.getMessage());
+ }
+ }
+ }
+
+
+}