diff --git a/config/alfresco/.keystore b/config/alfresco/.keystore
index 01bd383454..22d2b69a41 100644
Binary files a/config/alfresco/.keystore and b/config/alfresco/.keystore differ
diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml
index d479ed536d..453ce4b47b 100644
--- a/config/alfresco/authentication-services-context.xml
+++ b/config/alfresco/authentication-services-context.xml
@@ -375,7 +375,6 @@
-
diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml
index b5f10c3fe0..f2f1dc9a98 100644
--- a/config/alfresco/dao/dao-context.xml
+++ b/config/alfresco/dao/dao-context.xml
@@ -90,7 +90,6 @@
-
diff --git a/config/alfresco/encryption-context.xml b/config/alfresco/encryption-context.xml
index 1aeecdd71d..3c2e93cd09 100644
--- a/config/alfresco/encryption-context.xml
+++ b/config/alfresco/encryption-context.xml
@@ -17,8 +17,8 @@
diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml
index fa96d86121..3f103c2a20 100644
--- a/config/alfresco/model-specific-services-context.xml
+++ b/config/alfresco/model-specific-services-context.xml
@@ -148,7 +148,6 @@
-
diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml
index 856d9f5f7c..c7bbe87169 100644
--- a/config/alfresco/node-services-context.xml
+++ b/config/alfresco/node-services-context.xml
@@ -248,4 +248,10 @@
+
+
+
+
+
+
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index 44b0bac3b6..fa3d6aa0ac 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -640,8 +640,8 @@ encryption.keystore.location=classpath:alfresco/.keystore
encryption.keystore.provider=
encryption.keystore.type=JCEKS
# The password protecting the keystore entries
-encryption.keystore.password=ksPwd1
-# The password protecting the alias: METADATA
-encryption.keystore.password.METADATA=${encryption.keystore.password}
-# The password protecting the alias: SOLR
-encryption.keystore.password.SOLR=${encryption.keystore.password}
\ No newline at end of file
+encryption.keystore.password=mp6yc0UD9e
+# The password protecting the alias: metadata
+encryption.keystore.password.metadata=oKIWzVdEdA
+# The password protecting the alias: solr
+encryption.keystore.password.solr=TxHTtOnrwQ
\ No newline at end of file
diff --git a/source/java/org/alfresco/cmis/mapping/CMISMapping.java b/source/java/org/alfresco/cmis/mapping/CMISMapping.java
index 9f7c76d779..96f7398157 100644
--- a/source/java/org/alfresco/cmis/mapping/CMISMapping.java
+++ b/source/java/org/alfresco/cmis/mapping/CMISMapping.java
@@ -144,6 +144,7 @@ public class CMISMapping implements InitializingBean
//
mapAlfrescoToCmisDataType.put(DataTypeDefinition.ANY, null);
+ mapAlfrescoToCmisDataType.put(DataTypeDefinition.ENCRYPTED, null);
mapAlfrescoToCmisDataType.put(DataTypeDefinition.ASSOC_REF, null);
mapAlfrescoToCmisDataType.put(DataTypeDefinition.BOOLEAN, CMISDataTypeEnum.BOOLEAN);
mapAlfrescoToCmisDataType.put(DataTypeDefinition.CATEGORY, CMISDataTypeEnum.ID);
diff --git a/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java b/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java
index 2002f76892..db7c3a3cd7 100644
--- a/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java
+++ b/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java
@@ -56,6 +56,7 @@ public class DataTypeMap
dataTypeToPropertyType.put(DataTypeDefinition.NODE_REF, PropertyType.REFERENCE);
dataTypeToPropertyType.put(DataTypeDefinition.PATH, PropertyType.PATH);
dataTypeToPropertyType.put(DataTypeDefinition.ANY, PropertyType.UNDEFINED);
+ dataTypeToPropertyType.put(DataTypeDefinition.ENCRYPTED, PropertyType.UNDEFINED);
dataTypeToPropertyType.put(DataTypeDefinition.LOCALE, PropertyType.STRING);
}
diff --git a/source/java/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java b/source/java/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java
index 5d0d49bed4..94339e3193 100644
--- a/source/java/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java
+++ b/source/java/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java
@@ -354,31 +354,6 @@ public class RepoDictionaryDAOTest extends TestCase
propertyDef.isMandatoryEnforced());
}
- public void testEncrypted()
- {
- // get the properties for the test type
- QName testEncryptedQName = QName.createQName(TEST_URL, "encrypted");
- ClassDefinition testEncryptedClassDef = service.getClass(testEncryptedQName);
- Map testEncryptedPropertyDefs = testEncryptedClassDef.getProperties();
-
- PropertyDefinition propertyDef = null;
-
- QName testTextEncryptedQName = QName.createQName(TEST_URL, "text_encrypted");
- propertyDef = testEncryptedPropertyDefs.get(testTextEncryptedQName);
- assertNotNull("Property not found: " + testTextEncryptedQName,
- propertyDef);
- assertTrue("Expected property to be encrypted: " + testTextEncryptedQName,
- propertyDef.isEncrypted());
-
- QName testMLTextEncryptedQName = QName.createQName(TEST_URL, "mltext_encrypted");
- propertyDef = testEncryptedPropertyDefs.get(testMLTextEncryptedQName);
- assertNotNull("Property not found: " + testMLTextEncryptedQName,
- propertyDef);
- assertTrue("Expected property to be encrypted: " + testMLTextEncryptedQName,
- propertyDef.isEncrypted());
-// TODO test for encrypted == false?
- }
-
public void testSubClassOf()
{
QName invalid = QName.createQName(TEST_URL, "invalid");
diff --git a/source/java/org/alfresco/repo/domain/PropertyValue.java b/source/java/org/alfresco/repo/domain/PropertyValue.java
index d1d0024cd4..3b7d4a8f27 100644
--- a/source/java/org/alfresco/repo/domain/PropertyValue.java
+++ b/source/java/org/alfresco/repo/domain/PropertyValue.java
@@ -592,6 +592,7 @@ public class PropertyValue implements Cloneable, Serializable
{
valueTypesByPropertyType = new HashMap(37);
valueTypesByPropertyType.put(DataTypeDefinition.ANY, ValueType.SERIALIZABLE);
+ valueTypesByPropertyType.put(DataTypeDefinition.ENCRYPTED, ValueType.SERIALIZABLE);
valueTypesByPropertyType.put(DataTypeDefinition.BOOLEAN, ValueType.BOOLEAN);
valueTypesByPropertyType.put(DataTypeDefinition.INT, ValueType.INTEGER);
valueTypesByPropertyType.put(DataTypeDefinition.LONG, ValueType.LONG);
diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
index 9f1a7d013e..8996c57620 100644
--- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
@@ -52,7 +52,6 @@ import org.alfresco.repo.domain.permissions.AclDAO;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.usage.UsageDAO;
import org.alfresco.repo.policy.BehaviourFilter;
-import org.alfresco.repo.security.encryption.Encryptor;
import org.alfresco.repo.security.permissions.AccessControlListProperties;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
@@ -135,7 +134,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
private ContentDataDAO contentDataDAO;
private LocaleDAO localeDAO;
private UsageDAO usageDAO;
- private Encryptor encryptor;
/**
* Cache for the Store root nodes by StoreRef:
@@ -217,14 +215,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
this.dictionaryService = dictionaryService;
}
- /**
- * @param encryptor helper to do symmetric property encryption
- */
- public void setEncryptor(Encryptor encryptor)
- {
- this.encryptor = encryptor;
- }
-
/**
* @param policyBehaviourFilter the service to determine the behaviour for cm:auditable and
* other inherent capabilities.
@@ -370,9 +360,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
PropertyCheck.mandatory(this, "contentDataDAO", contentDataDAO);
PropertyCheck.mandatory(this, "localeDAO", localeDAO);
PropertyCheck.mandatory(this, "usageDAO", usageDAO);
- PropertyCheck.mandatory(this, "encryptor", encryptor);
- this.nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptor);
+ this.nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO);
}
/*
diff --git a/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java b/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java
index 9c3715d88e..fb7789e969 100644
--- a/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java
+++ b/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java
@@ -32,7 +32,6 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.domain.contentdata.ContentDataDAO;
import org.alfresco.repo.domain.locale.LocaleDAO;
import org.alfresco.repo.domain.qname.QNameDAO;
-import org.alfresco.repo.security.encryption.Encryptor;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -59,7 +58,6 @@ public class NodePropertyHelper
private static final Log logger = LogFactory.getLog(NodePropertyHelper.class);
private final DictionaryService dictionaryService;
- private final Encryptor encryptor;
private final QNameDAO qnameDAO;
private final LocaleDAO localeDAO;
private final ContentDataDAO contentDataDAO;
@@ -71,14 +69,12 @@ public class NodePropertyHelper
DictionaryService dictionaryService,
QNameDAO qnameDAO,
LocaleDAO localeDAO,
- ContentDataDAO contentDataDAO,
- Encryptor encryptor)
+ ContentDataDAO contentDataDAO)
{
this.dictionaryService = dictionaryService;
this.qnameDAO = qnameDAO;
this.localeDAO = localeDAO;
this.contentDataDAO = contentDataDAO;
- this.encryptor = encryptor;
}
public Map convertToPersistentProperties(Map in)
@@ -147,19 +143,16 @@ public class NodePropertyHelper
// Get or spoof the property datatype
QName propertyTypeQName;
- boolean isEncrypted;
if (propertyDef == null) // property not recognised
{
// allow it for now - persisting excess properties can be useful sometimes
propertyTypeQName = DataTypeDefinition.ANY;
- isEncrypted = false;
}
else
{
propertyTypeQName = propertyDef.getDataType().getName();
- isEncrypted = propertyDef.isEncrypted();
}
-
+
// A property may appear to be multi-valued if the model definition is loose and
// an unexploded collection is passed in. Otherwise, use the model-defined behaviour
// strictly.
@@ -651,17 +644,14 @@ public class NodePropertyHelper
}
// get property attributes
final QName propertyTypeQName;
- boolean isEncrypted;
if (propertyDef == null)
{
// allow this for now
propertyTypeQName = DataTypeDefinition.ANY;
- isEncrypted = false;
}
else
{
propertyTypeQName = propertyDef.getDataType().getName();
- isEncrypted = propertyDef.isEncrypted();
}
try
{
diff --git a/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java b/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java
index 6f58c9eb9d..7385b132c6 100644
--- a/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java
+++ b/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java
@@ -32,7 +32,6 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.contentdata.ContentDataDAO;
import org.alfresco.repo.domain.locale.LocaleDAO;
import org.alfresco.repo.domain.qname.QNameDAO;
-import org.alfresco.repo.security.encryption.Encryptor;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.version.VersionModel;
@@ -87,9 +86,8 @@ public class NodePropertyHelperTest extends TestCase
QNameDAO qnameDAO = (QNameDAO) ctx.getBean("qnameDAO");
LocaleDAO localeDAO = (LocaleDAO) ctx.getBean("localeDAO");
ContentDataDAO contentDataDAO = (ContentDataDAO) ctx.getBean("contentDataDAO");
- Encryptor encryptor = (Encryptor) ctx.getBean("encryptor");
- helper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptor);
+ helper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO);
transactionService = serviceRegistry.getTransactionService();
txnHelper = transactionService.getRetryingTransactionHelper();
txnHelper.setMinRetryWaitMs(10);
diff --git a/source/java/org/alfresco/repo/domain/node/NodePropertyValue.java b/source/java/org/alfresco/repo/domain/node/NodePropertyValue.java
index 5d8fbd50ae..7f086c6d98 100644
--- a/source/java/org/alfresco/repo/domain/node/NodePropertyValue.java
+++ b/source/java/org/alfresco/repo/domain/node/NodePropertyValue.java
@@ -553,7 +553,7 @@ public class NodePropertyValue implements Cloneable, Serializable
}
else
{
- throw new IllegalArgumentException("SealedObject value not supported: " + value);
+ throw new IllegalArgumentException("Encrypted properties must be encrypted by the client.");
}
}
},
@@ -689,6 +689,7 @@ public class NodePropertyValue implements Cloneable, Serializable
{
valueTypesByPropertyType = new HashMap(37);
valueTypesByPropertyType.put(DataTypeDefinition.ANY, ValueType.SERIALIZABLE);
+ valueTypesByPropertyType.put(DataTypeDefinition.ENCRYPTED, ValueType.SEALED_OBJECT);
valueTypesByPropertyType.put(DataTypeDefinition.BOOLEAN, ValueType.BOOLEAN);
valueTypesByPropertyType.put(DataTypeDefinition.INT, ValueType.INTEGER);
valueTypesByPropertyType.put(DataTypeDefinition.LONG, ValueType.LONG);
diff --git a/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java b/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java
index 9f9190d25b..7a5e463a4d 100644
--- a/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java
+++ b/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java
@@ -54,7 +54,6 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
private boolean isProtected = false;
private boolean mandatory = false;
private boolean multiValued = false;
- private boolean isEncrypted = false;
private MockClassAttributeDefinition(QName name)
{
@@ -133,227 +132,165 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
when(mock.targetClass.getName()).thenReturn(targetClassName);
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getConstraints()
- */
+ @Override
public List getConstraints()
{
return null;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getContainerClass
- * ()
- */
+ @Override
public ClassDefinition getContainerClass()
{
return null;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getDataType()
- */
+ @Override
public DataTypeDefinition getDataType()
{
return dataType;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getDefaultValue()
- */
+ @Override
public String getDefaultValue()
{
return defaultValue;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getDescription()
- */
+ @Override
public String getDescription()
{
return description;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getIndexTokenisationMode()
- */
+ @Override
public IndexTokenisationMode getIndexTokenisationMode()
{
return null;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getModel()
- */
+ @Override
public ModelDefinition getModel()
{
return null;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getName()
- */
+ @Override
public QName getName()
{
return name;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getTitle()
- */
+ @Override
public String getTitle()
{
return title;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isIndexed()
- */
+ @Override
public boolean isIndexed()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isIndexedAtomically()
- */
+ @Override
public boolean isIndexedAtomically()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isMandatory()
- */
+ @Override
public boolean isMandatory()
{
return mandatory;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isMandatoryEnforced()
- */
+ @Override
public boolean isMandatoryEnforced()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isMultiValued()
- */
+ @Override
public boolean isMultiValued()
{
return multiValued;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isOverride()
- */
+ @Override
public boolean isOverride()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isProtected()
- */
+ @Override
public boolean isProtected()
{
return isProtected;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isStoredInIndex()
- */
+ @Override
public boolean isStoredInIndex()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#getSourceClass()
- */
+ @Override
public ClassDefinition getSourceClass()
{
return null;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#getSourceRoleName()
- */
+ @Override
public QName getSourceRoleName()
{
return null;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#getTargetClass()
- */
+ @Override
public ClassDefinition getTargetClass()
{
return targetClass;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#getTargetRoleName()
- */
+ @Override
public QName getTargetRoleName()
{
return null;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isChild()
- */
+ @Override
public boolean isChild()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isSourceMandatory()
- */
+ @Override
public boolean isSourceMandatory()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isSourceMany()
- */
+ @Override
public boolean isSourceMany()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMandatory()
- */
+ @Override
public boolean isTargetMandatory()
{
return targetMandatory;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMandatoryEnforced()
- */
+ @Override
public boolean isTargetMandatoryEnforced()
{
return false;
}
- /*
- * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMany()
- */
+ @Override
public boolean isTargetMany()
{
return targetMany;
}
-
- @Override
- public boolean isEncrypted()
- {
- return isEncrypted;
- }
-
}
diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
index 06966519f9..2a5ae200c5 100644
--- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
+++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
@@ -34,6 +34,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import javax.crypto.SealedObject;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
@@ -41,6 +42,7 @@ import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.dictionary.DictionaryComponent;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
+import org.alfresco.repo.node.encryption.MetadataEncryptor;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
@@ -100,6 +102,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
public static final QName TYPE_QNAME_TEST_CONTENT = QName.createQName(NAMESPACE, "content");
public static final QName TYPE_QNAME_TEST_MANY_PROPERTIES = QName.createQName(NAMESPACE, "many-properties");
+ public static final QName TYPE_QNAME_TEST_MANY_PROPERTIES_ENCRYPTED = QName.createQName(NAMESPACE, "many-properties-encrypted");
public static final QName TYPE_QNAME_TEST_MANY_ML_PROPERTIES = QName.createQName(NAMESPACE, "many-ml-properties");
public static final QName TYPE_QNAME_EXTENDED_CONTENT = QName.createQName(NAMESPACE, "extendedcontent");
public static final QName ASPECT_QNAME_TEST_TITLED = QName.createQName(NAMESPACE, "titled");
@@ -135,7 +138,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
public static final QName ASSOC_TYPE_QNAME_TEST_CHILDREN = ContentModel.ASSOC_CHILDREN;
public static final QName ASSOC_TYPE_QNAME_TEST_CONTAINS = ContentModel.ASSOC_CONTAINS;
public static final QName ASSOC_TYPE_QNAME_TEST_NEXT = QName.createQName(NAMESPACE, "next");
-
+
public static final QName ASPECT_WITH_ASSOCIATIONS = QName.createQName(NAMESPACE, "withAssociations");
public static final QName ASSOC_ASPECT_CHILD_ASSOC = QName.createQName(NAMESPACE, "aspect-child-assoc");
public static final QName ASSOC_ASPECT_NORMAL_ASSOC = QName.createQName(NAMESPACE, "aspect-normal-assoc");
@@ -152,12 +155,16 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
public static final QName PROP_QNAME_ANY_PROP_SINGLE = QName.createQName(NAMESPACE, "anyprop-single");
public static final QName PROP_QNAME_ANY_PROP_MULTIPLE = QName.createQName(NAMESPACE, "anyprop-multiple");
+ public static final QName ASPECT_WITH_ENCRYPTED = QName.createQName(NAMESPACE, "withEncrypted");
+ public static final QName PROP_QNAME_ENCRYPTED_VALUE = QName.createQName(NAMESPACE, "encryptedValue");
+
protected PolicyComponent policyComponent;
protected DictionaryService dictionaryService;
protected TransactionService transactionService;
protected RetryingTransactionHelper retryingTransactionHelper;
protected AuthenticationComponent authenticationComponent;
protected NodeService nodeService;
+ protected MetadataEncryptor metadataEncryptor;
protected Dialect dialect;
/** populated during setup */
protected NodeRef rootNodeRef;
@@ -169,6 +176,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
super.onSetUpInTransaction();
dialect = (Dialect) applicationContext.getBean("dialect");
+ metadataEncryptor = (MetadataEncryptor) applicationContext.getBean("metadataEncryptor");
transactionService = (TransactionService) applicationContext.getBean("transactionComponent");
retryingTransactionHelper = (RetryingTransactionHelper) applicationContext.getBean("retryingTransactionHelper");
@@ -1707,8 +1715,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
}
/**
- * Ensures that the type you get out of a d:any property is the type that you
- * put in.
+ * Ensures that the type you get out of a d:any property is the type that you put in.
*/
public void testSerializableProperties() throws Exception
{
@@ -1736,6 +1743,43 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
assertTrue("Serialization/deserialization failed", checkPropertyQname instanceof QName);
}
+ /**
+ * Check that d:encrypted properties work correctly.
+ */
+ public void testEncryptedProperties() throws Exception
+ {
+ QName property = PROP_QNAME_CONTENT_VALUE;
+
+ Map properties = new HashMap(17);
+ properties.put(PROP_QNAME_ENCRYPTED_VALUE, property);
+
+ // We have encrypted properties, so encrypt them
+ properties = metadataEncryptor.encrypt(properties);
+ Serializable checkProperty = properties.get(PROP_QNAME_ENCRYPTED_VALUE);
+ assertTrue("Properties not encrypted", checkProperty instanceof SealedObject);
+
+ // create node
+ NodeRef nodeRef = nodeService.createNode(
+ rootNodeRef,
+ ASSOC_TYPE_QNAME_TEST_CHILDREN,
+ QName.createQName("pathA"),
+ ContentModel.TYPE_CONTAINER,
+ properties).getChildRef();
+ // persist
+ setComplete();
+ endTransaction();
+
+ // get the properties back
+ Map checkProperties = nodeService.getProperties(nodeRef);
+ checkProperty = checkProperties.get(PROP_QNAME_ENCRYPTED_VALUE);
+ assertTrue("Encrypted property not persisted", checkProperty instanceof SealedObject);
+
+ // Now make sure that the value can be null
+ nodeService.setProperty(nodeRef, PROP_QNAME_ENCRYPTED_VALUE, null);
+
+ // Finally, make sure that it fails if we don't encrypt
+ }
+
@SuppressWarnings("unchecked")
public void testMultiProp() throws Exception
{
@@ -1932,7 +1976,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
nodeService.setProperty(nodeRef, PROP_QNAME_MULTI_VALUE, "GHI");
Serializable checkProperty = nodeService.getProperty(nodeRef, PROP_QNAME_MULTI_VALUE);
assertTrue("Property not converted to a Collection", checkProperty instanceof Collection);
- assertTrue("Collection doesn't contain value", ((Collection)checkProperty).contains("GHI"));
+ assertTrue("Collection doesn't contain value", ((Collection>)checkProperty).contains("GHI"));
}
public void testPropertyLocaleBehaviour() throws Exception
diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest_model.xml b/source/java/org/alfresco/repo/node/BaseNodeServiceTest_model.xml
index f3c43de001..1a205197b4 100644
--- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest_model.xml
+++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest_model.xml
@@ -296,7 +296,8 @@
true
-
+
+
@@ -456,6 +457,16 @@
+
+
+ Aspect with encrypted properties
+
+
+ d:encrypted
+ true
+
+
+
diff --git a/source/java/org/alfresco/repo/node/db/EncryptedPropertiesTest.java b/source/java/org/alfresco/repo/node/db/EncryptedPropertiesTest.java
deleted file mode 100644
index 9b930025f9..0000000000
--- a/source/java/org/alfresco/repo/node/db/EncryptedPropertiesTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-package org.alfresco.repo.node.db;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.repo.dictionary.DictionaryBootstrap;
-import org.alfresco.repo.dictionary.DictionaryDAO;
-import org.alfresco.service.cmr.repository.MLText;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.NodeService;
-import org.alfresco.service.cmr.repository.StoreRef;
-import org.alfresco.service.namespace.QName;
-import org.alfresco.util.BaseSpringTest;
-
-public class EncryptedPropertiesTest extends BaseSpringTest
-{
- private static String NAMESPACE = "http://www.alfresco.org/test/encryptiontest/1.0";
- private static QName PROP1_TYPE = QName.createQName(NAMESPACE, "prop1");
- private static QName PROP2_TYPE = QName.createQName(NAMESPACE, "prop2");
- private static QName ASPECT_PROP1_TYPE = QName.createQName(NAMESPACE, "aspectprop1");
- private static QName ASPECT_PROP2_TYPE = QName.createQName(NAMESPACE, "aspectprop2");
- private static QName ENCRYPTIONTEST_TYPE = QName.createQName(NAMESPACE, "testtype");
-
- private DictionaryDAO dictionaryDAO;
- private NodeService nodeService;
-
- private NodeRef rootNodeRef;
-
- @Override
- protected void onSetUpInTransaction() throws Exception
- {
- super.onSetUpInTransaction();
-
- dictionaryDAO = (DictionaryDAO) applicationContext.getBean("dictionaryDAO");
-
- nodeService = getNodeService();
-
- // Create the test model
- createTestModel();
-
- // create a first store directly
- StoreRef storeRef = nodeService.createStore(
- StoreRef.PROTOCOL_WORKSPACE,
- "Test_" + System.currentTimeMillis());
- rootNodeRef = nodeService.getRootNode(storeRef);
- }
-
- protected NodeService getNodeService()
- {
- // Force cascading
- DbNodeServiceImpl dbNodeServiceImpl = (DbNodeServiceImpl) applicationContext.getBean("dbNodeServiceImpl");
-
- return (NodeService) applicationContext.getBean("dbNodeService");
- }
-
- private void createTestModel()
- {
- // register the test model
- List bootstrapModels = new ArrayList();
- bootstrapModels.add("org/alfresco/repo/node/db/encrypted_properties_test_model.xml");
-
- DictionaryBootstrap bootstrap = new DictionaryBootstrap();
- bootstrap.setModels(bootstrapModels);
- bootstrap.setDictionaryDAO(dictionaryDAO);
-
- bootstrap.bootstrap();
- }
-
- public void testEncryptedProperties()
- {
- Map properties = new HashMap();
- properties.put(PROP1_TYPE, "test string value");
-
- MLText mlTextProperty = new MLText();
- mlTextProperty.addValue(Locale.ENGLISH, "Very good!");
- mlTextProperty.addValue(Locale.FRENCH, "Très bon!");
- mlTextProperty.addValue(Locale.GERMAN, "Sehr gut!");
- properties.put(PROP2_TYPE, mlTextProperty);
-
- NodeRef test1 = nodeService.createNode(
- rootNodeRef,
- ContentModel.ASSOC_CHILDREN,
- QName.createQName(NAMESPACE, "n1"),
- ENCRYPTIONTEST_TYPE,
- properties).getChildRef();
-
- NodeRef test2 = nodeService.createNode(
- rootNodeRef,
- ContentModel.ASSOC_CHILDREN,
- QName.createQName(NAMESPACE, "n2"),
- ContentModel.TYPE_CONTENT,
- null).getChildRef();
-
- properties = new HashMap();
- properties.put(ASPECT_PROP1_TYPE, "test string value");
-
- mlTextProperty = new MLText();
- mlTextProperty.addValue(Locale.ENGLISH, "Very good!");
- mlTextProperty.addValue(Locale.FRENCH, "Très bon!");
- mlTextProperty.addValue(Locale.GERMAN, "Sehr gut!");
- properties.put(ASPECT_PROP2_TYPE, mlTextProperty);
- nodeService.addAspect(test2, QName.createQName(NAMESPACE, "testaspect"), properties);
-
- String prop1 = (String)nodeService.getProperty(test1, PROP1_TYPE);
- assertEquals("test string value", prop1);
-
- MLText prop2 = (MLText)nodeService.getProperty(test1, PROP2_TYPE);
- assertEquals("Very good!", prop2.getValue(Locale.ENGLISH));
- assertEquals("Très bon!", prop2.getValue(Locale.FRENCH));
- assertEquals("Sehr gut!", prop2.getValue(Locale.GERMAN));
-
- String aspectprop1 = (String)nodeService.getProperty(test2, ASPECT_PROP1_TYPE);
- assertEquals("test string value", aspectprop1);
-
- MLText aspectprop2 = (MLText)nodeService.getProperty(test2, ASPECT_PROP2_TYPE);
- assertEquals("Very good!", aspectprop2.getValue(Locale.ENGLISH));
- assertEquals("Très bon!", aspectprop2.getValue(Locale.FRENCH));
- assertEquals("Sehr gut!", aspectprop2.getValue(Locale.GERMAN));
- }
-
-// protected void createNodes()
-// {
-// // create 1000 nodes with a single (non-encrypted) string property
-// for(int i = 0; i < 2000; i++)
-// {
-// Map properties = new HashMap();
-// properties.put(ContentModel.PROP_NAME, "encryption test name");
-// NodeRef test1 = nodeService.createNode(
-// rootNodeRef,
-// ContentModel.ASSOC_CHILDREN,
-// QName.createQName(NAMESPACE, "n" + i),
-// ContentModel.TYPE_CONTENT,
-// properties).getChildRef();
-//
-// }
-// }
-
-// protected void createEnryptedPropertyNodes()
-// {
-// // create 1000 nodes with a single encrypted string attribute
-// for(int i = 0; i < 2000; i++)
-// {
-// Map properties = new HashMap();
-// properties.put(PROP1_TYPE, "test string value");
-// NodeRef test1 = nodeService.createNode(
-// rootNodeRef,
-// ContentModel.ASSOC_CHILDREN,
-// QName.createQName(NAMESPACE, "n" + i),
-// ENCRYPTIONTEST_TYPE,
-// properties).getChildRef();
-//
-// }
-// }
-
-// public void testEncryptedPropertiesSpeed()
-// {
-// // warm up
-// createNodes();
-// createEnryptedPropertyNodes();
-//
-// // time
-// long start = System.currentTimeMillis();
-// createEnryptedPropertyNodes();
-// long end = System.currentTimeMillis();
-// System.out.println("Encrypted property 1000 nodes in " + (end - start) + "ms");
-//
-// start = System.currentTimeMillis();
-// createNodes();
-// end = System.currentTimeMillis();
-// System.out.println("Non-encrypted property 1000 nodes in " + (end - start) + "ms");
-// }
-}
diff --git a/source/java/org/alfresco/repo/node/db/encrypted_properties_test_model.xml b/source/java/org/alfresco/repo/node/db/encrypted_properties_test_model.xml
deleted file mode 100644
index b43eef36dd..0000000000
--- a/source/java/org/alfresco/repo/node/db/encrypted_properties_test_model.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
- VersionStoreBaseTest model
- Alfresco
- 2005-05-30
- 1.0
-
-
-
-
-
-
-
-
-
-
-
-
-
- Test type
- The test type
- cm:content
-
-
-
- d:text
- false
-
- true
-
-
- d:mltext
- false
- true
-
-
-
-
-
-
-
-
-
-
-
- Test Aspect
- The test aspect
-
-
-
-
- d:text
- false
-
- true
-
-
- d:mltext
- false
- true
-
-
-
-
-
-
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java b/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java
new file mode 100644
index 0000000000..9cfb335880
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java
@@ -0,0 +1,178 @@
+package org.alfresco.repo.node.encryption;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.SealedObject;
+
+import org.alfresco.repo.security.authentication.AuthenticationException;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.encryption.Encryptor;
+import org.alfresco.repo.security.encryption.KeyProvider;
+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.QName;
+
+/**
+ * Component to convert encrypt/decrypt properties.
+ *
+ * This is a helper; it is up to the client how and when encryption and decryption is done,
+ * but metadata integrity enforcement will expect that encrypted properties are already
+ * encrypted.
+ *
+ * This class must always be used
+ * {@link AuthenticationUtil#runAs(org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork, String) running as 'system'}.
+ *
+ * @author Derek Hulley
+ * @since 4.0
+ */
+public class MetadataEncryptor
+{
+ private DictionaryService dictionaryService;
+ private Encryptor encryptor;
+
+ /**
+ * @param dictionaryService service to check if properties need encrypting
+ */
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
+ /**
+ * @param encryptor the class that does the encryption/decryption
+ */
+ public void setEncryptor(Encryptor encryptor)
+ {
+ this.encryptor = encryptor;
+ }
+
+ /**
+ * @throws AuthenticationException if the thread is not running as 'system'
+ */
+ private final void checkAuthentication()
+ {
+ if (!AuthenticationUtil.isRunAsUserTheSystemUser())
+ {
+ throw new AuthenticationException("Metadata decryption can only be done by the system user.");
+ }
+ }
+
+ /**
+ * Encrypt a properties if the data definition (model-specific) requires it.
+ *
+ * This method has no specific authentication requirements.
+ *
+ * @param propertyQName the property qualified name
+ * @param inbound the property to encrypt
+ * @return the encrypted property or the original if encryption is not required
+ */
+ public Serializable encrypt(QName propertyQName, Serializable inbound)
+ {
+ PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
+ if (inbound == null || propertyDef == null || !(propertyDef.getDataType().getName().equals(DataTypeDefinition.ENCRYPTED)))
+ {
+ return inbound;
+ }
+ Serializable outbound = encryptor.sealObject(KeyProvider.ALIAS_METADATA, null, inbound);
+ // Done
+ return outbound;
+ }
+
+ /**
+ * Encrypt properties if their data definition (model-specific) requires it.
+ * The values provided can be mixed; values will be encrypted only if required.
+ *
+ * This method has no specific authentication requirements.
+ *
+ * @param inbound the properties to encrypt
+ * @return a new map of values if some encryption occured
+ * otherwise the original inbound map is returned
+ */
+ public Map encrypt(Map inbound)
+ {
+ boolean encrypt = false;
+ for (Map.Entry entry : inbound.entrySet())
+ {
+ QName key = entry.getKey();
+ PropertyDefinition propertyDef = dictionaryService.getProperty(key);
+ if (propertyDef != null && (propertyDef.getDataType().getName().equals(DataTypeDefinition.ENCRYPTED)))
+ {
+ encrypt = true;
+ break;
+ }
+ }
+ if (!encrypt)
+ {
+ // Nothing to do
+ return inbound;
+ }
+ // Encrypt, in place, using a copied map
+ Map outbound = new HashMap(inbound);
+ for (Map.Entry entry : inbound.entrySet())
+ {
+ Serializable value = entry.getValue();
+ if (value != null && (value instanceof SealedObject))
+ {
+ // Straight copy, i.e. do nothing
+ continue;
+ }
+ // Have to decrypt the value
+ Serializable encryptedValue = encryptor.sealObject(KeyProvider.ALIAS_METADATA, null, value);
+ // Store it back
+ outbound.put(entry.getKey(), encryptedValue);
+ }
+ // Done
+ return outbound;
+ }
+
+ /**
+ * Decrypt properties if they are decryptable. The values provided can be mixed;
+ * encrypted values will be sought out and decrypted.
+ *
+ * This method can only be called by the 'system' user.
+ *
+ * @param inbound the properties to decrypt
+ * @return a new map of values if some decryption occured
+ * otherwise the original inbound map is returned
+ */
+ public Map decrypt(Map inbound)
+ {
+ checkAuthentication();
+
+ boolean decrypt = false;
+ for (Map.Entry entry : inbound.entrySet())
+ {
+ Serializable value = entry.getValue();
+ if (value != null && (value instanceof SealedObject))
+ {
+ decrypt = true;
+ break;
+ }
+ }
+ if (!decrypt)
+ {
+ // Nothing to do
+ return inbound;
+ }
+ // Decrypt, in place, using a copied map
+ Map outbound = new HashMap(inbound);
+ for (Map.Entry entry : inbound.entrySet())
+ {
+ Serializable value = entry.getValue();
+ if (value != null && (value instanceof SealedObject))
+ {
+ // Straight copy, i.e. do nothing
+ continue;
+ }
+ // Have to decrypt the value
+ Serializable decryptedValue = encryptor.unsealObject(KeyProvider.ALIAS_METADATA, value);
+ // Store it back
+ outbound.put(entry.getKey(), decryptedValue);
+ }
+ // Done
+ return outbound;
+ }
+}
diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryFactory.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryFactory.java
index c4cb434574..3aadf45703 100644
--- a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryFactory.java
+++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryFactory.java
@@ -38,7 +38,6 @@ import org.alfresco.repo.domain.node.NodePropertyHelper;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.query.CannedQueryDAO;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.encryption.Encryptor;
import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -64,8 +63,6 @@ public class GetChildrenCannedQueryFactory extends AbstractCannedQueryFactory getCannedQuery(CannedQueryParameters parameters)
{
- NodePropertyHelper nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptor);
+ NodePropertyHelper nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO);
Method method = null;
for (Method m : methodService.getClass().getMethods())
diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java b/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java
index b02ac2c9f7..8380fdc8dd 100644
--- a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java
+++ b/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java
@@ -62,6 +62,7 @@ public class IntegrityTest extends TestCase
public static final QName TEST_TYPE_WITHOUT_ANYTHING = QName.createQName(NAMESPACE, "typeWithoutAnything");
public static final QName TEST_TYPE_WITH_ASPECT = QName.createQName(NAMESPACE, "typeWithAspect");
public static final QName TEST_TYPE_WITH_PROPERTIES = QName.createQName(NAMESPACE, "typeWithProperties");
+ public static final QName TEST_TYPE_WITH_ENCRYPTED_PROPERTIES = QName.createQName(NAMESPACE, "typeWithEncryptedProperties");
public static final QName TEST_TYPE_WITH_ASSOCS = QName.createQName(NAMESPACE, "typeWithAssocs");
public static final QName TEST_TYPE_WITH_CHILD_ASSOCS = QName.createQName(NAMESPACE, "typeWithChildAssocs");
public static final QName TEST_TYPE_WITH_NON_ENFORCED_CHILD_ASSOCS = QName.createQName(NAMESPACE, "typeWithNonEnforcedChildAssocs");
@@ -82,6 +83,9 @@ public class IntegrityTest extends TestCase
public static final QName TEST_PROP_INT_A = QName.createQName(NAMESPACE, "prop-int-a");
public static final QName TEST_PROP_INT_B = QName.createQName(NAMESPACE, "prop-int-b");
public static final QName TEST_PROP_INT_C = QName.createQName(NAMESPACE, "prop-int-c");
+ public static final QName TEST_PROP_ENCRYPTED_A = QName.createQName(NAMESPACE, "prop-encrypted-a");
+ public static final QName TEST_PROP_ENCRYPTED_B = QName.createQName(NAMESPACE, "prop-encrypted-b");
+ public static final QName TEST_PROP_ENCRYPTED_C = QName.createQName(NAMESPACE, "prop-encrypted-c");
public static ApplicationContext ctx;
static
@@ -135,6 +139,8 @@ public class IntegrityTest extends TestCase
allProperties.put(TEST_PROP_TEXT_B, "DEF");
allProperties.put(TEST_PROP_INT_A, "123");
allProperties.put(TEST_PROP_INT_B, "456");
+ allProperties.put(TEST_PROP_ENCRYPTED_A, "ABC");
+ allProperties.put(TEST_PROP_ENCRYPTED_B, "DEF");
}
public void tearDown() throws Exception
@@ -239,6 +245,12 @@ public class IntegrityTest extends TestCase
checkIntegrityExpectFailure("Failed to detect missing removed properties", 1);
}
+ public void testCreateWithoutEncryption() throws Exception
+ {
+ NodeRef nodeRef = createNode("abc", TEST_TYPE_WITH_ENCRYPTED_PROPERTIES, allProperties);
+ checkIntegrityExpectFailure("Failed to detect unencrypted properties", 2);
+ }
+
public void testCreateWithoutPropertiesForAspect() throws Exception
{
NodeRef nodeRef = createNode("abc", TEST_TYPE_WITH_ASPECT, null);
diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml b/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml
index c64d330f64..b6883d345a 100644
--- a/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml
+++ b/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml
@@ -38,6 +38,24 @@
+
+
+ Type With Encrypted Properties
+ sys:base
+
+
+ d:encrypted
+ true
+
+
+ d:encrypted
+
+
+ d:encrypted
+ true
+
+
+
Type With Aspect
diff --git a/source/java/org/alfresco/repo/node/integrity/PropertiesIntegrityEvent.java b/source/java/org/alfresco/repo/node/integrity/PropertiesIntegrityEvent.java
index 3eaf786228..e6ad0cfaf5 100644
--- a/source/java/org/alfresco/repo/node/integrity/PropertiesIntegrityEvent.java
+++ b/source/java/org/alfresco/repo/node/integrity/PropertiesIntegrityEvent.java
@@ -24,11 +24,14 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.crypto.SealedObject;
+
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.Constraint;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintException;
+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.TypeDefinition;
@@ -76,9 +79,6 @@ public class PropertiesIntegrityEvent extends AbstractIntegrityEvent
/**
* Checks the properties for the type and aspects of the given node.
- *
- * @param nodeRef
- * @param eventResults
*/
private void checkAllProperties(NodeRef nodeRef, List eventResults)
{
@@ -163,6 +163,19 @@ public class PropertiesIntegrityEvent extends AbstractIntegrityEvent
continue;
}
Serializable propertyValue = nodeProperties.get(propertyQName);
+ // Check for encryption first
+ if (propertyDef.getDataType().getName().equals(DataTypeDefinition.ENCRYPTED))
+ {
+ if (propertyValue != null && !(propertyValue instanceof SealedObject))
+ {
+ IntegrityRecord result = new IntegrityRecord(
+ "Property must be encrypted: \n" +
+ " Node: " + nodeRef + "\n" +
+ " Type: " + typeQName + "\n" +
+ " Property: " + propertyQName);
+ eventResults.add(result);
+ }
+ }
// check constraints
List constraintDefs = propertyDef.getConstraints();
for (ConstraintDefinition constraintDef : constraintDefs)
diff --git a/source/java/org/alfresco/repo/security/encryption/AbstractEncryptor.java b/source/java/org/alfresco/repo/security/encryption/AbstractEncryptor.java
index 172bf9d56c..afe45ab5a5 100644
--- a/source/java/org/alfresco/repo/security/encryption/AbstractEncryptor.java
+++ b/source/java/org/alfresco/repo/security/encryption/AbstractEncryptor.java
@@ -176,14 +176,57 @@ public abstract class AbstractEncryptor implements Encryptor
}
@Override
- public SealedObject sealObject(String keyAlias, AlgorithmParameters params, Serializable input)
+ public Serializable sealObject(String keyAlias, AlgorithmParameters params, Serializable input)
{
- throw new UnsupportedOperationException();
+ if (input == null)
+ {
+ return null;
+ }
+ Cipher cipher = getCipher(keyAlias, params, Cipher.ENCRYPT_MODE);
+ if (cipher == null)
+ {
+ return input;
+ }
+ try
+ {
+ return new SealedObject(input, cipher);
+ }
+ catch (Exception e)
+ {
+ throw new AlfrescoRuntimeException("Failed to seal object", e);
+ }
}
@Override
- public Serializable unsealObject(String keyAlias, SealedObject input)
+ public Serializable unsealObject(String keyAlias, Serializable input)
{
- throw new UnsupportedOperationException();
- }
+ if (input == null)
+ {
+ return input;
+ }
+ // Don't unseal it if it is not sealed
+ if (!(input instanceof SealedObject))
+ {
+ return input;
+ }
+ // Get the Key, rather than a Cipher
+ Key key = keyProvider.getKey(keyAlias);
+ if (key == null)
+ {
+ // The client will be expecting to unseal the object
+ throw new IllegalStateException("No key matching " + keyAlias + ". Cannot unseal object.");
+ }
+ // Unseal it using the key
+ SealedObject sealedInput = (SealedObject) input;
+ try
+ {
+ Serializable output = (Serializable) sealedInput.getObject(key);
+ // Done
+ return output;
+ }
+ catch (Exception e)
+ {
+ throw new AlfrescoRuntimeException("Failed to unseal object", e);
+ }
+ }
}
diff --git a/source/java/org/alfresco/repo/security/encryption/AbstractKeyProvider.java b/source/java/org/alfresco/repo/security/encryption/AbstractKeyProvider.java
index 4ab5f7e640..51ea70d58f 100644
--- a/source/java/org/alfresco/repo/security/encryption/AbstractKeyProvider.java
+++ b/source/java/org/alfresco/repo/security/encryption/AbstractKeyProvider.java
@@ -1,21 +1,17 @@
package org.alfresco.repo.security.encryption;
-import java.security.Key;
-
-import org.alfresco.util.ParameterCheck;
-
/**
* Basic support for key providers
+ *
+ * TODO: This class will provide the alias name mapping so that use-cases can be mapped
+ * to different alias names in the keystore.
*
* @author Derek Hulley
* @since 4.0
*/
public abstract class AbstractKeyProvider implements KeyProvider
{
- @Override
- public Key getKey(AlfrescoKeyAlias keyAlias)
- {
- ParameterCheck.mandatory("keyAlias", keyAlias);
- return getKey(keyAlias.name());
- }
+ /*
+ * Not a useless class.
+ */
}
diff --git a/source/java/org/alfresco/repo/security/encryption/Encryptor.java b/source/java/org/alfresco/repo/security/encryption/Encryptor.java
index ff5d075692..89e8ed1c20 100644
--- a/source/java/org/alfresco/repo/security/encryption/Encryptor.java
+++ b/source/java/org/alfresco/repo/security/encryption/Encryptor.java
@@ -4,7 +4,6 @@ import java.io.Serializable;
import java.security.AlgorithmParameters;
import javax.crypto.Cipher;
-import javax.crypto.SealedObject;
import org.alfresco.util.Pair;
@@ -64,23 +63,31 @@ public interface Encryptor
Object decryptObject(String keyAlias, AlgorithmParameters params, byte[] input);
/**
- * Convenience method to seal on object up cryptographically
+ * Convenience method to seal on object up cryptographically.
+ *
+ * Note that the original object may be returned directly if there is no key associated with
+ * the alias.
*
* @param keyAlias the encryption key alias
* @param input the object to encrypt and seal
* @return the sealed object that can be decrypted with the original key
*/
- SealedObject sealObject(String keyAlias, AlgorithmParameters params, Serializable input);
+ Serializable sealObject(String keyAlias, AlgorithmParameters params, Serializable input);
/**
- * Convenience method to unseal on object up cryptographically.
+ * Convenience method to unseal on object sealed up cryptographically.
*
- * Note that the algorithm parameters are stored in the sealed object and are
- * not therefore required for decryption.
+ * Note that the algorithm parameters not provided on the assumption that a symmetric key
+ * algorithm is in use - only the key is required for unsealing.
+ *
+ * Note that the original object may be returned directly if there is no key associated with
+ * the alias or if the input object is not a SealedObject
.
*
* @param keyAlias the encryption key alias
* @param input the object to decrypt and unseal
* @return the original unsealed object that was encrypted with the original key
+ * @throws IllegalStateException if the key alias is not valid and the input is a
+ * SealedObject
*/
- Serializable unsealObject(String keyAlias, SealedObject input);
+ Serializable unsealObject(String keyAlias, Serializable input);
}
diff --git a/source/java/org/alfresco/repo/security/encryption/EncryptorTest.java b/source/java/org/alfresco/repo/security/encryption/EncryptorTest.java
index e60a9f1f4d..9b254064eb 100644
--- a/source/java/org/alfresco/repo/security/encryption/EncryptorTest.java
+++ b/source/java/org/alfresco/repo/security/encryption/EncryptorTest.java
@@ -1,5 +1,6 @@
package org.alfresco.repo.security.encryption;
+import java.io.Serializable;
import java.security.AlgorithmParameters;
import junit.framework.TestCase;
@@ -58,4 +59,13 @@ public class EncryptorTest extends TestCase
encryptedPair.getFirst());
assertEquals("Encryption round trip failed. ", testObject, output);
}
+
+ public void testSealedObject()
+ {
+ Serializable testObject = " This is a string, but will be serialized ";
+
+ Serializable sealedObject = encryptor.sealObject("mykey2", null, testObject);
+ Object output = encryptor.unsealObject("mykey2", sealedObject);
+ assertEquals("Encryption round trip failed. ", testObject, output);
+ }
}
diff --git a/source/java/org/alfresco/repo/security/encryption/KeyProvider.java b/source/java/org/alfresco/repo/security/encryption/KeyProvider.java
index 044d65a9f0..282836a3b1 100644
--- a/source/java/org/alfresco/repo/security/encryption/KeyProvider.java
+++ b/source/java/org/alfresco/repo/security/encryption/KeyProvider.java
@@ -9,17 +9,15 @@ import java.security.Key;
*/
public interface KeyProvider
{
+ // TODO: Allow the aliases to be configured i.e. include an alias mapper
/**
- * Enumeration of key aliases supported internally by Alfresco
- *
- * @author derekh
- * @since 4.0
+ * Constant representing the keystore alias for keys to encrypt/decrype node metadata
*/
- public static enum AlfrescoKeyAlias
- {
- METADATA,
- SOLR
- }
+ public static final String ALIAS_METADATA = "metadata";
+ /**
+ * Constant representing the keystore alias for keys to encrypt/decrype SOLR transfer data
+ */
+ public static final String ALIAS_SOLR = "solr";
/**
* Get an encryption key if available.
@@ -28,12 +26,4 @@ public interface KeyProvider
* @return the encryption key or null if there is no associated key
*/
public Key getKey(String keyAlias);
-
- /**
- * Get an encryption key if available, using a convenience constant.
- *
- * @param keyAlias the key alias
- * @return the encryption key or null if there is no associated key
- */
- public Key getKey(AlfrescoKeyAlias keyAlias);
}
diff --git a/source/java/org/alfresco/repo/security/encryption/KeyStoreKeyProviderTest.java b/source/java/org/alfresco/repo/security/encryption/KeyStoreKeyProviderTest.java
index da03c05c0c..d72d3119df 100644
--- a/source/java/org/alfresco/repo/security/encryption/KeyStoreKeyProviderTest.java
+++ b/source/java/org/alfresco/repo/security/encryption/KeyStoreKeyProviderTest.java
@@ -28,12 +28,11 @@ public class KeyStoreKeyProviderTest extends TestCase
private static final String ALIAS_TWO = "mykey2";
private static final String ALIAS_THREE = "mykey3";
- public void setUp() throws Exception
- {
- }
-
/**
* Helper utility to create a two-alias keystore.
+ *
+ * TODO: Allow the required aliases and key types to be specified and generate
+ * a keystore on the fly
*/
/* package */ static KeystoreKeyProvider getTestKeyStoreProvider()
{
@@ -49,6 +48,10 @@ public class KeyStoreKeyProviderTest extends TestCase
return ks;
}
+ public void setUp() throws Exception
+ {
+ }
+
public void testNoKeyStorePasswords() throws Exception
{
KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(