Repository: tomee
Updated Branches:
  refs/heads/master 3f79e41b8 -> 7066eaa47


TOMEE-1603 cdi password cipher basic support


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/7066eaa4
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/7066eaa4
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/7066eaa4

Branch: refs/heads/master
Commit: 7066eaa4731379e9db11a009c667b5726505f701
Parents: 3f79e41
Author: Romain Manni-Bucau <[email protected]>
Authored: Thu Jun 11 05:34:58 2015 +0200
Committer: Romain Manni-Bucau <[email protected]>
Committed: Thu Jun 11 05:34:58 2015 +0200

----------------------------------------------------------------------
 .../openejb/assembler/classic/Assembler.java    | 18 ++++-
 .../openejb/cipher/CdiPasswordCipher.java       | 66 ++++++++++++++++
 .../openejb/testing/ApplicationComposers.java   |  4 +-
 .../openejb/util/PropertyPlaceHolderHelper.java | 18 ++++-
 .../cdi                                         |  1 +
 .../openejb/cipher/CdiPasswordCipherTest.java   | 82 ++++++++++++++++++++
 6 files changed, 182 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/7066eaa4/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java
----------------------------------------------------------------------
diff --git 
a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java
 
b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java
index d3af5f2..33e10c2 100644
--- 
a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java
+++ 
b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java
@@ -2605,9 +2605,13 @@ public class Assembler extends AssemblerTool implements 
org.apache.openejb.spi.A
     }
 
     public void createResource(final ResourceInfo serviceInfo) throws 
OpenEJBException {
-        final Object service = 
"true".equalsIgnoreCase(String.valueOf(serviceInfo.properties.remove("Lazy"))) ?
+        final boolean usesCdiPwdCipher = usesCdiPwdCipher(serviceInfo);
+        final Object service = 
"true".equalsIgnoreCase(String.valueOf(serviceInfo.properties.remove("Lazy"))) 
|| usesCdiPwdCipher ?
                 newLazyResource(serviceInfo) :
                 doCreateResource(serviceInfo);
+        if (usesCdiPwdCipher && 
!serviceInfo.properties.contains("InitializeAfterDeployment")) {
+            serviceInfo.properties.put("InitializeAfterDeployment", "true");
+        }
 
         bindResource(serviceInfo.id, service, false);
         for (final String alias : serviceInfo.aliases) {
@@ -2630,6 +2634,15 @@ public class Assembler extends AssemblerTool implements 
org.apache.openejb.spi.A
         }
     }
 
+    private boolean usesCdiPwdCipher(final ResourceInfo serviceInfo) {
+        for (final Object val : serviceInfo.properties.values()) {
+            if (String.valueOf(val).startsWith("cipher:cdi:")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private LazyResource newLazyResource(final ResourceInfo serviceInfo) {
         final ClassLoader loader = 
Thread.currentThread().getContextClassLoader();
         return new LazyResource(new Callable<Object>() {
@@ -2684,7 +2697,6 @@ public class Assembler extends AssemblerTool implements 
org.apache.openejb.spi.A
             }
         });
 
-        final Properties props = 
PropertyPlaceHolderHelper.holds(serviceInfo.properties);
         if (serviceInfo.properties.containsKey("Definition")) {
             try { // we catch classcast etc..., if it fails it is not important
                 final InputStream is = new 
ByteArrayInputStream(serviceInfo.properties.getProperty("Definition").getBytes());
@@ -3236,7 +3248,7 @@ public class Assembler extends AssemblerTool implements 
org.apache.openejb.spi.A
 
         final ObjectRecipe serviceRecipe = prepareRecipe(info);
         final Object value = info.properties.remove("SkipImplicitAttributes"); 
// we don't want this one to go in recipe
-        serviceRecipe.setAllProperties(info.properties);
+        
serviceRecipe.setAllProperties(PropertyPlaceHolderHelper.simpleHolds(info.properties));
         if (value != null) {
             info.properties.put("SkipImplicitAttributes", value);
         }

http://git-wip-us.apache.org/repos/asf/tomee/blob/7066eaa4/container/openejb-core/src/main/java/org/apache/openejb/cipher/CdiPasswordCipher.java
----------------------------------------------------------------------
diff --git 
a/container/openejb-core/src/main/java/org/apache/openejb/cipher/CdiPasswordCipher.java
 
b/container/openejb-core/src/main/java/org/apache/openejb/cipher/CdiPasswordCipher.java
new file mode 100644
index 0000000..0e8077e
--- /dev/null
+++ 
b/container/openejb-core/src/main/java/org/apache/openejb/cipher/CdiPasswordCipher.java
@@ -0,0 +1,66 @@
+/**
+ * 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.cipher;
+
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.container.BeanManagerImpl;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+
+// use: cipher:cdi:<your bean class>:<encrypted pwd>
+public final class CdiPasswordCipher implements PasswordCipher {
+    @Override
+    public char[] encrypt(final String plainPassword) {
+        throw new UnsupportedOperationException("cdi password cipher only 
supports decryption");
+    }
+
+    @Override
+    public String decrypt(final char[] encryptedPassword) {
+        final String string = new String(encryptedPassword);
+        final WebBeansContext wbc = WebBeansContext.currentInstance();
+        final BeanManagerImpl mgr = wbc.getBeanManagerImpl();
+        if (!mgr.isInUse()) { // not yet the time to use CDI, container is not 
started
+            // would be cool to log a warning here but would pollute the logs 
with false positives
+            return "cipher:cdi:" + string;
+        }
+
+        final int split = string.indexOf(':');
+        final String delegate = string.substring(0, split);
+        final String pwdStr = string.substring(split + 1, string.length());
+        final char[] pwd = pwdStr.toCharArray();
+
+        try {
+            final Class<?> beanType = 
Thread.currentThread().getContextClassLoader().loadClass(delegate);
+            final Bean<?> bean = mgr.resolve(mgr.getBeans(beanType));
+            if (bean == null) {
+                throw new IllegalArgumentException("No bean for " + delegate);
+            }
+
+            final CreationalContext<?> cc = mgr.createCreationalContext(null);
+            try {
+                return PasswordCipher.class.cast(mgr.getReference(bean, 
PasswordCipher.class, cc)).decrypt(pwd);
+            } finally {
+                if (!mgr.isNormalScope(bean.getScope())) {
+                    cc.release();
+                }
+            }
+        } catch (final ClassNotFoundException e) {
+            throw new IllegalArgumentException("Can't find " + delegate, e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/7066eaa4/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
----------------------------------------------------------------------
diff --git 
a/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
 
b/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
index 99fd7a2..495e7e9 100644
--- 
a/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
+++ 
b/container/openejb-core/src/main/java/org/apache/openejb/testing/ApplicationComposers.java
@@ -414,6 +414,7 @@ public class ApplicationComposers {
 
         // Invoke the @Module producer methods to build out the AppModule
         int moduleNumber = 0;
+        int notBusinessModuleNumber = 0; // we dont consider resources.xml to 
set an app as standalone or not
         final Map<Object, List<Method>> moduleMethods = new HashMap<>();
         findAnnotatedMethods(moduleMethods, Module.class);
         findAnnotatedMethods(moduleMethods, 
org.apache.openejb.junit.Module.class);
@@ -601,6 +602,7 @@ public class ApplicationComposers {
                     final Resources asResources = Resources.class.cast(obj);
                     appModule.getResources().addAll(asResources.getResource());
                     
appModule.getContainers().addAll(asResources.getContainer());
+                    notBusinessModuleNumber++;
                 } else if (obj instanceof AppModule) {
                     // we can probably go further here
                     final AppModule module = (AppModule) obj;
@@ -685,7 +687,7 @@ public class ApplicationComposers {
             }
         }
 
-        if (moduleNumber == 1 && webModulesNb == 1) {
+        if (moduleNumber - notBusinessModuleNumber == 1 && webModulesNb == 1) {
             appModule.setStandloneWebModule();
         }
 

http://git-wip-us.apache.org/repos/asf/tomee/blob/7066eaa4/container/openejb-core/src/main/java/org/apache/openejb/util/PropertyPlaceHolderHelper.java
----------------------------------------------------------------------
diff --git 
a/container/openejb-core/src/main/java/org/apache/openejb/util/PropertyPlaceHolderHelper.java
 
b/container/openejb-core/src/main/java/org/apache/openejb/util/PropertyPlaceHolderHelper.java
index b218d82..61cbc7c 100644
--- 
a/container/openejb-core/src/main/java/org/apache/openejb/util/PropertyPlaceHolderHelper.java
+++ 
b/container/openejb-core/src/main/java/org/apache/openejb/util/PropertyPlaceHolderHelper.java
@@ -85,8 +85,11 @@ public final class PropertyPlaceHolderHelper {
     }
 
     public static String value(final String aw) {
-        if (aw == null || !aw.contains(PREFIX) || !aw.contains(SUFFIX)) {
-            return aw;
+        if (aw == null) {
+            return null;
+        }
+        if (!aw.contains(PREFIX) || !aw.contains(SUFFIX)) {
+            return decryptIfNeeded(aw);
         }
 
         String value = CACHE.getProperty(aw);
@@ -99,7 +102,15 @@ public final class PropertyPlaceHolderHelper {
         return value;
     }
 
+    public static Properties simpleHolds(final Properties properties) {
+        return holds(properties, false);
+    }
+
     public static Properties holds(final Properties properties) {
+        return holds(properties, true);
+    }
+
+    private static Properties holds(final Properties properties, final boolean 
cache) {
         // we can put null values in SuperProperties, since properties is 
often of this type we need to tolerate it
         final Properties updated = new SuperProperties();
         if (properties == null) {
@@ -109,7 +120,8 @@ public final class PropertyPlaceHolderHelper {
         for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
             final Object rawValue = entry.getValue();
             if (rawValue instanceof String) {
-                updated.put(entry.getKey(), value((String) rawValue));
+                final String value = (String) rawValue;
+                updated.put(entry.getKey(), cache ? value(value) : 
simpleValue(value));
             } else {
                 updated.put(entry.getKey(), rawValue);
             }

http://git-wip-us.apache.org/repos/asf/tomee/blob/7066eaa4/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.cipher.PasswordCipher/cdi
----------------------------------------------------------------------
diff --git 
a/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.cipher.PasswordCipher/cdi
 
b/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.cipher.PasswordCipher/cdi
new file mode 100644
index 0000000..f8a99c1
--- /dev/null
+++ 
b/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.cipher.PasswordCipher/cdi
@@ -0,0 +1 @@
+org.apache.openejb.cipher.CdiPasswordCipher

http://git-wip-us.apache.org/repos/asf/tomee/blob/7066eaa4/container/openejb-core/src/test/java/org/apache/openejb/cipher/CdiPasswordCipherTest.java
----------------------------------------------------------------------
diff --git 
a/container/openejb-core/src/test/java/org/apache/openejb/cipher/CdiPasswordCipherTest.java
 
b/container/openejb-core/src/test/java/org/apache/openejb/cipher/CdiPasswordCipherTest.java
new file mode 100644
index 0000000..e42cb2b
--- /dev/null
+++ 
b/container/openejb-core/src/test/java/org/apache/openejb/cipher/CdiPasswordCipherTest.java
@@ -0,0 +1,82 @@
+/**
+ * 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.cipher;
+
+import org.apache.openejb.config.sys.Resource;
+import org.apache.openejb.config.sys.Resources;
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.testing.SimpleLog;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+import static org.junit.Assert.assertEquals;
+
+@SimpleLog
+@Classes(innerClassesAsBean = true, cdi = true)
+@RunWith(ApplicationComposer.class)
+public class CdiPasswordCipherTest {
+    @javax.annotation.Resource
+    private AResource resource;
+
+    @Test
+    public void checkPwd() {
+        assertEquals("decrypted", resource.thePassword);
+    }
+
+    @Module
+    public Resources resources() {
+        final Resource resource = new Resource();
+        resource.setClassName(AResource.class.getName());
+        resource.setId("the");
+        resource.getProperties().setProperty("thePassword", "cipher:cdi:" + 
AdvancedAlgorithm.class.getName() + ":this");
+
+        final Resources resources = new Resources();
+        resources.getResource().add(resource);
+        return resources;
+    }
+
+    public static class AResource {
+        private String thePassword;
+    }
+
+    public static class AdvancedAlgorithm implements PasswordCipher {
+        @Inject
+        private Decrypter decrypter;
+
+        @Override
+        public char[] encrypt(final String plainPassword) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String decrypt(final char[] encryptedPassword) {
+            return decrypter.decrypt(encryptedPassword);
+        }
+    }
+
+    @ApplicationScoped
+    public static class Decrypter {
+        public String decrypt(final char[] encryptedPassword) {
+            return new String(encryptedPassword).equals("this") ? "decrypted" 
: "failed";
+        }
+    }
+}

Reply via email to