Remaining commits for ALF-9510:

o re-encryptor
   o secret key keystore creation
Removed secret key keystores from Solr
Added pcks12 secret key keystore for use in browsers

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@30208 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Steven Glover
2011-09-02 16:29:39 +00:00
parent c2f32afaaa
commit 8fddcdfd1b
13 changed files with 202 additions and 189 deletions

Binary file not shown.

View File

@@ -1013,7 +1013,7 @@
</property> </property>
</bean> </bean>
<bean id="solrHttpClientFactory" class="org.alfresco.httpclient.HttpClientFactory"> <bean id="solrHttpClientFactory" class="org.alfresco.httpclient.HttpClientFactory" init-method="init">
<property name="secureCommsType" value="${solr.secureComms}"/> <property name="secureCommsType" value="${solr.secureComms}"/>
<property name="sSLEncryptionParameters" ref="sslEncryptionParameters"/> <property name="sSLEncryptionParameters" ref="sslEncryptionParameters"/>
<property name="keyResourceLoader" ref="springKeyResourceLoader"/> <property name="keyResourceLoader" ref="springKeyResourceLoader"/>

View File

@@ -11,7 +11,9 @@
<!-- Beans to initilize encryption --> <!-- Beans to initilize encryption -->
<!-- TODO i18n for key store names -->
<bean id="sslKeyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters"> <bean id="sslKeyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters">
<property name="name" value="SSL Key Store"/>
<property name="location" value="${encryption.ssl.keystore.location}"/> <property name="location" value="${encryption.ssl.keystore.location}"/>
<property name="type" value="${encryption.ssl.keystore.type}"/> <property name="type" value="${encryption.ssl.keystore.type}"/>
<property name="provider" value="${encryption.ssl.keystore.provider}"/> <property name="provider" value="${encryption.ssl.keystore.provider}"/>
@@ -19,6 +21,7 @@
</bean> </bean>
<bean id="sslTrustStoreParameters" class="org.alfresco.encryption.KeyStoreParameters"> <bean id="sslTrustStoreParameters" class="org.alfresco.encryption.KeyStoreParameters">
<property name="name" value="SSL Trust Store"/>
<property name="location" value="${encryption.ssl.truststore.location}"/> <property name="location" value="${encryption.ssl.truststore.location}"/>
<property name="type" value="${encryption.ssl.truststore.type}"/> <property name="type" value="${encryption.ssl.truststore.type}"/>
<property name="provider" value="${encryption.ssl.truststore.provider}"/> <property name="provider" value="${encryption.ssl.truststore.provider}"/>
@@ -30,6 +33,21 @@
<property name="trustStoreParameters" ref="sslTrustStoreParameters"/> <property name="trustStoreParameters" ref="sslTrustStoreParameters"/>
</bean> </bean>
<bean id="ssl.keyStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
<property name="keyStoreParameters" ref="sslKeyStoreParameters"/>
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
</bean>
<bean id="ssl.trustStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
<property name="keyStoreParameters" ref="sslTrustStoreParameters"/>
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
</bean>
<!--
<bean id="authSSLProtocolSocketFactory" class="org.alfresco.encryption.ssl.AuthSSLProtocolSocketFactory">
<property name="keyStore" ref="ssl.keyStore"/>
<property name="trustStore" ref="ssl.trustStore"/>
</bean>
-->
<bean id="md5EncryptionParameters" class="org.alfresco.httpclient.MD5EncryptionParameters"> <bean id="md5EncryptionParameters" class="org.alfresco.httpclient.MD5EncryptionParameters">
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}"/> <property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}"/>
<property name="messageTimeout" value="${encryption.mac.messageTimeout}"/> <property name="messageTimeout" value="${encryption.mac.messageTimeout}"/>
@@ -40,6 +58,7 @@
</bean> </bean>
<bean id="keyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters"> <bean id="keyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters">
<property name="name" value="Key Store"/>
<property name="location" value="${encryption.keystore.location}"/> <property name="location" value="${encryption.keystore.location}"/>
<property name="provider" value="${encryption.keystore.provider}"/> <property name="provider" value="${encryption.keystore.provider}"/>
<property name="type" value="${encryption.keystore.type}"/> <property name="type" value="${encryption.keystore.type}"/>
@@ -56,6 +75,7 @@
</bean> </bean>
<bean id="backupKeyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters"> <bean id="backupKeyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters">
<property name="name" value="Backup Key Store"/>
<property name="location" value="${encryption.keystore.backup.location}"/> <property name="location" value="${encryption.keystore.backup.location}"/>
<property name="provider" value="${encryption.keystore.backup.provider}"/> <property name="provider" value="${encryption.keystore.backup.provider}"/>
<property name="type" value="${encryption.keystore.backup.type}"/> <property name="type" value="${encryption.keystore.backup.type}"/>
@@ -86,20 +106,6 @@
<property name="fallback" ref="backupEncryptor" /> <property name="fallback" ref="backupEncryptor" />
</bean> </bean>
<bean id="reEncryptor" class="org.alfresco.encryption.ReEncryptor">
<property name="chunkSize" value="${encryption.reencryptor.chunkSize}"/>
<property name="backupKeyStore" ref="backupKeyStore"/>
<property name="keyStore" ref="keyStore"/>
<property name="backupKeyProvider" ref="backupKeyProvider"/>
<property name="keyProvider" ref="keyProvider"/>
<property name="transactionService" ref="transactionService"/>
<property name="dictionaryService" ref="dictionaryService"/>
<property name="nodeDAO" ref="nodeDAO"/>
<property name="dictionaryDAO" ref="dictionaryDAO"/>
<property name="qnameDAO" ref="qnameDAO"/>
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}"/>
</bean>
<bean id="encryptionKeysRegistry" class="org.alfresco.encryption.EncryptionKeysRegistryImpl"> <bean id="encryptionKeysRegistry" class="org.alfresco.encryption.EncryptionKeysRegistryImpl">
<property name="transactionService" ref="transactionService"/> <property name="transactionService" ref="transactionService"/>
<property name="attributeService" ref="attributeService"/> <property name="attributeService" ref="attributeService"/>

Binary file not shown.

Binary file not shown.

View File

@@ -4,8 +4,4 @@ keystore.password=mp6yc0UD9e
# The password protecting the alias: metadata # The password protecting the alias: metadata
metadata.keyData= metadata.keyData=
metadata.algorithm=DESede metadata.algorithm=DESede
metadata.password=oKIWzVdEdA metadata.password=oKIWzVdEdA
# The password protecting the alias: solr
#solr.keyData=
#solr.algorithm=DESede
#solr.password=TxHTtOnrwQ

View File

@@ -37,6 +37,9 @@ system.mt.warn.upgrade_mt_admin_context=Please update your alfresco/extension/mt
system.usage.err.no_txn=RepoUsageComponent must be called in a transaction. system.usage.err.no_txn=RepoUsageComponent must be called in a transaction.
system.usage.err.no_txn_readwrite=RepoUsageComponent must be called in a read-write transaction. system.usage.err.no_txn_readwrite=RepoUsageComponent must be called in a read-write transaction.
# Re-encryptor
reencryptor.batchprocessor.name=Reencryptor
# START TRANSLATION # START TRANSLATION
system.usage.warn.limit_users_approached=The allowable user limit of {0} is being approached. There are {1} users in the system. system.usage.warn.limit_users_approached=The allowable user limit of {0} is being approached. There are {1} users in the system.
system.usage.warn.limit_users_reached=The allowable user limit of {0} has been reached. There are {1} users in the system. system.usage.warn.limit_users_reached=The allowable user limit of {0} has been reached. There are {1} users in the system.

View File

@@ -249,5 +249,16 @@
<property name="dictionaryService" ref="dictionaryService" /> <property name="dictionaryService" ref="dictionaryService" />
<property name="encryptor" ref="fallbackEncryptor" /> <property name="encryptor" ref="fallbackEncryptor" />
</bean> </bean>
<bean id="reEncryptor" class="org.alfresco.encryption.ReEncryptor">
<property name="chunkSize" value="${encryption.reencryptor.chunkSize}"/>
<property name="backupKeyProvider" ref="backupKeyProvider"/>
<property name="keyProvider" ref="keyProvider"/>
<property name="transactionService" ref="transactionService"/>
<property name="nodeDAO" ref="nodeDAO"/>
<property name="dictionaryDAO" ref="dictionaryDAO"/>
<property name="qnameDAO" ref="qnameDAO"/>
<property name="metadataEncryptor" ref="metadataEncryptor"/>
<property name="jobLockService" ref="jobLockService"/>
</bean>
</beans> </beans>

View File

@@ -56,7 +56,6 @@ import org.springframework.context.ApplicationContext;
public class EncryptionTests extends TestCase public class EncryptionTests extends TestCase
{ {
private static final String TEST_MODEL = "org/alfresco/encryption/reencryption_model.xml"; private static final String TEST_MODEL = "org/alfresco/encryption/reencryption_model.xml";
// private static final String TEST_BUNDLE = "org/alfresco/encryption/encryptiontest_model";
private static int NUM_PROPERTIES = 500; private static int NUM_PROPERTIES = 500;
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
@@ -72,11 +71,13 @@ public class EncryptionTests extends TestCase
private MetadataEncryptor metadataEncryptor; private MetadataEncryptor metadataEncryptor;
private ReEncryptor reEncryptor; private ReEncryptor reEncryptor;
private String cipherAlgorithm = "DESede/CBC/PKCS5Padding"; private String cipherAlgorithm = "DESede/CBC/PKCS5Padding";
private KeyStoreParameters keyStoreParameters;
private KeyStoreParameters backupKeyStoreParameters; private KeyStoreParameters backupKeyStoreParameters;
private AlfrescoKeyStoreImpl backupKeyStore;
private KeyResourceLoader keyResourceLoader; private KeyResourceLoader keyResourceLoader;
private EncryptionKeysRegistryImpl encryptionKeysRegistry; private EncryptionKeysRegistryImpl encryptionKeysRegistry;
private KeyStoreChecker keyStoreChecker; private KeyStoreChecker keyStoreChecker;
private DefaultEncryptor mainEncryptor;
private DefaultEncryptor backupEncryptor;
private AuthenticationComponent authenticationComponent; private AuthenticationComponent authenticationComponent;
private DictionaryDAO dictionaryDAO; private DictionaryDAO dictionaryDAO;
@@ -102,8 +103,10 @@ public class EncryptionTests extends TestCase
reEncryptor = (ReEncryptor)ctx.getBean("reEncryptor"); reEncryptor = (ReEncryptor)ctx.getBean("reEncryptor");
backupKeyStoreParameters = (KeyStoreParameters)ctx.getBean("backupKeyStoreParameters"); backupKeyStoreParameters = (KeyStoreParameters)ctx.getBean("backupKeyStoreParameters");
keyStoreChecker = (KeyStoreChecker)ctx.getBean("keyStoreChecker"); keyStoreChecker = (KeyStoreChecker)ctx.getBean("keyStoreChecker");
keyStoreParameters = (KeyStoreParameters)ctx.getBean("keyStoreParameters");
encryptionKeysRegistry = (EncryptionKeysRegistryImpl)ctx.getBean("encryptionKeysRegistry"); encryptionKeysRegistry = (EncryptionKeysRegistryImpl)ctx.getBean("encryptionKeysRegistry");
backupKeyStore = (AlfrescoKeyStoreImpl)ctx.getBean("backupKeyStore");
mainEncryptor = (DefaultEncryptor)ctx.getBean("encryptor");
backupEncryptor = (DefaultEncryptor)ctx.getBean("backupEncryptor");
// reencrypt in one txn (since we don't commit the model, the qnames won't be available across transactions) // reencrypt in one txn (since we don't commit the model, the qnames won't be available across transactions)
reEncryptor.setSplitTxns(false); reEncryptor.setSplitTxns(false);
@@ -162,18 +165,26 @@ public class EncryptionTests extends TestCase
{ {
return keys.get(keyAlias); return keys.get(keyAlias);
} }
@Override
public void refresh()
{
// nothing to do
}
}; };
return keyProvider; return keyProvider;
} }
protected Encryptor getEncryptor(KeyProvider keyProvider) protected Encryptor getFallbackEncryptor(KeyProvider keyProvider)
{ {
DefaultEncryptor encryptor = new DefaultEncryptor(); DefaultEncryptor encryptor = new DefaultEncryptor();
encryptor.setCipherAlgorithm(cipherAlgorithm); encryptor.setCipherAlgorithm(cipherAlgorithm);
encryptor.setCipherProvider(null); encryptor.setCipherProvider(null);
encryptor.setKeyProvider(keyProvider); encryptor.setKeyProvider(keyProvider);
DefaultFallbackEncryptor fallbackEncryptor = new DefaultFallbackEncryptor(encryptor, mainEncryptor);
return encryptor; return fallbackEncryptor;
} }
protected MetadataEncryptor getMetadataEncryptor(Encryptor encryptor) protected MetadataEncryptor getMetadataEncryptor(Encryptor encryptor)
@@ -185,7 +196,7 @@ public class EncryptionTests extends TestCase
return metadataEncryptor; return metadataEncryptor;
} }
protected void createEncryptedProperties(List<NodeRef> nodes, MetadataEncryptor metadataEncryptor) protected void createEncryptedProperties(List<NodeRef> nodes)
{ {
for(int i = 0; i < NUM_PROPERTIES; i++) for(int i = 0; i < NUM_PROPERTIES; i++)
{ {
@@ -218,20 +229,26 @@ public class EncryptionTests extends TestCase
public void testReEncrypt() public void testReEncrypt()
{ {
KeyProvider backupKeyProvider = backupEncryptor.getKeyProvider();
KeyProvider mainKeyProvider = mainEncryptor.getKeyProvider();
try try
{ {
// Create encrypted properties using the configured encryptor and key provider // Create encrypted properties using the configured encryptor and key provider
createEncryptedProperties(before, metadataEncryptor); createEncryptedProperties(before);
// Create encrypted properties using the new encryptor and key provider // Create encrypted properties using the new encryptor and key provider
KeyProvider newKeyProvider = getKeyProvider(newKeys); KeyProvider newKeyProvider = getKeyProvider(newKeys);
Encryptor newEncryptor = getEncryptor(newKeyProvider);
MetadataEncryptor newMetadataEncryptor = getMetadataEncryptor(newEncryptor); // set backup encryptor key provider to main encryptor key provider and drop in
createEncryptedProperties(after, newMetadataEncryptor); // new key provider for main encryptor
backupEncryptor.setKeyProvider(mainEncryptor.getKeyProvider());
mainEncryptor.setKeyProvider(newKeyProvider);
createEncryptedProperties(after);
// re-encrypt // re-encrypt
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
reEncryptor.reEncrypt(newKeyProvider); System.out.println(reEncryptor.reEncrypt() + " properties re-encrypted");
System.out.println("Re-encrypted " + NUM_PROPERTIES*2 + " properties in " + (System.currentTimeMillis() - start) + "ms"); System.out.println("Re-encrypted " + NUM_PROPERTIES*2 + " properties in " + (System.currentTimeMillis() - start) + "ms");
// check that the nodes have been re-encrypted properly i.e. check that the properties // check that the nodes have been re-encrypted properly i.e. check that the properties
@@ -239,7 +256,7 @@ public class EncryptionTests extends TestCase
for(NodeRef nodeRef : before) for(NodeRef nodeRef : before)
{ {
Map<QName, Serializable> props = nodeService.getProperties(nodeRef); Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
props = newMetadataEncryptor.decrypt(props); props = metadataEncryptor.decrypt(props);
assertNotNull("", props.get(PROP)); assertNotNull("", props.get(PROP));
assertEquals("", nodeRef.toString(), props.get(PROP)); assertEquals("", nodeRef.toString(), props.get(PROP));
} }
@@ -247,11 +264,15 @@ public class EncryptionTests extends TestCase
for(NodeRef nodeRef : after) for(NodeRef nodeRef : after)
{ {
Map<QName, Serializable> props = nodeService.getProperties(nodeRef); Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
props = newMetadataEncryptor.decrypt(props); props = metadataEncryptor.decrypt(props);
assertNotNull("", props.get(PROP)); assertNotNull("", props.get(PROP));
assertEquals("", nodeRef.toString(), props.get(PROP)); assertEquals("", nodeRef.toString(), props.get(PROP));
} }
} }
catch(MissingKeyStoreException e)
{
fail(e.getMessage());
}
catch(AlfrescoRuntimeException e) catch(AlfrescoRuntimeException e)
{ {
if(e.getCause() instanceof InvalidKeyException) if(e.getCause() instanceof InvalidKeyException)
@@ -260,12 +281,21 @@ public class EncryptionTests extends TestCase
fail(); fail();
} }
} }
finally
{
backupEncryptor.setKeyProvider(backupKeyProvider);
mainEncryptor.setKeyProvider(mainKeyProvider);
}
} }
public void testBootstrapReEncrypt() public void testBootstrapReEncrypt()
{ {
try try
{ {
// ensure that the backup key store is not available
backupKeyStoreParameters.setLocation("");
backupKeyStore.reload();
reEncryptor.bootstrapReEncrypt(); reEncryptor.bootstrapReEncrypt();
fail("Should have caught missing backup key store"); fail("Should have caught missing backup key store");
} }

View File

@@ -61,7 +61,7 @@ public class KeyStoreKeyProviderTest extends TestCase
passwords.put(AlfrescoKeyStore.KEY_KEYSTORE_PASSWORD, "ksPwd2"); passwords.put(AlfrescoKeyStore.KEY_KEYSTORE_PASSWORD, "ksPwd2");
passwords.put(ALIAS_ONE, "aliasPwd1"); passwords.put(ALIAS_ONE, "aliasPwd1");
passwords.put(ALIAS_TWO, "aliasPwd2"); passwords.put(ALIAS_TWO, "aliasPwd2");
KeyStoreParameters encryptionParameters = new KeyStoreParameters("JCEKS", "SunJCE", null, FILE_TWO); KeyStoreParameters encryptionParameters = new KeyStoreParameters("test", "JCEKS", "SunJCE", null, FILE_TWO);
KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(encryptionParameters, getKeyStoreLoader(passwords)); KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(encryptionParameters, getKeyStoreLoader(passwords));
// FILE_TWO, // FILE_TWO,
// getKeyStoreLoader(), // getKeyStoreLoader(),
@@ -77,7 +77,7 @@ public class KeyStoreKeyProviderTest extends TestCase
// passwords.put(KeyStoreManager.KEY_KEYSTORE_PASSWORD, "ksPwd2"); // passwords.put(KeyStoreManager.KEY_KEYSTORE_PASSWORD, "ksPwd2");
// passwords.put(ALIAS_ONE, "aliasPwd1"); // passwords.put(ALIAS_ONE, "aliasPwd1");
// passwords.put(ALIAS_TWO, "aliasPwd2"); // passwords.put(ALIAS_TWO, "aliasPwd2");
KeyStoreParameters encryptionParameters = new KeyStoreParameters("JCEKS", "SunJCE", null, keyStoreLocation); KeyStoreParameters encryptionParameters = new KeyStoreParameters("test", "JCEKS", "SunJCE", null, keyStoreLocation);
KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(encryptionParameters, getKeyStoreLoader(passwords)); KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(encryptionParameters, getKeyStoreLoader(passwords));
// FILE_TWO, // FILE_TWO,
// getKeyStoreLoader(), // getKeyStoreLoader(),

View File

@@ -35,11 +35,14 @@ import org.alfresco.repo.domain.node.NodePropertyEntity;
import org.alfresco.repo.domain.node.NodePropertyKey; import org.alfresco.repo.domain.node.NodePropertyKey;
import org.alfresco.repo.domain.node.NodePropertyValue; import org.alfresco.repo.domain.node.NodePropertyValue;
import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.lock.JobLockService;
import org.alfresco.repo.lock.LockAcquisitionException;
import org.alfresco.repo.node.encryption.MetadataEncryptor; import org.alfresco.repo.node.encryption.MetadataEncryptor;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; 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.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@@ -49,7 +52,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.I18NUtil;
// TODO lock so that only one encryptor can run at a time
/** /**
* Re-encrypts encryptable repository properties using a new set of encryption keys. * Re-encrypts encryptable repository properties using a new set of encryption keys.
* Decrypts the repository properties using the default encryptor, falling back to * Decrypts the repository properties using the default encryptor, falling back to
@@ -59,8 +61,8 @@ import org.springframework.extensions.surf.util.I18NUtil;
* Can run in one of two ways: * Can run in one of two ways:
* *
* <ul> * <ul>
* <li> during bootstrap (used by the community edition of the software) * <li> during bootstrap.
* <li> by using JMX. In this case, the system can stay running while the re-encryption takes place. * <li> by using JMX (available only to Enterprise). In this case, the system can stay running while the re-encryption takes place.
* </ul> * </ul>
* *
* @since 4.0 * @since 4.0
@@ -71,32 +73,28 @@ public class ReEncryptor implements ApplicationContextAware
private NodeDAO nodeDAO; private NodeDAO nodeDAO;
private DictionaryDAO dictionaryDAO; private DictionaryDAO dictionaryDAO;
private DictionaryService dictionaryService;
private TransactionService transactionService;
private QNameDAO qnameDAO; private QNameDAO qnameDAO;
private MetadataEncryptor metadataEncryptor; private MetadataEncryptor metadataEncryptor;
// private KeyStoreParameters keyStoreParameters;
// private KeyStoreParameters backupKeyStoreParameters;
private AlfrescoKeyStore backupKeyStore;
private AlfrescoKeyStore keyStore;
private KeyProvider backupKeyProvider; private KeyProvider backupKeyProvider;
private KeyProvider keyProvider; private KeyProvider keyProvider;
// private KeyResourceLoader keyResourceLoader;
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
private TransactionService transactionService;
private RetryingTransactionHelper transactionHelper; private RetryingTransactionHelper transactionHelper;
private String cipherAlgorithm;
private int chunkSize; private int chunkSize;
private boolean splitTxns = true; private boolean splitTxns = true;
private static final QName LOCK = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "OrphanReaper");
private JobLockService jobLockService;
/** /**
* Set the transaction provider so that each execution can be performed within a transaction * Set the transaction provider so that each execution can be performed within a transaction
*/ */
public void setTransactionService(TransactionService transactionService) public void setTransactionService(TransactionService transactionService)
{ {
this.transactionService = transactionService; this.transactionService = transactionService;
this.transactionHelper = transactionService.getRetryingTransactionHelper(); this.transactionHelper = transactionService.getRetryingTransactionHelper();
this.transactionHelper.setForceWritable(true); this.transactionHelper.setForceWritable(true);
} }
@@ -106,6 +104,16 @@ public class ReEncryptor implements ApplicationContextAware
this.metadataEncryptor = metadataEncryptor; this.metadataEncryptor = metadataEncryptor;
} }
public MetadataEncryptor getMetadataEncryptor()
{
return metadataEncryptor;
}
public void setJobLockService(JobLockService jobLockService)
{
this.jobLockService = jobLockService;
}
public void setChunkSize(int chunkSize) public void setChunkSize(int chunkSize)
{ {
this.chunkSize = chunkSize; this.chunkSize = chunkSize;
@@ -126,47 +134,11 @@ public class ReEncryptor implements ApplicationContextAware
this.dictionaryDAO = dictionaryDAO; this.dictionaryDAO = dictionaryDAO;
} }
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
public void setQnameDAO(QNameDAO qnameDAO) public void setQnameDAO(QNameDAO qnameDAO)
{ {
this.qnameDAO = qnameDAO; this.qnameDAO = qnameDAO;
} }
public void setCipherAlgorithm(String cipherAlgorithm)
{
this.cipherAlgorithm = cipherAlgorithm;
}
// public void setKeyStoreParameters(KeyStoreParameters keyStoreParameters)
// {
// this.keyStoreParameters = keyStoreParameters;
// }
//
// public void setBackupKeyStoreParameters(KeyStoreParameters backupKeyStoreParameters)
// {
// this.backupKeyStoreParameters = backupKeyStoreParameters;
// }
// protected KeyProvider getKeyProvider(AlfrescoKeyStore keyStore)
// {
// KeyProvider keyProvider = new KeystoreKeyProvider(keyStore);
// return keyProvider;
// }
public void setBackupKeyStore(AlfrescoKeyStore backupKeyStore)
{
this.backupKeyStore = backupKeyStore;
}
public void setKeyStore(AlfrescoKeyStore keyStore)
{
this.keyStore = keyStore;
}
public void setBackupKeyProvider(KeyProvider backupKeyProvider) public void setBackupKeyProvider(KeyProvider backupKeyProvider)
{ {
this.backupKeyProvider = backupKeyProvider; this.backupKeyProvider = backupKeyProvider;
@@ -176,87 +148,44 @@ public class ReEncryptor implements ApplicationContextAware
{ {
this.keyProvider = keyProvider; this.keyProvider = keyProvider;
} }
// public void setKeyResourceLoader(KeyResourceLoader keyResourceLoader)
// {
// this.keyResourceLoader = keyResourceLoader;
// }
// public MetadataEncryptor getMetadataEncryptor()
// {
// DefaultEncryptor backupEncryptor = new DefaultEncryptor();
// backupEncryptor.setCipherProvider(null); // TODO parameterize
// backupEncryptor.setCipherAlgorithm(cipherAlgorithm);
// backupEncryptor.setKeyProvider(backupKeyProvider);
//
// DefaultEncryptor encryptor = new DefaultEncryptor();
// encryptor.setCipherProvider(null); // TODO parameterize
// encryptor.setCipherAlgorithm(cipherAlgorithm);
// encryptor.setKeyProvider(keyProvider);
//
// DefaultFallbackEncryptor fallbackEncryptor = new DefaultFallbackEncryptor(encryptor, backupEncryptor);
// MetadataEncryptor metadataEncryptor = new MetadataEncryptor();
// metadataEncryptor.setEncryptor(fallbackEncryptor);
// metadataEncryptor.setDictionaryService(dictionaryService);
// return metadataEncryptor;
// }
//
// public MetadataEncryptor getMetadataEncryptor(KeyProvider backupKeyProvider, KeyProvider newKeyProvider)
// {
// DefaultEncryptor backupEncryptor = new DefaultEncryptor();
// backupEncryptor.setCipherProvider(null); // TODO parameterize
// backupEncryptor.setCipherAlgorithm(cipherAlgorithm);
// backupEncryptor.setKeyProvider(backupKeyProvider);
//
// DefaultEncryptor encryptor = new DefaultEncryptor();
// encryptor.setCipherProvider(null); // TODO parameterize
// encryptor.setCipherAlgorithm(cipherAlgorithm);
// encryptor.setKeyProvider(newKeyProvider);
//
// DefaultFallbackEncryptor fallbackEncryptor = new DefaultFallbackEncryptor(encryptor, backupEncryptor);
// MetadataEncryptor metadataEncryptor = new MetadataEncryptor();
// metadataEncryptor.setEncryptor(fallbackEncryptor);
// metadataEncryptor.setDictionaryService(dictionaryService);
// return metadataEncryptor;
// }
// protected KeyProvider getKeyProvider(final Map<String, Key> keys) /**
// { * Attempts to get the lock. If the lock couldn't be taken, then <tt>null</tt> is returned.
// KeyProvider keyProvider = new KeyProvider() *
// { * @return Returns the lock token or <tt>null</tt>
// @Override */
// public Key getKey(String keyAlias) private String getLock(long time)
// { {
// return keys.get(keyAlias); try
// } {
// }; return jobLockService.getLock(LOCK, time);
// return keyProvider; }
// } catch (LockAcquisitionException e)
{
return null;
}
}
protected Encryptor getEncryptor(KeyProvider keyProvider) /**
{ * Attempts to get the lock. If it fails, the current transaction is marked for rollback.
DefaultEncryptor encryptor = new DefaultEncryptor(); *
encryptor.setCipherProvider(null); // TODO parameterize * @return Returns the lock token
encryptor.setCipherAlgorithm(cipherAlgorithm); */
encryptor.setKeyProvider(keyProvider); private void refreshLock(String lockToken, long time)
{
if (lockToken == null)
{
throw new IllegalArgumentException("Must provide existing lockToken");
}
jobLockService.refreshLock(lockToken, LOCK, time);
}
return encryptor; protected void reEncryptProperties(final List<NodePropertyEntity> properties, final String lockToken)
}
/**
* For testing use.
*
* @param keyProvider
*/
void reEncrypt(KeyProvider keyProvider)
{
metadataEncryptor.setEncryptor(getEncryptor(keyProvider));
reEncryptImpl();
}
protected void reEncrypt(final MetadataEncryptor metadataEncryptor, final List<NodePropertyEntity> properties)
{ {
final Iterator<NodePropertyEntity> it = properties.iterator(); final Iterator<NodePropertyEntity> it = properties.iterator();
// TODO use BatchProcessWorkerAdaptor?
BatchProcessor.BatchProcessWorker<NodePropertyEntity> worker = new BatchProcessor.BatchProcessWorker<NodePropertyEntity>() BatchProcessor.BatchProcessWorker<NodePropertyEntity> worker = new BatchProcessor.BatchProcessWorker<NodePropertyEntity>()
{ {
public String getIdentifier(NodePropertyEntity entity) public String getIdentifier(NodePropertyEntity entity)
@@ -266,6 +195,7 @@ public class ReEncryptor implements ApplicationContextAware
public void beforeProcess() throws Throwable public void beforeProcess() throws Throwable
{ {
refreshLock(lockToken, chunkSize * 100L);
} }
public void afterProcess() throws Throwable public void afterProcess() throws Throwable
@@ -287,9 +217,9 @@ public class ReEncryptor implements ApplicationContextAware
// decrypt... // decrypt...
Serializable decrypted = metadataEncryptor.decrypt(propertyQName, sealed); Serializable decrypted = metadataEncryptor.decrypt(propertyQName, sealed);
// ...and then re-encrypt. The new keys will be used. // ...and then re-encrypt. The new key will be used.
Serializable resealed = metadataEncryptor.encrypt(propertyQName, decrypted); Serializable resealed = metadataEncryptor.encrypt(propertyQName, decrypted);
// TODO update resealed using batch update? // TODO update resealed using batch update?
// does the node DAO do batch updating? // does the node DAO do batch updating?
nodeDAO.setNodeProperties(entity.getNodeId(), Collections.singletonMap(propertyQName, resealed)); nodeDAO.setNodeProperties(entity.getNodeId(), Collections.singletonMap(propertyQName, resealed));
@@ -315,12 +245,16 @@ public class ReEncryptor implements ApplicationContextAware
@Override @Override
public Collection<NodePropertyEntity> getNextWork() public Collection<NodePropertyEntity> getNextWork()
{ {
int count = 0;
List<NodePropertyEntity> sublist = new ArrayList<NodePropertyEntity>(chunkSize); List<NodePropertyEntity> sublist = new ArrayList<NodePropertyEntity>(chunkSize);
while(it.hasNext() && count < chunkSize)
synchronized(it)
{ {
sublist.add(it.next()); int count = 0;
count++; while(it.hasNext() && count < chunkSize)
{
sublist.add(it.next());
count++;
}
} }
return sublist; return sublist;
@@ -329,12 +263,12 @@ public class ReEncryptor implements ApplicationContextAware
// TODO, "propertize" these numbers // TODO, "propertize" these numbers
new BatchProcessor<NodePropertyEntity>( new BatchProcessor<NodePropertyEntity>(
I18NUtil.getMessage(""), I18NUtil.getMessage("reencryptor.batchprocessor.name"), // TODO i18n name
transactionHelper, transactionHelper,
provider, provider,
2, 20, 2, 100,
applicationContext, applicationContext,
logger, 20).process(worker, splitTxns); logger, 100).process(worker, splitTxns);
} }
/** /**
@@ -342,7 +276,7 @@ public class ReEncryptor implements ApplicationContextAware
*/ */
public int bootstrapReEncrypt() throws MissingKeyStoreException public int bootstrapReEncrypt() throws MissingKeyStoreException
{ {
if(backupKeyStore.getKey(KeyProvider.ALIAS_METADATA) == null) if(backupKeyProvider.getKey(KeyProvider.ALIAS_METADATA) == null)
{ {
throw new MissingKeyStoreException("Backup key store is either not present or does not contain a metadata encryption key"); throw new MissingKeyStoreException("Backup key store is either not present or does not contain a metadata encryption key");
} }
@@ -353,39 +287,59 @@ public class ReEncryptor implements ApplicationContextAware
* Re-encrypt by decrypting using the configured keystore and encrypting using a keystore configured using the provided new key store parameters. * Re-encrypt by decrypting using the configured keystore and encrypting using a keystore configured using the provided new key store parameters.
* Called from e.g. JMX. * Called from e.g. JMX.
* *
* Note: it is the responsibility of the end user to ensure that the keystore configured by newKeyStoreParameters is * Note: it is the responsibility of the end user to ensure that the underlying keystores have been set up appropriately
* placed in the repository keystore directory. This can be done while the repository is running and it will be picked * i.e. the old key store is backed up to the location defined by the property '${dir.keystore}/backup-keystore' and the new
* up automatically the next time the repository restarts. * key store replaces it. This can be done while the repository is running.
*/ */
public int reEncrypt() throws MissingKeyStoreException public int reEncrypt() throws MissingKeyStoreException
{ {
backupKeyStore.reload(); // refresh the key providers to pick up changes made
keyStore.reload(); backupKeyProvider.refresh();
if(keyStore.getKey(KeyProvider.ALIAS_METADATA) == null) keyProvider.refresh();
if(keyProvider.getKey(KeyProvider.ALIAS_METADATA) == null)
{ {
throw new MissingKeyStoreException("Main key store is either not present or does not contain a metadata encryption key"); throw new MissingKeyStoreException("Main key store is either not present or does not contain a metadata encryption key");
} }
if(backupKeyStore.getKey(KeyProvider.ALIAS_METADATA) == null) if(backupKeyProvider.getKey(KeyProvider.ALIAS_METADATA) == null)
{ {
throw new MissingKeyStoreException("Backup key store is either not present or does not contain a metadata encryption key"); throw new MissingKeyStoreException("Backup key store is either not present or does not contain a metadata encryption key");
} }
return reEncryptImpl();
int numProps = reEncryptImpl();
return numProps;
} }
protected int reEncryptImpl() protected int reEncryptImpl()
{ {
// get properties that are encrypted RetryingTransactionCallback<String> txnWork = new RetryingTransactionCallback<String>()
{
public String execute() throws Exception
{
String lockToken = getLock(20000L);
return lockToken;
}
};
String lockToken = transactionService.getRetryingTransactionHelper().doInTransaction(txnWork, false, true);
if(lockToken == null)
{
logger.warn("Can't get lock. Assume multiple re-encryptors ...");
return 0;
}
// get encrypted properties
Collection<PropertyDefinition> propertyDefs = dictionaryDAO.getPropertiesOfDataType(DataTypeDefinition.ENCRYPTED); Collection<PropertyDefinition> propertyDefs = dictionaryDAO.getPropertiesOfDataType(DataTypeDefinition.ENCRYPTED);
// TODO use callback mechanism // TODO use callback mechanism, or select based on set of nodes?
List<NodePropertyEntity> properties = nodeDAO.selectProperties(propertyDefs); List<NodePropertyEntity> properties = nodeDAO.selectProperties(propertyDefs);
if(logger.isDebugEnabled()) if(logger.isDebugEnabled())
{ {
logger.debug("Found " + properties.size() + " properties to re-encrypt..."); logger.debug("Found " + properties.size() + " properties to re-encrypt...");
} }
// reencrypt these properties // reencrypt these properties TODO don't call if num props == 0
reEncrypt(metadataEncryptor, properties); reEncryptProperties(properties, lockToken);
if(logger.isDebugEnabled()) if(logger.isDebugEnabled())
{ {
@@ -396,8 +350,7 @@ public class ReEncryptor implements ApplicationContextAware
} }
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
throws BeansException
{ {
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
} }

View File

@@ -7,6 +7,7 @@
<imports> <imports>
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/> <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
</imports> </imports>
<namespaces> <namespaces>
@@ -23,7 +24,7 @@
<type name="test:base"> <type name="test:base">
<title>Base</title> <title>Base</title>
<description>The Base Type</description> <description>The Base Type</description>
<parent></parent> <parent>cm:content</parent>
<properties> <properties>
<property name="test:prop1"> <property name="test:prop1">

View File

@@ -42,8 +42,11 @@ import org.alfresco.service.cmr.search.SearchParameters.FieldFacetSort;
import org.alfresco.service.cmr.search.SearchParameters.SortDefinition; import org.alfresco.service.cmr.search.SearchParameters.SortDefinition;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.apache.commons.codec.net.URLCodec; import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
@@ -303,13 +306,23 @@ public class SolrQueryHTTPClient
body.put("textAttributes", textAttributes); body.put("textAttributes", textAttributes);
PostMethod post = new PostMethod(url.toString()); PostMethod post = new PostMethod(url.toString());
// TOOD deal with redirects for SSL
post.setRequestEntity(new ByteArrayRequestEntity(body.toString().getBytes("UTF-8"), "application/json")); post.setRequestEntity(new ByteArrayRequestEntity(body.toString().getBytes("UTF-8"), "application/json"));
try try
{ {
httpClient.executeMethod(post); httpClient.executeMethod(post);
if(post.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY || post.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY)
{
Header locationHeader = post.getResponseHeader("location");
if (locationHeader != null)
{
String redirectLocation = locationHeader.getValue();
post.setURI(new URI(redirectLocation, true));
httpClient.executeMethod(post);
}
}
if (post.getStatusCode() != HttpServletResponse.SC_OK) if (post.getStatusCode() != HttpServletResponse.SC_OK)
{ {
throw new LuceneQueryParserException("Request failed " + post.getStatusCode() + " " + url.toString()); throw new LuceneQueryParserException("Request failed " + post.getStatusCode() + " " + url.toString());