Fix keyring
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 16 Sep 2016 12:08:30 +0000 (12:08 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 16 Sep 2016 12:08:30 +0000 (12:08 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@9181 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

org.argeo.cms.ui.workbench/META-INF/spring/common.xml
org.argeo.cms.ui.workbench/META-INF/spring/keyring.xml
org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeKeyRing.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java
org.argeo.node.api/src/org/argeo/node/NodeConstants.java

index c89ca70b855602bfb3bdbf036be3170aac109a9f..db613a05bb1173e50ea7f6efba5b3c817f25f00c 100644 (file)
                <property name="userAdminServiceReference" ref="userAdmin" />\r
        </bean>\r
 \r
-       <bean\r
-               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">\r
-               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />\r
-               <property name="locations">\r
-                       <value>osgibundle:keyring.properties</value>\r
-               </property>\r
-       </bean>\r
+<!--   <bean -->\r
+<!--           class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> -->\r
+<!--           <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" /> -->\r
+<!--           <property name="locations"> -->\r
+<!--                   <value>osgibundle:keyring.properties</value> -->\r
+<!--           </property> -->\r
+<!--   </bean> -->\r
 \r
 </beans>
\ No newline at end of file
index 0929524d84fa980798f1c497a584152faafdd59d..004e9b3e7c8451e17cd3440ac3bb6c80647d6fc9 100644 (file)
@@ -4,15 +4,15 @@
        xmlns:p="http://www.springframework.org/schema/p"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
-       <bean id="nodeSession" class="org.argeo.jcr.spring.ThreadBoundSession"
-               destroy-method="dispose">
-               <property name="repository" ref="nodeRepository" />
-       </bean>
+<!--   <bean id="nodeSession" class="org.argeo.jcr.spring.ThreadBoundSession" -->
+<!--           destroy-method="dispose"> -->
+<!--           <property name="repository" ref="nodeRepository" /> -->
+<!--   </bean> -->
 
-       <bean id="keyring" class="org.argeo.cms.security.JcrKeyring">
-               <property name="session" ref="nodeSession" />
-               <property name="defaultCallbackHandler" ref="defaultCallbackHandler" />
-               <property name="secreteKeyLength" value="${argeo.keyring.secreteKeyLength}" />
-       </bean>
+<!--   <bean id="keyring" class="org.argeo.cms.security.JcrKeyring"> -->
+<!--           <property name="session" ref="nodeSession" /> -->
+<!--           <property name="defaultCallbackHandler" ref="defaultCallbackHandler" /> -->
+<!--           <property name="secreteKeyLength" value="${argeo.keyring.secreteKeyLength}" /> -->
+<!--   </bean> -->
 
 </beans>
index dc6e2707f8d3f080e9c845ef6aedef465e07a0b5..1204f7d64b5521d49768b14ac83821f03f54ec48 100644 (file)
@@ -15,7 +15,7 @@
                        unbind-method="unregister" />\r
        </list>\r
        <reference id="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
-<!--   <reference id="keyring" interface="org.argeo.node.security.CryptoKeyring" /> -->\r
+       <reference id="keyring" interface="org.argeo.node.security.CryptoKeyring" />\r
 \r
        <!-- <reference id="nodeRepository" interface="javax.jcr.Repository" -->\r
        <!-- filter="(argeo.jcr.repository.alias=node)" /> -->\r
@@ -37,5 +37,5 @@
 \r
 \r
        <!-- SERVICES -->\r
-       <service interface="org.argeo.node.security.CryptoKeyring" ref="keyring" />\r
+<!--   <service interface="org.argeo.node.security.CryptoKeyring" ref="keyring" /> -->\r
 </beans:beans>
\ No newline at end of file
index 0373e3690c6da4ee5607b34700000da1acbd7cd8..20a899eca2a8a2fb7f6e3e82ca4bf529008f7c12 100644 (file)
@@ -14,6 +14,7 @@ import java.util.Set;
 
 import javax.jcr.Repository;
 import javax.jcr.Session;
+import javax.security.auth.callback.CallbackHandler;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -25,6 +26,8 @@ import org.argeo.node.DataModelNamespace;
 import org.argeo.node.NodeConstants;
 import org.argeo.node.NodeDeployment;
 import org.argeo.node.NodeState;
+import org.argeo.node.security.CryptoKeyring;
+import org.argeo.util.LangUtils;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -34,6 +37,7 @@ import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleWire;
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ManagedService;
 import org.osgi.service.http.HttpService;
 import org.osgi.service.useradmin.UserAdmin;
 import org.osgi.util.tracker.ServiceTracker;
@@ -127,6 +131,7 @@ public class CmsDeployment implements NodeDeployment {
                        throw new CmsException("Deployment is already available");
                }
 
+               // home
                prepareDataModel(KernelUtils.openAdminSession(deployedNodeRepository));
                Hashtable<String, String> regProps = new Hashtable<String, String>();
                regProps.put(NodeConstants.CN, NodeConstants.ALIAS_HOME);
@@ -134,6 +139,20 @@ public class CmsDeployment implements NodeDeployment {
                homeRepository = new HomeRepository(deployedNodeRepository);
                // register
                bc.registerService(Repository.class, homeRepository, regProps);
+
+               new ServiceTracker<CallbackHandler, CallbackHandler>(bc, CallbackHandler.class, null) {
+
+                       @Override
+                       public CallbackHandler addingService(ServiceReference<CallbackHandler> reference) {
+                               NodeKeyRing nodeKeyring = new NodeKeyRing(homeRepository);
+                               CallbackHandler callbackHandler = bc.getService(reference);
+                               nodeKeyring.setDefaultCallbackHandler(callbackHandler);
+                               bc.registerService(LangUtils.names(CryptoKeyring.class, ManagedService.class), nodeKeyring,
+                                               LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_KEYRING_PID));
+                               return callbackHandler;
+                       }
+
+               }.open();
        }
 
        /** Session is logged out. */
index 7906c28faf99473f6d3190f78f4a64193a59ea14..9ff1540a7bf3f1d6d9eb498279f57697e15aef63 100644 (file)
@@ -8,8 +8,6 @@ import java.io.File;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Locale;
 import java.util.UUID;
@@ -102,9 +100,8 @@ public class CmsState implements NodeState {
                // Security
                NodeUserAdmin userAdmin = new NodeUserAdmin(AuthConstants.ROLES_BASEDN);
                shutdownHooks.add(() -> userAdmin.destroy());
-               Dictionary<String, Object> props = new Hashtable<>();
-               props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID);
-               bc.registerService(ManagedServiceFactory.class, userAdmin, props);
+               bc.registerService(ManagedServiceFactory.class, userAdmin,
+                               LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID));
        }
 
        private void initTransactionManager() {
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeKeyRing.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeKeyRing.java
new file mode 100644 (file)
index 0000000..0b774d9
--- /dev/null
@@ -0,0 +1,20 @@
+package org.argeo.cms.internal.kernel;
+
+import java.util.Dictionary;
+
+import javax.jcr.Repository;
+
+import org.argeo.cms.security.JcrKeyring;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+class NodeKeyRing extends JcrKeyring implements ManagedService{
+       
+       public NodeKeyRing(Repository repository) {
+               super(repository);
+       }
+
+       @Override
+       public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
+       }
+}
index 1ccb81731e0770b5101bcfee802b665f5ae561d9..5c35e27799733b28f14ccacb86e4d9916540d310 100644 (file)
@@ -29,10 +29,12 @@ import javax.crypto.spec.IvParameterSpec;
 import javax.jcr.Binary;
 import javax.jcr.Node;
 import javax.jcr.Property;
+import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
 import org.apache.commons.io.IOUtils;
+import org.argeo.cms.CmsException;
 import org.argeo.jcr.ArgeoJcrException;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.node.ArgeoNames;
@@ -57,8 +59,17 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
        private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
        private String cipherName = DEFAULT_CIPHER_NAME;
 
-       private Session session;
+       private final Repository repository;
+       private ThreadLocal<Session> sessionThreadLocal = new ThreadLocal<Session>() {
 
+               @Override
+               protected Session initialValue() {
+                       return login();
+               }
+
+       };
+
+       // FIXME is it really still needed?
        /**
         * When setup is called the session has not yet been saved and we don't want
         * to save it since there maybe other data which would be inconsistent. So
@@ -74,13 +85,34 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                }
        };
 
+       public JcrKeyring(Repository repository) {
+               this.repository = repository;
+       }
+
+       private Session session() {
+               Session session = this.sessionThreadLocal.get();
+               if (!session.isLive()) {
+                       session = login();
+                       sessionThreadLocal.set(session);
+               }
+               return session;
+       }
+
+       private Session login() {
+               try {
+                       return repository.login();
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot login key ring session", e);
+               }
+       }
+
        @Override
        protected Boolean isSetup() {
                try {
                        if (notYetSavedKeyring.get() != null)
                                return true;
 
-                       Node userHome = NodeUtils.getUserHome(session);
+                       Node userHome = NodeUtils.getUserHome(session());
                        return userHome.hasNode(ARGEO_KEYRING);
                } catch (RepositoryException e) {
                        throw new ArgeoJcrException("Cannot check whether keyring is setup", e);
@@ -92,14 +124,14 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                Binary binary = null;
                InputStream in = null;
                try {
-                       Node userHome = NodeUtils.getUserHome(session);
+                       Node userHome = NodeUtils.getUserHome(session());
                        if (userHome.hasNode(ARGEO_KEYRING))
                                throw new ArgeoJcrException("Keyring already setup");
                        Node keyring = userHome.addNode(ARGEO_KEYRING);
                        keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC);
 
                        // deterministic salt and iteration count based on username
-                       String username = session.getUserID();
+                       String username = session().getUserID();
                        byte[] salt = new byte[8];
                        byte[] usernameBytes = username.getBytes();
                        for (int i = 0; i < salt.length; i++) {
@@ -109,7 +141,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                                        salt[i] = 0;
                        }
                        in = new ByteArrayInputStream(salt);
-                       binary = session.getValueFactory().createBinary(in);
+                       binary = session().getValueFactory().createBinary(in);
                        keyring.setProperty(ARGEO_SALT, binary);
 
                        Integer iterationCount = username.length() * iterationCountFactor;
@@ -129,7 +161,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        // JcrUtils.closeQuietly(binary);
                        // byte[] btPass = hash(password, salt, iterationCount);
                        // in = new ByteArrayInputStream(btPass);
-                       // binary = session.getValueFactory().createBinary(in);
+                       // binary = session().getValueFactory().createBinary(in);
                        // keyring.setProperty(ARGEO_PASSWORD, binary);
 
                        notYetSavedKeyring.set(keyring);
@@ -138,14 +170,14 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                } finally {
                        JcrUtils.closeQuietly(binary);
                        IOUtils.closeQuietly(in);
-                       // JcrUtils.discardQuietly(session);
+                       // JcrUtils.discardQuietly(session());
                }
        }
 
        @Override
        protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
                try {
-                       Node userHome = NodeUtils.getUserHome(session);
+                       Node userHome = NodeUtils.getUserHome(session());
                        Node keyring;
                        if (userHome.hasNode(ARGEO_KEYRING))
                                keyring = userHome.getNode(ARGEO_KEYRING);
@@ -178,14 +210,14 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                try {
                        Cipher cipher = createCipher();
                        Node node;
-                       if (!session.nodeExists(path)) {
+                       if (!session().nodeExists(path)) {
                                String parentPath = JcrUtils.parentPath(path);
-                               if (!session.nodeExists(parentPath))
+                               if (!session().nodeExists(parentPath))
                                        throw new ArgeoJcrException("No parent node of " + path);
-                               Node parentNode = session.getNode(parentPath);
+                               Node parentNode = session().getNode(parentPath);
                                node = parentNode.addNode(JcrUtils.nodeNameFromPath(path));
                        } else {
-                               node = session.getNode(path);
+                               node = session().getNode(path);
                        }
                        node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED);
                        SecureRandom random = new SecureRandom();
@@ -195,15 +227,16 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv);
 
                        in = new CipherInputStream(unencrypted, cipher);
-                       binary = session.getValueFactory().createBinary(in);
+                       binary = session().getValueFactory().createBinary(in);
                        node.setProperty(Property.JCR_DATA, binary);
-                       session.save();
+                       session().save();
                } catch (Exception e) {
                        throw new ArgeoJcrException("Cannot encrypt", e);
                } finally {
                        IOUtils.closeQuietly(unencrypted);
                        IOUtils.closeQuietly(in);
                        JcrUtils.closeQuietly(binary);
+                       JcrUtils.logoutQuietly(session());
                }
        }
 
@@ -213,7 +246,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                InputStream encrypted = null;
                Reader reader = null;
                try {
-                       if (!session.nodeExists(path)) {
+                       if (!session().nodeExists(path)) {
                                char[] password = ask();
                                reader = new CharArrayReader(password);
                                return new ByteArrayInputStream(IOUtils.toByteArray(reader));
@@ -223,7 +256,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
 
                                Cipher cipher = createCipher();
 
-                               Node node = session.getNode(path);
+                               Node node = session().getNode(path);
                                if (node.hasProperty(ARGEO_IV)) {
                                        byte[] iv = JcrUtils.getBinaryAsBytes(node.getProperty(ARGEO_IV));
                                        cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
@@ -241,12 +274,13 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        IOUtils.closeQuietly(encrypted);
                        IOUtils.closeQuietly(reader);
                        JcrUtils.closeQuietly(binary);
+                       JcrUtils.logoutQuietly(session());
                }
        }
 
        protected Cipher createCipher() {
                try {
-                       Node userHome = NodeUtils.getUserHome(session);
+                       Node userHome = NodeUtils.getUserHome(session());
                        if (!userHome.hasNode(ARGEO_KEYRING))
                                throw new ArgeoJcrException("Keyring not setup");
                        Node keyring = userHome.getNode(ARGEO_KEYRING);
@@ -267,9 +301,9 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted
        }
 
-       public synchronized void setSession(Session session) {
-               this.session = session;
-       }
+       // public synchronized void setSession(Session session) {
+       // this.session = session;
+       // }
 
        public void setIterationCountFactor(Integer iterationCountFactor) {
                this.iterationCountFactor = iterationCountFactor;
index dcff9a545ec627df537212823f2243360a1497af..9f472ef0a9964f63dafdcd41031a8f5b655c5a62 100644 (file)
@@ -10,8 +10,8 @@ public interface NodeConstants {
        String NODE_DEPLOYMENT_PID = "org.argeo.node.deployment";
        String NODE_INSTANCE_PID = "org.argeo.node.instance";
 
-       // String NODE_REPO_PID = "org.argeo.node.repo";
        String NODE_USER_ADMIN_PID = "org.argeo.node.userAdmin";
+       String NODE_KEYRING_PID = "org.argeo.node.keyring";
 
        /*
         * FACTORY PIDs