All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.org.jets3t.tests.BaseS3ServiceTest Maven / Gradle / Ivy

/*
 * jets3t : Java Extra-Tasty S3 Toolkit (for Amazon S3 online storage service)
 * This is a java.net project, see https://jets3t.dev.java.net/
 * 
 * Copyright 2006 James Murty
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package org.jets3t.tests;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.TimeZone;

import junit.framework.TestCase;

import org.jets3t.service.S3ObjectsChunk;
import org.jets3t.service.S3Service;
import org.jets3t.service.S3ServiceException;
import org.jets3t.service.acl.AccessControlList;
import org.jets3t.service.acl.GroupGrantee;
import org.jets3t.service.acl.Permission;
import org.jets3t.service.impl.rest.httpclient.RestS3Service;
import org.jets3t.service.model.S3Bucket;
import org.jets3t.service.model.S3BucketLoggingStatus;
import org.jets3t.service.model.S3Object;
import org.jets3t.service.model.S3Owner;
import org.jets3t.service.security.AWSCredentials;
import org.jets3t.service.utils.Mimetypes;
import org.jets3t.service.utils.RestUtils;
import org.jets3t.service.utils.ServiceUtils;

/**
 * Runs generic S3Service-related tests that any S3Service implementation should be able to perform.
 * 

* Any test case for S3Service implementations should extend this class as a starting point, then * add more test cases more specific to that particular implementation. * * @author James Murty */ public abstract class BaseS3ServiceTest extends TestCase { protected String TEST_PROPERTIES_FILENAME = "test.properties"; protected AWSCredentials awsCredentials = null; public BaseS3ServiceTest() throws IOException { InputStream propertiesIS = ClassLoader.getSystemResourceAsStream(TEST_PROPERTIES_FILENAME); if (propertiesIS == null) { throw new IOException("Unable to load test properties file from classpath: " + TEST_PROPERTIES_FILENAME); } Properties testProperties = new Properties(); testProperties.load(propertiesIS); awsCredentials = new AWSCredentials( testProperties.getProperty("aws.accesskey"), testProperties.getProperty("aws.secretkey")); } protected abstract S3Service getS3Service(AWSCredentials awsCredentials) throws S3ServiceException; public void testObtainAnonymousServices() throws Exception { getS3Service(null); } public void testListBucketsWithoutCredentials() throws Exception { try { getS3Service(null).listAllBuckets(); fail("Bucket listing should fail without authentication"); } catch (S3ServiceException e) { } } public void testListBucketsWithCredentials() throws Exception { getS3Service(awsCredentials).listAllBuckets(); } public void testBucketManagement() throws Exception { S3Service s3Service = getS3Service(awsCredentials); try { s3Service.createBucket(new S3Bucket()); fail("Cannot create a bucket with name unset"); } catch (S3ServiceException e) { } try { s3Service.createBucket(""); fail("Cannot create a bucket with empty name"); } catch (S3ServiceException e) { } try { s3Service.createBucket("test"); fail("Cannot create a bucket with non-unique name"); } catch (S3ServiceException e) { } String bucketName = awsCredentials.getAccessKey() + ".jets3t_TestCases_temp"; s3Service.createBucket(bucketName); boolean bucketExists = s3Service.isBucketAccessible(bucketName); assertTrue("Bucket should exist", bucketExists); try { s3Service.deleteBucket((S3Bucket) null); fail("Cannot delete a bucket with name null"); } catch (S3ServiceException e) { } try { s3Service.deleteBucket(""); fail("Cannot delete a bucket with empty name"); } catch (S3ServiceException e) { } try { s3Service.deleteBucket("test"); fail("Cannot delete a bucket you don't own"); } catch (S3ServiceException e) { } s3Service.deleteBucket(bucketName); } public void testObjectManagement() throws Exception { S3Service s3Service = getS3Service(awsCredentials); String bucketName = awsCredentials.getAccessKey() + ".jets3t_TestCases"; S3Bucket bucket = s3Service.createBucket(bucketName); S3Object object = new S3Object("TestObject"); try { s3Service.putObject( (S3Bucket) null, null); fail("Cannot create an object without a valid bucket"); } catch (S3ServiceException e) { } try { s3Service.putObject( (S3Bucket) null, object); fail("Cannot create an object without a valid bucket"); } catch (S3ServiceException e) { } try { s3Service.putObject(bucket, new S3Object(null)); fail("Cannot create an object without a valid object"); } catch (S3ServiceException e) { } // Create basic object with default content type and no data. S3Object basicObject = s3Service.putObject(bucket, object); assertEquals("Unexpected default content type", Mimetypes.MIMETYPE_OCTET_STREAM, basicObject.getContentType()); // Retrieve object to ensure it was correctly created. basicObject = s3Service.getObject(bucket, object.getKey()); assertEquals("Unexpected default content type", Mimetypes.MIMETYPE_OCTET_STREAM, basicObject.getContentType()); assertEquals("Unexpected size for 'empty' object", 0, basicObject.getContentLength()); basicObject.closeDataInputStream(); // Make sure bucket cannot be removed while it has contents. try { s3Service.deleteBucket(bucket.getName()); fail("Cannot delete a bucket containing objects"); } catch (S3ServiceException e) { } // Update/overwrite object to be a 'directory' object which has a specific content type and // no data. String contentType = Mimetypes.MIMETYPE_JETS3T_DIRECTORY; object.setContentType(contentType); S3Object directoryObject = s3Service.putObject(bucket, object); assertEquals("Unexpected default content type", contentType, directoryObject .getContentType()); // Retrieve object to ensure it was correctly created. directoryObject = s3Service.getObject(bucket, object.getKey()); assertEquals("Unexpected default content type", contentType, directoryObject .getContentType()); assertEquals("Unexpected size for 'empty' object", 0, directoryObject.getContentLength()); basicObject.closeDataInputStream(); // Update/overwrite object with real data content and some metadata. contentType = "text/plain"; String objectData = "Just some rubbish text to include as data"; String dataMd5HashAsHex = ServiceUtils.toHex( ServiceUtils.computeMD5Hash(objectData.getBytes())); HashMap metadata = new HashMap(); metadata.put("creator", "S3ServiceTest"); metadata.put("purpose", "For testing purposes"); object.replaceAllMetadata(metadata); object.setContentType(contentType); object.setDataInputStream(new ByteArrayInputStream(objectData.getBytes())); S3Object dataObject = s3Service.putObject(bucket, object); assertEquals("Unexpected content type", contentType, dataObject.getContentType()); assertEquals("Mismatching MD5 hex hash", dataMd5HashAsHex, dataObject.getETag()); // Retrieve data object to ensure it was correctly created, the server-side hash matches // what we expect, and we get our metadata back. dataObject = s3Service.getObject(bucket, object.getKey()); assertEquals("Unexpected default content type", "text/plain", dataObject.getContentType()); assertEquals("Unexpected size for object", objectData.length(), dataObject .getContentLength()); assertEquals("Mismatching hash", dataMd5HashAsHex, dataObject.getETag()); assertEquals("Missing creator metadata", "S3ServiceTest", dataObject.getMetadata( "creator")); assertEquals("Missing purpose metadata", "For testing purposes", dataObject.getMetadata("purpose")); assertNotNull("Expected data input stream to be available", dataObject.getDataInputStream()); // Ensure we can get the data from S3. StringBuffer sb = new StringBuffer(); int b = -1; while ((b = dataObject.getDataInputStream().read()) != -1) { sb.append((char) b); } dataObject.closeDataInputStream(); assertEquals("Mismatching data", objectData, sb.toString()); // Retrieve only HEAD of data object (all metadata is available, but not the object content // data input stream) dataObject = s3Service.getObjectDetails(bucket, object.getKey()); assertEquals("Unexpected default content type", "text/plain", dataObject.getContentType()); assertEquals("Unexpected size for object", objectData.length(), dataObject.getContentLength()); assertEquals("Mismatching hash", dataMd5HashAsHex, dataObject.getETag()); assertEquals("Missing creator metadata", "S3ServiceTest", dataObject.getMetadata( "creator")); assertEquals("Missing purpose metadata", "For testing purposes", dataObject.getMetadata("purpose")); assertNull("Expected data input stream to be unavailable", dataObject.getDataInputStream()); // Test object GET constraints. Calendar objectCreationTimeCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.US); objectCreationTimeCal.setTime(dataObject.getLastModifiedDate()); objectCreationTimeCal.add(Calendar.SECOND, 1); Calendar afterObjectCreation = (Calendar) objectCreationTimeCal.clone(); objectCreationTimeCal.add(Calendar.DAY_OF_YEAR, -1); Calendar yesterday = (Calendar) objectCreationTimeCal.clone(); objectCreationTimeCal.add(Calendar.DAY_OF_YEAR, +2); Calendar tomorrow = (Calendar) objectCreationTimeCal.clone(); // Precondition: Modified since yesterday s3Service.getObjectDetails(bucket, object.getKey(), yesterday, null, null, null); // Precondition: Mot modified since after creation date. try { s3Service.getObjectDetails(bucket, object.getKey(), afterObjectCreation, null, null, null); fail("Cannot have been modified since object was created"); } catch (S3ServiceException e) { } // Precondition: Not modified since yesterday try { s3Service.getObjectDetails(bucket, object.getKey(), null, yesterday, null, null); fail("Cannot be unmodified since yesterday"); } catch (S3ServiceException e) { } // Precondition: Not modified since tomorrow s3Service.getObjectDetails(bucket, object.getKey(), null, tomorrow, null, null); // Precondition: matches correct hash s3Service.getObjectDetails(bucket, object.getKey(), null, null, new String[] {dataMd5HashAsHex}, null); // Precondition: doesn't match incorrect hash try { s3Service.getObjectDetails(bucket, object.getKey(), null, null, new String[] {"__" + dataMd5HashAsHex.substring(2)}, null); fail("Hash values should not match"); } catch (S3ServiceException e) { } // Precondition: doesn't match correct hash try { s3Service.getObjectDetails(bucket, object.getKey(), null, null, null, new String[] {dataMd5HashAsHex}); fail("Hash values should mis-match"); } catch (S3ServiceException e) { } // Precondition: doesn't match incorrect hash s3Service.getObjectDetails(bucket, object.getKey(), null, null, null, new String[] {"__" + dataMd5HashAsHex.substring(2)}); // Retrieve only a limited byte-range of the data, with a start and end. Long byteRangeStart = new Long(3); Long byteRangeEnd = new Long(12); dataObject = s3Service.getObject(bucket, object.getKey(), null, null, null, null, byteRangeStart, byteRangeEnd); String dataReceived = readStringFromInputStream(dataObject.getDataInputStream()); String dataExpected = objectData.substring(byteRangeStart.intValue(), byteRangeEnd.intValue() + 1); assertEquals("Mismatching data from range precondition", dataExpected, dataReceived); // Retrieve only a limited byte-range of the data, with a start range only. byteRangeStart = new Long(7); byteRangeEnd = null; dataObject = s3Service.getObject(bucket, object.getKey(), null, null, null, null, byteRangeStart, byteRangeEnd); dataReceived = readStringFromInputStream(dataObject.getDataInputStream()); dataExpected = objectData.substring(byteRangeStart.intValue()); assertEquals("Mismatching data from range precondition", dataExpected, dataReceived); // Retrieve only a limited byte-range of the data, with an end range only. byteRangeStart = null; byteRangeEnd = new Long(13); dataObject = s3Service.getObject(bucket, object.getKey(), null, null, null, null, byteRangeStart, byteRangeEnd); dataReceived = readStringFromInputStream(dataObject.getDataInputStream()); dataExpected = objectData.substring(objectData.length() - byteRangeEnd.intValue()); assertEquals("Mismatching data from range precondition", dataExpected, dataReceived); // Clean-up. s3Service.deleteObject(bucket, object.getKey()); // Create object with tricky key. String trickyKey = "http://example.site.com/some/path/document name.html?param1=a@b#c$d¶m2=(089)"; S3Object trickyObject = s3Service.putObject(bucket, new S3Object(bucket, trickyKey, "Some test data")); assertEquals("Tricky key name mistmatch", trickyKey, trickyObject.getKey()); // Make sure the tricky named object really exists with its full name. S3Object[] objects = s3Service.listObjects(bucket); boolean trickyNamedObjectExists = false; for (int i = 0; !trickyNamedObjectExists && i < objects.length; i++) { if (trickyKey.equals(objects[i].getKey())) { trickyNamedObjectExists = true; } } assertTrue("Tricky key name object does not exist with its full name", trickyNamedObjectExists); // Delete object with tricky key. s3Service.deleteObject(bucket, trickyObject.getKey()); // s3Service.deleteBucket(bucket.getName()); } public void testACLManagement() throws Exception { String s3Url = "https://s3.amazonaws.com"; // Access public "third-party" bucket S3Service anonymousS3Service = getS3Service(null); boolean jets3tBucketAvailable = anonymousS3Service.isBucketAccessible("jets3t"); assertTrue("Cannot find public jets3t bucket", jets3tBucketAvailable); S3Service s3Service = getS3Service(awsCredentials); String bucketName = awsCredentials.getAccessKey() + ".jets3t_TestCases"; S3Bucket bucket = s3Service.createBucket(bucketName); S3Object object = null; // Create private object (default permissions). String privateKey = "Private Object #1"; object = new S3Object(bucket, privateKey, "Private object sample text"); s3Service.putObject(bucket, object); URL url = new URL(s3Url + "/" + bucketName + "/" + RestUtils.encodeUrlString(privateKey)); assertEquals("Expected denied access (403) error", 403, ((HttpURLConnection) url .openConnection()).getResponseCode()); // Get ACL details for private object so we can determine the bucket owner. AccessControlList bucketACL = s3Service.getBucketAcl(bucket); S3Owner bucketOwner = bucketACL.getOwner(); // Create a public object. String publicKey = "Public Object #1"; object = new S3Object(bucket, publicKey, "Public object sample text"); AccessControlList acl = new AccessControlList(); acl.setOwner(bucketOwner); acl.grantPermission(GroupGrantee.ALL_USERS, Permission.PERMISSION_READ); object.setAcl(acl); s3Service.putObject(bucket, object); url = new URL(s3Url + "/" + bucketName + "/" + RestUtils.encodeUrlString(publicKey)); assertEquals("Expected access (200)", 200, ((HttpURLConnection)url.openConnection()).getResponseCode()); // Update ACL to make private object public. AccessControlList privateToPublicACL = s3Service.getObjectAcl(bucket, privateKey); privateToPublicACL.grantPermission(GroupGrantee.ALL_USERS, Permission.PERMISSION_READ); object.setKey(privateKey); object.setAcl(privateToPublicACL); s3Service.putObjectAcl(bucket, object); url = new URL(s3Url + "/" + bucketName + "/" + RestUtils.encodeUrlString(privateKey)); assertEquals("Expected access (200)", 200, ((HttpURLConnection) url.openConnection()) .getResponseCode()); // Create a non-standard uncanned public object. String publicKey2 = "Public Object #2"; object = new S3Object(publicKey2); object.setAcl(privateToPublicACL); // This ACL has ALL_USERS READ permission set above. s3Service.putObject(bucket, object); url = new URL(s3Url + "/" + bucketName + "/" + RestUtils.encodeUrlString(publicKey2)); assertEquals("Expected access (200)", 200, ((HttpURLConnection) url.openConnection()) .getResponseCode()); // Update ACL to make public object private. AccessControlList publicToPrivateACL = s3Service.getObjectAcl(bucket, publicKey); publicToPrivateACL.revokeAllPermissions(GroupGrantee.ALL_USERS); object.setKey(publicKey); object.setAcl(publicToPrivateACL); s3Service.putObjectAcl(bucket, object); url = new URL(s3Url + "/" + bucketName + "/" + RestUtils.encodeUrlString(publicKey)); assertEquals("Expected denied access (403) error", 403, ((HttpURLConnection) url .openConnection()).getResponseCode()); // Clean-up. s3Service.deleteObject(bucket, privateKey); s3Service.deleteObject(bucket, publicKey); s3Service.deleteObject(bucket, publicKey2); // s3Service.deleteBucket(bucket.getName()); } public void testObjectListing() throws Exception { S3Service s3Service = getS3Service(awsCredentials); String bucketName = awsCredentials.getAccessKey() + ".jets3t_TestCases"; S3Bucket bucket = s3Service.createBucket(bucketName); // Represent a directory structure in S3. List objectsList = new ArrayList(); objectsList.add(new S3Object(bucket, "dir1")); objectsList.add(new S3Object(bucket, "dir1/doc1Level1")); objectsList.add(new S3Object(bucket, "dir1/doc2level1")); objectsList.add(new S3Object(bucket, "dir1/dir1Level1")); objectsList.add(new S3Object(bucket, "dir1/dir1Level1/doc1Level2")); objectsList.add(new S3Object(bucket, "dir1/dir1Level1/dir1Level2")); objectsList.add(new S3Object(bucket, "dir1/dir1Level1/dir1Level2/doc1Level3")); // Create objects Iterator iter = objectsList.iterator(); while (iter.hasNext()) { S3Object object = (S3Object) iter.next(); s3Service.putObject(bucket, object); } S3Object[] objects = null; // List all items in directory. objects = s3Service.listObjects(bucket); assertEquals("Incorrect number of objects in directory structure", objectsList.size(), objects.length); // List items in chunks of size 2, ensure we get a total of seven. int chunkedObjectsCount = 0; int chunkedIterationsCount = 0; String priorLastKey = null; do { S3ObjectsChunk chunk = s3Service.listObjectsChunked( bucket.getName(), null, null, 2, priorLastKey); priorLastKey = chunk.getPriorLastKey(); chunkedObjectsCount += chunk.getObjects().length; chunkedIterationsCount++; } while (priorLastKey != null); assertEquals("Chunked bucket listing retreived incorrect number of objects", objectsList.size(), chunkedObjectsCount); assertEquals("Chunked bucket listing ran for an unexpected number of iterations", (objectsList.size() + 1) / 2, chunkedIterationsCount); // List objects with a prefix and delimiter to check common prefixes. S3ObjectsChunk chunk = s3Service.listObjectsChunked( bucket.getName(), "dir1/", "/", 100, null); assertEquals("Chunked bucket listing with prefix and delimiter retreived incorrect number of objects", 3, chunk.getObjects().length); assertEquals("Chunked bucket listing with prefix and delimiter retreived incorrect number of common prefixes", 1, chunk.getCommonPrefixes().length); // List the same items with a prefix. objects = s3Service.listObjects(bucket, "dir1", null); assertEquals("Incorrect number of objects matching prefix", 7, objects.length); // List items up one directory with a prefix (will include dir1Level1) objects = s3Service.listObjects(bucket, "dir1/dir1Level1", null); assertEquals("Incorrect number of objects matching prefix", 4, objects.length); // List items up one directory with a prefix (will not include dir1Level1) objects = s3Service.listObjects(bucket, "dir1/dir1Level1/", null); assertEquals("Incorrect number of objects matching prefix", 3, objects.length); // Try a prefix matching no object keys. objects = s3Service.listObjects(bucket, "dir1-NonExistent", null); assertEquals("Expected no results", 0, objects.length); // Use delimiter with an partial prefix. objects = s3Service.listObjects(bucket, "dir", "/"); assertEquals("Expected no results", 1, objects.length); // Use delimiter to find item dir1 only. objects = s3Service.listObjects(bucket, "dir1", "/"); assertEquals("Incorrect number of objects matching prefix and delimiter", 1, objects.length); // Use delimiter to find items within dir1 only. objects = s3Service.listObjects(bucket, "dir1/", "/"); assertEquals("Incorrect number of objects matching prefix and delimiter", 3, objects.length); // List items up one directory with prefix and delimiter (will include only dir1Level1) objects = s3Service.listObjects(bucket, "dir1/dir1Level1", "/"); assertEquals("Incorrect number of objects matching prefix", 1, objects.length); // List items up one directory with prefix and delimiter (will include only contents of dir1Level1) objects = s3Service.listObjects(bucket, "dir1/dir1Level1/", "/"); assertEquals("Incorrect number of objects matching prefix", 2, objects.length); // Clean up. iter = objectsList.iterator(); while (iter.hasNext()) { S3Object object = (S3Object) iter.next(); s3Service.deleteObject(bucket, object.getKey()); } // s3Service.deleteBucket(bucket.getName()); } public void testBucketLogging() throws Exception { S3Service s3Service = getS3Service(awsCredentials); String bucketName = awsCredentials.getAccessKey() + ".jets3t_TestCases"; S3Bucket bucket = s3Service.createBucket(bucketName); // Check logging status is false S3BucketLoggingStatus loggingStatus = s3Service.getBucketLoggingStatus(bucket.getName()); assertFalse("Expected logging to be disabled for bucket " + bucketName, loggingStatus.isLoggingEnabled()); // Enable logging (non-existent target bucket) try { S3BucketLoggingStatus newLoggingStatus = new S3BucketLoggingStatus( awsCredentials.getAccessKey() + ".NonExistentBucketName", "access-log-"); s3Service.setBucketLoggingStatus(bucket.getName(), newLoggingStatus, true); fail("Using non-existent target bucket should have caused an exception"); } catch (Exception e) { } // Enable logging (in same bucket) S3BucketLoggingStatus newLoggingStatus = new S3BucketLoggingStatus(bucketName, "access-log-"); s3Service.setBucketLoggingStatus(bucket.getName(), newLoggingStatus, true); loggingStatus = s3Service.getBucketLoggingStatus(bucket.getName()); assertTrue("Expected logging to be enabled for bucket " + bucketName, loggingStatus.isLoggingEnabled()); assertEquals("Target bucket", bucketName, loggingStatus.getTargetBucketName()); assertEquals("Log file prefix", "access-log-", loggingStatus.getLogfilePrefix()); // Disable logging newLoggingStatus = new S3BucketLoggingStatus(); s3Service.setBucketLoggingStatus(bucket.getName(), newLoggingStatus, true); loggingStatus = s3Service.getBucketLoggingStatus(bucket.getName()); assertFalse("Expected logging to be disabled for bucket " + bucketName, loggingStatus.isLoggingEnabled()); // s3Service.deleteBucket(bucket.getName()); } public void testUrlSigning() throws Exception { S3Service s3Service = getS3Service(awsCredentials); String bucketName = awsCredentials.getAccessKey() + ".jets3t_TestCases"; S3Bucket bucket = s3Service.createBucket(bucketName); // Create test object, with private ACL String dataString = "Text for the URL Signing test object..."; S3Object object = new S3Object(bucket, "Testing URL Signing", dataString); object.setContentType("text/html"); object.addMetadata("x-amz-example-header", "example-value"); object.setAcl(AccessControlList.REST_CANNED_PRIVATE); // Determine what the time will be in 5 minutes. Calendar cal = Calendar.getInstance(); cal.add(Calendar.MINUTE, 5); Date expiryDate = cal.getTime(); // Create a signed HTTP PUT URL. String signedPutUrl = S3Service.createSignedPutUrl(bucket.getName(), object.getKey(), object.getMetadataMap(), awsCredentials, expiryDate, false); // Put the object in S3 using the signed URL (no AWS credentials required) RestS3Service restS3Service = new RestS3Service(null); restS3Service.putObjectWithSignedUrl(signedPutUrl, object); // Ensure the object was created. S3Object objects[] = s3Service.listObjects(bucket, object.getKey(), null); assertEquals("Signed PUT URL failed to put/create object", objects.length, 1); // Change the object's content-type and ensure the signed PUT URL disallows the put. object.setContentType("application/octet-stream"); try { restS3Service.putObjectWithSignedUrl(signedPutUrl, object); fail("Should not be able to use a signed URL for an object with a changed content-type"); } catch (S3ServiceException e) { object.setContentType("text/html"); } // Add an object header and ensure the signed PUT URL disallows the put. object.addMetadata("x-amz-example-header-2", "example-value"); try { restS3Service.putObjectWithSignedUrl(signedPutUrl, object); fail("Should not be able to use a signed URL for an object with changed metadata"); } catch (S3ServiceException e) { object.removeMetadata("x-amz-example-header-2"); } // Change the object's name and ensure the signed PUT URL uses the signed name, not the object name. String originalName = object.getKey(); object.setKey("Testing URL Signing 2"); object.setDataInputStream(new ByteArrayInputStream(dataString.getBytes())); S3Object renamedObject = restS3Service.putObjectWithSignedUrl(signedPutUrl, object); assertEquals("Ensure returned object key is renamed based on signed PUT URL", originalName, renamedObject.getKey()); // Ensure we can't get the object with a normal URL. String s3Url = "https://s3.amazonaws.com"; URL url = new URL(s3Url + "/" + bucketName + "/" + RestUtils.encodeUrlString(object.getKey())); assertEquals("Expected denied access (403) error", 403, ((HttpURLConnection) url .openConnection()).getResponseCode()); // Create a signed HTTP GET URL. String signedGetUrl = S3Service.createSignedGetUrl(bucket.getName(), object.getKey(), awsCredentials, expiryDate, false); // Ensure the signed URL can retrieve the object. url = new URL(signedGetUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); assertEquals("Expected signed GET URL ("+ signedGetUrl +") to retrieve object with response code 200", 200, conn.getResponseCode()); // Sanity check the data in the S3 object. String objectData = (new BufferedReader( new InputStreamReader(conn.getInputStream()))) .readLine(); assertEquals("Unexpected data content in S3 object", dataString, objectData); // Clean up. s3Service.deleteObject(bucket, object.getKey()); // s3Service.deleteBucket(bucket.getName()); } public void testHashVerifiedUploads() throws Exception { S3Service s3Service = getS3Service(awsCredentials); String bucketName = awsCredentials.getAccessKey() + ".jets3t_TestCases"; S3Bucket bucket = s3Service.createBucket(bucketName); // Create test object with an MD5 hash of the data. String dataString = "Text for MD5 hashing..."; S3Object object = new S3Object(bucket, "Testing MD5 Hashing", dataString); object.setContentType("text/plain"); // Calculate hash data for object. byte[] md5Hash = ServiceUtils.computeMD5Hash(dataString.getBytes()); // Ensure that using an invalid hash value fails. try { object.addMetadata("Content-MD5", "123"); s3Service.putObject(bucket, object); fail("Should have failed due to invalid hash value"); } catch (S3ServiceException e) { // This error check would be nice to have, but it only works for the REST interface, not SOAP. // assertEquals("Expected error code indicating invalid md5 hash", "InvalidDigest", e.getErrorCode()); } object = new S3Object(bucket, "Testing MD5 Hashing", dataString); // Ensure that using the wrong hash value fails. try { byte[] incorrectHash = new byte[md5Hash.length]; System.arraycopy(md5Hash, 0, incorrectHash, 0, incorrectHash.length); incorrectHash[0] = incorrectHash[1]; object.setMd5Hash(incorrectHash); s3Service.putObject(bucket, object); fail("Should have failed due to incorrect hash value"); } catch (S3ServiceException e) { // This error checks would be nice to have, but it only works for the REST interface, not SOAP. // assertEquals("Expected error code indicating invalid md5 hash", "BadDigest", e.getErrorCode()); } object = new S3Object(bucket, "Testing MD5 Hashing", dataString); // Ensure that correct hash value succeeds. object.setMd5Hash(md5Hash); S3Object resultObject = s3Service.putObject(bucket, object); // Ensure the ETag result matches the hex-encoded MD5 hash. assertEquals("Hex-encoded MD5 hash should match ETag", resultObject.getETag(), ServiceUtils.toHex(md5Hash)); // Ensure we can convert the hex-encoded ETag to Base64 that matches the Base64 md5 hash. String md5HashBase64 = ServiceUtils.toBase64(md5Hash); String eTagBase64 = ServiceUtils.toBase64(ServiceUtils.fromHex(resultObject.getETag())); assertEquals("Could not convert ETag and MD5 hash to matching Base64-encoded strings", md5HashBase64, eTagBase64); // Clean up. s3Service.deleteObject(bucket, object.getKey()); // s3Service.deleteBucket(bucket.getName()); } private String readStringFromInputStream(InputStream is) throws IOException { StringBuffer sb = new StringBuffer(); int b = -1; while ((b = is.read()) != -1) { sb.append((char)b); } return sb.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy