Skip to content

Commit

Permalink
Merge pull request #360 from trellis-ldp/trellis-359
Browse files Browse the repository at this point in the history
Remove support for Digest/Want-Digest
  • Loading branch information
acoburn authored Feb 12, 2019
2 parents 6c9c59a + 53d7484 commit 73844e2
Show file tree
Hide file tree
Showing 32 changed files with 40 additions and 852 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ public class CORSConfiguration {
"GET", "HEAD", "OPTIONS", "POST");

private List<String> allowHeaders = asList("Content-Type", "Link", "Accept",
"Accept-Datetime", "Prefer", "Want-Digest", "Slug", "Digest", "Origin");
"Accept-Datetime", "Prefer", "Slug", "Origin");

private List<String> exposeHeaders = asList("Content-Type", "Link",
"Memento-Datetime", "Preference-Applied", "Location",
"Accept-Patch", "Accept-Post", "Digest", "Accept-Ranges", "ETag", "Vary");
"Accept-Patch", "Accept-Post", "Accept-Ranges", "ETag", "Vary");

private boolean allowCredentials = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public void testConfigurationCORS1() throws Exception {

assertTrue(config.getCors().getEnabled(), "CORS not enabled!");
assertTrue(config.getCors().getAllowOrigin().contains("*"), "'*' not in CORS allow-origin!");
assertTrue(config.getCors().getAllowHeaders().contains("Want-Digest"), "want-digest not in CORS allow-headers");
assertTrue(config.getCors().getAllowHeaders().contains("Link"), "Link not in CORS allow-headers");
assertTrue(config.getCors().getAllowMethods().contains("PUT"), "PUT not in CORS allow-methods!");
assertTrue(config.getCors().getExposeHeaders().contains("Memento-Datetime"),
"memento-datetime missing from CORS expose-headers!");
Expand Down
1 change: 0 additions & 1 deletion components/app/src/test/resources/config1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ cors:
- "PATCH"
allowHeaders:
- "Content-Type"
- "Want-Digest"
- "Link"
exposeHeaders:
- "Link"
Expand Down
1 change: 0 additions & 1 deletion components/file/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ dependencies {
api("org.glassfish.hk2.external:javax.inject:$javaxInjectVersion")
api project(':trellis-api')

implementation("commons-codec:commons-codec:$commonsCodecVersion")
implementation("commons-io:commons-io:$commonsIoVersion")
implementation("org.apache.commons:commons-rdf-jena:$commonsRdfVersion") {
exclude group: 'org.apache.jena', module: 'jena-osgi'
Expand Down
1 change: 0 additions & 1 deletion components/file/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
requires transitive org.trellisldp.api;
requires transitive org.trellisldp.vocabulary;

requires org.apache.commons.codec;
requires org.apache.commons.io;
requires org.apache.commons.rdf.api;
requires org.apache.commons.rdf.jena;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,19 @@
import static java.nio.file.Files.copy;
import static java.nio.file.Files.delete;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.of;
import static java.util.ServiceLoader.load;
import static java.util.concurrent.CompletableFuture.supplyAsync;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.codec.digest.DigestUtils.updateDigest;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.MD2;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.MD5;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA3_256;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA3_384;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA3_512;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_1;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_256;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_384;
import static org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_512;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.URI;
import java.security.MessageDigest;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import java.util.function.Supplier;

Expand All @@ -61,24 +46,6 @@

/**
* A {@link BinaryService} implementation that stores LDP-NR resources as files on a local filesystem.
*
* <p>This service supports the following digest algorithms:
* <ul>
* <li>MD5</li>
* <li>MD2</li>
* <li>SHA</li>
* <li>SHA-1</li>
* <li>SHA-256</li>
* <li>SHA-384</li>
* <li>SHA-512</li>
* </ul>
*
* <p>When running under JDK 9+, the following additional digest algorithms are supported:
* <ul>
* <li>SHA3-256</li>
* <li>SHA3-384</li>
* <li>SHA3-512</li>
* </ul>
*/
public class FileBinaryService implements BinaryService {

Expand All @@ -92,14 +59,9 @@ public class FileBinaryService implements BinaryService {
public static final String CONFIG_FILE_BINARY_LENGTH = "trellis.file.binary.length";

private static final Logger LOGGER = getLogger(FileBinaryService.class);
private static final String SHA = "SHA";
private static final int DEFAULT_HIERARCHY = 3;
private static final int DEFAULT_LENGTH = 2;

private static final Set<String> algorithms = asList(MD5, MD2, SHA, SHA_1, SHA_256, SHA_384, SHA_512,
SHA3_256, SHA3_384, SHA3_512).stream()
.collect(toSet());

private final String basePath;
private final Supplier<String> idSupplier;

Expand Down Expand Up @@ -176,16 +138,6 @@ public CompletionStage<Void> setContent(final BinaryMetadata metadata, final Inp
});
}

@Override
public CompletionStage<MessageDigest> calculateDigest(final IRI identifier, final MessageDigest algorithm) {
return supplyAsync(() -> computeDigest(identifier, algorithm));
}

@Override
public Set<String> supportedAlgorithms() {
return algorithms;
}

@Override
public String generateIdentifier() {
return idSupplier.get();
Expand All @@ -198,14 +150,6 @@ private File getFileFromIdentifier(final IRI identifier) {
.orElseThrow(() -> new IllegalArgumentException("Could not create File object from IRI: " + identifier));
}

private MessageDigest computeDigest(final IRI identifier, final MessageDigest algorithm) {
try (final InputStream input = new FileInputStream(getFileFromIdentifier(identifier))) {
return updateDigest(algorithm, input);
} catch (final IOException ex) {
throw new UncheckedIOException("Error computing digest", ex);
}
}

private static String trimStart(final String str, final String trim) {
if (str.startsWith(trim)) {
return trimStart(str.substring(trim.length()), trim);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,14 @@
package org.trellisldp.file;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Base64.getEncoder;
import static org.apache.commons.codec.digest.DigestUtils.getDigest;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.condition.JRE.JAVA_8;
import static org.mockito.Mockito.mock;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.concurrent.CompletionException;

Expand All @@ -36,7 +32,6 @@
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.trellisldp.api.Binary;
import org.trellisldp.api.BinaryMetadata;
import org.trellisldp.api.BinaryService;
Expand Down Expand Up @@ -65,15 +60,6 @@ public static void cleanUp() {
System.clearProperty(FileBinaryService.CONFIG_FILE_BINARY_BASE_PATH);
}

@Test
public void testSupportedAlgorithms() {
final BinaryService service = new FileBinaryService();
assertTrue(service.supportedAlgorithms().contains("SHA"));
assertTrue(service.supportedAlgorithms().contains("SHA-1"));
assertTrue(service.supportedAlgorithms().contains("SHA-256"));
assertTrue(service.supportedAlgorithms().contains("MD5"));
}

@Test
public void testFilePurge() {
final BinaryService service = new FileBinaryService();
Expand Down Expand Up @@ -171,38 +157,6 @@ public void testGetFileSkipContentError() throws Exception {
}).toCompletableFuture().join());
}

@Test
public void testBase64Digest() throws IOException {
final BinaryService service = new FileBinaryService();
assertEquals("oZ1Y1O/8vs39RH31fh9lrA==", service.calculateDigest(file, getDigest("MD5"))
.thenApply(MessageDigest::digest).thenApply(getEncoder()::encodeToString)
.toCompletableFuture().join(), "Bad MD5 digest!");
assertEquals("QJuYLse9SK/As177lt+rSfixyH0=", service.calculateDigest(file, getDigest("SHA-1"))
.thenApply(MessageDigest::digest).thenApply(getEncoder()::encodeToString)
.toCompletableFuture().join(), "Bad SHA digest!");
assertThrows(CompletionException.class, () ->
service.calculateDigest(rdf.createIRI("file:///" + randomFilename()), getDigest("MD5"))
.toCompletableFuture().join(), "Computing digest on invalid file should throw an exception!");
}

@Test
@DisabledOnJre(JAVA_8)
public void testJdk9Digests() throws IOException {
final BinaryService service = new FileBinaryService();
assertEquals("FQgyH2yU2NhyMTZ7YDDKwV5vcWUBM1zq0uoIYUiHH+4=",
service.calculateDigest(file, getDigest("SHA3-256")).thenApply(MessageDigest::digest)
.thenApply(getEncoder()::encodeToString).toCompletableFuture().join(),
"Bad SHA3-256 digest!");
assertEquals("746UDLrFXM61gzI0FnoVT2S0Z7EmQUfhHnoSYwkR2MHzbBe6j9rMigQBfR8ApZUA",
service.calculateDigest(file, getDigest("SHA3-384")).thenApply(MessageDigest::digest)
.thenApply(getEncoder()::encodeToString).toCompletableFuture().join(),
"Bad SHA3-384 digest!");
assertEquals("Ecu/R0kV4eL0J/VOpyVA2Lz0T6qsJj9ioQ+QorJDztJeMj6uhf6zqyhZnu9zMYiwrkX8U4oWiZMDT/0fWjOyYg==",
service.calculateDigest(file, getDigest("SHA3-512")).thenApply(MessageDigest::digest)
.thenApply(getEncoder()::encodeToString).toCompletableFuture().join(),
"Bad SHA3-512 digest!");
}

@Test
public void testBadIdentifier() {
final BinaryService service = new FileBinaryService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.trellisldp.api.TrellisUtils.getInstance;
import static org.trellisldp.http.core.HttpConstants.DIGEST;
import static org.trellisldp.http.core.HttpConstants.SLUG;
import static org.trellisldp.http.core.HttpConstants.WANT_DIGEST;
import static org.trellisldp.http.core.RdfMediaType.APPLICATION_SPARQL_UPDATE;
import static org.trellisldp.http.core.RdfMediaType.TEXT_TURTLE_TYPE;
import static org.trellisldp.test.TestUtils.readEntityAsGraph;
Expand Down Expand Up @@ -127,23 +125,6 @@ default void testPostBinary() {
}
}

/**
* Test creating a new binary via POST with a digest header.
*/
@Test
@DisplayName("Test creating a new binary via POST with a digest header")
default void testPostBinaryWithDigest() {
// POST an LDP-NR
try (final Response res = target().request().header(DIGEST, "md5=bUMuG430lSc5B2PWyoNIgA==")
.header(SLUG, generateRandomValue(getClass().getSimpleName()))
.post(entity(CONTENT, TEXT_PLAIN))) {
assertAll("Check POSTing LDP-NR with digest", checkNonRdfResponse(res, null));
final String resource = res.getLocation().toString();
assertTrue(resource.startsWith(getBaseURL()), "Check the response location");
assertTrue(resource.length() > getBaseURL().length(), "Check for a nested response location");
}
}

/**
* Test modifying a binary's description via PATCH.
*/
Expand Down Expand Up @@ -211,44 +192,4 @@ default void testBinaryIsInContainer() {
rdf.createIRI(getResourceLocation())), "Check for an ldp:contains triple");
}
}

/**
* Test that the SHA digest is generated.
*/
@Test
@DisplayName("Test that the SHA digest is generated")
default void testBinaryWantDigestSha() {
// Test the SHA-1 algorithm
try (final Response res = target(getResourceLocation()).request().header(WANT_DIGEST, "SHA,MD5").get()) {
assertAll("Check binary with SHA-1 digest", checkNonRdfResponse(res, TEXT_PLAIN_TYPE));
assertEquals("sha=Z5pg2cWB1IqkKKMjh57cQKAeKp0=", res.getHeaderString(DIGEST), "Check the SHA digest value");
}
}

/**
* Test that the SHA-256 digest is generated.
*/
@Test
@DisplayName("Test that the SHA-256 digest is generated")
default void testBinaryWantDigestSha256() {
// Test the SHA-256 algorithm
try (final Response res = target(getResourceLocation()).request().header(WANT_DIGEST, "SHA-256").get()) {
assertAll("Check binary with SHA-256 digest", checkNonRdfResponse(res, TEXT_PLAIN_TYPE));
assertEquals("sha-256=wZXqBpAjgZLSoADF419CRpJCurDcagOwnb/8VAiiQXA=", res.getHeaderString(DIGEST),
"Check the SHA-256 digest value");
}
}

/**
* Test that an unknown digest is ignored.
*/
@Test
@DisplayName("Test that an unknown digest is ignored")
default void testBinaryWantDigestUnknown() {
// Test an unknown digest algorithm
try (final Response res = target(getResourceLocation()).request().header(WANT_DIGEST, "FOO").get()) {
assertAll("Check binary with unknown digest", checkNonRdfResponse(res, TEXT_PLAIN_TYPE));
assertNull(res.getHeaderString(DIGEST), "Check that no Digest header is present");
}
}
}
36 changes: 0 additions & 36 deletions core/api/src/main/java/org/trellisldp/api/BinaryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
package org.trellisldp.api;

import java.io.InputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.Set;
import java.util.concurrent.CompletionStage;

import org.apache.commons.rdf.api.IRI;
Expand All @@ -39,21 +36,6 @@ public interface BinaryService extends RetrievalService<Binary> {
*/
CompletionStage<Void> setContent(BinaryMetadata metadata, InputStream stream);

/**
* Set the content for a binary object using a digest algorithm.
*
* @implSpec The default implementation will compute a digest while processing the {@code InputStream}.
* @param metadata the binary metadata
* @param stream the context
* @param algorithm the digest algorithm
* @return the new completion stage containing the server-computed digest.
*/
default CompletionStage<MessageDigest> setContent(final BinaryMetadata metadata, final InputStream stream,
final MessageDigest algorithm) {
final DigestInputStream input = new DigestInputStream(stream, algorithm);
return setContent(metadata, input).thenApply(future -> input.getMessageDigest());
}

/**
* Purge the content from its corresponding datastore.
*
Expand All @@ -65,24 +47,6 @@ default CompletionStage<MessageDigest> setContent(final BinaryMetadata metadata,
*/
CompletionStage<Void> purgeContent(IRI identifier);

/**
* Calculate the digest for a binary object.
*
* @apiNote As per RFC 3230, the digest value is calculated over the entire resource,
* not just the HTTP payload.
* @param identifier the identifier
* @param algorithm the algorithm
* @return the new completion stage containing a computed digest for the binary resource
*/
CompletionStage<MessageDigest> calculateDigest(IRI identifier, MessageDigest algorithm);

/**
* Get a list of supported algorithms.
*
* @return the supported digest algorithms
*/
Set<String> supportedAlgorithms();

/**
* Get a new identifier.
*
Expand Down
Loading

0 comments on commit 73844e2

Please sign in to comment.