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


Reply via email to