Author: chetanm
Date: Wed Mar 26 08:00:31 2014
New Revision: 1581724
URL: http://svn.apache.org/r1581724
Log:
OAK-1604 - Support for signed references in Blob (WIP)
-- Adding support for reference in AbstractBlobStore
-- DataStoreBlobStore would not return reference for in lined blobs
-- Adding fixture for FileBlobStore
Modified:
jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStore.java
jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/BlobStore.java
jackrabbit/oak/trunk/oak-blob/src/test/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStoreTest.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/OakFileDataStore.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStoreTest.java
Modified:
jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStore.java?rev=1581724&r1=1581723&r2=1581724&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStore.java
(original)
+++
jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStore.java
Wed Mar 26 08:00:31 2014
@@ -22,7 +22,9 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
+import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayDeque;
@@ -35,10 +37,19 @@ import java.util.NoSuchElementException;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicReference;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.BaseEncoding;
import org.apache.commons.io.FileUtils;
import org.apache.jackrabbit.oak.commons.cache.Cache;
import org.apache.jackrabbit.oak.commons.IOUtils;
import org.apache.jackrabbit.oak.commons.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* An abstract data store that splits the binaries in relatively small blocks,
@@ -98,6 +109,18 @@ public abstract class AbstractBlobStore
*/
private AtomicReference<byte[]> blockBuffer = new
AtomicReference<byte[]>();
+ /**
+ * Encryption algorithm used to encrypt blobId as references
+ */
+ private static final String ALGORITHM = "HmacSHA1";
+
+ /**
+ * Encryption key for creating secure references from blobId
+ */
+ private byte[] referenceKey;
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
public void setBlockSizeMin(int x) {
validateBlockSize(x);
this.blockSizeMin = x;
@@ -165,14 +188,64 @@ public abstract class AbstractBlobStore
@Override
public String getReference(String blobId) {
- return null;
+ checkNotNull(blobId, "BlobId must be specified");
+ try {
+ Mac mac = Mac.getInstance(ALGORITHM);
+ mac.init(new SecretKeySpec(referenceKey, ALGORITHM));
+ byte[] hash = mac.doFinal(blobId.getBytes("UTF-8"));
+ return blobId + ':' + BaseEncoding.base32Hex().encode(hash);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException(e);
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException(e);
+ }
}
@Override
public String getBlobId(String reference) {
+ checkNotNull(reference, "BlobId must be specified");
+ int colon = reference.indexOf(':');
+ if (colon != -1) {
+ String blobId = reference.substring(0, colon);
+ if (reference.equals(getReference(blobId))) {
+ return blobId;
+ }else{
+ log.debug("Possibly invalid reference as blobId does not match
{}", reference);
+ }
+ }
return null;
}
+ public void setReferenceKey(byte[] referenceKey) {
+ this.referenceKey = referenceKey;
+ }
+
+ /**
+ * Set the referenceKey from Base64 encoded byte array
+ * @param encodedKey base64 encoded key
+ */
+ public void setReferenceKeyEncoded(String encodedKey) {
+ this.referenceKey = BaseEncoding.base64().decode(encodedKey);
+ }
+
+ /**
+ * Set the referenceKey from plain text. Key content would be
+ * UTF-8 encoding of the string.
+ *
+ * <p>This is useful when setting key via generic
+ * bean property manipulation from string properties. User can specify the
+ * key in plain text and that would be passed on this object via
+ * {@link
org.apache.jackrabbit.oak.commons.PropertiesUtil#populate(Object,
java.util.Map, boolean)}
+ *
+ * @param textKey base64 encoded key
+ * @see org.apache.jackrabbit.oak.commons.PropertiesUtil#populate(Object,
java.util.Map, boolean)
+ */
+ public void setReferenceKeyPlainText(String textKey) {
+ this.referenceKey = textKey.getBytes(Charsets.UTF_8);
+ }
+
protected void usesBlobId(String blobId) {
inUse.put(blobId, new WeakReference<String>(blobId));
}
Modified:
jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/BlobStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/BlobStore.java?rev=1581724&r1=1581723&r2=1581724&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/BlobStore.java
(original)
+++
jackrabbit/oak/trunk/oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/BlobStore.java
Wed Mar 26 08:00:31 2014
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.InputStream;
import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
/**
* An interface to store and read large binary objects.
@@ -76,7 +77,7 @@ public interface BlobStore {
* @return matching blobId, or {@code null}
*/
@CheckForNull
- String getBlobId(String reference);
+ String getBlobId(@Nonnull String reference);
/**
* Returns a secure reference to blob referred by blobid, or {@code null}
if no such
@@ -86,6 +87,6 @@ public interface BlobStore {
* @return binary reference, or {@code null}
*/
@CheckForNull
- String getReference(String blobId);
+ String getReference(@Nonnull String blobId);
}
Modified:
jackrabbit/oak/trunk/oak-blob/src/test/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStoreTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob/src/test/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStoreTest.java?rev=1581724&r1=1581723&r2=1581724&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-blob/src/test/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStoreTest.java
(original)
+++
jackrabbit/oak/trunk/oak-blob/src/test/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStoreTest.java
Wed Mar 26 08:00:31 2014
@@ -40,9 +40,11 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeThat;
/**
* Tests a BlobStore implementation.
@@ -270,6 +272,23 @@ public abstract class AbstractBlobStoreT
assertTrue("failedCount: " + failedCount, failedCount > 0);
}
+ @Test
+ public void testReference() throws Exception {
+ assumeThat(store, instanceOf(AbstractBlobStore.class));
+ AbstractBlobStore abs = (AbstractBlobStore) store;
+ Random r = new Random();
+ byte[] key = new byte[256];
+ r.nextBytes(key);
+ abs.setReferenceKey(key);
+
+ byte[] data = new byte[1000];
+ r.nextBytes(data);
+ String blobId = store.writeBlob(new ByteArrayInputStream(data));
+ String reference = store.getReference(blobId);
+ String blobId2 = store.getBlobId(reference);
+ assertEquals(blobId, blobId2);
+ }
+
private void doTest(int maxLength, int count) throws Exception {
String[] s = new String[count * 2];
Random r = new Random(0);
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java?rev=1581724&r1=1581723&r2=1581724&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java
Wed Mar 26 08:00:31 2014
@@ -172,6 +172,7 @@ public class DataStoreBlobStore implemen
@Override
public String getBlobId(String reference) {
+ checkNotNull(reference);
DataRecord record;
try {
record = delegate.getRecordFromReference(reference);
@@ -186,6 +187,12 @@ public class DataStoreBlobStore implemen
@Override
public String getReference(String blobId) {
+ checkNotNull(blobId);
+ //Reference are not created for in memory record
+ if(InMemoryDataRecord.isInstance(blobId)){
+ return null;
+ }
+
DataRecord record;
try {
record = delegate.getRecord(new DataIdentifier(blobId));
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/OakFileDataStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/OakFileDataStore.java?rev=1581724&r1=1581723&r2=1581724&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/OakFileDataStore.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/OakFileDataStore.java
Wed Mar 26 08:00:31 2014
@@ -19,6 +19,7 @@
package org.apache.jackrabbit.oak.plugins.blob.datastore;
+import com.google.common.base.Charsets;
import com.google.common.io.BaseEncoding;
import org.apache.jackrabbit.core.data.DataStoreException;
import org.apache.jackrabbit.core.data.FileDataStore;
@@ -28,12 +29,12 @@ import org.apache.jackrabbit.core.data.F
* provisioning the signing key via OSGi config
*/
public class OakFileDataStore extends FileDataStore {
- private byte[] signingKey;
+ private byte[] referenceKey;
@Override
protected byte[] getOrCreateReferenceKey() throws DataStoreException {
- if(signingKey != null){
- return signingKey;
+ if(referenceKey != null){
+ return referenceKey;
}
return super.getOrCreateReferenceKey();
}
@@ -41,7 +42,27 @@ public class OakFileDataStore extends Fi
/**
* Set Base64 encoded signing key
*/
- public void setSigningKey(String encodedKey) {
- this.signingKey = BaseEncoding.base64().decode(encodedKey);
+ public void setReferenceKeyEncoded(String encodedKey) {
+ this.referenceKey = BaseEncoding.base64().decode(encodedKey);
+ }
+
+ /**
+ * Set the referenceKey from plain text. Key content would be
+ * UTF-8 encoding of the string.
+ *
+ * <p>This is useful when setting key via generic
+ * bean property manipulation from string properties. User can specify the
+ * key in plain text and that would be passed on this object via
+ * {@link
org.apache.jackrabbit.oak.commons.PropertiesUtil#populate(Object,
java.util.Map, boolean)}
+ *
+ * @param textKey base64 encoded key
+ * @see org.apache.jackrabbit.oak.commons.PropertiesUtil#populate(Object,
java.util.Map, boolean)
+ */
+ public void setReferenceKeyPlainText(String textKey) {
+ this.referenceKey = textKey.getBytes(Charsets.UTF_8);
+ }
+
+ public void setReferenceKey(byte[] referenceKey) {
+ this.referenceKey = referenceKey;
}
}
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStoreTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStoreTest.java?rev=1581724&r1=1581723&r2=1581724&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStoreTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStoreTest.java
Wed Mar 26 08:00:31 2014
@@ -34,6 +34,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
@@ -75,7 +76,7 @@ public class DataStoreBlobStoreTest {
new Random().nextBytes(data);
DataIdentifier testDI = new DataIdentifier("test");
- DataRecord testDR = new ByteArrayDataRecord(data, testDI);
+ DataRecord testDR = new ByteArrayDataRecord(data, testDI,
"testReference");
DataStore mockedDS = mock(DataStore.class);
when(mockedDS.getMinRecordLength()).thenReturn(maxInlineSize);
@@ -99,13 +100,37 @@ public class DataStoreBlobStoreTest {
assertEquals(testDI.toString(), ds.writeBlob(new
ByteArrayInputStream(data)));
}
+ @Test
+ public void testReference() throws DataStoreException, IOException {
+ String reference = "testReference";
+ String blobId = "test";
+ DataIdentifier testDI = new DataIdentifier(blobId);
+ DataRecord testDR = new ByteArrayDataRecord("foo".getBytes(), testDI,
reference);
+
+ DataStore mockedDS = mock(DataStore.class);
+ when(mockedDS.getRecordFromReference(reference)).thenReturn(testDR);
+ when(mockedDS.getRecord(testDI)).thenReturn(testDR);
+ DataStoreBlobStore ds = new DataStoreBlobStore(mockedDS);
+
+ assertEquals(reference,ds.getReference(blobId));
+ assertEquals(blobId, ds.getBlobId(reference));
+
+ String inMemBlobId = InMemoryDataRecord.getInstance("foo".getBytes())
+ .getIdentifier().toString();
+
+ //For in memory record the reference should be null
+ assertNull(ds.getReference(inMemBlobId));
+ }
+
private static class ByteArrayDataRecord implements DataRecord {
private final byte[] data;
private final DataIdentifier identifier;
+ private final String reference;
- private ByteArrayDataRecord(byte[] data, DataIdentifier di) {
+ private ByteArrayDataRecord(byte[] data, DataIdentifier di, String
reference) {
this.data = data;
this.identifier = di;
+ this.reference = reference;
}
@Override
@@ -115,7 +140,7 @@ public class DataStoreBlobStoreTest {
@Override
public String getReference() {
- return null;
+ return reference;
}
@Override