diff --git a/.drone.yml b/.drone.yml
index 3f21fd6..2a5da85 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -59,6 +59,7 @@ steps:
settings:
repo: scireum/s3-ninja
tags:
+ - latest
- "${DRONE_TAG}"
environment:
DOCKER_PASSWORD:
diff --git a/pom.xml b/pom.xml
index f0087da..3e0b043 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,8 +16,8 @@
http://s3ninja.net
- dev-23.0.0
- dev-35.0.0
+ dev-23.5.0
+ dev-39.8.0
diff --git a/src/main/java/ninja/Aws4HashCalculator.java b/src/main/java/ninja/Aws4HashCalculator.java
index a7093c0..f6f999a 100644
--- a/src/main/java/ninja/Aws4HashCalculator.java
+++ b/src/main/java/ninja/Aws4HashCalculator.java
@@ -60,7 +60,7 @@ public boolean supports(final WebContext webContext) {
* Computes the authentication hash as specified by the AWS SDK for verification purposes.
*
* @param webContext the current request to fetch parameters from
- * @param pathPrefix the path prefix to preped to the {@link S3Dispatcher#getEffectiveURI(WebContext) effective URI}
+ * @param pathPrefix the path prefix to preped to the {@link S3Dispatcher#getEffectiveURI(String) effective URI}
* of the request
* @return the computes hash value
* @throws Exception when hashing fails
diff --git a/src/main/java/ninja/AwsLegacyHashCalculator.java b/src/main/java/ninja/AwsLegacyHashCalculator.java
index 07980dd..d5feaf5 100644
--- a/src/main/java/ninja/AwsLegacyHashCalculator.java
+++ b/src/main/java/ninja/AwsLegacyHashCalculator.java
@@ -63,7 +63,7 @@ public class AwsLegacyHashCalculator {
* Computes the authentication hash as specified by the AWS SDK for verification purposes.
*
* @param webContext the current request to fetch parameters from
- * @param pathPrefix the path prefix to preped to the {@link S3Dispatcher#getEffectiveURI(WebContext) effective URI}
+ * @param pathPrefix the path prefix to preped to the {@link S3Dispatcher#getEffectiveURI(String) effective URI}
* of the request
* @return the computes hash value
* @throws Exception when hashing fails
@@ -95,7 +95,7 @@ public String computeHash(WebContext webContext, String pathPrefix) throws Excep
stringToSign.append("\n");
}
- stringToSign.append(pathPrefix).append('/').append(S3Dispatcher.getEffectiveURI(webContext));
+ stringToSign.append(pathPrefix).append('/').append(S3Dispatcher.getEffectiveURI(webContext.getRawRequestedURI()));
char separator = '?';
for (String parameterName : webContext.getParameterNames().stream().sorted().collect(Collectors.toList())) {
diff --git a/src/main/java/ninja/ListFileTreeVisitor.java b/src/main/java/ninja/ListFileTreeVisitor.java
index 05e1711..6576de5 100644
--- a/src/main/java/ninja/ListFileTreeVisitor.java
+++ b/src/main/java/ninja/ListFileTreeVisitor.java
@@ -55,9 +55,9 @@ protected ListFileTreeVisitor(XMLStructuredOutput output,
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
File file = path.toFile();
- String name = file.getName();
+ String name = StoredObject.decodeKey(file.getName());
- if (!file.isFile() || name.startsWith("$")) {
+ if (!file.isFile() || file.getName().startsWith("$")) {
return FileVisitResult.CONTINUE;
}
if (!markerReached) {
diff --git a/src/main/java/ninja/NinjaController.java b/src/main/java/ninja/NinjaController.java
index 1b4368c..480e941 100644
--- a/src/main/java/ninja/NinjaController.java
+++ b/src/main/java/ninja/NinjaController.java
@@ -118,7 +118,7 @@ private void buckets(WebContext webContext) {
try {
buckets = storage.getBuckets();
} catch (HandledException e) {
- UserContext.message(Message.error(e.getMessage()));
+ UserContext.message(Message.error().withTextMessage(e.getMessage()));
}
webContext.respondWith()
.template("/templates/index.html.pasta",
@@ -151,21 +151,21 @@ public void bucket(WebContext webContext, String bucketName) {
// handle /ui/[bucket]?create
if (webContext.hasParameter("create")) {
if (bucket.exists()) {
- UserContext.message(Message.error("Bucket does already exist."));
+ UserContext.message(Message.error().withTextMessage("Bucket does already exist."));
webContext.respondWith().redirectTemporarily("/ui/" + bucket.getEncodedName());
return;
}
bucket.create();
- UserContext.message(Message.info("Bucket successfully created."));
+ UserContext.message(Message.info().withTextMessage("Bucket successfully created."));
webContext.respondWith().redirectTemporarily("/ui/" + bucket.getEncodedName());
return;
}
// from this point on, make sure that the bucket exists
if (!bucket.exists()) {
- UserContext.message(Message.error("Bucket does not exist."));
+ UserContext.message(Message.error().withTextMessage("Bucket does not exist."));
webContext.respondWith().redirectTemporarily("/ui");
return;
}
@@ -174,7 +174,7 @@ public void bucket(WebContext webContext, String bucketName) {
if (webContext.hasParameter("make-public")) {
bucket.makePublic();
- UserContext.message(Message.info("ACLs successfully changed"));
+ UserContext.message(Message.info().withTextMessage("ACLs successfully changed"));
webContext.respondWith().redirectTemporarily("/ui/" + bucket.getEncodedName());
return;
}
@@ -183,7 +183,7 @@ public void bucket(WebContext webContext, String bucketName) {
if (webContext.hasParameter("make-private")) {
bucket.makePrivate();
- UserContext.message(Message.info("ACLs successfully changed"));
+ UserContext.message(Message.info().withTextMessage("ACLs successfully changed"));
webContext.respondWith().redirectTemporarily("/ui/" + bucket.getEncodedName());
return;
}
@@ -192,7 +192,7 @@ public void bucket(WebContext webContext, String bucketName) {
if (webContext.hasParameter("delete")) {
bucket.delete();
- UserContext.message(Message.info("Bucket successfully deleted."));
+ UserContext.message(Message.info().withTextMessage("Bucket successfully deleted."));
webContext.respondWith().redirectTemporarily("/ui");
return;
}
@@ -274,7 +274,7 @@ private void objects(WebContext webContext, Bucket bucket) {
public void object(WebContext webContext, String bucketName, List idParts) {
Bucket bucket = storage.getBucket(bucketName);
if (!bucket.exists()) {
- UserContext.message(Message.error("Bucket does not exist."));
+ UserContext.message(Message.error().withTextMessage("Bucket does not exist."));
webContext.respondWith().redirectTemporarily("/ui");
return;
}
@@ -282,7 +282,7 @@ public void object(WebContext webContext, String bucketName, List idPart
String id = String.join("/", idParts);
StoredObject object = bucket.getObject(id);
if (!object.exists()) {
- UserContext.message(Message.error("Object does not exist."));
+ UserContext.message(Message.error().withTextMessage("Object does not exist."));
webContext.respondWith().redirectTemporarily("/ui/" + bucket.getEncodedName());
return;
}
@@ -291,7 +291,7 @@ public void object(WebContext webContext, String bucketName, List idPart
if (webContext.hasParameter("delete")) {
object.delete();
- UserContext.message(Message.info("Object successfully deleted."));
+ UserContext.message(Message.info().withTextMessage("Object successfully deleted."));
webContext.respondWith().redirectTemporarily("/ui/" + bucket.getEncodedName());
return;
}
diff --git a/src/main/java/ninja/S3Dispatcher.java b/src/main/java/ninja/S3Dispatcher.java
index 070dbd1..7c3d542 100644
--- a/src/main/java/ninja/S3Dispatcher.java
+++ b/src/main/java/ninja/S3Dispatcher.java
@@ -258,11 +258,10 @@ public DispatchDecision dispatch(WebContext webContext) throws Exception {
* As we have to support legacy URIs which have an /s3 prefix, we cut this here, and
* also the first "/" and only return the effective URI to process.
*
- * @param webContext the current request
+ * @param uri the requested URI
* @return the effective URI to process
*/
- public static String getEffectiveURI(WebContext webContext) {
- String uri = webContext.getRequestedURI();
+ public static String getEffectiveURI(String uri) {
if (uri.startsWith("/s3")) {
uri = uri.substring(3);
}
@@ -270,7 +269,7 @@ public static String getEffectiveURI(WebContext webContext) {
uri = uri.substring(1);
}
- return Strings.urlEncode(uri).replace("+", "%20").replace("%2F", "/");
+ return uri;
}
/**
@@ -280,7 +279,7 @@ public static String getEffectiveURI(WebContext webContext) {
* @return a structured {@link S3Request}.
*/
private static S3Request parseRequest(WebContext webContext) {
- String uri = getEffectiveURI(webContext);
+ String uri = getEffectiveURI(webContext.getRequestedURI());
// we treat the first parameter without value as query string
Iterator parameterIterator = webContext.getParameterNames().iterator();
diff --git a/src/site/index.html b/src/site/index.html
index 17b9564..f3ccb75 100644
--- a/src/site/index.html
+++ b/src/site/index.html
@@ -59,7 +59,7 @@ S3 ninja
- A readily packaged docker image is available at scireum/s3-ninja
- - Run like docker run -p 9444:9000 scireum/s3-ninja:7
+ - Run like docker run -p 9444:9000 scireum/s3-ninja:latest
- Navigate to http://localhost:9444/ui
- Run S3 API-Calls against http://localhost:9444/ (e.g. http://localhost:9444/test-bucket/test-object)
- Provide an volume for /home/sirius/data to persist data accross restarts.
S3Ninja runs as user id 2000 inside the container. If you link a existing directory into your container, change the user/group id to 2000.
diff --git a/src/test/java/BaseAWSSpec.groovy b/src/test/java/BaseAWSSpec.groovy
index 2ea2bde..0ac4dc1 100644
--- a/src/test/java/BaseAWSSpec.groovy
+++ b/src/test/java/BaseAWSSpec.groovy
@@ -171,6 +171,43 @@ abstract class BaseAWSSpec extends BaseSpecification {
summaries.get(1).getKey() == key2
}
+ // reported in https://github.com/scireum/s3ninja/issues/180
+ def "PUT and then LIST with prefix work as expected"() {
+ given:
+ def bucketName = DEFAULT_BUCKET_NAME
+ def key1 = DEFAULT_KEY + "/Eins"
+ def key2 = DEFAULT_KEY + "/Zwei"
+ def key3 = "a/key/with a different/prefix/Drei"
+ def client = getClient()
+ when:
+ if (client.doesBucketExist(bucketName)) {
+ client.deleteBucket(bucketName)
+ }
+ client.createBucket(bucketName)
+ and:
+ client.putObject(
+ bucketName,
+ key1,
+ new ByteArrayInputStream("Eins".getBytes(Charsets.UTF_8)),
+ new ObjectMetadata())
+ client.putObject(
+ bucketName,
+ key2,
+ new ByteArrayInputStream("Zwei".getBytes(Charsets.UTF_8)),
+ new ObjectMetadata())
+ client.putObject(
+ bucketName,
+ key3,
+ new ByteArrayInputStream("Drei".getBytes(Charsets.UTF_8)),
+ new ObjectMetadata())
+ then:
+ def listing = client.listObjects(bucketName, DEFAULT_KEY + '/')
+ def summaries = listing.getObjectSummaries()
+ summaries.size() == 2
+ summaries.get(0).getKey() == key1
+ summaries.get(1).getKey() == key2
+ }
+
def "PUT and then DELETE work as expected"() {
given:
def bucketName = DEFAULT_BUCKET_NAME