forked from sovity/edc-ce
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: stable contract offer id in DspCatalogService (sovity#795)
- Loading branch information
Showing
7 changed files
with
112 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
...atalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspContractOfferUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package de.sovity.edc.utils.catalog.mapper; | ||
|
||
import de.sovity.edc.utils.JsonUtils; | ||
import de.sovity.edc.utils.jsonld.JsonLdUtils; | ||
import de.sovity.edc.utils.jsonld.vocab.Prop; | ||
import jakarta.json.Json; | ||
import jakarta.json.JsonObject; | ||
import lombok.val; | ||
import org.eclipse.edc.connector.contract.spi.ContractId; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.security.MessageDigest; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.util.Base64; | ||
|
||
public class DspContractOfferUtils { | ||
|
||
/** | ||
* /!\ Workaround | ||
* <p> | ||
* The Eclipse EDC uses a new random UUID for each policy that it returns and in turn a new contract ID. | ||
* This Eclipse ID can't be used as such. | ||
* As a workaround, we must introduce our own ID. | ||
* For a first iteration, we will assume that the content of the policy remains the same (same content, same order) | ||
* and hash it to use it as a key. | ||
* | ||
* @param contract The contract to compute an ID from | ||
* @return A base64 string that can be used as an id for the {@code contract} | ||
*/ | ||
public static String buildStableId(JsonObject contract) { | ||
// FIXME: This doesn't enforce any property order and may cause trouble if the returned policy schema is not consistent. | ||
// Use canonical form if needed later. | ||
val noId = Json.createObjectBuilder(contract).remove(Prop.ID).build(); | ||
val policyId = hash(noId); | ||
|
||
val currentId = ContractId.parseId(JsonLdUtils.string(contract, Prop.ID)) | ||
.orElseThrow((failure) -> { | ||
throw new RuntimeException("Failed to parse the contract id: " + failure.getFailureDetail()); | ||
}); | ||
|
||
return currentId.definitionPart() + ":" + currentId.assetIdPart() + ":" + policyId; | ||
} | ||
|
||
@NotNull | ||
private static String hash(JsonObject noId) { | ||
val policyJsonString = JsonUtils.toJson(noId); | ||
val sha1 = sha1(policyJsonString); | ||
// encoding with base16 to make the hash readable to humans (similarly to how the random UUID would have been readable) | ||
val base16 = toBase16(sha1); | ||
return toBase64(base16); | ||
} | ||
|
||
@NotNull | ||
private static String toBase64(String string) { | ||
byte[] stringBytes = string.getBytes(StandardCharsets.UTF_8); | ||
byte[] bytes = Base64.getEncoder().encode(stringBytes); | ||
return new String(bytes); | ||
} | ||
|
||
@NotNull | ||
private static String toBase16(byte[] bytes) { | ||
val sb = new StringBuilder(); | ||
for (byte b : bytes) { | ||
sb.append(Character.forDigit(b >> 4 & 0xf, 16)); | ||
sb.append(Character.forDigit(b & 0xf, 16)); | ||
} | ||
return sb.toString(); | ||
} | ||
|
||
private static byte[] sha1(String string) { | ||
try { | ||
return MessageDigest.getInstance("sha-1").digest(string.getBytes()); | ||
} catch (NoSuchAlgorithmException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
...og-parser/src/test/java/de/sovity/edc/utils/catalog/mapper/DspContractOfferUtilsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package de.sovity.edc.utils.catalog.mapper; | ||
|
||
import jakarta.json.Json; | ||
import lombok.val; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; | ||
|
||
class DspContractOfferUtilsTest { | ||
@Test | ||
void testCanConvertTheRandomIdToStableId() { | ||
// arrange | ||
val contractOffer = Json.createObjectBuilder() | ||
.add("@id", "part1:part2:part3") | ||
.add("somefield", "somevalue") | ||
.build(); | ||
|
||
// act | ||
val result = DspContractOfferUtils.buildStableId(contractOffer); | ||
|
||
// assert | ||
assertThat(result).isEqualTo("part1:part2:MjliNzcwMjdjMzA2YzE3ZGYyZWNhZjY2OGI3OTYxY2U5OTY1YmExNw=="); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters