From 12caa0288053858bade6e16372a0998ae4fd2820 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Fri, 16 Sep 2016 12:08:30 +0000 Subject: [PATCH] Fix keyring git-svn-id: https://svn.argeo.org/commons/trunk@9181 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../META-INF/spring/common.xml | 14 ++-- .../META-INF/spring/keyring.xml | 18 ++--- .../META-INF/spring/osgi.xml | 4 +- .../cms/internal/kernel/CmsDeployment.java | 19 +++++ .../argeo/cms/internal/kernel/CmsState.java | 7 +- .../cms/internal/kernel/NodeKeyRing.java | 20 +++++ .../org/argeo/cms/security/JcrKeyring.java | 74 ++++++++++++++----- .../src/org/argeo/node/NodeConstants.java | 2 +- 8 files changed, 114 insertions(+), 44 deletions(-) create mode 100644 org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeKeyRing.java diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/common.xml b/org.argeo.cms.ui.workbench/META-INF/spring/common.xml index c89ca70b8..db613a05b 100644 --- a/org.argeo.cms.ui.workbench/META-INF/spring/common.xml +++ b/org.argeo.cms.ui.workbench/META-INF/spring/common.xml @@ -16,12 +16,12 @@ - - - - osgibundle:keyring.properties - - + + + + + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/keyring.xml b/org.argeo.cms.ui.workbench/META-INF/spring/keyring.xml index 0929524d8..004e9b3e7 100644 --- a/org.argeo.cms.ui.workbench/META-INF/spring/keyring.xml +++ b/org.argeo.cms.ui.workbench/META-INF/spring/keyring.xml @@ -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"> - - - + + + + - - - - - + + + + + diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml b/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml index dc6e2707f..1204f7d64 100644 --- a/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml +++ b/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml @@ -15,7 +15,7 @@ unbind-method="unregister" /> - + @@ -37,5 +37,5 @@ - + \ No newline at end of file diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java index 0373e3690..20a899eca 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java @@ -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 regProps = new Hashtable(); 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(bc, CallbackHandler.class, null) { + + @Override + public CallbackHandler addingService(ServiceReference 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. */ diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java index 7906c28fa..9ff1540a7 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java @@ -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 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 index 000000000..0b774d934 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeKeyRing.java @@ -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 properties) throws ConfigurationException { + } +} diff --git a/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java b/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java index 1ccb81731..5c35e2779 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java +++ b/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java @@ -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 sessionThreadLocal = new ThreadLocal() { + @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; diff --git a/org.argeo.node.api/src/org/argeo/node/NodeConstants.java b/org.argeo.node.api/src/org/argeo/node/NodeConstants.java index dcff9a545..9f472ef0a 100644 --- a/org.argeo.node.api/src/org/argeo/node/NodeConstants.java +++ b/org.argeo.node.api/src/org/argeo/node/NodeConstants.java @@ -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 -- 2.30.2