[ACS-9379] NodeRefRadixHasher no longer expects UUID format for NodeRef.id. Every not empty string works. Backward compatible. (#3247)

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>
This commit is contained in:
cezary-witkowski
2025-03-17 14:17:54 +01:00
committed by GitHub
parent 6a26cb0e0f
commit d282e347dc
12 changed files with 906 additions and 403 deletions

View File

@@ -0,0 +1,39 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.virtual.ref;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Creates and looks up string hash codes of id part of {@link NodeRef}s.<br>
*/
interface NodeIdHasher
{
String lookup(String idHash);
String hash(String id);
}

View File

@@ -32,7 +32,7 @@ import org.alfresco.util.Pair;
/** /**
* Creates and looks up string-pair hash codes of {@link NodeRef}s.<br> * Creates and looks up string-pair hash codes of {@link NodeRef}s.<br>
*/ */
public interface NodeRefHasher interface NodeRefHasher
{ {
NodeRef lookup(Pair<String, String> hash); NodeRef lookup(Pair<String, String> hash);

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -26,27 +26,25 @@
package org.alfresco.repo.virtual.ref; package org.alfresco.repo.virtual.ref;
import java.math.BigInteger; import java.util.regex.Pattern;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.apache.commons.lang3.StringUtils;
/** /**
* Creates string-pair hashes of {@link NodeRef}s where the first string is a * Creates string-pair hashes of {@link NodeRef}s where the first string is a stored hash combination for {@link NodeRef} store elements (protocol and id) and the second is a radix 36 encoded {@link NodeRef} id.
* stored hash combination for {@link NodeRef} store elements (protocol and id)
* and the second is a radix 36 encoded {@link NodeRef} id.
*/ */
public class NodeRefRadixHasher implements NodeRefHasher class NodeRefRadixHasher implements NodeRefHasher
{ {
public static final NodeRefRadixHasher RADIX_36_HASHER = new NodeRefRadixHasher(36); public static final NodeRefRadixHasher RADIX_36_HASHER = new NodeRefRadixHasher(36);
private HashStore storeProtocolStore; static final Pattern UUID_PATTERN = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
static final String NOT_UUID_FORMAT_MARKER = "X";
private HashStore storeIdStore; private final StoreRefHasher storeRefHasher;
private final NodeIdHasher uuidNodeIdHasher;
private int radix; private final NodeIdHasher notUuidNodeIdHasher;
public NodeRefRadixHasher() public NodeRefRadixHasher()
{ {
@@ -56,87 +54,51 @@ public class NodeRefRadixHasher implements NodeRefHasher
public NodeRefRadixHasher(int radix) public NodeRefRadixHasher(int radix)
{ {
super(); super();
this.radix = radix; this.storeRefHasher = new StoredStoreRefHasher();
this.storeProtocolStore = HashStoreConfiguration.getInstance().getStoreProtocolStore(); this.uuidNodeIdHasher = new UuidNodeIdRadixHasher(radix);
this.storeIdStore = HashStoreConfiguration.getInstance().getStoreIdStore(); this.notUuidNodeIdHasher = new NotUuidNodeIdRadixHasher(radix);
} }
@Override @Override
public Pair<String, String> hash(NodeRef nodeRef) public Pair<String, String> hash(NodeRef nodeRef)
{ {
String uuid = nodeRef.getId();
if (uuid.length() != 36)
{
throw new RuntimeException("Invalid noderf id length " + uuid);
}
String bigInt16String = uuid.replaceAll("-",
"");
if (bigInt16String.length() != 32)
{
throw new RuntimeException("Invalid noderf id format " + uuid);
}
BigInteger bigIntId = new BigInteger(bigInt16String,
16);
StoreRef storeRef = nodeRef.getStoreRef(); StoreRef storeRef = nodeRef.getStoreRef();
String storeProtocolHash = storeProtocolStore.hash(storeRef.getProtocol()); String storeHash = storeRefHasher.hash(storeRef);
String storeIdHash = storeIdStore.hash(storeRef.getIdentifier());
if (storeProtocolHash == null || storeIdHash == null)
{
throw new RuntimeException("Missing hash for " + storeRef);
}
String storeHash = storeProtocolHash + storeIdHash;
return new Pair<String, String>(storeHash,
bigIntId.toString(radix));
String id = nodeRef.getId();
String idHash = idToIdHash(id);
return new Pair<>(storeHash, idHash);
}
private String idToIdHash(String id)
{
if (UUID_PATTERN.matcher(id).matches())
{
return uuidNodeIdHasher.hash(id);
}
return NOT_UUID_FORMAT_MARKER + notUuidNodeIdHasher.hash(id);
} }
@Override @Override
public NodeRef lookup(Pair<String, String> hash) public NodeRef lookup(Pair<String, String> hash)
{ {
String storeHash = hash.getFirst(); String storeHash = hash.getFirst();
String storeProtocolHash = storeHash.substring(0, StoreRef storeRef = storeRefHasher.lookup(storeHash);
1);
String storeIdHash = storeHash.substring(1,
2);
String storeProtocol = storeProtocolStore.lookup(storeProtocolHash); String hashId = hash.getSecond();
String storeId = storeIdStore.lookup(storeIdHash); String id = hashIdToId(hashId);
if (storeProtocol == null || storeId == null)
{ return new NodeRef(storeRef, id);
throw new RuntimeException("Lookup found no protocol or id for " + storeHash);
} }
BigInteger nodeId = new BigInteger(hash.getSecond(),
radix); private String hashIdToId(String hashId)
String nodeIdHexa = nodeId.toString(16);
nodeIdHexa = StringUtils.leftPad(nodeIdHexa,
32,
"0");
int leadZeros = 32 - nodeIdHexa.length();
if (leadZeros > 0)
{ {
} if (hashId.startsWith(NOT_UUID_FORMAT_MARKER))
String groups[] = new String[5];
groups[0] = nodeIdHexa.substring(0,
8);
groups[1] = nodeIdHexa.substring(8,
12);
groups[2] = nodeIdHexa.substring(12,
16);
groups[3] = nodeIdHexa.substring(16,
20);
groups[4] = nodeIdHexa.substring(20,
32);
StringBuilder idBuilder = new StringBuilder(groups[0]);
for (int i = 1; i < groups.length; i++)
{ {
idBuilder.append("-"); String hashIdWithoutMarker = hashId.substring(NOT_UUID_FORMAT_MARKER.length());
idBuilder.append(groups[i]); return notUuidNodeIdHasher.lookup(hashIdWithoutMarker);
} }
return new NodeRef(storeProtocol, return uuidNodeIdHasher.lookup(hashId);
storeId,
idBuilder.toString());
} }
} }

View File

@@ -0,0 +1,57 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.virtual.ref;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.math.BigInteger;
class NotUuidNodeIdRadixHasher implements NodeIdHasher
{
private final int radix;
public NotUuidNodeIdRadixHasher(int radix)
{
this.radix = radix;
}
@Override
public String lookup(String hashId)
{
BigInteger hashIdBigInt = new BigInteger(hashId, radix);
byte[] hashIdBytes = hashIdBigInt.toByteArray();
return new String(hashIdBytes);
}
@Override
public String hash(String id)
{
byte[] bytes = id.getBytes(UTF_8);
BigInteger bigIntegerBytes = new BigInteger(bytes);
return bigIntegerBytes.toString(radix);
}
}

View File

@@ -0,0 +1,40 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.virtual.ref;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
/**
* Creates and looks up string hash codes of {@link StoreRef} part of {@link NodeRef}s.<br>
*/
interface StoreRefHasher
{
StoreRef lookup(String storeHash);
String hash(StoreRef storeRef);
}

View File

@@ -0,0 +1,68 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.virtual.ref;
import org.alfresco.service.cmr.repository.StoreRef;
class StoredStoreRefHasher implements StoreRefHasher
{
private final HashStore storeProtocolStore;
private final HashStore storeIdStore;
public StoredStoreRefHasher()
{
this.storeProtocolStore = HashStoreConfiguration.getInstance().getStoreProtocolStore();
this.storeIdStore = HashStoreConfiguration.getInstance().getStoreIdStore();
}
@Override
public StoreRef lookup(String storeHash)
{
String storeProtocolHash = storeHash.substring(0, 1);
String storeIdHash = storeHash.substring(1, 2);
String storeProtocol = storeProtocolStore.lookup(storeProtocolHash);
String storeId = storeIdStore.lookup(storeIdHash);
if (storeProtocol == null || storeId == null)
{
throw new RuntimeException("Lookup found no protocol or id for " + storeHash);
}
return new StoreRef(storeProtocol, storeId);
}
@Override
public String hash(StoreRef storeRef)
{
String storeProtocolHash = storeProtocolStore.hash(storeRef.getProtocol());
String storeIdHash = storeIdStore.hash(storeRef.getIdentifier());
if (storeProtocolHash == null || storeIdHash == null)
{
throw new RuntimeException("Missing hash for " + storeRef);
}
return storeProtocolHash + storeIdHash;
}
}

View File

@@ -0,0 +1,63 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.virtual.ref;
import java.math.BigInteger;
import org.apache.commons.lang3.StringUtils;
class UuidNodeIdRadixHasher implements NodeIdHasher
{
private final int radix;
public UuidNodeIdRadixHasher(int radix)
{
this.radix = radix;
}
@Override
public String lookup(String hashUuid)
{
BigInteger nodeId = new BigInteger(hashUuid, radix);
String nodeIdHex = nodeId.toString(16);
String paddedNodeIdHex = StringUtils.leftPad(nodeIdHex, 32, "0");
return String.join("-",
paddedNodeIdHex.substring(0, 8),
paddedNodeIdHex.substring(8, 12),
paddedNodeIdHex.substring(12, 16),
paddedNodeIdHex.substring(16, 20),
paddedNodeIdHex.substring(20, 32));
}
@Override
public String hash(String uuid)
{
String uuidWithoutDashes = uuid.replaceAll("-", "");
BigInteger bigIntUuidWithoutDashesHex = new BigInteger(uuidWithoutDashes, 16);
return bigIntUuidWithoutDashesHex.toString(radix);
}
}

View File

@@ -25,6 +25,10 @@
*/ */
package org.alfresco; package org.alfresco;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.alfresco.repo.security.authentication.identityservice.ClientRegistrationProviderUnitTest; import org.alfresco.repo.security.authentication.identityservice.ClientRegistrationProviderUnitTest;
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBeanTest; import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBeanTest;
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceJITProvisioningHandlerUnitTest; import org.alfresco.repo.security.authentication.identityservice.IdentityServiceJITProvisioningHandlerUnitTest;
@@ -35,17 +39,13 @@ import org.alfresco.repo.security.authentication.identityservice.admin.AdminCons
import org.alfresco.repo.security.authentication.identityservice.admin.IdentityServiceAdminConsoleAuthenticatorUnitTest; import org.alfresco.repo.security.authentication.identityservice.admin.IdentityServiceAdminConsoleAuthenticatorUnitTest;
import org.alfresco.util.testing.category.DBTests; import org.alfresco.util.testing.category.DBTests;
import org.alfresco.util.testing.category.NonBuildTests; import org.alfresco.util.testing.category.NonBuildTests;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/** /**
* All Repository project UNIT test classes (no application context) should be added to this test suite. * All Repository project UNIT test classes (no application context) should be added to this test suite. Tests marked as DBTests are automatically excluded and are run as part of {@link AllDBTestsTestSuite}.
* Tests marked as DBTests are automatically excluded and are run as part of {@link AllDBTestsTestSuite}.
*/ */
@RunWith(Categories.class) @RunWith(Categories.class)
@Categories.ExcludeCategory({DBTests.class, NonBuildTests.class}) @Categories.ExcludeCategory({DBTests.class, NonBuildTests.class})
@Suite.SuiteClasses({ @Suite.SuiteClasses(value = {
org.alfresco.repo.site.SiteMembershipTest.class, org.alfresco.repo.site.SiteMembershipTest.class,
org.alfresco.encryption.EncryptorTest.class, org.alfresco.encryption.EncryptorTest.class,
org.alfresco.encryption.KeyStoreKeyProviderTest.class, org.alfresco.encryption.KeyStoreKeyProviderTest.class,
@@ -179,6 +179,9 @@ import org.junit.runners.Suite;
org.alfresco.repo.virtual.ref.HashStringifierTest.class, org.alfresco.repo.virtual.ref.HashStringifierTest.class,
org.alfresco.repo.virtual.ref.NodeRefRadixHasherTest.class, org.alfresco.repo.virtual.ref.NodeRefRadixHasherTest.class,
org.alfresco.repo.virtual.ref.StoredStoreRefHasherTest.class,
org.alfresco.repo.virtual.ref.UuidNodeIdRadixHasherTest.class,
org.alfresco.repo.virtual.ref.NotUuidNodeIdRadixHasherTest.class,
org.alfresco.repo.virtual.ref.NumericPathHasherTest.class, org.alfresco.repo.virtual.ref.NumericPathHasherTest.class,
org.alfresco.repo.virtual.ref.StoredPathHasherTest.class, org.alfresco.repo.virtual.ref.StoredPathHasherTest.class,
@@ -265,5 +268,4 @@ import org.junit.runners.Suite;
org.alfresco.repo.serviceaccount.ServiceAccountRegistryImplTest.class org.alfresco.repo.serviceaccount.ServiceAccountRegistryImplTest.class
}) })
public class AllUnitTestsSuite public class AllUnitTestsSuite
{ {}
}

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -26,119 +26,98 @@
package org.alfresco.repo.virtual.ref; package org.alfresco.repo.virtual.ref;
import junit.framework.TestCase; import static org.junit.Assert.*;
import static org.junit.runners.Parameterized.*;
import static org.alfresco.repo.version.VersionModel.STORE_ID;
import static org.alfresco.service.cmr.repository.StoreRef.*;
import static org.alfresco.service.cmr.repository.StoreRef.PROTOCOL_DELETED;
import static org.alfresco.service.cmr.version.VersionService.VERSION_STORE_PROTOCOL;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.alfresco.repo.version.Version2Model; import org.alfresco.repo.version.Version2Model;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
public class NodeRefRadixHasherTest extends TestCase @RunWith(Parameterized.class)
public class NodeRefRadixHasherTest
{ {
private static Log logger = LogFactory.getLog(NodeRefRadixHasherTest.class); private final NodeRefRadixHasher nodeRefRadixHasher;
@Parameters(name = "radix: {0}")
public static Collection<Object[]> data()
{
return List.of(
new Object[]{NodeRefRadixHasher.RADIX_36_HASHER},
new Object[]{new NodeRefRadixHasher()},
new Object[]{new NodeRefRadixHasher(32)});
}
public NodeRefRadixHasherTest(NodeRefRadixHasher nodeRefRadixHasher)
{
this.nodeRefRadixHasher = nodeRefRadixHasher;
}
@Test @Test
public void testSupportedStores() throws Exception public void testSupportedStoresWithRandomUuids()
{ {
NodeRefRadixHasher h = NodeRefRadixHasher.RADIX_36_HASHER; List<String> storeProtocols = List.of(PROTOCOL_WORKSPACE, PROTOCOL_ARCHIVE, PROTOCOL_AVM, PROTOCOL_DELETED, VERSION_STORE_PROTOCOL);
List<String> storeIds = List.of("SpacesStore", STORE_ID, Version2Model.STORE_ID);
List<String> uuidNodeIds = Stream.generate(UUID::randomUUID)
.map(UUID::toString)
.limit(5)
.toList();
String[] storeProtocols = new String[] { StoreRef.PROTOCOL_WORKSPACE, StoreRef.PROTOCOL_ARCHIVE, for (String storeProtocol : storeProtocols)
StoreRef.PROTOCOL_AVM, StoreRef.PROTOCOL_DELETED, VersionService.VERSION_STORE_PROTOCOL };
String[] storeIds = new String[] { "SpacesStore", VersionModel.STORE_ID, Version2Model.STORE_ID };
for (int i = 0; i < storeProtocols.length; i++)
{ {
for (int j = 0; j < storeIds.length; j++) for (String storeId : storeIds)
{ {
NodeRef nr = new NodeRef(storeProtocols[i], for (String uuidNodeId : uuidNodeIds)
storeIds[j], {
"0d3b26ff-c4c1-4680-8622-8608ea7ab4b2"); NodeRef nodeRef = new NodeRef(storeProtocol, storeId, uuidNodeId);
Pair<String, String> nh = h.hash(nr); Pair<String, String> hash = nodeRefRadixHasher.hash(nodeRef);
NodeRef nr2 = h.lookup(nh); assertFalse(hash.getSecond().startsWith(NodeRefRadixHasher.NOT_UUID_FORMAT_MARKER));
assertEquals("Could match hash-lookup " + nr, NodeRef actualNodeRef = nodeRefRadixHasher.lookup(hash);
nr, assertEquals(nodeRef, actualNodeRef);
nr2); }
} }
} }
} }
@Test @Test
public void testZeroPaddedNodeId() throws Exception public void testSpecificValidNotUuidNodeIds()
{ {
NodeRefRadixHasher h = NodeRefRadixHasher.RADIX_36_HASHER; StoreRef storeRef = new StoreRef(PROTOCOL_WORKSPACE, STORE_ID);
NodeRef nr = new NodeRef("workspace://SpacesStore/0d3b26ff-c4c1-4680-8622-8608ea7ab4b2"); List<String> notUuidNodeIds = List.of(
Pair<String, String> nh = h.hash(nr); "0d3b26ff-c4c1-4680-8622-8608ea7ab4",
NodeRef nr2 = h.lookup(nh); "0d3b26ff-c4c14680-8622-8608ea7ab4b29",
assertEquals(nr, "wf-email-html-ftl",
nr2); "a",
} NodeRefRadixHasher.NOT_UUID_FORMAT_MARKER,
"defrobldkfoeirjtuy79dfwwqperfiidoelb");
@Test for (String notUuidNodeId : notUuidNodeIds)
public void testInvalidStoreId() throws Exception
{ {
NodeRefRadixHasher h = NodeRefRadixHasher.RADIX_36_HASHER; NodeRef nodeRef = new NodeRef(storeRef, notUuidNodeId);
NodeRef nr = new NodeRef("workspace://ASpacesStore/0d3b26ff-c4c1-4680-8622-8608ea7ab4b2"); Pair<String, String> hash = nodeRefRadixHasher.hash(nodeRef);
try assertTrue(hash.getSecond().startsWith(NodeRefRadixHasher.NOT_UUID_FORMAT_MARKER));
{ NodeRef actualNodeRef = nodeRefRadixHasher.lookup(hash);
h.hash(nr); assertEquals(nodeRef, actualNodeRef);
fail("Should not be able to hash invalid store NodeRef " + nr);
}
catch (RuntimeException e)
{
logger.info("Caught invalid NodeRef " + e.getMessage());
} }
} }
@Test @Test(expected = RuntimeException.class)
public void testInvalidStoreProtocol() throws Exception public void testEmptyNodeId()
{ {
NodeRefRadixHasher h = NodeRefRadixHasher.RADIX_36_HASHER; NodeRef nodeRef = new NodeRef("workspace://SpacesStore/");
NodeRef nr = new NodeRef("Xworkspace://SpacesStore/0d3b26ff-c4c1-4680-8622-8608ea7ab4b2"); nodeRefRadixHasher.hash(nodeRef);
try
{
h.hash(nr);
fail("Should not be able to hash invalid store NodeRef " + nr);
}
catch (RuntimeException e)
{
logger.info("Caught invalid NodeRef " + e.getMessage());
}
}
@Test
public void testInvalidNodeId1() throws Exception
{
NodeRefRadixHasher h = NodeRefRadixHasher.RADIX_36_HASHER;
NodeRef nr = new NodeRef("workspace://SpacesStore/0d3b26ff-c4c1-4680-8622-8608ea7ab4");
try
{
h.hash(nr);
fail("Should not be able to hash invalid id (length) NodeRef " + nr);
}
catch (RuntimeException e)
{
logger.info("Caught invalid NodeRef " + e.getMessage());
}
}
@Test
public void testInvalidNodeId2() throws Exception
{
NodeRefRadixHasher h = NodeRefRadixHasher.RADIX_36_HASHER;
NodeRef nr = new NodeRef("workspace://SpacesStore/0d3b26ff-c4c14680-8622-8608ea7ab4b29");
try
{
h.hash(nr);
fail("Should not be able to hash invalid id (format) NodeRef " + nr);
}
catch (RuntimeException e)
{
logger.info("Caught invalid NodeRef " + e.getMessage());
}
} }
} }

View File

@@ -0,0 +1,100 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.virtual.ref;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class NotUuidNodeIdRadixHasherTest
{
@Test
public void testRadix36Hashing()
{
NodeIdHasher nodeIdHasher = new NotUuidNodeIdRadixHasher(36);
String id = "wf-email-html-ftl";
String hashedId = nodeIdHasher.hash(id);
String expected = "1e9lat6m0tvszgcle5scyylab8s";
assertEquals(expected, hashedId);
}
@Test
public void testRadix36Lookup()
{
NodeIdHasher nodeIdHasher = new NotUuidNodeIdRadixHasher(36);
String hashedUuid = "1e9lat6m0tvszgcle5scyylab8s";
String id = nodeIdHasher.lookup(hashedUuid);
String expected = "wf-email-html-ftl";
assertEquals(expected, id);
}
@Test
public void testRadix16Hashing()
{
NodeIdHasher nodeIdHasher = new NotUuidNodeIdRadixHasher(16);
String id = "wf-email-html-ftl";
String hashedId = nodeIdHasher.hash(id);
String expected = "77662d656d61696c2d68746d6c2d66746c";
assertEquals(expected, hashedId);
}
@Test
public void testRadix16Lookup()
{
NodeIdHasher nodeIdHasher = new NotUuidNodeIdRadixHasher(16);
String hashedUuid = "77662d656d61696c2d68746d6c2d66746c";
String id = nodeIdHasher.lookup(hashedUuid);
String expected = "wf-email-html-ftl";
assertEquals(expected, id);
}
@Test(expected = RuntimeException.class)
public void testHashForEmptyUuid()
{
NodeIdHasher nodeIdHasher = new NotUuidNodeIdRadixHasher(36);
String uuid = "";
nodeIdHasher.hash(uuid);
}
@Test(expected = RuntimeException.class)
public void testLookupForEmptyUuid()
{
NodeIdHasher nodeIdHasher = new NotUuidNodeIdRadixHasher(36);
String hashedUuid = "";
nodeIdHasher.lookup(hashedUuid);
}
}

View File

@@ -0,0 +1,91 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.virtual.ref;
import static org.junit.Assert.assertEquals;
import static org.alfresco.repo.version.VersionModel.STORE_ID;
import static org.alfresco.service.cmr.repository.StoreRef.*;
import static org.alfresco.service.cmr.version.VersionService.VERSION_STORE_PROTOCOL;
import java.util.List;
import org.junit.Test;
import org.alfresco.repo.version.Version2Model;
import org.alfresco.service.cmr.repository.StoreRef;
public class StoredStoreRefHasherTest
{
private final StoreRefHasher storeRefHasher = new StoredStoreRefHasher();
@Test
public void testSupportedStores()
{
List<String> storeProtocols = List.of(PROTOCOL_WORKSPACE, PROTOCOL_ARCHIVE, PROTOCOL_AVM, PROTOCOL_DELETED, VERSION_STORE_PROTOCOL);
List<String> storeIds = List.of("SpacesStore", STORE_ID, Version2Model.STORE_ID);
for (String storeProtocol : storeProtocols)
{
for (String storeId : storeIds)
{
StoreRef storeRef = new StoreRef(storeProtocol, storeId);
String hash = storeRefHasher.hash(storeRef);
StoreRef actualStoreRef = storeRefHasher.lookup(hash);
assertEquals(storeRef, actualStoreRef);
}
}
}
@Test(expected = RuntimeException.class)
public void testHashInvalidStoreId()
{
StoreRef storeRef = new StoreRef(PROTOCOL_WORKSPACE, "ASpacesStore");
storeRefHasher.hash(storeRef);
}
@Test(expected = RuntimeException.class)
public void testHashInvalidStoreProtocol()
{
StoreRef storeRef = new StoreRef("Xworkspace", STORE_ID);
storeRefHasher.hash(storeRef);
}
@Test(expected = RuntimeException.class)
public void testLookupInvalidStoreId()
{
storeRefHasher.lookup("91");
}
@Test(expected = RuntimeException.class)
public void testLookupInvalidStoreProtocol()
{
storeRefHasher.lookup("19");
}
}

View File

@@ -0,0 +1,102 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2025 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.virtual.ref;
import static org.junit.Assert.*;
import org.junit.Test;
public class UuidNodeIdRadixHasherTest
{
@Test
public void testRadix36Hashing()
{
NodeIdHasher nodeIdHasher = new UuidNodeIdRadixHasher(36);
String uuid = "0d3b26ff-c4c1-4680-8622-8608ea7ab4b2";
String hashedUuid = nodeIdHasher.hash(uuid);
String expected = "s765ou6qn3lf446dbvrkv3qq";
assertEquals(expected, hashedUuid);
}
@Test
public void testRadix36Lookup()
{
NodeIdHasher nodeIdHasher = new UuidNodeIdRadixHasher(36);
String hashedUuid = "s765ou6qn3lf446dbvrkv3qq";
String uuid = nodeIdHasher.lookup(hashedUuid);
String expected = "0d3b26ff-c4c1-4680-8622-8608ea7ab4b2";
assertEquals(expected, uuid);
}
@Test
public void testRadix16Hashing()
{
NodeIdHasher nodeIdHasher = new UuidNodeIdRadixHasher(16);
String uuid = "0d3b26ff-c4c1-4680-8622-8608ea7ab4b2";
String hashedUuid = nodeIdHasher.hash(uuid);
// pragma: allowlist nextline secret its just hashed random uuid
String expected = "d3b26ffc4c1468086228608ea7ab4b2";
assertEquals(expected, hashedUuid);
}
@Test
public void testRadix16Lookup()
{
NodeIdHasher nodeIdHasher = new UuidNodeIdRadixHasher(16);
// pragma: allowlist nextline secret its just hashed random uuid
String hashedUuid = "d3b26ffc4c1468086228608ea7ab4b2";
String uuid = nodeIdHasher.lookup(hashedUuid);
String expected = "0d3b26ff-c4c1-4680-8622-8608ea7ab4b2";
assertEquals(expected, uuid);
}
@Test(expected = RuntimeException.class)
public void testHashForEmptyUuid()
{
NodeIdHasher nodeIdHasher = new UuidNodeIdRadixHasher(36);
String uuid = "";
nodeIdHasher.hash(uuid);
}
@Test(expected = RuntimeException.class)
public void testLookupForEmptyUuid()
{
NodeIdHasher nodeIdHasher = new UuidNodeIdRadixHasher(36);
String hashedUuid = "";
nodeIdHasher.lookup(hashedUuid);
}
}