diff --git a/config/alfresco/application-context-core.xml b/config/alfresco/application-context-core.xml
index 9230844827..329f0c2fab 100644
--- a/config/alfresco/application-context-core.xml
+++ b/config/alfresco/application-context-core.xml
@@ -7,9 +7,9 @@
+
-
diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml
index 4429404dd3..caeeef3de8 100644
--- a/config/alfresco/bootstrap-context.xml
+++ b/config/alfresco/bootstrap-context.xml
@@ -168,16 +168,16 @@
-
+
-
-
- metadata
- solr
-
-
+
+
+
+
+
+
diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml
index 83c7b07908..a71fa35755 100644
--- a/config/alfresco/core-services-context.xml
+++ b/config/alfresco/core-services-context.xml
@@ -1012,14 +1012,22 @@
+
+
+
+
+
+
+
+
-
-
-
+
+
+
diff --git a/config/alfresco/encryption-context.xml b/config/alfresco/encryption-context.xml
index bbe5f12799..8433ba0743 100644
--- a/config/alfresco/encryption-context.xml
+++ b/config/alfresco/encryption-context.xml
@@ -9,33 +9,81 @@
http://code.google.com/p/spring-crypto-utils/schema/crypt
http://code.google.com/p/spring-crypto-utils/schema/crypt.xsd">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/alfresco/keystore/keystore b/config/alfresco/keystore/keystore
new file mode 100644
index 0000000000..7e966eebae
Binary files /dev/null and b/config/alfresco/keystore/keystore differ
diff --git a/config/alfresco/keystore/keystore-passwords.properties b/config/alfresco/keystore/keystore-passwords.properties
new file mode 100644
index 0000000000..2e51c4f163
--- /dev/null
+++ b/config/alfresco/keystore/keystore-passwords.properties
@@ -0,0 +1,20 @@
+keystore.password=mp6yc0UD9e
+
+aliases=metadata,solr
+# The password protecting the keystore entries
+keystore.password=mp6yc0UD9e
+# The password protecting the alias: metadata
+metadata.seed=
+metadata.algorithm=PBEWithMD5AndDES
+metadata.password=oKIWzVdEdA
+# The password protecting the alias: solr
+solr.seed=
+solr.algorithm=PBEWithMD5AndDES
+solr.password=TxHTtOnrwQ
+
+# The password protecting the keystore entries
+#keystore=mp6yc0UD9e
+# The password protecting the alias: metadata
+#metadata=oKIWzVdEdA
+# The password protecting the alias: solr
+#solr=TxHTtOnrwQ
\ No newline at end of file
diff --git a/config/alfresco/keystore/ssl-keystore-passwords.properties b/config/alfresco/keystore/ssl-keystore-passwords.properties
new file mode 100644
index 0000000000..3a32c534d6
--- /dev/null
+++ b/config/alfresco/keystore/ssl-keystore-passwords.properties
@@ -0,0 +1,7 @@
+aliases=ssl.alfresco.ca,ssl.repo
+# The ssl keystore password
+keystore.password=kT9X6oe68t
+# The password protecting the ssl repository key
+ssl.repo.password=kT9X6oe68t
+# The password protecting the ssl Alfresco CA key
+ssl.alfresco.ca.password=kT9X6oe68t
\ No newline at end of file
diff --git a/config/alfresco/keystore/ssl-truststore-passwords.properties b/config/alfresco/keystore/ssl-truststore-passwords.properties
new file mode 100644
index 0000000000..e5510fb03c
--- /dev/null
+++ b/config/alfresco/keystore/ssl-truststore-passwords.properties
@@ -0,0 +1,5 @@
+aliases=alfresco.ca
+# The ssl truststore password
+keystore.password=kT9X6oe68t
+# The password protecting the ssl Alfresco CA strust certificate
+alfresco.ca.password=kT9X6oe68t
\ No newline at end of file
diff --git a/config/alfresco/keystore/ssl.keystore b/config/alfresco/keystore/ssl.keystore
new file mode 100644
index 0000000000..ab8ed698e2
Binary files /dev/null and b/config/alfresco/keystore/ssl.keystore differ
diff --git a/config/alfresco/keystore/ssl.truststore b/config/alfresco/keystore/ssl.truststore
new file mode 100644
index 0000000000..1f127e5a64
Binary files /dev/null and b/config/alfresco/keystore/ssl.truststore differ
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index 74e86dfc93..b68743d0fc 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -673,21 +673,44 @@ deployment.filesystem.default.metadatadir=${deployment.filesystem.metadatadir}/d
#
# Encryption properties
#
-encryption.encryption.cipherAlgorithm=DESede/CBC/PKCS5Padding
-encryption.keystore.location=classpath:alfresco/.keystore
-encryption.keystore.passwordsFile.location=classpath:alfresco/keystore-passwords.properties
+#dir.keystore=${dir.root}/keystore
+dir.keystore=classpath:alfresco/keystore
+encryption.cipherAlgorithm=DESede/CBC/PKCS5Padding
+encryption.keystore.location=${dir.keystore}/keystore
+encryption.keystore.keyMetaData.location=${dir.keystore}/keystore-passwords.properties
encryption.keystore.provider=
encryption.keystore.type=JCEKS
-encryption.messageTimeout=30000
-encryption.macAlgorithm=HmacSHA1
+#encryption.keystore.keyAlgorithm=PBEWithMD5AndDES
+encryption.keystore.keyAlgorithm=DESede
+encryption.keystore.keyAliases=metadata, solr
-# SOLR connection details
-solr.solrHost=localhost
-solr.solrAdminPort=8834
-solr.solrUrl=http://${solr.solrHost}:8080/solr
+encryption.keystore.fallback.location=${dir.keystore}/fallback-keystore
+encryption.keystore.fallback.keyMetaData.location=${dir.keystore}/fallback-keystore-passwords.properties
+encryption.keystore.fallback.provider=
+encryption.keystore.fallback.type=JCEKS
+
+# mac
+encryption.mac.messageTimeout=30000
+encryption.mac.algorithm=HmacSHA1
+
+# ssl
+encryption.ssl.keystore.location=${dir.keystore}/ssl.keystore
+encryption.ssl.keystore.provider=
+encryption.ssl.keystore.type=JCEKS
+encryption.ssl.keystore.keyMetaData.location=${dir.keystore}/ssl-keystore-passwords.properties
+encryption.ssl.truststore.location=${dir.keystore}/ssl.truststore
+encryption.ssl.truststore.provider=
+encryption.ssl.truststore.type=JCEKS
+encryption.ssl.truststore.keyMetaData.location=${dir.keystore}/ssl-truststore-passwords.properties
+
+# SOLR connection details (e.g. for JMX)
+solr.host=localhost
+solr.port=8443
solr.solrUser=solr
solr.solrPassword=solr
-solr.secureComms.enabled=true
+# none, https
+solr.secureComms=https
+
# ms
solr.solrConnectTimeout=5000
solr.solrPingCronExpression=0 0/5 * * * ? *
diff --git a/config/alfresco/subsystems/Search/solr/solr-search-context.xml b/config/alfresco/subsystems/Search/solr/solr-search-context.xml
index f17b4aa2af..48cb523e66 100644
--- a/config/alfresco/subsystems/Search/solr/solr-search-context.xml
+++ b/config/alfresco/subsystems/Search/solr/solr-search-context.xml
@@ -29,10 +29,36 @@
-
+
+
+ ${solr.host}
+
+
+ ${solr.port}
+
+
+
+
diff --git a/source/java/org/alfresco/encryption/BootstrapReEncryptor.java b/source/java/org/alfresco/encryption/BootstrapReEncryptor.java
new file mode 100644
index 0000000000..7cab37f5a2
--- /dev/null
+++ b/source/java/org/alfresco/encryption/BootstrapReEncryptor.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.encryption;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.extensions.surf.util.AbstractLifecycleBean;
+
+/**
+ *
+ * @since 4.0
+ *
+ */
+public class BootstrapReEncryptor extends AbstractLifecycleBean
+{
+ private boolean enabled;
+ private ReEncryptor reEncryptor;
+ private KeyStoreParameters oldKeyStoreParameters;
+
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public void setReEncryptor(ReEncryptor reEncryptor)
+ {
+ this.reEncryptor = reEncryptor;
+ }
+
+ public void setOldKeyStoreParameters(KeyStoreParameters oldKeyStoreParameters)
+ {
+ this.oldKeyStoreParameters = oldKeyStoreParameters;
+ }
+
+ public void reEncrypt()
+ {
+ reEncryptor.execute(oldKeyStoreParameters);
+ }
+
+ @Override
+ protected void onBootstrap(ApplicationEvent event)
+ {
+ if(enabled)
+ {
+ reEncrypt();
+ }
+ }
+
+ @Override
+ protected void onShutdown(ApplicationEvent event)
+ {
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/encryption/EncryptionChecker.java b/source/java/org/alfresco/encryption/EncryptionChecker.java
index 0ea16d8025..31e60f812d 100644
--- a/source/java/org/alfresco/encryption/EncryptionChecker.java
+++ b/source/java/org/alfresco/encryption/EncryptionChecker.java
@@ -18,17 +18,6 @@
*/
package org.alfresco.encryption;
-import java.io.Serializable;
-import java.security.InvalidKeyException;
-import java.util.List;
-
-import org.alfresco.error.AlfrescoRuntimeException;
-import org.alfresco.repo.transaction.RetryingTransactionHelper;
-import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
-import org.alfresco.service.cmr.attributes.AttributeService;
-import org.alfresco.service.transaction.TransactionService;
-import org.alfresco.util.EqualsHelper;
-import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
@@ -38,6 +27,7 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
* The EncryptionChecker checks the state of the repository's encryption system.
* In particular it checks:
*
+ * - that the keystore exists and, if not, creates one.
*
- that the encryption keys have not been changed. If so, the bootstrap will be halted.
*
*
@@ -47,96 +37,31 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
public class EncryptionChecker extends AbstractLifecycleBean
{
private static Log logger = LogFactory. getLog(EncryptionChecker.class);
- public static String TOP_LEVEL_KEY = "keyCheck";
- private static enum KEY_STATUS
- {
- OK, CHANGED, MISSING;
- };
-
- private TransactionService transactionService;
- private AttributeService attributeService;
- private Encryptor encryptor;
- private List keyAliases;
- public void setTransactionService(TransactionService transactionService)
+ private KeyStoreChecker keyStoreChecker;
+ private KeyStoreParameters keyStoreParameters;
+ private KeyResourceLoader keyResourceLoader;
+
+ public void setkeyStoreParameters(KeyStoreParameters keyStoreParameters)
{
- this.transactionService = transactionService;
+ this.keyStoreParameters = keyStoreParameters;
}
- public void setAttributeService(AttributeService attributeService)
+ public void setKeyStoreChecker(KeyStoreChecker keyStoreChecker)
{
- this.attributeService = attributeService;
+ this.keyStoreChecker = keyStoreChecker;
}
-
- public void setKeyAliases(List keyAliases)
+
+ public void setKeyResourceLoader(KeyResourceLoader keyResourceLoader)
{
- this.keyAliases = keyAliases;
- }
-
- public void setEncryptor(Encryptor encryptor)
- {
- this.encryptor = encryptor;
- }
-
- private void removeKey(String keyAlias)
- {
- attributeService.removeAttributes(TOP_LEVEL_KEY);
- logger.info("Removed registered key " + keyAlias);
- }
-
- private KEY_STATUS checkKey(String keyAlias)
- {
- if(attributeService.exists(TOP_LEVEL_KEY, keyAlias))
- {
- try
- {
- // check that the key has not changed by decrypting the encrypted guid attribute
- // comparing against the guid
- KeyCheck keyCheck = (KeyCheck)attributeService.getAttribute(TOP_LEVEL_KEY, keyAlias);
- Serializable storedGUID = encryptor.unsealObject(keyAlias, keyCheck.getEncrypted());
- return EqualsHelper.nullSafeEquals(storedGUID, keyCheck.getGuid()) ? KEY_STATUS.OK : KEY_STATUS.CHANGED;
- }
- catch(InvalidKeyException e)
- {
- // key exception indicates that the key has changed - it can't decrypt the
- // previously-encrypted data
- return KEY_STATUS.CHANGED;
- }
- }
- else
- {
- // register the key by creating an attribute that stores a guid and its encrypted value
- String guid = GUID.generate();
- Serializable encrypted = encryptor.sealObject(keyAlias, null, guid);
- KeyCheck keyCheck = new KeyCheck(guid, encrypted);
- attributeService.createAttribute(keyCheck, TOP_LEVEL_KEY, keyAlias);
- logger.info("Registered key " + keyAlias);
- return KEY_STATUS.MISSING;
- }
+ this.keyResourceLoader = keyResourceLoader;
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
- RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
- final RetryingTransactionCallback checkKeysCallback = new RetryingTransactionCallback()
- {
- public Void execute() throws Throwable
- {
- for(String keyAlias : keyAliases)
- {
- KEY_STATUS keyStatus = checkKey(keyAlias);
- if(keyStatus == KEY_STATUS.CHANGED)
- {
- // Note: this will halt the application bootstrap.
- throw new AlfrescoRuntimeException("The key with alias " + keyAlias + " has been changed, re-instate the previous keystore");
- }
- }
-
- return null;
- }
- };
- retryingTransactionHelper.doInTransaction(checkKeysCallback, false);
+ AlfrescoKeyStore mainKeyStore = new CachingKeyStore(keyStoreParameters, keyResourceLoader);
+ keyStoreChecker.checkKeyStore(mainKeyStore);
}
@Override
@@ -144,70 +69,4 @@ public class EncryptionChecker extends AbstractLifecycleBean
{
}
-
- public void removeRegisteredKeys()
- {
- RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
- final RetryingTransactionCallback removeKeysCallback = new RetryingTransactionCallback()
- {
- public Void execute() throws Throwable
- {
- for(String keyAlias : keyAliases)
- {
- removeKey(keyAlias);
- }
-
- return null;
- }
- };
- retryingTransactionHelper.doInTransaction(removeKeysCallback, false);
- }
-
- /**
- * A KeyCheck object stores a well-known guid and it's encrypted value.
- *
- * @since 4.0
- *
- */
- private static class KeyCheck implements Serializable
- {
- private static final long serialVersionUID = 4514315444977162903L;
-
- private String guid;
- private Serializable encrypted;
-
- public KeyCheck(String guid, Serializable encrypted)
- {
- super();
- this.guid = guid;
- this.encrypted = encrypted;
- }
-
- public String getGuid()
- {
- return guid;
- }
-
- public Serializable getEncrypted()
- {
- return encrypted;
- }
-
- public boolean equals(Object other)
- {
- if(this == other)
- {
- return true;
- }
-
- if(!(other instanceof KeyCheck))
- {
- return false;
- }
- KeyCheck keyCheck = (KeyCheck)other;
- return EqualsHelper.nullSafeEquals(keyCheck.getGuid(), getGuid()) &&
- EqualsHelper.nullSafeEquals(keyCheck.getEncrypted(), getEncrypted());
- }
- }
-
}
diff --git a/source/java/org/alfresco/encryption/KeyStoreChecker.java b/source/java/org/alfresco/encryption/KeyStoreChecker.java
new file mode 100644
index 0000000000..666dd1189a
--- /dev/null
+++ b/source/java/org/alfresco/encryption/KeyStoreChecker.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.encryption;
+
+import java.io.FileOutputStream;
+import java.io.Serializable;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.cmr.attributes.AttributeService;
+import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.GUID;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Validates an Alfresco keystore by ensuring that it's registered keys have not changed.
+ *
+ * @since 4.0
+ *
+ */
+public class KeyStoreChecker
+{
+ public static String TOP_LEVEL_KEY = "keyCheck";
+ private static final Log logger = LogFactory.getLog(KeyStoreChecker.class);
+ private static enum KEY_STATUS
+ {
+ OK, CHANGED, MISSING;
+ };
+
+ private AttributeService attributeService;
+ private Encryptor encryptor;
+ private TransactionService transactionService;
+
+ public KeyStoreChecker()
+ {
+ }
+
+ public void setAttributeService(AttributeService attributeService)
+ {
+ this.attributeService = attributeService;
+ }
+
+ public void setEncryptor(Encryptor encryptor)
+ {
+ this.encryptor = encryptor;
+ }
+
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
+ public KeyStoreChecker(AttributeService attributeService, Encryptor encryptor, TransactionService transactionService)
+ {
+ this.attributeService = attributeService;
+ this.transactionService = transactionService;
+ this.encryptor = encryptor;
+ }
+
+ private KEY_STATUS checkKey(AlfrescoKeyStore keyStore, String keyAlias)
+ {
+ if(attributeService.exists(TOP_LEVEL_KEY, keyStore.getLocation(), keyAlias))
+ {
+ try
+ {
+ // check that the key has not changed by decrypting the encrypted guid attribute
+ // comparing against the guid
+ KeyCheck keyCheck = (KeyCheck)attributeService.getAttribute(TOP_LEVEL_KEY, keyStore.getLocation(), keyAlias);
+ Serializable storedGUID = encryptor.unsealObject(keyAlias, keyCheck.getEncrypted());
+ return EqualsHelper.nullSafeEquals(storedGUID, keyCheck.getGuid()) ? KEY_STATUS.OK : KEY_STATUS.CHANGED;
+ }
+ catch(InvalidKeyException e)
+ {
+ // key exception indicates that the key has changed - it can't decrypt the
+ // previously-encrypted data
+ return KEY_STATUS.CHANGED;
+ }
+ }
+ else
+ {
+ return KEY_STATUS.MISSING;
+ }
+ }
+
+ private void registerKey(AlfrescoKeyStore keyStore, String keyAlias)
+ {
+ // register the key by creating an attribute that stores a guid and its encrypted value
+ String guid = GUID.generate();
+ Serializable encrypted = encryptor.sealObject(keyAlias, null, guid);
+ KeyCheck keyCheck = new KeyCheck(guid, encrypted);
+ attributeService.createAttribute(keyCheck, TOP_LEVEL_KEY, keyStore.getLocation(), keyAlias);
+ logger.info("Registered key " + keyAlias);
+ }
+
+ protected KeysReport getKeysReport(AlfrescoKeyStore keyStore)
+ {
+ final List registeredKeys = new ArrayList();
+
+ if(attributeService.exists(TOP_LEVEL_KEY, keyStore.getLocation()))
+ {
+ attributeService.getAttributes(new AttributeQueryCallback()
+ {
+ public boolean handleAttribute(Long id, Serializable value,
+ Serializable[] keys)
+ {
+ registeredKeys.add((String)value);
+ return true;
+ }
+
+ },
+ TOP_LEVEL_KEY, keyStore.getLocation());
+ }
+
+ List keyAliasesChanged = new ArrayList();
+ List keyAliasesUnchanged = new ArrayList();
+
+ for(String keyAlias : registeredKeys)
+ {
+ KEY_STATUS keyStatus = checkKey(keyStore, keyAlias);
+ if(keyStatus == KEY_STATUS.CHANGED)
+ {
+ keyAliasesChanged.add(keyAlias);
+ }
+ else
+ {
+ keyAliasesUnchanged.add(keyAlias);
+ }
+ }
+
+ return new KeysReport(keyAliasesChanged, keyAliasesUnchanged);
+ }
+
+ public void checkKeyStore(final AlfrescoKeyStore keyStore)
+ {
+ RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
+ final RetryingTransactionCallback checkKeysCallback = new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ KeysReport keysReport = getKeysReport(keyStore);
+
+ // Check for the existence of a key store first
+ if(keyStore.exists())
+ {
+ // The keystore exists - check whether any keys have been changed
+ if(keysReport.getKeysChanged().size() > 0)
+ {
+ // Note: this will halt the application bootstrap.
+ throw new AlfrescoRuntimeException("The keys with aliases " + keysReport.getKeysChanged() + " have been changed, re-instate the previous keystore");
+ }
+ }
+ else
+ {
+ // keystore not found, check whether any keys have been registered
+ if(keysReport.getKeysChanged().size() + keysReport.getKeysUnchanged().size() > 0)
+ {
+ // Note: this will halt the application bootstrap.
+ throw new AlfrescoRuntimeException("Keys have already been registered, re-instate the previous keystore");
+ }
+
+ // no keys found, create a new keystore
+ // TODO
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Keystore not found, creating...");
+ }
+ createKeyStore(keyStore);
+ }
+
+ return null;
+ }
+ };
+ retryingTransactionHelper.doInTransaction(checkKeysCallback, false);
+ }
+
+ protected void createKeyStore(AlfrescoKeyStore keyStore)
+ {
+ new CreatingKeyStore(keyStore).create();
+ }
+
+ public static class KeysReport
+ {
+ private List keysChanged;
+ private List keysUnchanged;
+
+ public KeysReport(List keysChanged, List keysUnchanged)
+ {
+ super();
+ this.keysChanged = keysChanged;
+ this.keysUnchanged = keysUnchanged;
+ }
+
+ public List getKeysChanged()
+ {
+ return keysChanged;
+ }
+
+ public List getKeysUnchanged()
+ {
+ return keysUnchanged;
+ }
+ }
+
+ /**
+ * A KeyCheck object stores a well-known guid and it's encrypted value.
+ *
+ * @since 4.0
+ *
+ */
+ private static class KeyCheck implements Serializable
+ {
+ private static final long serialVersionUID = 4514315444977162903L;
+
+ private String guid;
+ private Serializable encrypted;
+
+ public KeyCheck(String guid, Serializable encrypted)
+ {
+ super();
+ this.guid = guid;
+ this.encrypted = encrypted;
+ }
+
+ public String getGuid()
+ {
+ return guid;
+ }
+
+ public Serializable getEncrypted()
+ {
+ return encrypted;
+ }
+
+ public boolean equals(Object other)
+ {
+ if(this == other)
+ {
+ return true;
+ }
+
+ if(!(other instanceof KeyCheck))
+ {
+ return false;
+ }
+ KeyCheck keyCheck = (KeyCheck)other;
+ return EqualsHelper.nullSafeEquals(keyCheck.getGuid(), getGuid()) &&
+ EqualsHelper.nullSafeEquals(keyCheck.getEncrypted(), getEncrypted());
+ }
+ }
+
+ public void removeRegisteredKeys(final AlfrescoKeyStore keyStore)
+ {
+ RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
+ final RetryingTransactionCallback removeKeysCallback = new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ attributeService.removeAttributes(TOP_LEVEL_KEY, keyStore.getLocation());
+
+ return null;
+ }
+ };
+ retryingTransactionHelper.doInTransaction(removeKeysCallback, false);
+ }
+
+ private class CreatingKeyStore extends CachingKeyStore
+ {
+ CreatingKeyStore(AlfrescoKeyStore keyStore)
+ {
+ super(keyStore.getkeyStoreParameters(), keyStore.getKeyResourceLoader());
+ }
+
+ public void create()
+ {
+ KeyInfoManager keyInfoManager = null;
+
+ try
+ {
+ keyInfoManager = getKeyInfoManager();
+
+ ks.load(null, null);
+ String keyStorePassword = keyInfoManager.getKeyStorePassword();
+ if(keyStorePassword == null)
+ {
+ throw new AlfrescoRuntimeException("Key store password is null for keystore at location " + getLocation()
+ + ", key store meta data location" + getKeyMetaDataFileLocation());
+ }
+
+ // Add keys from the passwords file to the keystore
+ for(Map.Entry keyEntry : keyInfoManager.getKeyInfo().entrySet())
+ {
+ KeyInformation keyInfo = keyInfoManager.getKeyInformation(keyEntry.getKey());
+ String keyPassword = keyInfo.getPassword();
+ if(keyPassword == null)
+ {
+ throw new AlfrescoRuntimeException("No password found for encryption key " + keyEntry.getKey());
+ }
+ Key key = generateSecretKey(keyEntry.getValue());
+ ks.setKeyEntry(keyInfo.getAlias(), key, keyInfo.getPassword().toCharArray(), null);
+ }
+
+ ks.store(new FileOutputStream(getLocation()), keyStorePassword.toCharArray());
+
+ // Register the key store keys
+ for(Map.Entry keyEntry : keyInfoManager.getKeyInfo().entrySet())
+ {
+ registerKey(this, keyEntry.getKey());
+ }
+ }
+ catch(Throwable e)
+ {
+ throw new AlfrescoRuntimeException(
+ "Failed to create keystore: \n" +
+ " Location: " + getLocation() + "\n" +
+ " Provider: " + getProvider() + "\n" +
+ " Type: " + getType(),
+ e);
+ }
+ finally
+ {
+ if(keyInfoManager != null)
+ {
+ keyInfoManager.clear();
+ }
+ }
+ }
+ }
+}
diff --git a/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java b/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java
index be451e34d9..a764bdd4ad 100644
--- a/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java
+++ b/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java
@@ -18,12 +18,15 @@
*/
package org.alfresco.encryption;
+import java.io.FileNotFoundException;
+import java.io.IOException;
import java.security.Key;
import java.security.KeyStore;
import java.security.UnrecoverableKeyException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Properties;
import junit.framework.TestCase;
@@ -55,21 +58,71 @@ public class KeyStoreKeyProviderTest extends TestCase
/* package */ static KeystoreKeyProvider getTestKeyStoreProvider()
{
Map passwords = new HashMap(5);
- passwords.put(KeystoreKeyProvider.KEY_KEYSTORE_PASSWORD, "ksPwd2");
+ passwords.put(AlfrescoKeyStore.KEY_KEYSTORE_PASSWORD, "ksPwd2");
passwords.put(ALIAS_ONE, "aliasPwd1");
passwords.put(ALIAS_TWO, "aliasPwd2");
- KeystoreKeyProvider ks = new KeystoreKeyProvider(
- FILE_TWO,
- getKeyStoreLoader(),
- "SunJCE",
- "JCEKS",
- passwords);
- return ks;
+ KeyStoreParameters encryptionParameters = new KeyStoreParameters("JCEKS", "SunJCE", null, FILE_TWO);
+ KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(encryptionParameters, getKeyStoreLoader(passwords));
+// FILE_TWO,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// passwords);
+ return keyProvider;
+ }
+
+ /* package */ static KeystoreKeyProvider getTestKeyStoreProvider(String keyStoreLocation, Map passwords)
+ {
+// Map passwords = new HashMap(5);
+// passwords.put(KeyStoreManager.KEY_KEYSTORE_PASSWORD, "ksPwd2");
+// passwords.put(ALIAS_ONE, "aliasPwd1");
+// passwords.put(ALIAS_TWO, "aliasPwd2");
+ KeyStoreParameters encryptionParameters = new KeyStoreParameters("JCEKS", "SunJCE", null, keyStoreLocation);
+ KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(encryptionParameters, getKeyStoreLoader(passwords));
+// FILE_TWO,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// passwords);
+ return keyProvider;
}
- protected static KeyResourceLoader getKeyStoreLoader()
+ private static class TestKeyResourceLoader extends SpringKeyResourceLoader
{
- return new SpringKeyResourceLoader();
+ private Properties props;
+
+ TestKeyResourceLoader(Map passwords)
+ {
+ StringBuilder aliases = new StringBuilder();
+ props = new Properties();
+
+ int i = 0;
+ for(Map.Entry password : passwords.entrySet())
+ {
+ props.put(password.getKey() + ".password", password.getValue());
+
+ aliases.append(password.getKey());
+ if(i < passwords.size() - 1)
+ {
+ aliases.append(",");
+ i++;
+ }
+ }
+
+ props.put("aliases", aliases.toString());
+ }
+
+ @Override
+ public Properties loadKeyMetaData(String keyMetaDataFileLocation)
+ throws IOException, FileNotFoundException
+ {
+ return props;
+ }
+ }
+
+ protected static KeyResourceLoader getKeyStoreLoader(Map passwords)
+ {
+ return new TestKeyResourceLoader(passwords);
}
public void setUp() throws Exception
@@ -78,24 +131,28 @@ public class KeyStoreKeyProviderTest extends TestCase
public void testNoKeyStorePasswords() throws Exception
{
- KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(
- FILE_ONE,
- getKeyStoreLoader(),
- "SunJCE",
- "JCEKS",
- Collections.emptyMap());
+ KeystoreKeyProvider keyProvider = getTestKeyStoreProvider(FILE_ONE, Collections.emptyMap());
+
+// KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(
+// FILE_ONE,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// Collections.emptyMap());
// This has succeeded because we have not attempted to access it
assertNull("Should be no keys available", keyProvider.getKey(ALIAS_ONE));
}
public void testKeyStoreWithOnlyAliasPasswords() throws Exception
{
- KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(
- FILE_TWO,
- getKeyStoreLoader(),
- "SunJCE",
- "JCEKS",
- Collections.singletonMap(ALIAS_ONE, "aliasPwd1"));
+ KeystoreKeyProvider keyProvider = getTestKeyStoreProvider(FILE_ONE, Collections.singletonMap(ALIAS_ONE, "aliasPwd1"));
+
+// KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(
+// FILE_TWO,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// Collections.singletonMap(ALIAS_ONE, "aliasPwd1"));
// This has succeeded because we have not attempted to access it
assertNotNull("Should be able to key alias with same password", keyProvider.getKey(ALIAS_ONE));
}
@@ -104,12 +161,14 @@ public class KeyStoreKeyProviderTest extends TestCase
{
try
{
- new KeystoreKeyProvider(
- FILE_ONE,
- getKeyStoreLoader(),
- "SunJCE",
- "JCEKS",
- Collections.singletonMap(ALIAS_ONE, "password_fail"));
+ getTestKeyStoreProvider(FILE_ONE, Collections.singletonMap(ALIAS_ONE, "password_fail"));
+
+// new KeystoreKeyProvider(
+// FILE_ONE,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// Collections.singletonMap(ALIAS_ONE, "password_fail"));
fail("Expect to fail because password is incorrect");
}
catch (AlfrescoRuntimeException e)
@@ -123,12 +182,13 @@ public class KeyStoreKeyProviderTest extends TestCase
{
try
{
- new KeystoreKeyProvider(
- FILE_TWO,
- getKeyStoreLoader(),
- "SunJCE",
- "JCEKS",
- Collections.singletonMap(ALIAS_TWO, "password_fail"));
+ getTestKeyStoreProvider(FILE_TWO, Collections.singletonMap(ALIAS_TWO, "password_fail"));
+// new KeystoreKeyProvider(
+// FILE_TWO,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// Collections.singletonMap(ALIAS_TWO, "password_fail"));
fail("Expect to fail because password is incorrect");
}
catch (AlfrescoRuntimeException e)
@@ -140,12 +200,14 @@ public class KeyStoreKeyProviderTest extends TestCase
public void testAliasWithCorrectPassword_One() throws Exception
{
- KeystoreKeyProvider ks = new KeystoreKeyProvider(
- FILE_ONE,
- getKeyStoreLoader(),
- "SunJCE",
- "JCEKS",
- Collections.singletonMap(ALIAS_ONE, "aliasPwd1"));
+ KeystoreKeyProvider ks = getTestKeyStoreProvider(FILE_ONE, Collections.singletonMap(ALIAS_ONE, "aliasPwd1"));
+
+// KeystoreKeyProvider ks = new KeystoreKeyProvider(
+// FILE_ONE,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// Collections.singletonMap(ALIAS_ONE, "aliasPwd1"));
Key keyOne = ks.getKey(ALIAS_ONE);
assertNotNull(keyOne);
}
@@ -155,12 +217,16 @@ public class KeyStoreKeyProviderTest extends TestCase
Map passwords = new HashMap(5);
passwords.put(ALIAS_ONE, "aliasPwd1");
passwords.put(ALIAS_TWO, "aliasPwd2");
- KeystoreKeyProvider ks = new KeystoreKeyProvider(
- FILE_TWO,
- getKeyStoreLoader(),
- "SunJCE",
- "JCEKS",
- passwords);
+
+ KeystoreKeyProvider ks = getTestKeyStoreProvider(FILE_TWO, passwords);
+
+// KeystoreKeyProvider ks = new KeystoreKeyProvider(
+// FILE_TWO,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// passwords);
+
assertNotNull(ks.getKey(ALIAS_ONE));
assertNotNull(ks.getKey(ALIAS_TWO));
}
@@ -171,12 +237,14 @@ public class KeyStoreKeyProviderTest extends TestCase
passwords.put(ALIAS_ONE, "aliasPwd1");
passwords.put(ALIAS_TWO, "aliasPwd2");
passwords.put(ALIAS_THREE, "aliasPwd3");
- KeystoreKeyProvider ks = new KeystoreKeyProvider(
- FILE_THREE,
- getKeyStoreLoader(),
- "SunJCE",
- "JCEKS",
- passwords);
+ KeystoreKeyProvider ks = getTestKeyStoreProvider(FILE_THREE, passwords);
+
+// KeystoreKeyProvider ks = new KeystoreKeyProvider(
+// FILE_THREE,
+// getKeyStoreLoader(),
+// "SunJCE",
+// "JCEKS",
+// passwords);
assertNotNull(ks.getKey(ALIAS_ONE));
assertNotNull(ks.getKey(ALIAS_TWO));
assertNull(ks.getKey(ALIAS_THREE));
diff --git a/source/java/org/alfresco/encryption/ReEncryptor.java b/source/java/org/alfresco/encryption/ReEncryptor.java
new file mode 100644
index 0000000000..5d023d85a5
--- /dev/null
+++ b/source/java/org/alfresco/encryption/ReEncryptor.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.encryption;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.crypto.SealedObject;
+
+import org.alfresco.repo.batch.BatchProcessWorkProvider;
+import org.alfresco.repo.batch.BatchProcessor;
+import org.alfresco.repo.dictionary.DictionaryDAO;
+import org.alfresco.repo.dictionary.NamespaceDAO;
+import org.alfresco.repo.domain.node.NodeDAO;
+import org.alfresco.repo.domain.node.NodePropertyEntity;
+import org.alfresco.repo.domain.node.NodePropertyKey;
+import org.alfresco.repo.domain.qname.QNameDAO;
+import org.alfresco.repo.node.encryption.MetadataEncryptor;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.dictionary.PropertyDefinition;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.extensions.surf.util.I18NUtil;
+
+// TODO use Batch code to run in parallel
+// TODO lock so that only one encryptor can run at a time
+/**
+ * Re-encrypts encryptable repository properties using a new set of encryption keys.
+ * Decrypts the repository properties using the default encryptor, falling back to
+ * a backup decryptor (using the old encryption keys) if necessary, and then re-encrypts
+ * the properties.
+ *
+ * The system can stay running during this operation.
+ *
+ * @since 4.0
+ */
+public class ReEncryptor implements ApplicationContextAware
+{
+ private static Log logger = LogFactory.getLog(ReEncryptor.class);
+
+ private NodeDAO nodeDAO;
+ private NamespaceDAO namespaceDAO;
+ private DictionaryDAO dictionaryDAO;
+ private DictionaryService dictionaryService;
+ private QNameDAO qnameDAO;
+
+ private MetadataEncryptor metadataEncryptor;
+
+ private ApplicationContext applicationContext;
+
+ private TransactionService transactionService;
+ private RetryingTransactionHelper transactionHelper;
+
+ /**
+ * Set the transaction provider so that each execution can be performed within a transaction
+ */
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ this.transactionHelper = transactionService.getRetryingTransactionHelper();
+ this.transactionHelper.setForceWritable(true);
+ }
+
+// protected MetadataEncryptor getMetadataEncryptor(EncryptionParameters encryptionParameters)
+// {
+// DefaultEncryptor encryptor = new DefaultEncryptor();
+// encryptor.setCipherAlgorithm(encryptionParameters.getCipherAlgorithm());
+// encryptor.setCipherProvider(null);
+// KeystoreKeyProvider keyProvider = new KeystoreKeyProvider();
+// keyProvider.setLocation(encryptionParameters.getKeyStoreLocation());
+// keyProvider.setPasswordsFileLocation(encryptionParameters.getPasswordFileLocation());
+// keyProvider.setType(encryptionParameters.getKeyStoreType());
+// keyProvider.setKeyResourceLoader(new SpringKeyResourceLoader());
+// keyProvider.setProvider(encryptionParameters.getKeyStoreProvider()
+// );
+//
+// encryptor.setKeyProvider(keyProvider);
+//
+// MetadataEncryptor metadataEncryptor = new MetadataEncryptor();
+// metadataEncryptor.setEncryptor(encryptor);
+// metadataEncryptor.setDictionaryService(dictionaryService);
+//
+// return metadataEncryptor;
+// }
+
+ public void setMetadataEncryptor(MetadataEncryptor metadataEncryptor)
+ {
+ this.metadataEncryptor = metadataEncryptor;
+ }
+
+ public void init()
+ {
+ }
+
+ public void reencrypt(final KeyStoreParameters newEncryptionParameters, final List properties)
+ {
+ BatchProcessor.BatchProcessWorker worker = new BatchProcessor.BatchProcessWorker()
+ {
+ public String getIdentifier(NodePropertyEntity entity)
+ {
+ return String.valueOf(entity.getNodeId());
+ }
+
+ public void beforeProcess() throws Throwable
+ {
+ }
+
+ public void afterProcess() throws Throwable
+ {
+ }
+
+ public void process(NodePropertyEntity entity) throws Throwable
+ {
+ Object value = entity.getValue();
+ if(value instanceof SealedObject)
+ {
+ SealedObject sealed = (SealedObject)value;
+
+ NodePropertyKey nodeKey = entity.getKey();
+ QName propertyQName = qnameDAO.getQName(nodeKey.getQnameId()).getSecond();
+
+ // metadataEncryptor uses a fallback encryptor; decryption will try the
+ // default (new) keys first (which will fail for properties created before the
+ // change in keys), followed by the backup keys.
+ Serializable decrypted = metadataEncryptor.decrypt(propertyQName, sealed);
+
+ // Re-encrypt. The new keys will be used.
+ Serializable resealed = metadataEncryptor.encrypt(propertyQName, decrypted);
+
+ // TODO update resealed using batch update?
+ // does the node DAO do batch updating?
+ nodeDAO.setNodeProperties(entity.getNodeId(), Collections.singletonMap(propertyQName, resealed));
+ }
+ else
+ {
+ // TODO
+ }
+ }
+ };
+
+ BatchProcessWorkProvider provider = new BatchProcessWorkProvider()
+ {
+ private int start = 0;
+
+ @Override
+ public int getTotalEstimatedWorkSize()
+ {
+ return properties.size();
+ }
+
+ @Override
+ public Collection getNextWork()
+ {
+ int end = start + 20;
+ if(end > properties.size())
+ {
+ end = properties.size();
+ }
+ List sublist = properties.subList(start, end);
+ start += 20;
+ return sublist;
+ }
+ };
+
+ // Migrate using 2 threads, 20 authorities per transaction. Log every 100 entries.
+ // TODO, propertize these numbers
+ new BatchProcessor(
+ I18NUtil.getMessage(""),
+ transactionHelper,
+ provider,
+ 2, 20,
+ applicationContext,
+ logger, 100).process(worker, true);
+ }
+
+ public void execute(KeyStoreParameters newEncryptionParameters)
+ {
+ // Proceed only if fallback is available i.e. the systems has both old and new keys
+ if(metadataEncryptor.isFallbackAvailable())
+ {
+ QName model = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, namespaceDAO);
+
+ // get properties that are encrypted
+ Collection propertyDefs = dictionaryDAO.getProperties(model, DataTypeDefinition.ENCRYPTED);
+ List properties = nodeDAO.getProperties(propertyDefs);
+
+ // reencrypt these properties
+ reencrypt(newEncryptionParameters, properties);
+ }
+ else
+ {
+ // TODO
+ }
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException
+ {
+ this.applicationContext = applicationContext;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
index 229d143fe7..0d1721e748 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
@@ -27,6 +27,7 @@ import java.util.Set;
import org.alfresco.repo.node.NodeBulkLoader;
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
+import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
@@ -36,6 +37,7 @@ import org.alfresco.service.cmr.repository.StoreExistsException;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
+import org.springframework.dao.ConcurrencyFailureException;
/**
* DAO services for alf_node and related tables
@@ -671,4 +673,5 @@ public interface NodeDAO extends NodeBulkLoader
*/
public void setNodeDefiningAclId(Long nodeId, long id);
+ public List getProperties(Collection propertyDefs);
}
diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
index b4ff691417..485aa14c9b 100644
--- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
@@ -49,6 +49,7 @@ import org.alfresco.repo.domain.node.TransactionEntity;
import org.alfresco.repo.domain.node.TransactionQueryEntity;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -90,6 +91,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
private static final String SELECT_NODES_BY_UUIDS = "alfresco.node.select_NodesByUuids";
private static final String SELECT_NODES_BY_IDS = "alfresco.node.select_NodesByIds";
private static final String SELECT_NODE_PROPERTIES = "alfresco.node.select_NodeProperties";
+ private static final String SELECT_PROPERTIES_BY_TYPE = "alfresco.node.select_PropertiesByType";
private static final String SELECT_NODE_ASPECTS = "alfresco.node.select_NodeAspects";
private static final String INSERT_NODE_PROPERTY = "alfresco.node.insert.insert_NodeProperty";
private static final String UPDATE_PRIMARY_CHILDREN_SHARED_ACL = "alfresco.node.update.update_PrimaryChildrenSharedAcl";
@@ -1482,6 +1484,31 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
}
}
+ // TODO - use a callback approach
+ public List getProperties(Collection propertyDefs)
+ {
+ Set qnames = new HashSet();
+ for(PropertyDefinition propDef : propertyDefs)
+ {
+ qnames.add(propDef.getName());
+ }
+
+ final List props = new ArrayList();
+
+ // qnames of properties that are encrypted
+ Set qnameIds = qnameDAO.convertQNamesToIds(qnames, false);
+ template.select(SELECT_PROPERTIES_BY_TYPE, qnameIds, new ResultHandler()
+ {
+ @Override
+ public void handleResult(ResultContext context)
+ {
+ props.add((NodePropertyEntity)context.getResultObject());
+ }
+ });
+
+ return props;
+ }
+
/*
* DAO OVERRIDES
*/
diff --git a/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java b/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java
index 4d88913ea2..9540a238ab 100644
--- a/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java
+++ b/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java
@@ -1,6 +1,7 @@
package org.alfresco.repo.node.encryption;
import java.io.Serializable;
+import java.security.InvalidKeyException;
import java.security.KeyException;
import java.util.HashMap;
import java.util.HashSet;
@@ -10,6 +11,7 @@ import java.util.Set;
import javax.crypto.SealedObject;
import org.alfresco.encryption.Encryptor;
+import org.alfresco.encryption.FallbackEncryptor;
import org.alfresco.encryption.KeyProvider;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AuthenticationException;
@@ -221,4 +223,23 @@ public class MetadataEncryptor
// Done
return outbound;
}
-}
+
+ public Serializable reencrypt(QName propertyQName, Serializable sealed) throws InvalidKeyException
+ {
+ // metadataEncryptor uses a fallback encryptor; decryption will try the
+ // default (new) keys first (which will fail for properties created before the
+ // change in keys), followed by the backup keys.
+ Serializable decrypted = decrypt(propertyQName, sealed);
+
+ // Re-encrypt. The new keys will be used.
+ Serializable resealed = encrypt(propertyQName, decrypted);
+
+ return resealed;
+ }
+
+ public boolean isFallbackAvailable()
+ {
+ return false;
+// return encryptor.isFallbackAvailable();
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
index 1c5b9f6f85..953a8c90e4 100644
--- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
+++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
@@ -29,7 +29,7 @@ import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.error.AlfrescoRuntimeException;
-import org.alfresco.model.ContentModel;
+import org.alfresco.httpclient.HttpClientFactory;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.search.impl.lucene.LuceneQueryParserException;
import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet;
@@ -44,12 +44,8 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
-import org.apache.commons.httpclient.UsernamePasswordCredentials;
-import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
-import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
@@ -73,21 +69,35 @@ public class SolrQueryHTTPClient
private Map storeMappings;
+ private String solrHost;
+ private int solrPort;
private String baseUrl;
private HttpClient httpClient;
-
+ private HttpClientFactory httpClientFactory;
+
public SolrQueryHTTPClient()
{
- MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
- httpClient = new HttpClient(connectionManager);
- HttpClientParams params = httpClient.getParams();
- params.setBooleanParameter("http.tcp.nodelay", true);
- params.setBooleanParameter("http.connection.stalecheck", false);
- params.setBooleanParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, true);
- httpClient.getState().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), new UsernamePasswordCredentials("admin", "admin"));
}
+ public void init()
+ {
+ StringBuilder sb = new StringBuilder();
+// sb.append("http://");
+// sb.append(solrHost);
+// sb.append(":");
+// sb.append(solrPort);
+ sb.append("/solr");
+ this.baseUrl = sb.toString();
+
+ httpClient = httpClientFactory.getHttpClient(solrHost, solrPort);
+ }
+
+ public void setHttpClientFactory(HttpClientFactory httpClientFactory)
+ {
+ this.httpClientFactory = httpClientFactory;
+ }
+
public void setNodeDAO(NodeDAO nodeDAO)
{
this.nodeDAO = nodeDAO;
@@ -108,12 +118,47 @@ public class SolrQueryHTTPClient
this.storeMappings = storeMappings;
}
- public void setBaseUrl(String baseUrl)
+ public void setSolrHost(String solrHost)
{
- this.baseUrl = baseUrl;
+ this.solrHost = solrHost;
}
+
+ public void setSolrPort(int solrPort)
+ {
+ this.solrPort = solrPort;
+ }
+
+// public void setBaseUrl(String baseUrl)
+// {
+// this.baseUrl = baseUrl;
+// }
- public ResultSet executeQuery(SearchParameters searchParameters, String language)
+// public void setKeyStoreLocation(String keyStoreLocation)
+// {
+// this.keyStoreLocation = keyStoreLocation;
+// }
+//
+// public void setTrustStoreLocation(String trustStoreLocation)
+// {
+// this.trustStoreLocation = trustStoreLocation;
+// }
+//
+// public void setKeyStoreType(String keyStoreType)
+// {
+// this.keyStoreType = keyStoreType;
+// }
+//
+// public void setTrustStoreType(String trustStoreType)
+// {
+// this.trustStoreType = trustStoreType;
+// }
+//
+// public void setPasswordFileLocation(String passwordFileLocation)
+// {
+// this.passwordFileLocation = passwordFileLocation;
+// }
+
+ public ResultSet executeQuery(SearchParameters searchParameters, String language)
{
try
{
diff --git a/source/java/org/alfresco/repo/solr/SOLRAdminClient.java b/source/java/org/alfresco/repo/solr/SOLRAdminClient.java
index 74106da37f..bc52ba86a0 100644
--- a/source/java/org/alfresco/repo/solr/SOLRAdminClient.java
+++ b/source/java/org/alfresco/repo/solr/SOLRAdminClient.java
@@ -29,8 +29,10 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.httpclient.HttpClientFactory;
import org.alfresco.util.ParameterCheck;
import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.solr.client.solrj.SolrServerException;
@@ -68,6 +70,8 @@ public class SOLRAdminClient implements ApplicationEventPublisherAware
private ApplicationEventPublisher applicationEventPublisher;
private SolrTracker solrTracker;
+
+ private HttpClientFactory httpClientFactory;
public SOLRAdminClient()
{
@@ -83,11 +87,6 @@ public class SOLRAdminClient implements ApplicationEventPublisherAware
this.solrPort = Integer.parseInt(solrPort);
}
- public void setSolrUrl(String url)
- {
- this.solrUrl = url;
- }
-
public void setSolrUser(String solrUser)
{
this.solrUser = solrUser;
@@ -114,7 +113,29 @@ public class SOLRAdminClient implements ApplicationEventPublisherAware
this.solrPingCronExpression = solrPingCronExpression;
}
- public void init()
+ public void setHttpClientFactory(HttpClientFactory httpClientFactory)
+ {
+ this.httpClientFactory = httpClientFactory;
+ }
+
+// protected HttpClient getHttpClient()
+// {
+// return httpClientFactory.getHttpClient(solrHost, solrPort);
+//// HttpClient httpClient = new HttpClient();
+////
+//// HttpClientParams params = httpClient.getParams();
+//// params.setBooleanParameter("http.tcp.nodelay", true);
+//// params.setBooleanParameter("http.connection.stalecheck", false);
+////
+//// ProtocolSocketFactory socketFactory = new AuthSSLProtocolSocketFactory(
+//// keyResourceLoader, encryptionParameters);
+//// Protocol myhttps = new Protocol("https", socketFactory, 8843);
+//// httpClient.getHostConfiguration().setHost(solrHost, 8080, myhttps);
+////
+//// return httpClient;
+// }
+
+ public void init()
{
ParameterCheck.mandatory("solrHost", solrHost);
ParameterCheck.mandatory("solrPort", solrPort);
@@ -126,7 +147,17 @@ public class SOLRAdminClient implements ApplicationEventPublisherAware
try
{
- server = new CommonsHttpSolrServer(solrUrl);
+ StringBuilder sb = new StringBuilder();
+ sb.append(httpClientFactory.isSSL() ? "https://" : "http://");
+ sb.append(solrHost);
+ sb.append(":");
+ sb.append(solrPort);
+ sb.append("/solr");
+ this.solrUrl = sb.toString();
+ HttpClient httpClient = httpClientFactory.getHttpClient(solrHost, solrPort);
+
+ server = new CommonsHttpSolrServer(solrUrl, httpClient);
+ // TODO remove credentials because we're using SSL?
Credentials defaultcreds = new UsernamePasswordCredentials(solrUser, solrPassword);
server.getHttpClient().getState().setCredentials(new AuthScope(solrHost, solrPort, AuthScope.ANY_REALM),
defaultcreds);