From 8fddcdfd1b1e3e7a50098f2415fe33af4f74d4a2 Mon Sep 17 00:00:00 2001 From: Steven Glover Date: Fri, 2 Sep 2011 16:29:39 +0000 Subject: [PATCH] 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 --- config/alfresco/.keystore | Bin 1254 -> 0 bytes config/alfresco/core-services-context.xml | 2 +- config/alfresco/encryption-context.xml | 34 ++- config/alfresco/keystore/browser.p12 | Bin 0 -> 2648 bytes config/alfresco/keystore/keystore | Bin 1254 -> 645 bytes .../keystore/keystore-passwords.properties | 6 +- .../messages/system-messages.properties | 3 + config/alfresco/node-services-context.xml | 13 +- .../alfresco/encryption/EncryptionTests.java | 58 +++- .../encryption/KeyStoreKeyProviderTest.java | 4 +- .../org/alfresco/encryption/ReEncryptor.java | 253 +++++++----------- .../encryption/reencryption_model.xml | 3 +- .../search/impl/solr/SolrQueryHTTPClient.java | 15 +- 13 files changed, 202 insertions(+), 189 deletions(-) delete mode 100644 config/alfresco/.keystore create mode 100644 config/alfresco/keystore/browser.p12 diff --git a/config/alfresco/.keystore b/config/alfresco/.keystore deleted file mode 100644 index 7e966eebae5433614e59d7d350981be1add2c621..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1254 zcmX?i?%X*B1_mZ5W@g~XO)W`GNi0cZU|=+uyYa`PHE$VMi;EbHlk;=+ic9nKl8Y(} zO7iszit@`cQ&Nlcf>RT7Qd9hsvQm>v-13XOQ!4|C@=Ji6{Gzkrry`#l><$K+UQxs# znUz?USOL?Itj*5McG=ryDXA#|iA9OI#U%_((N56}0zg5Ky{ReA z`FSO&c_oDmbqq{C44egERgO97B@9A7APe+!67$magG-7s^U|$-7}$$}&I3w=)H7EU zloo-^Tk~1`2OCGo1CYB4fIjCp;AP=pS5Aw%-N{guQEH^W^G zGA`K^em?L$Xi0v?^1_Ct`=sU^zGjqpT<3(u1>-eaX6t_n{P^$B>KTs0et%MUKeH@Y zlO1F9I_AgAA3S&e-iU2i7B=}9Cc=2(*!Pa*Uu)y|JPXM22Bnx)^qpIpb^sT{NW#a<_@di%}#`RkSxhgsY+Wzx-=^7O$98^y=z z`$TgNO%OOx_ix|i8-@k-OTLNlWfaHwBu3VBxX%l@#iCroAQs@{8lG8_;p<}Rn3ob# zlv$9I>f#!VQvw*=%nU5W`8h?v@HOOIU>7`65}4hu%G;|B*%zQBFn4*4>~|TlPa7H5 zDF5Hxm2mo5gM)VKO}`1|AN_SV?p3z z^y~KhjZcG4s_e^sBD*j@y<_HrjUG!%8B^;v@EpyQN?6>Z{c29LHp?~T`_JcX_4z1v zURCyy+twEw9hTZA%l_NGHD+=%Q^1_e<=eQHO? - + diff --git a/config/alfresco/encryption-context.xml b/config/alfresco/encryption-context.xml index b05dccd1bb..219741adec 100644 --- a/config/alfresco/encryption-context.xml +++ b/config/alfresco/encryption-context.xml @@ -11,7 +11,9 @@ + + @@ -19,6 +21,7 @@ + @@ -30,6 +33,21 @@ + + + + + + + + + + @@ -40,6 +58,7 @@ + @@ -56,6 +75,7 @@ + @@ -86,20 +106,6 @@ - - - - - - - - - - - - - - diff --git a/config/alfresco/keystore/browser.p12 b/config/alfresco/keystore/browser.p12 new file mode 100644 index 0000000000000000000000000000000000000000..3288f49129376d70855c9a169a762ced695b87fd GIT binary patch literal 2648 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 a0x}e_lt#oHhZsNCVyf39Ep}9?C1Nos%CP(Z diff --git a/config/alfresco/keystore/keystore-passwords.properties b/config/alfresco/keystore/keystore-passwords.properties index 06a5dd76ec..cbdb6fb66c 100644 --- a/config/alfresco/keystore/keystore-passwords.properties +++ b/config/alfresco/keystore/keystore-passwords.properties @@ -4,8 +4,4 @@ keystore.password=mp6yc0UD9e # The password protecting the alias: metadata metadata.keyData= metadata.algorithm=DESede -metadata.password=oKIWzVdEdA -# The password protecting the alias: solr -#solr.keyData= -#solr.algorithm=DESede -#solr.password=TxHTtOnrwQ \ No newline at end of file +metadata.password=oKIWzVdEdA \ No newline at end of file diff --git a/config/alfresco/messages/system-messages.properties b/config/alfresco/messages/system-messages.properties index 03e1508c2e..3bda362996 100644 --- a/config/alfresco/messages/system-messages.properties +++ b/config/alfresco/messages/system-messages.properties @@ -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_readwrite=RepoUsageComponent must be called in a read-write transaction. +# Re-encryptor +reencryptor.batchprocessor.name=Reencryptor + # 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_reached=The allowable user limit of {0} has been reached. There are {1} users in the system. diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index 33361054f9..b2a06e01ca 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -249,5 +249,16 @@ - + + + + + + + + + + + + diff --git a/source/java/org/alfresco/encryption/EncryptionTests.java b/source/java/org/alfresco/encryption/EncryptionTests.java index e5874bd0ca..38a28edf49 100644 --- a/source/java/org/alfresco/encryption/EncryptionTests.java +++ b/source/java/org/alfresco/encryption/EncryptionTests.java @@ -56,7 +56,6 @@ import org.springframework.context.ApplicationContext; public class EncryptionTests extends TestCase { 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 ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); @@ -72,11 +71,13 @@ public class EncryptionTests extends TestCase private MetadataEncryptor metadataEncryptor; private ReEncryptor reEncryptor; private String cipherAlgorithm = "DESede/CBC/PKCS5Padding"; - private KeyStoreParameters keyStoreParameters; private KeyStoreParameters backupKeyStoreParameters; + private AlfrescoKeyStoreImpl backupKeyStore; private KeyResourceLoader keyResourceLoader; private EncryptionKeysRegistryImpl encryptionKeysRegistry; private KeyStoreChecker keyStoreChecker; + private DefaultEncryptor mainEncryptor; + private DefaultEncryptor backupEncryptor; private AuthenticationComponent authenticationComponent; private DictionaryDAO dictionaryDAO; @@ -102,8 +103,10 @@ public class EncryptionTests extends TestCase reEncryptor = (ReEncryptor)ctx.getBean("reEncryptor"); backupKeyStoreParameters = (KeyStoreParameters)ctx.getBean("backupKeyStoreParameters"); keyStoreChecker = (KeyStoreChecker)ctx.getBean("keyStoreChecker"); - keyStoreParameters = (KeyStoreParameters)ctx.getBean("keyStoreParameters"); 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) reEncryptor.setSplitTxns(false); @@ -162,18 +165,26 @@ public class EncryptionTests extends TestCase { return keys.get(keyAlias); } + + @Override + public void refresh() + { + // nothing to do + } }; return keyProvider; } - protected Encryptor getEncryptor(KeyProvider keyProvider) + protected Encryptor getFallbackEncryptor(KeyProvider keyProvider) { DefaultEncryptor encryptor = new DefaultEncryptor(); encryptor.setCipherAlgorithm(cipherAlgorithm); encryptor.setCipherProvider(null); encryptor.setKeyProvider(keyProvider); + + DefaultFallbackEncryptor fallbackEncryptor = new DefaultFallbackEncryptor(encryptor, mainEncryptor); - return encryptor; + return fallbackEncryptor; } protected MetadataEncryptor getMetadataEncryptor(Encryptor encryptor) @@ -185,7 +196,7 @@ public class EncryptionTests extends TestCase return metadataEncryptor; } - protected void createEncryptedProperties(List nodes, MetadataEncryptor metadataEncryptor) + protected void createEncryptedProperties(List nodes) { for(int i = 0; i < NUM_PROPERTIES; i++) { @@ -218,20 +229,26 @@ public class EncryptionTests extends TestCase public void testReEncrypt() { + KeyProvider backupKeyProvider = backupEncryptor.getKeyProvider(); + KeyProvider mainKeyProvider = mainEncryptor.getKeyProvider(); try { // 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 KeyProvider newKeyProvider = getKeyProvider(newKeys); - Encryptor newEncryptor = getEncryptor(newKeyProvider); - MetadataEncryptor newMetadataEncryptor = getMetadataEncryptor(newEncryptor); - createEncryptedProperties(after, newMetadataEncryptor); - + + // set backup encryptor key provider to main encryptor key provider and drop in + // new key provider for main encryptor + backupEncryptor.setKeyProvider(mainEncryptor.getKeyProvider()); + mainEncryptor.setKeyProvider(newKeyProvider); + + createEncryptedProperties(after); + // re-encrypt 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"); // 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) { Map props = nodeService.getProperties(nodeRef); - props = newMetadataEncryptor.decrypt(props); + props = metadataEncryptor.decrypt(props); assertNotNull("", props.get(PROP)); assertEquals("", nodeRef.toString(), props.get(PROP)); } @@ -247,11 +264,15 @@ public class EncryptionTests extends TestCase for(NodeRef nodeRef : after) { Map props = nodeService.getProperties(nodeRef); - props = newMetadataEncryptor.decrypt(props); + props = metadataEncryptor.decrypt(props); assertNotNull("", props.get(PROP)); assertEquals("", nodeRef.toString(), props.get(PROP)); } } + catch(MissingKeyStoreException e) + { + fail(e.getMessage()); + } catch(AlfrescoRuntimeException e) { if(e.getCause() instanceof InvalidKeyException) @@ -260,12 +281,21 @@ public class EncryptionTests extends TestCase fail(); } } + finally + { + backupEncryptor.setKeyProvider(backupKeyProvider); + mainEncryptor.setKeyProvider(mainKeyProvider); + } } public void testBootstrapReEncrypt() { try { + // ensure that the backup key store is not available + backupKeyStoreParameters.setLocation(""); + backupKeyStore.reload(); + reEncryptor.bootstrapReEncrypt(); fail("Should have caught missing backup key store"); } diff --git a/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java b/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java index a764bdd4ad..8f782bdf7f 100644 --- a/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java +++ b/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java @@ -61,7 +61,7 @@ public class KeyStoreKeyProviderTest extends TestCase passwords.put(AlfrescoKeyStore.KEY_KEYSTORE_PASSWORD, "ksPwd2"); passwords.put(ALIAS_ONE, "aliasPwd1"); 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)); // FILE_TWO, // getKeyStoreLoader(), @@ -77,7 +77,7 @@ public class KeyStoreKeyProviderTest extends TestCase // 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); + KeyStoreParameters encryptionParameters = new KeyStoreParameters("test", "JCEKS", "SunJCE", null, keyStoreLocation); KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(encryptionParameters, getKeyStoreLoader(passwords)); // FILE_TWO, // getKeyStoreLoader(), diff --git a/source/java/org/alfresco/encryption/ReEncryptor.java b/source/java/org/alfresco/encryption/ReEncryptor.java index a1299e4a29..9a250c1637 100644 --- a/source/java/org/alfresco/encryption/ReEncryptor.java +++ b/source/java/org/alfresco/encryption/ReEncryptor.java @@ -35,11 +35,14 @@ import org.alfresco.repo.domain.node.NodePropertyEntity; import org.alfresco.repo.domain.node.NodePropertyKey; import org.alfresco.repo.domain.node.NodePropertyValue; 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.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; 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; @@ -49,7 +52,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; 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. * 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: * *
    - *
  • during bootstrap (used by the community edition of the software) - *
  • by using JMX. In this case, the system can stay running while the re-encryption takes place. + *
  • during bootstrap. + *
  • by using JMX (available only to Enterprise). In this case, the system can stay running while the re-encryption takes place. *
* * @since 4.0 @@ -71,32 +73,28 @@ public class ReEncryptor implements ApplicationContextAware private NodeDAO nodeDAO; private DictionaryDAO dictionaryDAO; - private DictionaryService dictionaryService; - private TransactionService transactionService; private QNameDAO qnameDAO; private MetadataEncryptor metadataEncryptor; -// private KeyStoreParameters keyStoreParameters; -// private KeyStoreParameters backupKeyStoreParameters; - private AlfrescoKeyStore backupKeyStore; - private AlfrescoKeyStore keyStore; private KeyProvider backupKeyProvider; private KeyProvider keyProvider; -// private KeyResourceLoader keyResourceLoader; private ApplicationContext applicationContext; + private TransactionService transactionService; private RetryingTransactionHelper transactionHelper; - private String cipherAlgorithm; private int chunkSize; 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 */ public void setTransactionService(TransactionService transactionService) { - this.transactionService = transactionService; + this.transactionService = transactionService; this.transactionHelper = transactionService.getRetryingTransactionHelper(); this.transactionHelper.setForceWritable(true); } @@ -106,6 +104,16 @@ public class ReEncryptor implements ApplicationContextAware this.metadataEncryptor = metadataEncryptor; } + public MetadataEncryptor getMetadataEncryptor() + { + return metadataEncryptor; + } + + public void setJobLockService(JobLockService jobLockService) + { + this.jobLockService = jobLockService; + } + public void setChunkSize(int chunkSize) { this.chunkSize = chunkSize; @@ -126,47 +134,11 @@ public class ReEncryptor implements ApplicationContextAware this.dictionaryDAO = dictionaryDAO; } - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - public void setQnameDAO(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) { this.backupKeyProvider = backupKeyProvider; @@ -176,87 +148,44 @@ public class ReEncryptor implements ApplicationContextAware { 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 keys) -// { -// KeyProvider keyProvider = new KeyProvider() -// { -// @Override -// public Key getKey(String keyAlias) -// { -// return keys.get(keyAlias); -// } -// }; -// return keyProvider; -// } + /** + * Attempts to get the lock. If the lock couldn't be taken, then null is returned. + * + * @return Returns the lock token or null + */ + private String getLock(long time) + { + try + { + return jobLockService.getLock(LOCK, time); + } + catch (LockAcquisitionException e) + { + return null; + } + } - protected Encryptor getEncryptor(KeyProvider keyProvider) - { - DefaultEncryptor encryptor = new DefaultEncryptor(); - encryptor.setCipherProvider(null); // TODO parameterize - encryptor.setCipherAlgorithm(cipherAlgorithm); - encryptor.setKeyProvider(keyProvider); + /** + * Attempts to get the lock. If it fails, the current transaction is marked for rollback. + * + * @return Returns the lock token + */ + private void refreshLock(String lockToken, long time) + { + if (lockToken == null) + { + throw new IllegalArgumentException("Must provide existing lockToken"); + } + jobLockService.refreshLock(lockToken, LOCK, time); + } - return encryptor; - } - - /** - * For testing use. - * - * @param keyProvider - */ - void reEncrypt(KeyProvider keyProvider) - { - metadataEncryptor.setEncryptor(getEncryptor(keyProvider)); - reEncryptImpl(); - } - - protected void reEncrypt(final MetadataEncryptor metadataEncryptor, final List properties) + protected void reEncryptProperties(final List properties, final String lockToken) { final Iterator it = properties.iterator(); + + // TODO use BatchProcessWorkerAdaptor? + BatchProcessor.BatchProcessWorker worker = new BatchProcessor.BatchProcessWorker() { public String getIdentifier(NodePropertyEntity entity) @@ -266,6 +195,7 @@ public class ReEncryptor implements ApplicationContextAware public void beforeProcess() throws Throwable { + refreshLock(lockToken, chunkSize * 100L); } public void afterProcess() throws Throwable @@ -287,9 +217,9 @@ public class ReEncryptor implements ApplicationContextAware // decrypt... 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); - + // TODO update resealed using batch update? // does the node DAO do batch updating? nodeDAO.setNodeProperties(entity.getNodeId(), Collections.singletonMap(propertyQName, resealed)); @@ -315,12 +245,16 @@ public class ReEncryptor implements ApplicationContextAware @Override public Collection getNextWork() { - int count = 0; List sublist = new ArrayList(chunkSize); - while(it.hasNext() && count < chunkSize) + + synchronized(it) { - sublist.add(it.next()); - count++; + int count = 0; + while(it.hasNext() && count < chunkSize) + { + sublist.add(it.next()); + count++; + } } return sublist; @@ -329,12 +263,12 @@ public class ReEncryptor implements ApplicationContextAware // TODO, "propertize" these numbers new BatchProcessor( - I18NUtil.getMessage(""), + I18NUtil.getMessage("reencryptor.batchprocessor.name"), // TODO i18n name transactionHelper, provider, - 2, 20, + 2, 100, 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 { - 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"); } @@ -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. * Called from e.g. JMX. * - * Note: it is the responsibility of the end user to ensure that the keystore configured by newKeyStoreParameters is - * placed in the repository keystore directory. This can be done while the repository is running and it will be picked - * up automatically the next time the repository restarts. + * Note: it is the responsibility of the end user to ensure that the underlying keystores have been set up appropriately + * i.e. the old key store is backed up to the location defined by the property '${dir.keystore}/backup-keystore' and the new + * key store replaces it. This can be done while the repository is running. */ public int reEncrypt() throws MissingKeyStoreException { - backupKeyStore.reload(); - keyStore.reload(); - if(keyStore.getKey(KeyProvider.ALIAS_METADATA) == null) + // refresh the key providers to pick up changes made + backupKeyProvider.refresh(); + 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"); } - 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"); } - return reEncryptImpl(); + + int numProps = reEncryptImpl(); + return numProps; } protected int reEncryptImpl() { - // get properties that are encrypted + RetryingTransactionCallback txnWork = new RetryingTransactionCallback() + { + 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 propertyDefs = dictionaryDAO.getPropertiesOfDataType(DataTypeDefinition.ENCRYPTED); - // TODO use callback mechanism + // TODO use callback mechanism, or select based on set of nodes? List properties = nodeDAO.selectProperties(propertyDefs); - + if(logger.isDebugEnabled()) { logger.debug("Found " + properties.size() + " properties to re-encrypt..."); } - // reencrypt these properties - reEncrypt(metadataEncryptor, properties); + // reencrypt these properties TODO don't call if num props == 0 + reEncryptProperties(properties, lockToken); if(logger.isDebugEnabled()) { @@ -396,8 +350,7 @@ public class ReEncryptor implements ApplicationContextAware } @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } diff --git a/source/java/org/alfresco/encryption/reencryption_model.xml b/source/java/org/alfresco/encryption/reencryption_model.xml index 4850508c10..762ab23bc8 100644 --- a/source/java/org/alfresco/encryption/reencryption_model.xml +++ b/source/java/org/alfresco/encryption/reencryption_model.xml @@ -7,6 +7,7 @@ + @@ -23,7 +24,7 @@ Base The Base Type - + cm:content 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 173ca8bbde..1d807e1aab 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java @@ -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.security.PermissionService; import org.apache.commons.codec.net.URLCodec; +import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; 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.auth.AuthScope; import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; @@ -303,13 +306,23 @@ public class SolrQueryHTTPClient body.put("textAttributes", textAttributes); PostMethod post = new PostMethod(url.toString()); - // TOOD deal with redirects for SSL post.setRequestEntity(new ByteArrayRequestEntity(body.toString().getBytes("UTF-8"), "application/json")); try { 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) { throw new LuceneQueryParserException("Request failed " + post.getStatusCode() + " " + url.toString());