From 7c2a2e410edcf14dd5b55ece6bd464b3683990f4 Mon Sep 17 00:00:00 2001
From: "Bala.FA"
Date: Mon, 15 Jul 2019 18:29:34 +0530
Subject: [PATCH] fix: handle SSE in copyObject() properly and have one source
code.
Added new version of copyObject(). Note: this method makes source and
destination argument swapping correctly to denote on which bucket and
object the request is sent.
```
public void copyObject(String bucketName, String objectName, Map headerMap, ServerSideEncryption sse,
String srcBucketName, String srcObjectName, ServerSideEncryption srcSse,
CopyConditions copyConditions)
```
---
api/src/main/java/io/minio/MinioClient.java | 191 ++++++++++++++------
examples/CopyObject.java | 4 +-
examples/CopyObjectEncrypted.java | 4 +-
examples/CopyObjectEncryptedKms.java | 6 +-
examples/CopyObjectEncryptedS3.java | 6 +-
examples/CopyObjectMetadata.java | 4 +-
functional/FunctionalTest.java | 26 +--
7 files changed, 157 insertions(+), 84 deletions(-)
diff --git a/api/src/main/java/io/minio/MinioClient.java b/api/src/main/java/io/minio/MinioClient.java
index 192404932..6db6b5ea3 100755
--- a/api/src/main/java/io/minio/MinioClient.java
+++ b/api/src/main/java/io/minio/MinioClient.java
@@ -1300,6 +1300,19 @@ private void checkReadRequestSse(ServerSideEncryption sse) throws InvalidArgumen
}
}
+ private void checkWriteRequestSse(ServerSideEncryption sse) throws InvalidArgumentException {
+ if (sse == null) {
+ return;
+ }
+
+ if (!this.baseUrl.isHttps()) {
+ if (sse.getType() == ServerSideEncryption.Type.SSE_C || sse.getType() == ServerSideEncryption.Type.SSE_KMS) {
+ throw new InvalidArgumentException(sse.getType().name()
+ + " operations must be performed over a secure connection.");
+ }
+ }
+ }
+
/**
* Executes GET method for given request parameters.
*
@@ -2002,13 +2015,15 @@ public void getObject(String bucketName, String objectName, ServerSideEncryption
* @throws XmlPullParserException upon parsing response xml
* @throws InvalidArgumentException upon invalid value is passed to a method.
* @throws InvalidResponseException upon a non-xml response from server
+ *
+ * @deprecated As of release 6.1
*/
+ @Deprecated
public void copyObject(String bucketName, String objectName, String destBucketName)
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
InvalidArgumentException, InvalidResponseException {
-
- copyObject(bucketName, objectName, destBucketName, null, null, null);
+ copyObject(destBucketName, objectName, null, null, bucketName, objectName, null, null);
}
/**
@@ -2045,13 +2060,18 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
* @throws XmlPullParserException upon parsing response xml
* @throws InvalidArgumentException upon invalid value is passed to a method.
* @throws InvalidResponseException upon a non-xml response from server
+ *
+ * @deprecated As of release 6.1
*/
+ @Deprecated
public void copyObject(String bucketName, String objectName, String destBucketName, String destObjectName)
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
InvalidArgumentException, InvalidResponseException {
-
- copyObject(bucketName, objectName, destBucketName, destObjectName, null, null);
+ if (destObjectName == null) {
+ destObjectName = objectName;
+ }
+ copyObject(destBucketName, destObjectName, null, null, bucketName, objectName, null, null);
}
/**
@@ -2090,14 +2110,15 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
* @throws XmlPullParserException upon parsing response xml
* @throws InvalidArgumentException upon invalid value is passed to a method.
* @throws InvalidResponseException upon a non-xml response from server
+ *
+ * @deprecated As of release 6.1
*/
- public void copyObject(String bucketName, String objectName, String destBucketName,
- CopyConditions copyConditions)
+ @Deprecated
+ public void copyObject(String bucketName, String objectName, String destBucketName, CopyConditions copyConditions)
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
InvalidArgumentException, InvalidResponseException {
-
- copyObject(bucketName, objectName, destBucketName, null, copyConditions, null);
+ copyObject(destBucketName, objectName, null, null, bucketName, objectName, null, copyConditions);
}
/**
@@ -2139,14 +2160,19 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
* @throws XmlPullParserException upon parsing response xml
* @throws InvalidArgumentException upon invalid value is passed to a method.
* @throws InvalidResponseException upon a non-xml response from server
+ *
+ * @deprecated As of release 6.1
*/
- public void copyObject(String bucketName, String objectName, String destBucketName,
- String destObjectName, CopyConditions copyConditions)
+ @Deprecated
+ public void copyObject(String bucketName, String objectName, String destBucketName, String destObjectName,
+ CopyConditions copyConditions)
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
InvalidArgumentException, InvalidResponseException {
-
- copyObject(bucketName, objectName, destBucketName, destObjectName, copyConditions, null);
+ if (destObjectName == null) {
+ destObjectName = objectName;
+ }
+ copyObject(destBucketName, destObjectName, null, null, bucketName, objectName, null, copyConditions);
}
/**
@@ -2192,30 +2218,19 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
* @throws XmlPullParserException upon parsing response xml
* @throws InvalidArgumentException upon invalid value is passed to a method.
* @throws InvalidResponseException upon a non-xml response from server
+ *
+ * @deprecated As of release 6.1
*/
+ @Deprecated
public void copyObject(String bucketName, String objectName, ServerSideEncryption sseSource, String destBucketName,
String destObjectName, CopyConditions copyConditions, ServerSideEncryption sseTarget)
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
InvalidArgumentException, InvalidResponseException {
-
- if ((sseTarget.getType() == ServerSideEncryption.Type.SSE_C) && (!this.baseUrl.isHttps())) {
- throw new InvalidArgumentException("SSE_C operations must be performed over a secure connection.");
- } else if ((sseTarget.getType() == (ServerSideEncryption.Type.SSE_KMS)) && (!this.baseUrl.isHttps())) {
- throw new InvalidArgumentException("SSE_KMS operations must be performed over a secure connection.");
- }
- Map headerTarget = new HashMap<>();
- Map headers = new HashMap<>();
- sseTarget.marshal(headerTarget);
- if (sseTarget.getType() == ServerSideEncryption.Type.SSE_C) {
- Map headerSource = new HashMap<>();
- sseSource.marshal(headerSource);
- headers.putAll(headerSource);
- headers.putAll(headerTarget);
- } else {
- headers.putAll(headerTarget);
+ if (destObjectName == null) {
+ destObjectName = objectName;
}
- copyObject(bucketName, objectName, destBucketName, destObjectName, copyConditions, headers);
+ copyObject(destBucketName, destObjectName, null, sseTarget, bucketName, objectName, sseSource, copyConditions);
}
/**
@@ -2260,49 +2275,114 @@ public void copyObject(String bucketName, String objectName, ServerSideEncryptio
* @throws InternalException upon internal library error
* @throws InvalidArgumentException upon invalid value is passed to a method.
* @throws InvalidResponseException upon a non-xml response from server
+ *
+ * @deprecated As of release 6.1
*/
+ @Deprecated
public void copyObject(String bucketName, String objectName, String destBucketName,
String destObjectName, CopyConditions copyConditions,
Map metadata)
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
InvalidArgumentException, InvalidResponseException {
+ if (destObjectName == null) {
+ destObjectName = objectName;
+ }
+ copyObject(destBucketName, destObjectName, metadata, null, bucketName, objectName, null, copyConditions);
+ }
+
+ /**
+ * Copy a source object into a new object with the provided name in the provided bucket.
+ * optionally can take a key value CopyConditions and server side encryption as well for
+ * conditionally attempting copyObject.
+ *
+ *
+ * Example:
+ *
+ *
+ * {@code minioClient.copyObject("my-bucketname", "my-objectname", headers, sse, "my-srcbucketname",
+ * "my-srcobjname", srcSse, copyConditions);}
+ *
+ *
+ * @param bucketName
+ * Destination bucket name.
+ * @param objectName
+ * Destination object name.
+ * @param headerMap
+ * Destination object custom metadata.
+ * @param sse
+ * Server side encryption of destination object.
+ * @param srcBucketName
+ * Source bucket name.
+ * @param srcObjectName
+ * Source object name.
+ * @param srcSse
+ * Server side encryption of source object.
+ * @param copyConditions
+ * CopyConditions object with collection of supported CopyObject conditions.
+ *
+ * @throws InvalidBucketNameException upon invalid bucket name is given
+ * @throws NoSuchAlgorithmException
+ * upon requested algorithm was not found during signature calculation
+ * @throws InsufficientDataException upon getting EOFException while reading given
+ * InputStream even before reading given length
+ * @throws IOException upon connection error
+ * @throws InvalidKeyException
+ * upon an invalid access key or secret key
+ * @throws NoResponseException upon no response from server
+ * @throws XmlPullParserException upon parsing response xml
+ * @throws ErrorResponseException upon unsuccessful execution
+ * @throws InternalException upon internal library error
+ * @throws InvalidArgumentException upon invalid value is passed to a method.
+ * @throws InvalidResponseException upon a non-xml response from server
+ */
+ public void copyObject(String bucketName, String objectName, Map headerMap, ServerSideEncryption sse,
+ String srcBucketName, String srcObjectName, ServerSideEncryption srcSse,
+ CopyConditions copyConditions)
+ throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
+ NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
+ InvalidArgumentException, InvalidResponseException {
if (bucketName == null) {
- throw new InvalidArgumentException("Source bucket name cannot be empty");
+ throw new InvalidArgumentException("bucket name cannot be empty");
}
+
if (objectName == null) {
- throw new InvalidArgumentException("Source object name cannot be empty");
+ throw new InvalidArgumentException("object name cannot be empty");
}
- if (destBucketName == null) {
- throw new InvalidArgumentException("Destination bucket name cannot be empty");
+
+ checkWriteRequestSse(sse);
+
+ if (srcBucketName == null) {
+ throw new InvalidArgumentException("Source bucket name cannot be empty");
+ }
+
+ // Source object name is optional, if empty default to object name.
+ if (srcObjectName == null) {
+ srcObjectName = objectName;
}
- // Escape source object path.
- String sourceObjectPath = S3Escaper.encodePath(bucketName + "/" + objectName);
+ checkReadRequestSse(srcSse);
- // Destination object name is optional, if empty default to source object name.
- if (destObjectName == null) {
- destObjectName = objectName;
+ if (headerMap == null) {
+ headerMap = new HashMap<>();
}
- Map headerMap = new HashMap<>();
+ headerMap.put("x-amz-copy-source", S3Escaper.encodePath(srcBucketName + "/" + srcObjectName));
- // Set the object source
- headerMap.put("x-amz-copy-source", sourceObjectPath);
+ if (sse != null) {
+ sse.marshal(headerMap);
+ }
- // If no conditions available, skip addition else add the conditions to the header
- if (copyConditions != null) {
- headerMap.putAll(copyConditions.getConditions());
+ if (srcSse != null) {
+ srcSse.marshal(headerMap);
}
- // Set metadata on the destination of object.
- if (metadata != null) {
- headerMap.putAll(metadata);
+ if (copyConditions != null) {
+ headerMap.putAll(copyConditions.getConditions());
}
- HttpResponse response = executePut(destBucketName, destObjectName, headerMap,
- null, "", 0);
+ HttpResponse response = executePut(bucketName, objectName, headerMap, null, "", 0);
// For now ignore the copyObjectResult, just read and parse it.
CopyObjectResult result = new CopyObjectResult();
@@ -4285,23 +4365,14 @@ private void putObject(String bucketName, String objectName, Long size, Object d
}
if (sse != null) {
- if (sse.getType() == ServerSideEncryption.Type.SSE_C && !this.baseUrl.isHttps()) {
- throw new InvalidArgumentException("SSE_C operations must be performed over a secure connection.");
- } else if (sse.getType() == ServerSideEncryption.Type.SSE_KMS && !this.baseUrl.isHttps()) {
- throw new InvalidArgumentException("SSE_KMS operations must be performed over a secure connection.");
- }
-
+ checkWriteRequestSse(sse);
// The correct approach is see.marshal() needs to accept boolean isHttps and do above checks inside.
sse.marshal(headerMap);
}
if (size <= MIN_MULTIPART_SIZE) {
- // Single put object.
- if (sse != null) {
- sse.marshal(headerMap);
- }
- putObject(bucketName, objectName, data, size.intValue(),headerMap , null, 0);
+ putObject(bucketName, objectName, data, size.intValue(), headerMap, null, 0);
return;
}
diff --git a/examples/CopyObject.java b/examples/CopyObject.java
index 210c2aef3..75f1b1c3c 100644
--- a/examples/CopyObject.java
+++ b/examples/CopyObject.java
@@ -70,8 +70,8 @@ public static void main(String[] args)
bais.close();
System.out.println("my-objectname is uploaded successfully");
- minioClient.copyObject("my-bucketname", "my-objectname", "my-destbucketname",
- "my-objectname-copy");
+ minioClient.copyObject("my-destbucketname", "my-objectname-copy", null, null,
+ "my-bucketname", "my-objectname", null, null);
System.out.println("my-objectname-copy copied to my-destbucketname successfully");
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
diff --git a/examples/CopyObjectEncrypted.java b/examples/CopyObjectEncrypted.java
index acfa4b432..ce89cec00 100644
--- a/examples/CopyObjectEncrypted.java
+++ b/examples/CopyObjectEncrypted.java
@@ -64,8 +64,8 @@ public static void main(String[] args)
minioClient.putObject("my-bucketname", "my-objectname", bais, Long.valueOf(bais.available()), null, ssePut, null);
System.out.println("my-objectname is uploaded successfully");
- minioClient.copyObject("my-bucketname", "my-objectname", sseSource, "my-destbucketname",
- "my-objectname-copy", null, sseTarget);
+ minioClient.copyObject("my-destbucketname", "my-objectname-copy", null, sseTarget,
+ "my-bucketname", "my-objectname", sseSource, null);
bais.close();
diff --git a/examples/CopyObjectEncryptedKms.java b/examples/CopyObjectEncryptedKms.java
index be762a818..3b51341f6 100644
--- a/examples/CopyObjectEncryptedKms.java
+++ b/examples/CopyObjectEncryptedKms.java
@@ -61,8 +61,8 @@ public static void main(String[] args)
bais.close();
System.out.println("my-objectname is uploaded successfully");
- minioClient.copyObject("my-bucketname", "my-objectname", null, "my-destbucketname",
- "my-objectname-copy", null, sse);
+ minioClient.copyObject("my-destbucketname", "my-objectname-copy", null, sse,
+ "my-bucketname", "my-objectname", null, null);
System.out.println("my-objectname-copy copied to my-destbucketname successfully");
} catch (MinioException e) {
@@ -70,4 +70,4 @@ public static void main(String[] args)
}
System.out.println("finished");
}
-}
\ No newline at end of file
+}
diff --git a/examples/CopyObjectEncryptedS3.java b/examples/CopyObjectEncryptedS3.java
index ee92ceec8..9fd491583 100644
--- a/examples/CopyObjectEncryptedS3.java
+++ b/examples/CopyObjectEncryptedS3.java
@@ -55,12 +55,12 @@ public static void main(String[] args)
bais.close();
System.out.println("my-objectname is uploaded successfully");
- minioClient.copyObject("my-bucketname", "my-objectname", null, "my-destbucketname",
- "my-objectname-copy", null, sse);
+ minioClient.copyObject("my-destbucketname", "my-objectname-copy", null, sse,
+ "my-bucketname", "my-objectname", null, null);
System.out.println("my-objectname-copy copied to my-destbucketname successfully");
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
System.out.println("finished");
}
-}
\ No newline at end of file
+}
diff --git a/examples/CopyObjectMetadata.java b/examples/CopyObjectMetadata.java
index bf89e9370..84707970a 100644
--- a/examples/CopyObjectMetadata.java
+++ b/examples/CopyObjectMetadata.java
@@ -79,8 +79,8 @@ public static void main(String[] args)
Map metadata = new HashMap<>();
metadata.put("Content-Type", "application/javascript");
- minioClient.copyObject("my-bucketname", "my-objectname", "my-destbucketname",
- "my-objectname-copy", copyConditions, metadata);
+ minioClient.copyObject("my-destbucketname", "my-objectname-copy", metadata, null,
+ "my-bucketname", "my-objectname", null, copyConditions);
System.out.println("my-objectname-copy copied to my-destbucketname successfully");
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
diff --git a/functional/FunctionalTest.java b/functional/FunctionalTest.java
index 1bdc55823..c421576bd 100644
--- a/functional/FunctionalTest.java
+++ b/functional/FunctionalTest.java
@@ -2010,7 +2010,7 @@ public static void copyObject_test1() throws Exception {
String destBucketName = getRandomName();
client.makeBucket(destBucketName);
- client.copyObject(bucketName, objectName, destBucketName);
+ client.copyObject(destBucketName, objectName, null, null, bucketName, null, null, null);
client.getObject(destBucketName, objectName).close();
client.removeObject(bucketName, objectName);
@@ -2048,7 +2048,7 @@ public static void copyObject_test2() throws Exception {
invalidETag.setMatchETag("TestETag");
try {
- client.copyObject(bucketName, objectName, destBucketName, invalidETag);
+ client.copyObject(destBucketName, objectName, null, null, bucketName, null, null, invalidETag);
} catch (ErrorResponseException e) {
if (!e.errorResponse().code().equals("PreconditionFailed")) {
throw e;
@@ -2094,7 +2094,7 @@ public static void copyObject_test3() throws Exception {
copyConditions.setMatchETag(stat.etag());
// File should be copied as ETag set in copyConditions matches object's ETag.
- client.copyObject(bucketName, objectName, destBucketName, copyConditions);
+ client.copyObject(destBucketName, objectName, null, null, bucketName, null, null, copyConditions);
client.getObject(destBucketName, objectName).close();
client.removeObject(bucketName, objectName);
@@ -2136,7 +2136,7 @@ public static void copyObject_test4() throws Exception {
// File should be copied as ETag set in copyConditions doesn't match object's
// ETag.
- client.copyObject(bucketName, objectName, destBucketName, copyConditions);
+ client.copyObject(destBucketName, objectName, null, null, bucketName, null, null, copyConditions);
client.getObject(destBucketName, objectName).close();
client.removeObject(bucketName, objectName);
@@ -2179,7 +2179,7 @@ public static void copyObject_test5() throws Exception {
matchingETagNone.setMatchETagNone(stat.etag());
try {
- client.copyObject(bucketName, objectName, destBucketName, matchingETagNone);
+ client.copyObject(destBucketName, objectName, null, null, bucketName, null, null, matchingETagNone);
} catch (ErrorResponseException e) {
// File should not be copied as ETag set in copyConditions matches object's
// ETag.
@@ -2228,7 +2228,7 @@ public static void copyObject_test6() throws Exception {
modifiedDateCondition.setModified(dateRepresentation);
// File should be copied as object was modified after the set date.
- client.copyObject(bucketName, objectName, destBucketName, modifiedDateCondition);
+ client.copyObject(destBucketName, objectName, null, null, bucketName, null, null, modifiedDateCondition);
client.getObject(destBucketName, objectName).close();
client.removeObject(bucketName, objectName);
@@ -2272,7 +2272,7 @@ public static void copyObject_test7() throws Exception {
invalidUnmodifiedCondition.setUnmodified(dateRepresentation);
try {
- client.copyObject(bucketName, objectName, destBucketName, invalidUnmodifiedCondition);
+ client.copyObject(destBucketName, objectName, null, null, bucketName, null, null, invalidUnmodifiedCondition);
} catch (ErrorResponseException e) {
// File should not be copied as object was modified after date set in
// copyConditions.
@@ -2323,7 +2323,8 @@ public static void copyObject_test8() throws Exception {
Map metadata = new HashMap<>();
metadata.put("Content-Type", customContentType);
- client.copyObject(bucketName, objectName, destBucketName, objectName, copyConditions, metadata);
+ client.copyObject(destBucketName, objectName, metadata, null,
+ bucketName, objectName, null, copyConditions);
ObjectStat objectStat = client.statObject(destBucketName, objectName);
if (!customContentType.equals(objectStat.contentType())) {
@@ -2369,7 +2370,8 @@ public static void copyObject_test9() throws Exception {
CopyConditions copyConditions = new CopyConditions();
copyConditions.setReplaceMetadataDirective();
- client.copyObject(bucketName, objectName, bucketName, objectName, copyConditions, new HashMap());
+ client.copyObject(bucketName, objectName, new HashMap(), null,
+ bucketName, objectName, null, copyConditions);
ObjectStat objectStat = client.statObject(bucketName, objectName);
if (objectStat.httpHeaders().containsKey("X-Amz-Meta-Test")) {
throw new Exception("expected user-defined metadata has been removed");
@@ -2422,7 +2424,7 @@ public static void copyObject_test10() throws Exception {
CopyConditions copyConditions = new CopyConditions();
copyConditions.setReplaceMetadataDirective();
- client.copyObject(bucketName, objectName, sseSource, bucketName, objectName, copyConditions, sseTarget);
+ client.copyObject(bucketName, objectName, null, sseTarget, bucketName, objectName, sseSource, copyConditions);
ObjectStat objectStat = client.statObject(bucketName, objectName, sseTarget);
client.removeObject(bucketName, objectName);
@@ -2463,7 +2465,7 @@ public static void copyObject_test11() throws Exception {
CopyConditions copyConditions = new CopyConditions();
copyConditions.setReplaceMetadataDirective();
- client.copyObject(bucketName, objectName, null, bucketName, objectName, copyConditions, sse);
+ client.copyObject(bucketName, objectName, null, sse, bucketName, objectName, null, copyConditions);
ObjectStat objectStat = client.statObject(bucketName, objectName);
if (objectStat.httpHeaders().containsKey("X-Amz-Meta-Test")) {
throw new Exception("expected user-defined metadata has been removed");
@@ -2515,7 +2517,7 @@ public static void copyObject_test12() throws Exception {
CopyConditions copyConditions = new CopyConditions();
copyConditions.setReplaceMetadataDirective();
- client.copyObject(bucketName, objectName, null, bucketName, objectName, copyConditions, sse);
+ client.copyObject(bucketName, objectName, null, sse, bucketName, objectName, null, copyConditions);
ObjectStat objectStat = client.statObject(bucketName, objectName);
if (objectStat.httpHeaders().containsKey("X-Amz-Meta-Test")) {
throw new Exception("expected user-defined metadata has been removed");