From a9a0c53fb21b3c59e7b3a7a936cb1eb139f4a052 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Fri, 30 Jul 2010 23:58:31 -0700 Subject: [PATCH] Issue 191: added RSA PEM parsing to encryptionUtils --- .../ParseObjectMetadataFromHeadersTest.java | 7 +- .../aws/s3/xml/ListBucketHandlerTest.java | 80 +- .../jclouds/aws/sqs/SQSClientLiveTest.java | 15 +- .../internal/BaseBlobIntegrationTest.java | 5 +- .../BaseBlobStoreIntegrationTest.java | 93 +- .../BaseContainerIntegrationTest.java | 12 +- .../org/jclouds/chef/ChefAsyncClient.java | 25 +- .../java/org/jclouds/chef/ChefClient.java | 42 +- .../chef/config/BaseChefRestClientModule.java | 2 +- .../jclouds/chef/config/ChefParserModule.java | 96 +- .../java/org/jclouds/chef/domain/Client.java | 35 +- .../org/jclouds/chef/domain/Organization.java | 10 +- .../java/org/jclouds/chef/domain/User.java | 10 +- .../chef/filters/SignedHeaderAuth.java | 36 +- .../chef/functions/ParseKeyFromJson.java | 69 -- .../org/jclouds/chef/ChefAsyncClientTest.java | 65 +- .../org/jclouds/chef/ChefClientLiveTest.java | 28 +- .../functions/ParseClientFromJsonTest.java | 29 +- .../chef/functions/ParseKeyFromJsonTest.java | 51 - chef/core/src/test/resources/client.json | 1 + .../servlet/ChefRegistrationListener.java | 9 +- .../jclouds/encryption/EncryptionService.java | 23 +- .../internal/BaseEncryptionService.java | 165 +++- .../internal/JCEEncryptionService.java | 25 +- .../java/org/jclouds/http/BaseJettyTest.java | 37 +- .../http/filters/BasicAuthenticationTest.java | 3 +- .../jclouds/http/internal/WireLiveTest.java | 4 + core/src/test/resources/const.txt | 872 ++++++++++++++++++ .../BouncyCastleEncryptionService.java | 25 +- .../jclouds/gae/ConvertToGaeRequestTest.java | 5 +- .../gae/ConvertToJcloudsResponseTest.java | 3 + .../OpscodePlatformAsyncClient.java | 14 +- .../OpscodePlatformClient.java | 4 +- .../OpscodePlatformAsyncClientTest.java | 46 +- .../OpscodePlatformClientLiveTest.java | 14 +- .../cloudfiles/CloudFilesClientLiveTest.java | 53 +- 36 files changed, 1502 insertions(+), 511 deletions(-) delete mode 100644 chef/core/src/main/java/org/jclouds/chef/functions/ParseKeyFromJson.java delete mode 100644 chef/core/src/test/java/org/jclouds/chef/functions/ParseKeyFromJsonTest.java create mode 100644 chef/core/src/test/resources/client.json create mode 100644 core/src/test/resources/const.txt diff --git a/aws/core/src/test/java/org/jclouds/aws/s3/functions/ParseObjectMetadataFromHeadersTest.java b/aws/core/src/test/java/org/jclouds/aws/s3/functions/ParseObjectMetadataFromHeadersTest.java index f43ac43c04..2dbd6adefe 100644 --- a/aws/core/src/test/java/org/jclouds/aws/s3/functions/ParseObjectMetadataFromHeadersTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/s3/functions/ParseObjectMetadataFromHeadersTest.java @@ -24,6 +24,7 @@ import static org.easymock.classextension.EasyMock.replay; import static org.testng.Assert.assertEquals; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.Date; import java.util.Map; @@ -58,6 +59,8 @@ public class ParseObjectMetadataFromHeadersTest { encryptionService = new JCEEncryptionService(); } catch (NoSuchAlgorithmException e) { Throwables.propagate(e); + } catch (CertificateException e) { + Throwables.propagate(e); } } @@ -70,7 +73,7 @@ public class ParseObjectMetadataFromHeadersTest { http.getHeaders().put("Content-Disposition", "contentDisposition"); http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding"); ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http, "\"abc\""), - blobToObjectMetadata, encryptionService, "x-amz-meta-"); + blobToObjectMetadata, encryptionService, "x-amz-meta-"); MutableObjectMetadata response = parser.apply(http); assertEquals(response, expects); } @@ -86,7 +89,7 @@ public class ParseObjectMetadataFromHeadersTest { http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding"); http.getHeaders().put("x-amz-meta-object-eTag", "\"abc\""); ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http, null), - blobToObjectMetadata, encryptionService, "x-amz-meta-"); + blobToObjectMetadata, encryptionService, "x-amz-meta-"); MutableObjectMetadata response = parser.apply(http); assertEquals(response, expects); } diff --git a/aws/core/src/test/java/org/jclouds/aws/s3/xml/ListBucketHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/s3/xml/ListBucketHandlerTest.java index 323b10d445..8b7c6cb900 100755 --- a/aws/core/src/test/java/org/jclouds/aws/s3/xml/ListBucketHandlerTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/s3/xml/ListBucketHandlerTest.java @@ -22,6 +22,7 @@ import static org.testng.Assert.assertEquals; import java.io.InputStream; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.TreeSet; import org.jclouds.aws.s3.domain.CanonicalUser; @@ -55,6 +56,8 @@ public class ListBucketHandlerTest extends BaseHandlerTest { encryptionService = new JCEEncryptionService(); } catch (NoSuchAlgorithmException e) { Throwables.propagate(e); + } catch (CertificateException e) { + Throwables.propagate(e); } } @@ -69,56 +72,59 @@ public class ListBucketHandlerTest extends BaseHandlerTest { public void testApplyInputStream() { InputStream is = getClass().getResourceAsStream("/s3/list_bucket.xml"); CanonicalUser owner = new CanonicalUser("e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0", - "ferncam"); + "ferncam"); ListBucketResponse expected = new ListBucketResponseImpl("adriancole.org.jclouds.aws.s3.amazons3testdelimiter", - ImmutableList - .of( + ImmutableList.of( (ObjectMetadata) new BucketListObjectMetadata("apps/0", dateService - .iso8601DateParse("2009-05-07T18:27:08.000Z"), "\"c82e6a0025c31c5de5947fda62ac51ab\"", - encryptionService.fromHex("c82e6a0025c31c5de5947fda62ac51ab"), 8, owner, - StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/1", - dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"), - "\"944fab2c5a9a6bacf07db5e688310d7a\"", encryptionService - .fromHex("944fab2c5a9a6bacf07db5e688310d7a"), 8, owner, StorageClass.STANDARD), + .iso8601DateParse("2009-05-07T18:27:08.000Z"), "\"c82e6a0025c31c5de5947fda62ac51ab\"", + encryptionService.fromHex("c82e6a0025c31c5de5947fda62ac51ab"), 8, owner, + StorageClass.STANDARD), + (ObjectMetadata) new BucketListObjectMetadata("apps/1", dateService + .iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"944fab2c5a9a6bacf07db5e688310d7a\"", + encryptionService.fromHex("944fab2c5a9a6bacf07db5e688310d7a"), 8, owner, + StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/2", dateService - .iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"a227b8888045c8fd159fb495214000f0\"", - encryptionService.fromHex("a227b8888045c8fd159fb495214000f0"), 8, owner, - StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/3", - dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"), - "\"c9caa76c3dec53e2a192608ce73eef03\"", encryptionService - .fromHex("c9caa76c3dec53e2a192608ce73eef03"), 8, owner, StorageClass.STANDARD), + .iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"a227b8888045c8fd159fb495214000f0\"", + encryptionService.fromHex("a227b8888045c8fd159fb495214000f0"), 8, owner, + StorageClass.STANDARD), + (ObjectMetadata) new BucketListObjectMetadata("apps/3", dateService + .iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"c9caa76c3dec53e2a192608ce73eef03\"", + encryptionService.fromHex("c9caa76c3dec53e2a192608ce73eef03"), 8, owner, + StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/4", dateService - .iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"1ce5d0dcc6154a647ea90c7bdf82a224\"", - encryptionService.fromHex("1ce5d0dcc6154a647ea90c7bdf82a224"), 8, owner, - StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/5", - dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"), - "\"79433524d87462ee05708a8ef894ed55\"", encryptionService - .fromHex("79433524d87462ee05708a8ef894ed55"), 8, owner, StorageClass.STANDARD), + .iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"1ce5d0dcc6154a647ea90c7bdf82a224\"", + encryptionService.fromHex("1ce5d0dcc6154a647ea90c7bdf82a224"), 8, owner, + StorageClass.STANDARD), + (ObjectMetadata) new BucketListObjectMetadata("apps/5", dateService + .iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"79433524d87462ee05708a8ef894ed55\"", + encryptionService.fromHex("79433524d87462ee05708a8ef894ed55"), 8, owner, + StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/6", dateService - .iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"dd00a060b28ddca8bc5a21a49e306f67\"", - encryptionService.fromHex("dd00a060b28ddca8bc5a21a49e306f67"), 8, owner, - StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/7", - dateService.iso8601DateParse("2009-05-07T18:27:10.000Z"), - "\"8cd06eca6e819a927b07a285d750b100\"", encryptionService - .fromHex("8cd06eca6e819a927b07a285d750b100"), 8, owner, StorageClass.STANDARD), + .iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"dd00a060b28ddca8bc5a21a49e306f67\"", + encryptionService.fromHex("dd00a060b28ddca8bc5a21a49e306f67"), 8, owner, + StorageClass.STANDARD), + (ObjectMetadata) new BucketListObjectMetadata("apps/7", dateService + .iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"8cd06eca6e819a927b07a285d750b100\"", + encryptionService.fromHex("8cd06eca6e819a927b07a285d750b100"), 8, owner, + StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/8", dateService - .iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"174495094d0633b92cbe46603eee6bad\"", - encryptionService.fromHex("174495094d0633b92cbe46603eee6bad"), 8, owner, - StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/9", - dateService.iso8601DateParse("2009-05-07T18:27:10.000Z"), - "\"cd8a19b26fea8a827276df0ad11c580d\"", encryptionService - .fromHex("cd8a19b26fea8a827276df0ad11c580d"), 8, owner, StorageClass.STANDARD)), - "apps/", null, null, 1000, null, false, new TreeSet()); + .iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"174495094d0633b92cbe46603eee6bad\"", + encryptionService.fromHex("174495094d0633b92cbe46603eee6bad"), 8, owner, + StorageClass.STANDARD), + (ObjectMetadata) new BucketListObjectMetadata("apps/9", dateService + .iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"cd8a19b26fea8a827276df0ad11c580d\"", + encryptionService.fromHex("cd8a19b26fea8a827276df0ad11c580d"), 8, owner, + StorageClass.STANDARD)), "apps/", null, null, 1000, null, false, new TreeSet()); ListBucketResponse result = (ListBucketResponse) factory.create(injector.getInstance(ListBucketHandler.class)) - .parse(is); + .parse(is); assertEquals(result, expected); } ParseSax createParser() { ParseSax parser = (ParseSax) factory.create(injector - .getInstance(ListBucketHandler.class)); + .getInstance(ListBucketHandler.class)); return parser; } @@ -126,7 +132,7 @@ public class ListBucketHandlerTest extends BaseHandlerTest { public void testListMyBucketsWithDelimiterSlashAndCommonPrefixesAppsSlash() throws HttpException { ListBucketResponse bucket = createParser().parse( - Utils.toInputStream(listBucketWithSlashDelimiterAndCommonPrefixApps)); + Utils.toInputStream(listBucketWithSlashDelimiterAndCommonPrefixApps)); assertEquals(bucket.getCommonPrefixes().iterator().next(), "apps/"); assertEquals(bucket.getDelimiter(), "/"); assert bucket.getMarker() == null; diff --git a/aws/core/src/test/java/org/jclouds/aws/sqs/SQSClientLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/sqs/SQSClientLiveTest.java index 0640245dad..9abc99b9ad 100644 --- a/aws/core/src/test/java/org/jclouds/aws/sqs/SQSClientLiveTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/sqs/SQSClientLiveTest.java @@ -24,15 +24,12 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import java.io.IOException; -import java.security.NoSuchAlgorithmException; import java.util.Set; import java.util.SortedSet; import org.jclouds.aws.AWSResponseException; import org.jclouds.aws.domain.Region; import org.jclouds.aws.sqs.domain.Queue; -import org.jclouds.encryption.EncryptionService; -import org.jclouds.encryption.internal.JCEEncryptionService; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.rest.RestContext; import org.jclouds.rest.RestContextFactory; @@ -41,7 +38,6 @@ import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; -import com.google.common.base.Throwables; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.inject.Module; @@ -59,15 +55,6 @@ public class SQSClientLiveTest { private RestContext context; - protected volatile static EncryptionService encryptionService; - static { - try { - encryptionService = new JCEEncryptionService(); - } catch (NoSuchAlgorithmException e) { - Throwables.propagate(e); - } - } - private Set queues = Sets.newHashSet(); @BeforeGroups(groups = { "live" }) @@ -136,7 +123,7 @@ public class SQSClientLiveTest { @Test(dependsOnMethods = "testCreateQueue") void testSendMessage() throws InterruptedException { String message = "hardyharhar"; - byte[] md5 = encryptionService.md5(Utils.toInputStream(message)); + byte[] md5 = context.utils().encryption().md5(Utils.toInputStream(message)); for (Queue queue : queues) { assertEquals(client.sendMessage(queue, message), md5); } diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java index 169f092b83..76b2279fe8 100755 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java @@ -25,6 +25,7 @@ import static org.jclouds.blobstore.options.GetOptions.Builder.ifUnmodifiedSince import static org.jclouds.blobstore.options.GetOptions.Builder.range; import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNullAndClose; import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion; +import static org.jclouds.concurrent.ConcurrentUtils.compose; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; @@ -36,6 +37,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.Date; import java.util.Map; import java.util.concurrent.Future; @@ -49,7 +51,6 @@ import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.PageSet; import org.jclouds.blobstore.domain.StorageMetadata; import org.jclouds.blobstore.domain.StorageType; -import static org.jclouds.concurrent.ConcurrentUtils.*; import org.jclouds.encryption.EncryptionService; import org.jclouds.encryption.internal.JCEEncryptionService; import org.jclouds.http.BaseJettyTest; @@ -429,6 +430,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest { encryptionService = new JCEEncryptionService(); } catch (NoSuchAlgorithmException e) { Throwables.propagate(e); + } catch (CertificateException e) { + Throwables.propagate(e); } } diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java index 254b529f91..db2d4dec0d 100644 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java @@ -23,7 +23,6 @@ import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNull import static org.testng.Assert.assertEquals; import java.io.IOException; -import java.security.NoSuchAlgorithmException; import java.util.Map; import java.util.Set; import java.util.Map.Entry; @@ -40,8 +39,6 @@ import org.jclouds.blobstore.attr.ConsistencyModel; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.StorageMetadata; import org.jclouds.blobstore.domain.StorageType; -import org.jclouds.encryption.EncryptionService; -import org.jclouds.encryption.internal.JCEEncryptionService; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.testng.ITestContext; import org.testng.annotations.AfterClass; @@ -62,13 +59,13 @@ public class BaseBlobStoreIntegrationTest { protected static final String TEST_STRING = String.format(XML_STRING_FORMAT, "apple"); protected Map fiveStrings = ImmutableMap.of("one", String.format(XML_STRING_FORMAT, "apple"), "two", - String.format(XML_STRING_FORMAT, "bear"), "three", String.format(XML_STRING_FORMAT, "candy"), "four", String - .format(XML_STRING_FORMAT, "dogma"), "five", String.format(XML_STRING_FORMAT, "emma")); + String.format(XML_STRING_FORMAT, "bear"), "three", String.format(XML_STRING_FORMAT, "candy"), "four", + String.format(XML_STRING_FORMAT, "dogma"), "five", String.format(XML_STRING_FORMAT, "emma")); protected Map fiveStringsUnderPath = ImmutableMap.of("path/1", String.format(XML_STRING_FORMAT, - "apple"), "path/2", String.format(XML_STRING_FORMAT, "bear"), "path/3", String.format(XML_STRING_FORMAT, - "candy"), "path/4", String.format(XML_STRING_FORMAT, "dogma"), "path/5", String.format(XML_STRING_FORMAT, - "emma")); + "apple"), "path/2", String.format(XML_STRING_FORMAT, "bear"), "path/3", String.format(XML_STRING_FORMAT, + "candy"), "path/4", String.format(XML_STRING_FORMAT, "dogma"), "path/5", String.format(XML_STRING_FORMAT, + "emma")); public static long INCONSISTENCY_WINDOW = 10000; protected static volatile AtomicInteger containerIndex = new AtomicInteger(0); @@ -81,18 +78,8 @@ public class BaseBlobStoreIntegrationTest { */ private volatile static BlockingQueue containerNames = new ArrayBlockingQueue(containerCount); - protected volatile static EncryptionService encryptionService; - static { - try { - encryptionService = new JCEEncryptionService(); - } catch (NoSuchAlgorithmException e) { - Throwables.propagate(e); - } - } - /** - * There are a lot of retries here mainly from experience running inside - * amazon EC2. + * There are a lot of retries here mainly from experience running inside amazon EC2. */ @BeforeSuite public void setUpResourcesForAllThreads(ITestContext testContext) throws Exception { @@ -102,7 +89,7 @@ public class BaseBlobStoreIntegrationTest { @SuppressWarnings("unchecked") private BlobStoreContext getCloudResources(ITestContext testContext) throws ClassNotFoundException, - InstantiationException, IllegalAccessException, Exception { + InstantiationException, IllegalAccessException, Exception { String initializerClass = checkNotNull(System.getProperty("jclouds.test.initializer"), "jclouds.test.initializer"); Class clazz = (Class) Class.forName(initializerClass); BaseTestInitializer initializer = clazz.newInstance(); @@ -112,11 +99,10 @@ public class BaseBlobStoreIntegrationTest { protected ExecutorService exec; /** - * we are doing this at a class level, as the context.getBlobStore() object - * is going to be shared for all methods in the class. We don't want to do - * this for group, as some test classes may want to have a different - * implementation of context.getBlobStore(). For example, one class may want - * non-blocking i/o and another class google appengine. + * we are doing this at a class level, as the context.getBlobStore() object is going to be shared + * for all methods in the class. We don't want to do this for group, as some test classes may + * want to have a different implementation of context.getBlobStore(). For example, one class may + * want non-blocking i/o and another class google appengine. */ @BeforeClass(groups = { "integration", "live" }) public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception { @@ -136,7 +122,7 @@ public class BaseBlobStoreIntegrationTest { private static volatile boolean initialized = false; protected void createContainersSharedByAllThreads(BlobStoreContext context, ITestContext testContext) - throws Exception { + throws Exception { while (!initialized) { synchronized (BaseBlobStoreIntegrationTest.class) { if (!initialized) { @@ -187,12 +173,12 @@ public class BaseBlobStoreIntegrationTest { try { for (int i = 0; i < 2; i++) { Iterable testContainers = Iterables.filter(context.getBlobStore().list(), - new Predicate() { - public boolean apply(StorageMetadata input) { - return (input.getType() == StorageType.CONTAINER || input.getType() == StorageType.FOLDER) - && input.getName().startsWith(CONTAINER_PREFIX.toLowerCase()); - } - }); + new Predicate() { + public boolean apply(StorageMetadata input) { + return (input.getType() == StorageType.CONTAINER || input.getType() == StorageType.FOLDER) + && input.getName().startsWith(CONTAINER_PREFIX.toLowerCase()); + } + }); for (StorageMetadata container : testContainers) { deleteContainerOrWarnIfUnable(context, container.getName()); } @@ -209,12 +195,11 @@ public class BaseBlobStoreIntegrationTest { public static boolean SANITY_CHECK_RETURNED_BUCKET_NAME = false; /** - * Due to eventual consistency, container commands may not return correctly - * immediately. Hence, we will try up to the inconsistency window to see if - * the assertion completes. + * Due to eventual consistency, container commands may not return correctly immediately. Hence, + * we will try up to the inconsistency window to see if the assertion completes. */ protected static void assertConsistencyAware(BlobStoreContext context, Runnable assertion) - throws InterruptedException { + throws InterruptedException { if (context.getConsistencyModel() == ConsistencyModel.STRICT) { assertion.run(); return; @@ -240,7 +225,7 @@ public class BaseBlobStoreIntegrationTest { } protected static void createContainerAndEnsureEmpty(BlobStoreContext context, final String containerName) - throws InterruptedException { + throws InterruptedException { context.getBlobStore().createContainerInLocation(null, containerName); if (context.getConsistencyModel() == ConsistencyModel.EVENTUAL) Thread.sleep(1000); @@ -284,19 +269,19 @@ public class BaseBlobStoreIntegrationTest { } protected void assertConsistencyAwareContainerSize(final String containerName, final int count) - throws InterruptedException { + throws InterruptedException { assertConsistencyAware(new Runnable() { public void run() { try { assert context.getBlobStore().countBlobs(containerName) == count : String.format( - "expected only %d values in %s: %s", count, containerName, Sets.newHashSet(Iterables.transform( - context.getBlobStore().list(containerName), new Function() { + "expected only %d values in %s: %s", count, containerName, Sets.newHashSet(Iterables.transform( + context.getBlobStore().list(containerName), new Function() { - public String apply(StorageMetadata from) { - return from.getName(); - } + public String apply(StorageMetadata from) { + return from.getName(); + } - }))); + }))); } catch (Exception e) { Throwables.propagateIfPossible(e); } @@ -312,9 +297,9 @@ public class BaseBlobStoreIntegrationTest { } /** - * requestor will create a container using the name returned from this. This - * method will take care not to exceed the maximum containers permitted by a - * provider by deleting an existing container first. + * requestor will create a container using the name returned from this. This method will take + * care not to exceed the maximum containers permitted by a provider by deleting an existing + * container first. * * @throws InterruptedException */ @@ -326,13 +311,12 @@ public class BaseBlobStoreIntegrationTest { if (containerName != null) { containerNames.add(containerName); /* - * Ensure that any returned container name actually exists on the - * server. Return of a non-existent container introduces subtle testing - * bugs, where later unrelated tests will fail. + * Ensure that any returned container name actually exists on the server. Return of a + * non-existent container introduces subtle testing bugs, where later unrelated tests will + * fail. * - * NOTE: This sanity check should only be run for Stub-based - * Integration testing -- it will *substantially* slow down tests on a - * real server over a network. + * NOTE: This sanity check should only be run for Stub-based Integration testing -- it will + * *substantially* slow down tests on a real server over a network. */ if (SANITY_CHECK_RETURNED_BUCKET_NAME) { if (!Iterables.any(context.getBlobStore().list(), new Predicate() { @@ -347,8 +331,7 @@ public class BaseBlobStoreIntegrationTest { } /** - * abandon old container name instead of waiting for the container to be - * created. + * abandon old container name instead of waiting for the container to be created. * * @throws InterruptedException */ diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java index a8c4a14163..cc13116cff 100755 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java @@ -79,21 +79,21 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest { // normalize the // providers. object.getMetadata().getUserMetadata().put("Adrian", "powderpuff"); - object.getMetadata().setContentMD5(encryptionService.md5(toInputStream(TEST_STRING))); + object.getMetadata().setContentMD5(context.utils().encryption().md5(toInputStream(TEST_STRING))); String containerName = getContainerName(); try { addBlobToContainer(containerName, object); validateContent(containerName, key); PageSet container = context.getBlobStore().list(containerName, - maxResults(1).withDetails()); + maxResults(1).withDetails()); BlobMetadata metadata = BlobMetadata.class.cast(get(container, 0)); assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType(); assertEquals(metadata.getSize(), new Long(TEST_STRING.length())); assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff"); - assertEquals(metadata.getContentMD5(), encryptionService.md5(toInputStream(TEST_STRING))); + assertEquals(metadata.getContentMD5(), context.utils().encryption().md5(toInputStream(TEST_STRING))); } finally { returnContainer(containerName); } @@ -125,7 +125,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest { container = context.getBlobStore().list(containerName, afterMarker(marker)); assertEquals(container.getNextMarker(), null); assert container.size() == 25 : String.format("size should have been 25, but was %d: %s", container.size(), - container); + container); assert container.getNextMarker() == null; } finally { @@ -295,7 +295,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest { public void run() { try { assert !context.getBlobStore().containerExists(containerName) : "container " + containerName - + " still exists"; + + " still exists"; } catch (Exception e) { propagateIfPossible(e); } @@ -305,7 +305,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest { @Test(groups = { "integration", "live" }) public void testListContainer() throws InterruptedException, ExecutionException, TimeoutException, - UnsupportedEncodingException { + UnsupportedEncodingException { String containerName = getContainerName(); try { add15UnderRoot(containerName); diff --git a/chef/core/src/main/java/org/jclouds/chef/ChefAsyncClient.java b/chef/core/src/main/java/org/jclouds/chef/ChefAsyncClient.java index 1e350062ec..1bc09cc330 100644 --- a/chef/core/src/main/java/org/jclouds/chef/ChefAsyncClient.java +++ b/chef/core/src/main/java/org/jclouds/chef/ChefAsyncClient.java @@ -49,7 +49,6 @@ import org.jclouds.chef.domain.Role; import org.jclouds.chef.domain.Sandbox; import org.jclouds.chef.domain.UploadSandbox; import org.jclouds.chef.filters.SignedHeaderAuth; -import org.jclouds.chef.functions.ParseKeyFromJson; import org.jclouds.chef.functions.ParseKeySetFromJson; import org.jclouds.domain.JsonBall; import org.jclouds.rest.annotations.BinderParam; @@ -86,7 +85,7 @@ public interface ChefAsyncClient { @POST @Path("sandboxes") ListenableFuture getUploadSandboxForChecksums( - @BinderParam(BindChecksumsToJsonPayload.class) Set> md5s); + @BinderParam(BindChecksumsToJsonPayload.class) Set> md5s); @PUT ListenableFuture uploadContent(@BinderParam(BindChecksumsToJsonPayload.class) Set> md5s); @@ -97,7 +96,7 @@ public interface ChefAsyncClient { @PUT @Path("sandboxes/{id}") ListenableFuture commitSandbox(@PathParam("id") String id, - @BinderParam(BindIsCompletedToJsonPayload.class) boolean isCompleted); + @BinderParam(BindIsCompletedToJsonPayload.class) boolean isCompleted); /** * @see ChefCookbooks#listCookbooks @@ -114,7 +113,7 @@ public interface ChefAsyncClient { @PUT @Path("cookbooks/{cookbookname}/{version}") ListenableFuture updateCookbook(@PathParam("cookbookname") String cookbookName, - @PathParam("version") String version, @BinderParam(BindToJsonPayload.class) CookbookVersion cookbook); + @PathParam("version") String version, @BinderParam(BindToJsonPayload.class) CookbookVersion cookbook); /** * @see ChefCookbook#deleteCookbook(String) @@ -123,7 +122,7 @@ public interface ChefAsyncClient { @Path("cookbooks/{cookbookname}/{version}") @ExceptionParser(ReturnNullOnNotFoundOr404.class) ListenableFuture deleteCookbook(@PathParam("cookbookname") String cookbookName, - @PathParam("version") String version); + @PathParam("version") String version); /** * @see ChefCookbook#getVersionsOfCookbook @@ -141,24 +140,22 @@ public interface ChefAsyncClient { @Path("cookbooks/{cookbookname}/{version}") @ExceptionParser(ReturnNullOnNotFoundOr404.class) ListenableFuture getCookbook(@PathParam("cookbookname") String cookbookName, - @PathParam("version") String version); + @PathParam("version") String version); /** * @see ChefClient#createClient */ @POST @Path("clients") - @ResponseParser(ParseKeyFromJson.class) - ListenableFuture createClient(@BinderParam(BindClientnameToJsonPayload.class) String clientname); + ListenableFuture createClient(@BinderParam(BindClientnameToJsonPayload.class) String clientname); /** * @see ChefClient#generateKeyForClient */ @PUT @Path("clients/{clientname}") - @ResponseParser(ParseKeyFromJson.class) - ListenableFuture generateKeyForClient( - @PathParam("clientname") @BinderParam(BindGenerateKeyForClientToJsonPayload.class) String clientname); + ListenableFuture generateKeyForClient( + @PathParam("clientname") @BinderParam(BindGenerateKeyForClientToJsonPayload.class) String clientname); /** * @see ChefClient#clientExists @@ -206,7 +203,7 @@ public interface ChefAsyncClient { @PUT @Path("nodes/{nodename}") ListenableFuture updateNode( - @PathParam("nodename") @ParamParser(NodeName.class) @BinderParam(BindToJsonPayload.class) Node node); + @PathParam("nodename") @ParamParser(NodeName.class) @BinderParam(BindToJsonPayload.class) Node node); /** * @see ChefNode#nodeExists @@ -254,7 +251,7 @@ public interface ChefAsyncClient { @PUT @Path("roles/{rolename}") ListenableFuture updateRole( - @PathParam("rolename") @ParamParser(RoleName.class) @BinderParam(BindToJsonPayload.class) Role role); + @PathParam("rolename") @ParamParser(RoleName.class) @BinderParam(BindToJsonPayload.class) Role role); /** * @see ChefRole#roleExists @@ -302,7 +299,7 @@ public interface ChefAsyncClient { @PUT @Path("data/{path}") ListenableFuture updateDataBag(@PathParam("path") String path, - @BinderParam(BindToJsonPayload.class) JsonBall node); + @BinderParam(BindToJsonPayload.class) JsonBall node); /** * @see ChefDataBag#nodeExists diff --git a/chef/core/src/main/java/org/jclouds/chef/ChefClient.java b/chef/core/src/main/java/org/jclouds/chef/ChefClient.java index 258472e998..7a7dee8dcb 100644 --- a/chef/core/src/main/java/org/jclouds/chef/ChefClient.java +++ b/chef/core/src/main/java/org/jclouds/chef/ChefClient.java @@ -70,8 +70,7 @@ public interface ChefClient { * FIXME Comment this * * @param md5s - * raw md5s; uses {@code Bytes.asList()} and {@code - * Bytes.toByteArray()} as necessary + * raw md5s; uses {@code Bytes.asList()} and {@code Bytes.toByteArray()} as necessary * @return */ UploadSandbox getUploadSandboxForChecksums(Set> md5s); @@ -85,8 +84,7 @@ public interface ChefClient { *

* "401 Unauthorized" if the caller is not a recognized user. *

- * "403 Forbidden" if you do not have permission to see the - * cookbook list. + * "403 Forbidden" if you do not have permission to see the cookbook list. */ Set listCookbooks(); @@ -108,8 +106,7 @@ public interface ChefClient { *

* "401 Unauthorized" if you are not a recognized user. *

- * "403 Forbidden" if you do not have Delete rights on the - * cookbook. + * "403 Forbidden" if you do not have Delete rights on the cookbook. */ CookbookVersion deleteCookbook(String cookbookName, String version); @@ -121,14 +118,13 @@ public interface ChefClient { *

* "401 Unauthorized" if the caller is not a recognized user. *

- * "403 Forbidden" if the caller is not authorized to view the - * cookbook. + * "403 Forbidden" if the caller is not authorized to view the cookbook. */ Set getVersionsOfCookbook(String cookbookName); /** - * Returns a description of the cookbook, with links to all of its component - * parts, and the metadata. + * Returns a description of the cookbook, with links to all of its component parts, and the + * metadata. * * @return the cookbook or null, if not found * @@ -136,31 +132,28 @@ public interface ChefClient { *

* "401 Unauthorized" if the caller is not a recognized user. *

- * "403 Forbidden" if the caller is not authorized to view the - * cookbook. + * "403 Forbidden" if the caller is not authorized to view the cookbook. */ CookbookVersion getCookbook(String cookbookName, String version); /** * creates a new client * - * @return the private key of the client. You can then use this client name - * and private key to access the Opscode API. + * @return the private key of the client. You can then use this client name and private key to + * access the Opscode API. * @throws AuthorizationException *

* "401 Unauthorized" if the caller is not a recognized user. *

- * "403 Forbidden" if the caller is not authorized to create a - * client. + * "403 Forbidden" if the caller is not authorized to create a client. * @throws HttpResponseException * "409 Conflict" if the client already exists */ @Timeout(duration = 120, timeUnit = TimeUnit.SECONDS) - String createClient(String name); + Client createClient(String name); /** - * generate a new key-pair for this client, and return the new private key in - * the response body. + * generate a new key-pair for this client, and return the new private key in the response body. * * @return the new private key * @@ -168,11 +161,10 @@ public interface ChefClient { *

* "401 Unauthorized" if the caller is not a recognized user. *

- * "403 Forbidden" if the caller is not authorized to modify the - * client. + * "403 Forbidden" if the caller is not authorized to modify the client. */ @Timeout(duration = 120, timeUnit = TimeUnit.SECONDS) - String generateKeyForClient(String name); + Client generateKeyForClient(String name); /** * @return list of client names. @@ -229,8 +221,7 @@ public interface ChefClient { *

* "401 Unauthorized" if the caller is not a recognized user. *

- * "403 Forbidden" if the caller is not authorized to create a - * node. + * "403 Forbidden" if the caller is not authorized to create a node. * @throws HttpResponseException * "409 Conflict" if the node already exists */ @@ -302,8 +293,7 @@ public interface ChefClient { *

* "401 Unauthorized" if the caller is not a recognized user. *

- * "403 Forbidden" if the caller is not authorized to create a - * role. + * "403 Forbidden" if the caller is not authorized to create a role. * @throws HttpResponseException * "409 Conflict" if the role already exists */ diff --git a/chef/core/src/main/java/org/jclouds/chef/config/BaseChefRestClientModule.java b/chef/core/src/main/java/org/jclouds/chef/config/BaseChefRestClientModule.java index b019eaa330..f85aec8f00 100644 --- a/chef/core/src/main/java/org/jclouds/chef/config/BaseChefRestClientModule.java +++ b/chef/core/src/main/java/org/jclouds/chef/config/BaseChefRestClientModule.java @@ -88,7 +88,7 @@ public class BaseChefRestClientModule extends RestClientModule { @Singleton public PrivateKey provideKey(EncryptionService encryptionService, @Named(PROPERTY_CREDENTIAL) String pem) throws UnsupportedEncodingException { - return encryptionService.readPrivateKeyFromPEM(pem.getBytes("UTF-8")); + return encryptionService.privateKeyFromPEM(pem.getBytes("UTF-8")); } @Override diff --git a/chef/core/src/main/java/org/jclouds/chef/config/ChefParserModule.java b/chef/core/src/main/java/org/jclouds/chef/config/ChefParserModule.java index 85bcf983c7..2eca448385 100644 --- a/chef/core/src/main/java/org/jclouds/chef/config/ChefParserModule.java +++ b/chef/core/src/main/java/org/jclouds/chef/config/ChefParserModule.java @@ -18,17 +18,24 @@ */ package org.jclouds.chef.config; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; import java.util.Map; +import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.Constants; import org.jclouds.chef.domain.DataBagItem; +import org.jclouds.encryption.EncryptionService; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; @@ -47,6 +54,87 @@ import com.google.inject.Provides; * @author Adrian Cole */ public class ChefParserModule extends AbstractModule { + @ImplementedBy(PrivateKeyAdapterImpl.class) + public static interface PrivateKeyAdapter extends JsonDeserializer { + + } + + @Singleton + public static class PrivateKeyAdapterImpl implements PrivateKeyAdapter { + private final EncryptionService encryptionService; + + @Inject + PrivateKeyAdapterImpl(EncryptionService encryptionService) { + this.encryptionService = encryptionService; + } + + @Override + public PrivateKey deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + String keyText = json.getAsString().replaceAll("\\n", "\n"); + try { + return encryptionService.privateKeyFromPEM(keyText.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + Throwables.propagate(e); + return null; + } + } + } + + @ImplementedBy(PublicKeyAdapterImpl.class) + public static interface PublicKeyAdapter extends JsonDeserializer { + + } + + @Singleton + public static class PublicKeyAdapterImpl implements PublicKeyAdapter { + private final EncryptionService encryptionService; + + @Inject + PublicKeyAdapterImpl(EncryptionService encryptionService) { + this.encryptionService = encryptionService; + } + + @Override + public PublicKey deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + String keyText = json.getAsString().replaceAll("\\n", "\n"); + try { + return encryptionService.publicKeyFromPEM(keyText.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + Throwables.propagate(e); + return null; + } + } + } + + @ImplementedBy(X509CertificateAdapterImpl.class) + public static interface X509CertificateAdapter extends JsonDeserializer { + + } + + @Singleton + public static class X509CertificateAdapterImpl implements X509CertificateAdapter { + private final EncryptionService encryptionService; + + @Inject + X509CertificateAdapterImpl(EncryptionService encryptionService) { + this.encryptionService = encryptionService; + } + + @Override + public X509Certificate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + String keyText = json.getAsString().replaceAll("\\n", "\n"); + try { + return encryptionService.x509CertificateFromPEM(keyText.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + Throwables.propagate(e); + return null; + } + } + } + @ImplementedBy(DataBagItemAdapterImpl.class) public static interface DataBagItemAdapter extends JsonSerializer, JsonDeserializer { @@ -62,7 +150,7 @@ public class ChefParserModule extends AbstractModule { @Override public DataBagItem deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { IdHolder idHolder = context.deserialize(json, IdHolder.class); return new DataBagItem(idHolder.id, json.toString()); } @@ -75,8 +163,10 @@ public class ChefParserModule extends AbstractModule { @Provides @Singleton @Named(Constants.PROPERTY_GSON_ADAPTERS) - public Map provideCustomAdapterBindings(DataBagItemAdapter adapter) { - return ImmutableMap. of(DataBagItem.class, adapter); + public Map provideCustomAdapterBindings(DataBagItemAdapter adapter, PrivateKeyAdapter privateAdapter, + PublicKeyAdapter publicAdapter, X509CertificateAdapter certAdapter) { + return ImmutableMap. of(DataBagItem.class, adapter, PrivateKey.class, privateAdapter, + PublicKey.class, publicAdapter, X509Certificate.class, certAdapter); } @Override diff --git a/chef/core/src/main/java/org/jclouds/chef/domain/Client.java b/chef/core/src/main/java/org/jclouds/chef/domain/Client.java index 474b6f4672..afb4a104b3 100644 --- a/chef/core/src/main/java/org/jclouds/chef/domain/Client.java +++ b/chef/core/src/main/java/org/jclouds/chef/domain/Client.java @@ -23,6 +23,12 @@ */ package org.jclouds.chef.domain; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import javax.annotation.Nullable; + +import com.google.gson.annotations.SerializedName; /** * Client object. @@ -30,7 +36,9 @@ package org.jclouds.chef.domain; * @author Adrian Cole */ public class Client { - private String certificate; + private X509Certificate certificate; + @SerializedName("private_key") + private PrivateKey privateKey; private String orgname; private String clientname; private String name; @@ -41,7 +49,11 @@ public class Client { } - public String getCertificate() { + public PrivateKey getPrivateKey() { + return privateKey; + } + + public X509Certificate getCertificate() { return certificate; } @@ -64,7 +76,7 @@ public class Client { @Override public String toString() { return "[name=" + name + ", clientname=" + clientname + ", orgname=" + orgname + ", isValidator=" + validator - + ", certificate=" + certificate + "]"; + + ", certificate=" + certificate + ", privateKey=" + (privateKey != null) + "]"; } @Override @@ -73,9 +85,10 @@ public class Client { int result = 1; result = prime * result + ((certificate == null) ? 0 : certificate.hashCode()); result = prime * result + ((clientname == null) ? 0 : clientname.hashCode()); - result = prime * result + (validator ? 1231 : 1237); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((orgname == null) ? 0 : orgname.hashCode()); + result = prime * result + ((privateKey == null) ? 0 : privateKey.hashCode()); + result = prime * result + (validator ? 1231 : 1237); return result; } @@ -98,8 +111,6 @@ public class Client { return false; } else if (!clientname.equals(other.clientname)) return false; - if (validator != other.validator) - return false; if (name == null) { if (other.name != null) return false; @@ -110,14 +121,24 @@ public class Client { return false; } else if (!orgname.equals(other.orgname)) return false; + if (privateKey == null) { + if (other.privateKey != null) + return false; + } else if (!privateKey.equals(other.privateKey)) + return false; + if (validator != other.validator) + return false; return true; } - public Client(String certificate, String orgname, String clientname, String name, boolean isValidator) { + public Client(X509Certificate certificate, String orgname, String clientname, String name, boolean isValidator, + @Nullable PrivateKey privateKey) { this.certificate = certificate; this.orgname = orgname; this.clientname = clientname; this.name = name; this.validator = isValidator; + this.privateKey = privateKey; } + } \ No newline at end of file diff --git a/chef/core/src/main/java/org/jclouds/chef/domain/Organization.java b/chef/core/src/main/java/org/jclouds/chef/domain/Organization.java index 0d833e1842..e707ad5597 100644 --- a/chef/core/src/main/java/org/jclouds/chef/domain/Organization.java +++ b/chef/core/src/main/java/org/jclouds/chef/domain/Organization.java @@ -18,6 +18,8 @@ */ package org.jclouds.chef.domain; +import java.security.PrivateKey; + import com.google.gson.annotations.SerializedName; /** @@ -32,6 +34,8 @@ public class Organization implements Comparable { @SerializedName("org_type") private String orgType; private String clientname; + @SerializedName("private_key") + private PrivateKey privateKey; public Organization(String name) { this(); @@ -80,6 +84,10 @@ public class Organization implements Comparable { this.clientname = clientname; } + public PrivateKey getPrivateKey() { + return privateKey; + } + @Override public int hashCode() { final int prime = 31; @@ -126,7 +134,7 @@ public class Organization implements Comparable { @Override public String toString() { return "Organization [clientname=" + clientname + ", fullName=" + fullName + ", name=" + name + ", orgType=" - + orgType + "]"; + + orgType + "]"; } } diff --git a/chef/core/src/main/java/org/jclouds/chef/domain/User.java b/chef/core/src/main/java/org/jclouds/chef/domain/User.java index 0691b0ec43..e9a57dab93 100644 --- a/chef/core/src/main/java/org/jclouds/chef/domain/User.java +++ b/chef/core/src/main/java/org/jclouds/chef/domain/User.java @@ -18,6 +18,8 @@ */ package org.jclouds.chef.domain; +import java.security.PrivateKey; + import com.google.gson.annotations.SerializedName; /** @@ -37,6 +39,8 @@ public class User implements Comparable { private String displayName; private String email; private String password; + @SerializedName("private_key") + private PrivateKey privateKey; public User(String username) { this.username = username; @@ -108,6 +112,10 @@ public class User implements Comparable { return password; } + public PrivateKey getPrivateKey() { + return privateKey; + } + @Override public int hashCode() { final int prime = 31; @@ -172,7 +180,7 @@ public class User implements Comparable { @Override public String toString() { return "User [displayName=" + displayName + ", email=" + email + ", firstName=" + firstName + ", lastName=" - + lastName + ", middleName=" + middleName + ", password=" + password + ", username=" + username + "]"; + + lastName + ", middleName=" + middleName + ", password=" + password + ", username=" + username + "]"; } } diff --git a/chef/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java b/chef/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java index 26c075fdfa..b8d7133522 100644 --- a/chef/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java +++ b/chef/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java @@ -81,9 +81,8 @@ public class SignedHeaderAuth implements HttpRequestFilter { Logger signatureLog = Logger.NULL; @Inject - public SignedHeaderAuth(SignatureWire signatureWire, @Named(PROPERTY_IDENTITY) String userId, - PrivateKey privateKey, @TimeStamp Provider timeStampProvider, - EncryptionService encryptionService, HttpUtils utils) { + public SignedHeaderAuth(SignatureWire signatureWire, @Named(PROPERTY_IDENTITY) String userId, PrivateKey privateKey, + @TimeStamp Provider timeStampProvider, EncryptionService encryptionService, HttpUtils utils) { this.signatureWire = signatureWire; this.userId = userId; this.privateKey = privateKey; @@ -96,48 +95,41 @@ public class SignedHeaderAuth implements HttpRequestFilter { public void filter(HttpRequest request) throws HttpException { String contentHash = hashBody(request.getPayload()); - request.getHeaders().replaceValues("X-Ops-Content-Hash", - Collections.singletonList(contentHash)); + request.getHeaders().replaceValues("X-Ops-Content-Hash", Collections.singletonList(contentHash)); String timestamp = timeStampProvider.get(); - String toSign = createStringToSign(request.getMethod(), hashPath(request.getEndpoint() - .getPath()), contentHash, timestamp); + String toSign = createStringToSign(request.getMethod(), hashPath(request.getEndpoint().getPath()), contentHash, + timestamp); request.getHeaders().replaceValues("X-Ops-Userid", Collections.singletonList(userId)); - request.getHeaders().replaceValues("X-Ops-Sign", - Collections.singletonList(SIGNING_DESCRIPTION)); + request.getHeaders().replaceValues("X-Ops-Sign", Collections.singletonList(SIGNING_DESCRIPTION)); calculateAndReplaceAuthorizationHeaders(request, toSign); request.getHeaders().replaceValues("X-Ops-Timestamp", Collections.singletonList(timestamp)); utils.logRequest(signatureLog, request, "<<"); } @VisibleForTesting - void calculateAndReplaceAuthorizationHeaders(HttpRequest request, String toSign) - throws HttpException { + void calculateAndReplaceAuthorizationHeaders(HttpRequest request, String toSign) throws HttpException { String signature = sign(toSign); if (signatureWire.enabled()) signatureWire.input(Utils.toInputStream(signature)); - String[] signatureLines = Iterables.toArray(Splitter.fixedLength(60).split(signature), - String.class); + String[] signatureLines = Iterables.toArray(Splitter.fixedLength(60).split(signature), String.class); for (int i = 0; i < signatureLines.length; i++) { request.getHeaders().replaceValues("X-Ops-Authorization-" + (i + 1), Collections.singletonList(signatureLines[i])); } } - public String createStringToSign(String request, String hashedPath, String contentHash, - String timestamp) { + public String createStringToSign(String request, String hashedPath, String contentHash, String timestamp) { - return new StringBuilder().append("Method:").append(request).append("\n").append( - "Hashed Path:").append(hashedPath).append("\n").append("X-Ops-Content-Hash:") - .append(contentHash).append("\n").append("X-Ops-Timestamp:").append(timestamp) - .append("\n").append("X-Ops-UserId:").append(userId).toString(); + return new StringBuilder().append("Method:").append(request).append("\n").append("Hashed Path:").append( + hashedPath).append("\n").append("X-Ops-Content-Hash:").append(contentHash).append("\n").append( + "X-Ops-Timestamp:").append(timestamp).append("\n").append("X-Ops-UserId:").append(userId).toString(); } @VisibleForTesting String hashPath(String path) { try { - return encryptionService.base64(encryptionService.sha1(Utils - .toInputStream(canonicalPath(path)))); + return encryptionService.base64(encryptionService.sha1(Utils.toInputStream(canonicalPath(path)))); } catch (Exception e) { Throwables.propagateIfPossible(e); throw new HttpException("error creating sigature for path: " + path, e); @@ -190,7 +182,7 @@ public class SignedHeaderAuth implements HttpRequestFilter { public String sign(String toSign) { try { - byte[] encrypted = encryptionService.rsaSign(toSign, privateKey); + byte[] encrypted = encryptionService.rsaEncrypt(Payloads.newStringPayload(toSign), privateKey); return encryptionService.base64(encrypted); } catch (Exception e) { throw new HttpException("error signing request", e); diff --git a/chef/core/src/main/java/org/jclouds/chef/functions/ParseKeyFromJson.java b/chef/core/src/main/java/org/jclouds/chef/functions/ParseKeyFromJson.java deleted file mode 100644 index b5df2ed86c..0000000000 --- a/chef/core/src/main/java/org/jclouds/chef/functions/ParseKeyFromJson.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * - * Copyright (C) 2010 Cloud Conscious, LLC. - * - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.jclouds.chef.functions; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.jclouds.http.HttpResponse; -import org.jclouds.http.functions.ReturnStringIf2xx; - -import com.google.common.base.Function; - -/** - * - * - * @author Adrian Cole - */ -@Singleton -public class ParseKeyFromJson implements Function { - Pattern pattern = Pattern.compile(".*private_key\": *\"([^\"]+)\".*"); - private final ReturnStringIf2xx returnStringIf200; - - @Inject - ParseKeyFromJson(ReturnStringIf2xx returnStringIf200) { - this.returnStringIf200 = returnStringIf200; - } - - @Override - public String apply(HttpResponse response) { - String content = returnStringIf200.apply(response); - if (content == null) - return null; - return parse(content); - } - - - public String parse(String in) { - Matcher matcher = pattern.matcher(in); - while (matcher.find()) { - return matcher.group(1).replaceAll("\\\\n", "\n"); - } - assert false : String.format("pattern: %s didn't match %s", pattern, in); - return null; - } -} \ No newline at end of file diff --git a/chef/core/src/test/java/org/jclouds/chef/ChefAsyncClientTest.java b/chef/core/src/test/java/org/jclouds/chef/ChefAsyncClientTest.java index 7230ee0fb7..4a57a4ac00 100644 --- a/chef/core/src/test/java/org/jclouds/chef/ChefAsyncClientTest.java +++ b/chef/core/src/test/java/org/jclouds/chef/ChefAsyncClientTest.java @@ -36,7 +36,6 @@ import org.jclouds.chef.domain.Node; import org.jclouds.chef.domain.Role; import org.jclouds.chef.filters.SignedHeaderAuth; import org.jclouds.chef.filters.SignedHeaderAuthTest; -import org.jclouds.chef.functions.ParseKeyFromJson; import org.jclouds.chef.functions.ParseKeySetFromJson; import org.jclouds.date.TimeStamp; import org.jclouds.encryption.EncryptionService; @@ -74,9 +73,9 @@ public class ChefAsyncClientTest extends RestClientTest { Method method = ChefAsyncClient.class.getMethod("commitSandbox", String.class, boolean.class); GeneratedHttpRequest httpRequest = processor.createRequest(method, - "0189e76ccc476701d6b374e5a1a27347", true); + "0189e76ccc476701d6b374e5a1a27347", true); assertRequestLineEquals(httpRequest, - "PUT http://localhost:4000/sandboxes/0189e76ccc476701d6b374e5a1a27347 HTTP/1.1"); + "PUT http://localhost:4000/sandboxes/0189e76ccc476701d6b374e5a1a27347 HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals(httpRequest, "{\"is_completed\":\"true\"}", "application/json", false); @@ -92,15 +91,15 @@ public class ChefAsyncClientTest extends RestClientTest { EncryptionService encryptionService = injector.getInstance(EncryptionService.class); Method method = ChefAsyncClient.class.getMethod("getUploadSandboxForChecksums", Set.class); GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableSet.of(Bytes - .asList(encryptionService.fromHex("0189e76ccc476701d6b374e5a1a27347")), Bytes.asList(encryptionService - .fromHex("0c5ecd7788cf4f6c7de2a57193897a6c")), Bytes.asList(encryptionService - .fromHex("1dda05ed139664f1f89b9dec482b77c0")))); + .asList(encryptionService.fromHex("0189e76ccc476701d6b374e5a1a27347")), Bytes.asList(encryptionService + .fromHex("0c5ecd7788cf4f6c7de2a57193897a6c")), Bytes.asList(encryptionService + .fromHex("1dda05ed139664f1f89b9dec482b77c0")))); assertRequestLineEquals(httpRequest, "POST http://localhost:4000/sandboxes HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals( - httpRequest, - "{\"checksums\":{\"0189e76ccc476701d6b374e5a1a27347\":null,\"0c5ecd7788cf4f6c7de2a57193897a6c\":null,\"1dda05ed139664f1f89b9dec482b77c0\":null}}", - "application/json", false); + httpRequest, + "{\"checksums\":{\"0189e76ccc476701d6b374e5a1a27347\":null,\"0c5ecd7788cf4f6c7de2a57193897a6c\":null,\"1dda05ed139664f1f89b9dec482b77c0\":null}}", + "application/json", false); assertResponseParserClassEquals(method, httpRequest, ParseJson.class); assertSaxResponseParserClassEquals(method, null); @@ -142,16 +141,16 @@ public class ChefAsyncClientTest extends RestClientTest { public void testUpdateCookbook() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncClient.class.getMethod("updateCookbook", String.class, String.class, - CookbookVersion.class); + CookbookVersion.class); GeneratedHttpRequest httpRequest = processor.createRequest(method, "cookbook", "1.0.1", - new CookbookVersion("cookbook", "1.0.1")); + new CookbookVersion("cookbook", "1.0.1")); assertRequestLineEquals(httpRequest, "PUT http://localhost:4000/cookbooks/cookbook/1.0.1 HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals( - httpRequest, - "{\"name\":\"cookbook-1.0.1\",\"definitions\":[],\"attributes\":[],\"files\":[],\"metadata\":{\"suggestions\":{},\"dependencies\":{},\"conflicting\":{},\"providing\":{},\"platforms\":{},\"recipes\":{},\"replacing\":{},\"groupings\":{},\"attributes\":{},\"recommendations\":{}},\"providers\":[],\"cookbook_name\":\"cookbook\",\"resources\":[],\"templates\":[],\"libraries\":[],\"version\":\"1.0.1\",\"recipes\":[],\"root_files\":[],\"json_class\":\"Chef::CookbookVersion\",\"chef_type\":\"cookbook_version\"}", - "application/json", false); + httpRequest, + "{\"name\":\"cookbook-1.0.1\",\"definitions\":[],\"attributes\":[],\"files\":[],\"metadata\":{\"suggestions\":{},\"dependencies\":{},\"conflicting\":{},\"providing\":{},\"platforms\":{},\"recipes\":{},\"replacing\":{},\"groupings\":{},\"attributes\":{},\"recommendations\":{}},\"providers\":[],\"cookbook_name\":\"cookbook\",\"resources\":[],\"templates\":[],\"libraries\":[],\"version\":\"1.0.1\",\"recipes\":[],\"root_files\":[],\"json_class\":\"Chef::CookbookVersion\",\"chef_type\":\"cookbook_version\"}", + "application/json", false); assertResponseParserClassEquals(method, httpRequest, ParseJson.class); assertSaxResponseParserClassEquals(method, null); assertExceptionParserClassEquals(method, null); @@ -214,7 +213,7 @@ public class ChefAsyncClientTest extends RestClientTest { assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals(httpRequest, "{\"clientname\":\"client\"}", "application/json", false); - assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class); + assertResponseParserClassEquals(method, httpRequest, ParseJson.class); assertSaxResponseParserClassEquals(method, null); assertExceptionParserClassEquals(method, null); @@ -245,7 +244,7 @@ public class ChefAsyncClientTest extends RestClientTest { assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals(httpRequest, "{\"clientname\":\"client\", \"private_key\": true}", "application/json", false); - assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class); + assertResponseParserClassEquals(method, httpRequest, ParseJson.class); assertSaxResponseParserClassEquals(method, null); assertExceptionParserClassEquals(method, null); @@ -286,14 +285,14 @@ public class ChefAsyncClientTest extends RestClientTest { public void testCreateNode() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncClient.class.getMethod("createNode", Node.class); GeneratedHttpRequest httpRequest = processor.createRequest(method, new Node("testnode", - ImmutableSet.of("recipe[java]"))); + ImmutableSet.of("recipe[java]"))); assertRequestLineEquals(httpRequest, "POST http://localhost:4000/nodes HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals( - httpRequest, - "{\"name\":\"testnode\",\"normal\":{},\"override\":{},\"default\":{},\"automatic\":{},\"run_list\":[\"recipe[java]\"],\"json_class\":\"Chef::Node\",\"chef_type\":\"node\"}", - "application/json", false); + httpRequest, + "{\"name\":\"testnode\",\"normal\":{},\"override\":{},\"default\":{},\"automatic\":{},\"run_list\":[\"recipe[java]\"],\"json_class\":\"Chef::Node\",\"chef_type\":\"node\"}", + "application/json", false); assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class); assertSaxResponseParserClassEquals(method, null); @@ -306,14 +305,14 @@ public class ChefAsyncClientTest extends RestClientTest { public void testUpdateNode() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncClient.class.getMethod("updateNode", Node.class); GeneratedHttpRequest httpRequest = processor.createRequest(method, new Node("testnode", - ImmutableSet.of("recipe[java]"))); + ImmutableSet.of("recipe[java]"))); assertRequestLineEquals(httpRequest, "PUT http://localhost:4000/nodes/testnode HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals( - httpRequest, - "{\"name\":\"testnode\",\"normal\":{},\"override\":{},\"default\":{},\"automatic\":{},\"run_list\":[\"recipe[java]\"],\"json_class\":\"Chef::Node\",\"chef_type\":\"node\"}", - "application/json", false); + httpRequest, + "{\"name\":\"testnode\",\"normal\":{},\"override\":{},\"default\":{},\"automatic\":{},\"run_list\":[\"recipe[java]\"],\"json_class\":\"Chef::Node\",\"chef_type\":\"node\"}", + "application/json", false); assertResponseParserClassEquals(method, httpRequest, ParseJson.class); assertSaxResponseParserClassEquals(method, null); @@ -372,14 +371,14 @@ public class ChefAsyncClientTest extends RestClientTest { public void testCreateRole() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncClient.class.getMethod("createRole", Role.class); GeneratedHttpRequest httpRequest = processor.createRequest(method, new Role("testrole", - ImmutableSet.of("recipe[java]"))); + ImmutableSet.of("recipe[java]"))); assertRequestLineEquals(httpRequest, "POST http://localhost:4000/roles HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals( - httpRequest, - "{\"name\":\"testrole\",\"override_attributes\":{},\"default_attributes\":{},\"run_list\":[\"recipe[java]\"],\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"}", - "application/json", false); + httpRequest, + "{\"name\":\"testrole\",\"override_attributes\":{},\"default_attributes\":{},\"run_list\":[\"recipe[java]\"],\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"}", + "application/json", false); assertResponseParserClassEquals(method, httpRequest, ParseJson.class); assertSaxResponseParserClassEquals(method, null); @@ -392,14 +391,14 @@ public class ChefAsyncClientTest extends RestClientTest { public void testUpdateRole() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncClient.class.getMethod("updateRole", Role.class); GeneratedHttpRequest httpRequest = processor.createRequest(method, new Role("testrole", - ImmutableSet.of("recipe[java]"))); + ImmutableSet.of("recipe[java]"))); assertRequestLineEquals(httpRequest, "PUT http://localhost:4000/roles/testrole HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n"); assertPayloadEquals( - httpRequest, - "{\"name\":\"testrole\",\"override_attributes\":{},\"default_attributes\":{},\"run_list\":[\"recipe[java]\"],\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"}", - "application/json", false); + httpRequest, + "{\"name\":\"testrole\",\"override_attributes\":{},\"default_attributes\":{},\"run_list\":[\"recipe[java]\"],\"json_class\":\"Chef::Role\",\"chef_type\":\"role\"}", + "application/json", false); assertResponseParserClassEquals(method, httpRequest, ParseJson.class); assertSaxResponseParserClassEquals(method, null); @@ -455,6 +454,6 @@ public class ChefAsyncClientTest extends RestClientTest { @Override public ContextSpec createContextSpec() { return new RestContextFactory().createContextSpec("chef", "user", SignedHeaderAuthTest.PRIVATE_KEY, - new Properties()); + new Properties()); } } diff --git a/chef/core/src/test/java/org/jclouds/chef/ChefClientLiveTest.java b/chef/core/src/test/java/org/jclouds/chef/ChefClientLiveTest.java index 34cbf5a8b3..13f19e48c0 100644 --- a/chef/core/src/test/java/org/jclouds/chef/ChefClientLiveTest.java +++ b/chef/core/src/test/java/org/jclouds/chef/ChefClientLiveTest.java @@ -98,7 +98,7 @@ public class ChefClientLiveTest { Properties props = new Properties(); props.setProperty("chef.endpoint", endpoint); return (ChefContext) new RestContextFactory(). createContext("chef", identity, key, - ImmutableSet. of(new Log4JLoggingModule()), props); + ImmutableSet. of(new Log4JLoggingModule()), props); } public void testCreateNewCookbook() throws Exception { @@ -142,7 +142,9 @@ public class ChefClientLiveTest { @Test(dependsOnMethods = "testCreateClient") public void testGenerateKeyForClient() throws Exception { - clientKey = validatorConnection.getApi().generateKeyForClient(PREFIX); + clientKey = validatorConnection.utils().encryption().toPem( + validatorConnection.getApi().generateKeyForClient(PREFIX).getPrivateKey()); + assertNotNull(clientKey); clientConnection.close(); clientConnection = createConnection(PREFIX, clientKey); @@ -156,9 +158,9 @@ public class ChefClientLiveTest { System.err.printf("%s/%s:%n", cookbook, version); CookbookVersion cookbookO = adminConnection.getApi().getCookbook(cookbook, version); for (Resource resource : ImmutableList. builder().addAll(cookbookO.getDefinitions()).addAll( - cookbookO.getFiles()).addAll(cookbookO.getLibraries()).addAll(cookbookO.getProviders()).addAll( - cookbookO.getRecipes()).addAll(cookbookO.getResources()).addAll(cookbookO.getRootFiles()).addAll( - cookbookO.getTemplates()).build()) { + cookbookO.getFiles()).addAll(cookbookO.getLibraries()).addAll(cookbookO.getProviders()).addAll( + cookbookO.getRecipes()).addAll(cookbookO.getResources()).addAll(cookbookO.getRootFiles()).addAll( + cookbookO.getTemplates()).build()) { try { InputStream stream = adminConnection.utils().http().get(resource.getUrl()); byte[] md5 = adminConnection.utils().encryption().md5(stream); @@ -189,28 +191,30 @@ public class ChefClientLiveTest { CookbookVersion cook = adminConnection.getApi().getCookbook(cookbook, version); adminConnection.getApi().deleteCookbook(cookbook, version); assert adminConnection.getApi().getCookbook(cookbook, version) == null : cookbook + version; - adminConnection.getApi().updateCookbook(cookbook, version, cook); } } @Test public void testListClients() throws Exception { - Set clients = validatorConnection.getApi().listClients(); - assertNotNull(clients); - assert clients.contains(validator) : "validator: " + validator + " not in: " + clients; + for (String client : validatorConnection.getApi().listClients()) + assertNotNull(validatorConnection.getApi().getClient(client)); } @Test(dependsOnMethods = "testListClients") public void testCreateClient() throws Exception { validatorConnection.getApi().deleteClient(PREFIX); - clientKey = validatorConnection.getApi().createClient(PREFIX); - assertNotNull(clientKey); + + clientKey = validatorConnection.utils().encryption().toPem( + validatorConnection.getApi().createClient(PREFIX).getPrivateKey()); + System.out.println(clientKey); + assertNotNull(clientKey); clientConnection = createConnection(PREFIX, clientKey); clientConnection.getApi().clientExists(PREFIX); - Set clients = adminConnection.getApi().listClients(); + Set clients = validatorConnection.getApi().listClients(); assert clients.contains(PREFIX) : String.format("client %s not in %s", PREFIX, clients); + assertNotNull(validatorConnection.getApi().getClient(PREFIX)); } @Test(dependsOnMethods = "testCreateClient") diff --git a/chef/core/src/test/java/org/jclouds/chef/functions/ParseClientFromJsonTest.java b/chef/core/src/test/java/org/jclouds/chef/functions/ParseClientFromJsonTest.java index 426318695a..e8a5492e9b 100644 --- a/chef/core/src/test/java/org/jclouds/chef/functions/ParseClientFromJsonTest.java +++ b/chef/core/src/test/java/org/jclouds/chef/functions/ParseClientFromJsonTest.java @@ -1,14 +1,18 @@ package org.jclouds.chef.functions; -import static org.jclouds.io.Payloads.newStringPayload; +import static org.jclouds.io.Payloads.newInputStreamPayload; import static org.testng.Assert.assertEquals; import java.io.IOException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; import org.jclouds.chef.config.ChefParserModule; import org.jclouds.chef.domain.Client; +import org.jclouds.encryption.EncryptionService; import org.jclouds.http.HttpResponse; import org.jclouds.http.functions.ParseJson; +import org.jclouds.io.Payloads; import org.jclouds.json.config.GsonModule; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -26,22 +30,35 @@ import com.google.inject.TypeLiteral; @Test(groups = "unit", sequential = true, testName = "chef.ParseClientFromJsonTest") public class ParseClientFromJsonTest { + private static final String PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAyb2ZJJqGm0KKR+8nfQJNsSd+F9tXNMV7CfOcW6jsqs8EZgiV\nR09hD1IYOj4YqM0qJONlgyg4xRWewdSG7QTPj1lJpVAida9sXy2+kzyagZA1Am0O\nZcbqb5hoeIDgcX+eDa79s0u0DomjcfO9EKhvHLBz+zM+3QqPRkPV8nYTbfs+HjVz\nzOU6D1B0XR3+IPZZl2AnWs2d0qhnStHcDUvnRVQ0P482YwN9VgceOZtpPz0DCKEJ\n5Tx5STub8k0/zt/VAMHQafLSuQMLd2s4ZLuOZptN//uAsTmxireqd37z+8ZTdBbJ\n8LEpJ+iCXuSfm5aUh7iw6oxvToY2AL53+jK2UQIDAQABAoIBAQDA88B3i/xWn0vX\nBVxFamCYoecuNjGwXXkSyZew616A+EOCu47bh4aTurdFbYL0YFaAtaWvzlaN2eHg\nDb+HDuTefE29+WkcGk6SshPmiz5T0XOCAICWw6wSVDkHmGwS4jZvbAFm7W8nwGk9\nYhxgxFiRngswJZFopOLoF5WXs2td8guIYNslMpo7tu50iFnBHwKO2ZsPAk8t9nnS\nxlDavKruymEmqHCr3+dtio5eaenJcp3fjoXBQOKUk3ipII29XRB8NqeCVV/7Kxwq\nckqOBEbRwBclckyIbD+RiAgKvOelORjEiE9R42vuqvxRA6k9kd9o7utlX0AUtpEn\n3gZc6LepAoGBAP9ael5Y75+sK2JJUNOOhO8ae45cdsilp2yI0X+UBaSuQs2+dyPp\nkpEHAxd4pmmSvn/8c9TlEZhr+qYbABXVPlDncxpIuw2Ajbk7s/S4XaSKsRqpXL57\nzj/QOqLkRk8+OVV9q6lMeQNqLtEj1u6JPviX70Ro+FQtRttNOYbfdP/fAoGBAMpA\nXjR5woV5sUb+REg9vEuYo8RSyOarxqKFCIXVUNsLOx+22+AK4+CQpbueWN7jotrl\nYD6uT6svWi3AAC7kiY0UI/fjVPRCUi8tVoQUE0TaU5VLITaYOB+W/bBaDE4M9560\n1NuDWO90baA5dfU44iuzva02rGJXK9+nS3o8nk/PAoGBALOL6djnDe4mwAaG6Jco\ncd4xr8jkyPzCRZuyBCSBbwphIUXLc7hDprPky064ncJD1UDmwIdkXd/fpMkg2QmA\n/CUk6LEFjMisqHojOaCL9gQZJPhLN5QUN2x1PJWGjs1vQh8Tkx0iUUCOa8bQPXNR\n+34OTsW6TUna4CSZAycLfhffAoGBAIggVsefBCvuQkF0NeUhmDCRZfhnd8y55RHR\n1HCvqKIlpv+rhcX/zmyBLuteopYyRJRsOiE2FW00i8+rIPRu4Z3Q5nybx7w3PzV9\noHN5R5baE9OyI4KpZWztpYYitZF67NcnAvVULHHOvVJQGnKYfLHJYmrJF7GA1ojM\nAuMdFbjFAoGAPxUhxwFy8gaqBahKUEZn4F81HFP5ihGhkT4QL6AFPO2e+JhIGjuR\n27+85hcFqQ+HHVtFsm81b/a+R7P4UuCRgc8eCjxQMoJ1Xl4n7VbjPbHMnIN0Ryvd\nO4ZpWDWYnCO021JTOUUOJ4J/y0416Bvkw0z59y7sNX7wDBBHHbK/XCc=\n-----END RSA PRIVATE KEY-----\n"; + private static final String CERTIFICATE = "-----BEGIN CERTIFICATE-----\nMIIClzCCAgCgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxFjAUBgNVBAoM\nDU9wc2NvZGUsIEluYy4xHDAaBgNVBAsME0NlcnRpZmljYXRlIFNlcnZpY2UxMjAw\nBgNVBAMMKW9wc2NvZGUuY29tL2VtYWlsQWRkcmVzcz1hdXRoQG9wc2NvZGUuY29t\nMB4XDTEwMDczMDIwNDEzMFoXDTIwMDcyNzIwNDEzMFowADCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMm9mSSahptCikfvJ30CTbEnfhfbVzTFewnznFuo\n7KrPBGYIlUdPYQ9SGDo+GKjNKiTjZYMoOMUVnsHUhu0Ez49ZSaVQInWvbF8tvpM8\nmoGQNQJtDmXG6m+YaHiA4HF/ng2u/bNLtA6Jo3HzvRCobxywc/szPt0Kj0ZD1fJ2\nE237Ph41c8zlOg9QdF0d/iD2WZdgJ1rNndKoZ0rR3A1L50VUND+PNmMDfVYHHjmb\naT89AwihCeU8eUk7m/JNP87f1QDB0Gny0rkDC3drOGS7jmabTf/7gLE5sYq3qnd+\n8/vGU3QWyfCxKSfogl7kn5uWlIe4sOqMb06GNgC+d/oytlECAwEAATANBgkqhkiG\n9w0BAQUFAAOBgQBftzSZxstWw60GqRTDNN/F2GnrdtnKBoXzHww3r6jtGEylYq20\n5KfKpEx+sPX0gyZuYJiXC2CkEjImAluWKcdN9ZF6VD541sheAjbiaU7q7ZsztTxF\nWUH2tCvHeDXYKPKek3QzL7bYpUhLnCN/XxEv6ibeMDwtI7f5qpk2Aspzcw==\n-----END CERTIFICATE-----\n"; private ParseJson handler; + private EncryptionService encryptionService; + private PrivateKey privateKey; + private X509Certificate certificate; @BeforeTest protected void setUpInjector() throws IOException { Injector injector = Guice.createInjector(new ChefParserModule(), new GsonModule()); handler = injector.getInstance(Key.get(new TypeLiteral>() { })); + encryptionService = injector.getInstance(EncryptionService.class); + certificate = encryptionService.x509CertificateFromPEM(CERTIFICATE.getBytes("UTF-8")); + privateKey = encryptionService.privateKeyFromPEM(PRIVATE_KEY.getBytes("UTF-8")); } public void test() { - Client user = new Client("-----BEGIN CERTIFICATE-----dXQ==-----END CERTIFICATE-----", "jclouds", - "adrian-jcloudstest", "adrian-jcloudstest", false); + Client user = new Client(certificate, "jclouds", "adriancole-jcloudstest", "adriancole-jcloudstest", false, + privateKey); - String toParse = "{ \"certificate\":\"-----BEGIN CERTIFICATE-----dXQ==-----END CERTIFICATE-----\", \"orgname\":\"jclouds\", \"clientname\":\"adrian-jcloudstest\", \"name\": \"adrian-jcloudstest\",\"validator\": false }"; - System.out.println(toParse); - assertEquals(handler.apply(new HttpResponse(200, "ok", newStringPayload(toParse))), user); + byte[] encrypted = encryptionService.rsaEncrypt(Payloads.newPayload("fooya"), user.getCertificate() + .getPublicKey()); + + assertEquals(encryptionService.rsaDecrypt(Payloads.newPayload(encrypted), user.getPrivateKey()), "fooya" + .getBytes()); + + assertEquals(handler.apply(new HttpResponse(200, "ok", newInputStreamPayload(ParseClientFromJsonTest.class + .getResourceAsStream("/client.json")))), user); } } diff --git a/chef/core/src/test/java/org/jclouds/chef/functions/ParseKeyFromJsonTest.java b/chef/core/src/test/java/org/jclouds/chef/functions/ParseKeyFromJsonTest.java deleted file mode 100644 index fd688edb0a..0000000000 --- a/chef/core/src/test/java/org/jclouds/chef/functions/ParseKeyFromJsonTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.jclouds.chef.functions; - -import static org.testng.Assert.assertEquals; - -import java.io.IOException; - -import org.jclouds.chef.config.ChefParserModule; -import org.jclouds.http.HttpResponse; -import org.jclouds.io.Payloads; -import org.jclouds.json.config.GsonModule; -import org.jclouds.util.Utils; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; - -import com.google.inject.Guice; -import com.google.inject.Injector; - -/** - * Tests behavior of {@code ParseKeyFromJson} - * - * @author Adrian Cole - */ -@Test(groups = "unit", sequential = true, testName = "chef.ParseKeyFromJsonTest") -public class ParseKeyFromJsonTest { - - private ParseKeyFromJson handler; - - @BeforeTest - protected void setUpInjector() throws IOException { - Injector injector = Guice.createInjector(new ChefParserModule(), new GsonModule()); - handler = injector.getInstance(ParseKeyFromJson.class); - } - - public void testRegex() { - assertEquals( - handler - .apply(new HttpResponse( - 200, - "ok", - Payloads - .newPayload(Utils - .toInputStream("{\n\"uri\": \"https://api.opscode.com/users/bobo\", \"private_key\": \"RSA_PRIVATE_KEY\",}")))), - "RSA_PRIVATE_KEY"); - } - - public void test2() { - String key = handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(ParseKeyFromJsonTest.class - .getResourceAsStream("/newclient.txt")))); - assert key.startsWith("-----BEGIN RSA PRIVATE KEY-----\n"); - } -} diff --git a/chef/core/src/test/resources/client.json b/chef/core/src/test/resources/client.json new file mode 100644 index 0000000000..15ff8d06a7 --- /dev/null +++ b/chef/core/src/test/resources/client.json @@ -0,0 +1 @@ +{"orgname":"jclouds","certificate":"-----BEGIN CERTIFICATE-----\nMIIClzCCAgCgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxFjAUBgNVBAoM\nDU9wc2NvZGUsIEluYy4xHDAaBgNVBAsME0NlcnRpZmljYXRlIFNlcnZpY2UxMjAw\nBgNVBAMMKW9wc2NvZGUuY29tL2VtYWlsQWRkcmVzcz1hdXRoQG9wc2NvZGUuY29t\nMB4XDTEwMDczMDIwNDEzMFoXDTIwMDcyNzIwNDEzMFowADCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMm9mSSahptCikfvJ30CTbEnfhfbVzTFewnznFuo\n7KrPBGYIlUdPYQ9SGDo+GKjNKiTjZYMoOMUVnsHUhu0Ez49ZSaVQInWvbF8tvpM8\nmoGQNQJtDmXG6m+YaHiA4HF/ng2u/bNLtA6Jo3HzvRCobxywc/szPt0Kj0ZD1fJ2\nE237Ph41c8zlOg9QdF0d/iD2WZdgJ1rNndKoZ0rR3A1L50VUND+PNmMDfVYHHjmb\naT89AwihCeU8eUk7m/JNP87f1QDB0Gny0rkDC3drOGS7jmabTf/7gLE5sYq3qnd+\n8/vGU3QWyfCxKSfogl7kn5uWlIe4sOqMb06GNgC+d/oytlECAwEAATANBgkqhkiG\n9w0BAQUFAAOBgQBftzSZxstWw60GqRTDNN/F2GnrdtnKBoXzHww3r6jtGEylYq20\n5KfKpEx+sPX0gyZuYJiXC2CkEjImAluWKcdN9ZF6VD541sheAjbiaU7q7ZsztTxF\nWUH2tCvHeDXYKPKek3QzL7bYpUhLnCN/XxEv6ibeMDwtI7f5qpk2Aspzcw==\n-----END CERTIFICATE-----\n","uri":"https://api.opscode.com/organizations/jclouds/clients/adriancole-jcloudstest","private_key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAyb2ZJJqGm0KKR+8nfQJNsSd+F9tXNMV7CfOcW6jsqs8EZgiV\nR09hD1IYOj4YqM0qJONlgyg4xRWewdSG7QTPj1lJpVAida9sXy2+kzyagZA1Am0O\nZcbqb5hoeIDgcX+eDa79s0u0DomjcfO9EKhvHLBz+zM+3QqPRkPV8nYTbfs+HjVz\nzOU6D1B0XR3+IPZZl2AnWs2d0qhnStHcDUvnRVQ0P482YwN9VgceOZtpPz0DCKEJ\n5Tx5STub8k0/zt/VAMHQafLSuQMLd2s4ZLuOZptN//uAsTmxireqd37z+8ZTdBbJ\n8LEpJ+iCXuSfm5aUh7iw6oxvToY2AL53+jK2UQIDAQABAoIBAQDA88B3i/xWn0vX\nBVxFamCYoecuNjGwXXkSyZew616A+EOCu47bh4aTurdFbYL0YFaAtaWvzlaN2eHg\nDb+HDuTefE29+WkcGk6SshPmiz5T0XOCAICWw6wSVDkHmGwS4jZvbAFm7W8nwGk9\nYhxgxFiRngswJZFopOLoF5WXs2td8guIYNslMpo7tu50iFnBHwKO2ZsPAk8t9nnS\nxlDavKruymEmqHCr3+dtio5eaenJcp3fjoXBQOKUk3ipII29XRB8NqeCVV/7Kxwq\nckqOBEbRwBclckyIbD+RiAgKvOelORjEiE9R42vuqvxRA6k9kd9o7utlX0AUtpEn\n3gZc6LepAoGBAP9ael5Y75+sK2JJUNOOhO8ae45cdsilp2yI0X+UBaSuQs2+dyPp\nkpEHAxd4pmmSvn/8c9TlEZhr+qYbABXVPlDncxpIuw2Ajbk7s/S4XaSKsRqpXL57\nzj/QOqLkRk8+OVV9q6lMeQNqLtEj1u6JPviX70Ro+FQtRttNOYbfdP/fAoGBAMpA\nXjR5woV5sUb+REg9vEuYo8RSyOarxqKFCIXVUNsLOx+22+AK4+CQpbueWN7jotrl\nYD6uT6svWi3AAC7kiY0UI/fjVPRCUi8tVoQUE0TaU5VLITaYOB+W/bBaDE4M9560\n1NuDWO90baA5dfU44iuzva02rGJXK9+nS3o8nk/PAoGBALOL6djnDe4mwAaG6Jco\ncd4xr8jkyPzCRZuyBCSBbwphIUXLc7hDprPky064ncJD1UDmwIdkXd/fpMkg2QmA\n/CUk6LEFjMisqHojOaCL9gQZJPhLN5QUN2x1PJWGjs1vQh8Tkx0iUUCOa8bQPXNR\n+34OTsW6TUna4CSZAycLfhffAoGBAIggVsefBCvuQkF0NeUhmDCRZfhnd8y55RHR\n1HCvqKIlpv+rhcX/zmyBLuteopYyRJRsOiE2FW00i8+rIPRu4Z3Q5nybx7w3PzV9\noHN5R5baE9OyI4KpZWztpYYitZF67NcnAvVULHHOvVJQGnKYfLHJYmrJF7GA1ojM\nAuMdFbjFAoGAPxUhxwFy8gaqBahKUEZn4F81HFP5ihGhkT4QL6AFPO2e+JhIGjuR\n27+85hcFqQ+HHVtFsm81b/a+R7P4UuCRgc8eCjxQMoJ1Xl4n7VbjPbHMnIN0Ryvd\nO4ZpWDWYnCO021JTOUUOJ4J/y0416Bvkw0z59y7sNX7wDBBHHbK/XCc=\n-----END RSA PRIVATE KEY-----\n","clientname":"adriancole-jcloudstest","name":"adriancole-jcloudstest","validator":false} \ No newline at end of file diff --git a/chef/servlet/src/main/java/org/jclouds/chef/servlet/ChefRegistrationListener.java b/chef/servlet/src/main/java/org/jclouds/chef/servlet/ChefRegistrationListener.java index 9d1877c354..ec1dcc58e7 100644 --- a/chef/servlet/src/main/java/org/jclouds/chef/servlet/ChefRegistrationListener.java +++ b/chef/servlet/src/main/java/org/jclouds/chef/servlet/ChefRegistrationListener.java @@ -31,6 +31,7 @@ import java.util.Properties; import java.util.Set; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; @@ -39,14 +40,13 @@ import org.jclouds.chef.ChefAsyncClient; import org.jclouds.chef.ChefClient; import org.jclouds.chef.ChefContext; import org.jclouds.chef.ChefService; +import org.jclouds.chef.domain.Client; import org.jclouds.chef.reference.ChefConstants; import org.jclouds.chef.servlet.functions.InitParamsToProperties; import org.jclouds.logging.Logger; import org.jclouds.logging.jdk.JDKLogger; import org.jclouds.rest.RestContextFactory; -import java.util.concurrent.Future; - /** * Registers a new node in Chef and binds its name to {@link ChefConstants.CHEF_NODE}, its role to * {@link ChefConstants.CHEF_ROLE} and the {@link ChefService} for the client to @@ -121,7 +121,7 @@ public class ChefRegistrationListener implements ServletContextListener { private ChefService createClientAndNode(ChefService validatorClient, String role, String id, Properties overrides) { logger.trace("attempting to create client %s", id); - String clientKey = validatorClient.getContext().getApi().createClient(id); + Client client = validatorClient.getContext().getApi().createClient(id); logger.debug("created client %s", id); ChefService clientService = null; try { @@ -129,7 +129,8 @@ public class ChefRegistrationListener implements ServletContextListener { clientProperties.putAll(overrides); removeCredentials(clientProperties); clientProperties.setProperty("chef.identity", id); - clientProperties.setProperty("chef.credential", clientKey); + clientProperties.setProperty("chef.credential", validatorClient.getContext().utils().encryption().toPem( + client.getPrivateKey())); clientService = createService(clientProperties); clientService.createNodeAndPopulateAutomaticAttributes(id, singleton("role[" + role + "]")); return clientService; diff --git a/core/src/main/java/org/jclouds/encryption/EncryptionService.java b/core/src/main/java/org/jclouds/encryption/EncryptionService.java index e444c8e171..8dbb2d9742 100644 --- a/core/src/main/java/org/jclouds/encryption/EncryptionService.java +++ b/core/src/main/java/org/jclouds/encryption/EncryptionService.java @@ -24,6 +24,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.Key; import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; import org.jclouds.encryption.internal.JCEEncryptionService; import org.jclouds.http.PayloadEnclosing; @@ -46,7 +48,7 @@ public interface EncryptionService { byte[] fromHex(String encoded); - byte[] rsaSign(String toSign, Key privateKey); + byte[] rsaEncrypt(Payload payload, Key key); byte[] hmacSha256(String toEncode, byte[] key); @@ -64,7 +66,8 @@ public interface EncryptionService { *

Note

*

* If this is an InputStream, it will be converted to a byte array first. - * @throws IOException + * + * @throws IOException */ T generateMD5BufferingIfNotRepeatable(T payloadEnclosing); @@ -74,8 +77,8 @@ public interface EncryptionService { MD5OutputStream md5OutputStream(OutputStream out); - PrivateKey readPrivateKeyFromPEM(byte [] pem); - + PrivateKey privateKeyFromPEM(byte[] pem); + public static abstract class MD5OutputStream extends FilterOutputStream { public MD5OutputStream(OutputStream out) { super(out); @@ -84,4 +87,16 @@ public interface EncryptionService { public abstract byte[] getMD5(); } + PublicKey publicKeyFromPEM(byte[] pem); + + X509Certificate x509CertificateFromPEM(byte[] pem); + + byte[] rsaDecrypt(Payload payload, Key key); + + String toPem(X509Certificate cert); + + String toPem(PublicKey key); + + String toPem(PrivateKey key); + } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/encryption/internal/BaseEncryptionService.java b/core/src/main/java/org/jclouds/encryption/internal/BaseEncryptionService.java index bed6442bcd..9493f67363 100755 --- a/core/src/main/java/org/jclouds/encryption/internal/BaseEncryptionService.java +++ b/core/src/main/java/org/jclouds/encryption/internal/BaseEncryptionService.java @@ -20,15 +20,33 @@ package org.jclouds.encryption.internal; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Throwables.propagate; +import static com.google.common.io.Closeables.closeQuietly; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.Key; import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; import javax.annotation.Resource; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; import net.oauth.signature.pem.PEMReader; import net.oauth.signature.pem.PKCS1EncodedKeySpec; @@ -52,12 +70,14 @@ public abstract class BaseEncryptionService implements EncryptionService { protected static final int BUF_SIZE = 0x2000; // 8 final byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', - (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' }; + (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' }; private final KeyFactory rsaKeyFactory; + private final CertificateFactory certFactory; - public BaseEncryptionService(KeyFactory rsaKeyFactory) { + public BaseEncryptionService(KeyFactory rsaKeyFactory, CertificateFactory certFactory) { this.rsaKeyFactory = rsaKeyFactory; + this.certFactory = certFactory; } @Override @@ -118,7 +138,7 @@ public abstract class BaseEncryptionService implements EncryptionService { * {@inheritDoc} */ @Override - public PrivateKey readPrivateKeyFromPEM(byte[] pem) { + public PrivateKey privateKeyFromPEM(byte[] pem) { PEMReader reader; try { reader = new PEMReader(pem); @@ -131,7 +151,7 @@ public abstract class BaseEncryptionService implements EncryptionService { } else if (PEMReader.PRIVATE_PKCS8_MARKER.equals(reader.getBeginMarker())) { keySpec = new PKCS8EncodedKeySpec(bytes); } else { - throw new IOException("Invalid PEM file: Unknown marker " + "for private key " + reader.getBeginMarker()); + throw new IOException("Invalid PEM file: Unknown marker for private key " + reader.getBeginMarker()); } return rsaKeyFactory.generatePrivate(keySpec); } catch (Exception e) { @@ -139,4 +159,141 @@ public abstract class BaseEncryptionService implements EncryptionService { return null; } } + + /** + * {@inheritDoc} + */ + @Override + public PublicKey publicKeyFromPEM(byte[] pem) { + PEMReader reader; + try { + reader = new PEMReader(pem); + + byte[] bytes = reader.getDerBytes(); + KeySpec keySpec; + + if (PEMReader.PUBLIC_X509_MARKER.equals(reader.getBeginMarker())) { + keySpec = new X509EncodedKeySpec(bytes); + } else { + throw new IOException("Invalid PEM file: Unknown marker for public key " + reader.getBeginMarker()); + } + return rsaKeyFactory.generatePublic(keySpec); + } catch (Exception e) { + Throwables.propagate(e); + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public X509Certificate x509CertificateFromPEM(byte[] pem) { + PEMReader reader; + try { + reader = new PEMReader(pem); + + byte[] bytes = reader.getDerBytes(); + + if (PEMReader.CERTIFICATE_X509_MARKER.equals(reader.getBeginMarker())) { + return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(bytes)); + } else { + throw new IOException("Invalid PEM file: Unknown marker for public key " + reader.getBeginMarker()); + } + + } catch (Exception e) { + Throwables.propagate(e); + return null; + } + } + + @Override + public byte[] rsaEncrypt(Payload payload, Key key) { + // TODO convert this to BC code + Cipher cipher = null; + try { + cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, key); + } catch (NoSuchAlgorithmException e) { + Throwables.propagate(e); + } catch (NoSuchPaddingException e) { + Throwables.propagate(e); + } catch (InvalidKeyException e) { + Throwables.propagate(e); + } + return cipherPayload(cipher, payload); + } + + @Override + public byte[] rsaDecrypt(Payload payload, Key key) { + // TODO convert this to BC code + Cipher cipher = null; + try { + cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, key); + } catch (NoSuchAlgorithmException e) { + Throwables.propagate(e); + } catch (NoSuchPaddingException e) { + Throwables.propagate(e); + } catch (InvalidKeyException e) { + Throwables.propagate(e); + } + return cipherPayload(cipher, payload); + } + + private byte[] cipherPayload(Cipher cipher, Payload payload) { + byte[] resBuf = new byte[cipher.getOutputSize(payload.getContentLength().intValue())]; + byte[] buffer = new byte[BUF_SIZE]; + long length = 0; + int numRead = -1; + InputStream plainBytes = payload.getInput(); + try { + do { + numRead = plainBytes.read(buffer); + if (numRead > 0) { + length += numRead; + cipher.update(buffer, 0, numRead); + } + } while (numRead != -1); + } catch (IOException e) { + propagate(e); + } finally { + closeQuietly(plainBytes); + } + try { + int size = cipher.doFinal(resBuf, 0); + return Arrays.copyOfRange(resBuf, 0, size); + } catch (IllegalBlockSizeException e) { + Throwables.propagate(e); + } catch (ShortBufferException e) { + Throwables.propagate(e); + } catch (BadPaddingException e) { + Throwables.propagate(e); + } + assert false; + return null; + } + + @Override + public String toPem(X509Certificate cert) { + try { + return new StringBuilder("-----BEGIN CERTIFICATE-----\n").append(base64(cert.getEncoded())).append( + "\n-----END CERTIFICATE-----\n").toString(); + } catch (CertificateEncodingException e) { + Throwables.propagate(e); + return null; + } + } + + @Override + public String toPem(PublicKey key) { + return new StringBuilder("-----BEGIN PUBLIC KEY-----\n").append(base64(key.getEncoded())).append( + "\n-----END PUBLIC KEY-----\n").toString(); + } + + @Override + public String toPem(PrivateKey key) { + return new StringBuilder("-----BEGIN PRIVATE KEY-----\n").append(base64(key.getEncoded())).append( + "\n-----END PRIVATE KEY-----\n").toString(); + } } diff --git a/core/src/main/java/org/jclouds/encryption/internal/JCEEncryptionService.java b/core/src/main/java/org/jclouds/encryption/internal/JCEEncryptionService.java index 7d4943222d..6862ed7b1e 100644 --- a/core/src/main/java/org/jclouds/encryption/internal/JCEEncryptionService.java +++ b/core/src/main/java/org/jclouds/encryption/internal/JCEEncryptionService.java @@ -27,12 +27,12 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.DigestOutputStream; import java.security.InvalidKeyException; -import java.security.Key; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; -import javax.crypto.Cipher; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; @@ -44,12 +44,12 @@ import org.jclouds.io.payloads.ByteArrayPayload; */ public class JCEEncryptionService extends BaseEncryptionService { - public JCEEncryptionService(KeyFactory rsaKeyFactory) { - super(rsaKeyFactory); + public JCEEncryptionService(KeyFactory rsaKeyFactory, CertificateFactory certFactory) { + super(rsaKeyFactory, certFactory); } - public JCEEncryptionService() throws NoSuchAlgorithmException { - super(KeyFactory.getInstance("RSA")); + public JCEEncryptionService() throws NoSuchAlgorithmException, CertificateException { + this(KeyFactory.getInstance("RSA"), CertificateFactory.getInstance("X.509")); } @Override @@ -199,17 +199,4 @@ public class JCEEncryptionService extends BaseEncryptionService { return digest.digest(); } - @Override - public byte[] rsaSign(String toSign, Key key) { - Cipher cipher; - try { - cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.ENCRYPT_MODE, key); - return cipher.doFinal(toSign.getBytes()); - } catch (Exception e) { - propagate(e); - return null; - } - } - } diff --git a/core/src/test/java/org/jclouds/http/BaseJettyTest.java b/core/src/test/java/org/jclouds/http/BaseJettyTest.java index 93f60801ed..2a57046f6a 100644 --- a/core/src/test/java/org/jclouds/http/BaseJettyTest.java +++ b/core/src/test/java/org/jclouds/http/BaseJettyTest.java @@ -24,7 +24,6 @@ import static org.jclouds.rest.RestContextFactory.createContextBuilder; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.security.NoSuchAlgorithmException; import java.util.Map; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; @@ -39,7 +38,6 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.encryption.EncryptionService; import org.jclouds.encryption.internal.Base64; -import org.jclouds.encryption.internal.JCEEncryptionService; import org.jclouds.rest.RestContext; import org.jclouds.rest.RestContextBuilder; import org.jclouds.rest.RestContextFactory.ContextSpec; @@ -91,7 +89,7 @@ public abstract class BaseJettyTest { Handler server1Handler = new AbstractHandler() { public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) - throws IOException, ServletException { + throws IOException, ServletException { if (failIfNoContentLength(request, response)) { return; } else if (target.indexOf("sleep") > 0) { @@ -124,13 +122,7 @@ public abstract class BaseJettyTest { if (request.getContentLength() > 0) { if (request.getHeader("Content-MD5") != null) { String expectedMd5 = request.getHeader("Content-MD5"); - String realMd5FromRequest; - try { - realMd5FromRequest = Base64.encodeBytes(new JCEEncryptionService() - .md5(request.getInputStream())); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } + String realMd5FromRequest = Base64.encodeBytes(encryptionService.md5(request.getInputStream())); boolean matched = expectedMd5.equals(realMd5FromRequest); if (matched) { response.setContentType("text/xml"); @@ -152,8 +144,7 @@ public abstract class BaseJettyTest { response.getWriter().println("test"); } else if (request.getMethod().equals("HEAD")) { /* - * NOTE: by HTML specification, HEAD response MUST NOT include a - * body + * NOTE: by HTML specification, HEAD response MUST NOT include a body */ response.setContentType("text/xml"); response.setStatus(HttpServletResponse.SC_OK); @@ -175,7 +166,7 @@ public abstract class BaseJettyTest { Handler server2Handler = new AbstractHandler() { public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) - throws IOException, ServletException { + throws IOException, ServletException { if (request.getMethod().equals("PUT")) { if (request.getContentLength() > 0) { response.setStatus(HttpServletResponse.SC_OK); @@ -186,12 +177,7 @@ public abstract class BaseJettyTest { if (request.getHeader("Content-MD5") != null) { String expectedMd5 = request.getHeader("Content-MD5"); String realMd5FromRequest; - try { - realMd5FromRequest = Base64.encodeBytes(new JCEEncryptionService() - .md5(request.getInputStream())); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } + realMd5FromRequest = Base64.encodeBytes(encryptionService.md5(request.getInputStream())); boolean matched = expectedMd5.equals(realMd5FromRequest); if (matched) { response.setContentType("text/xml"); @@ -207,8 +193,7 @@ public abstract class BaseJettyTest { } } else if (request.getMethod().equals("HEAD")) { /* - * NOTE: by HTML specification, HEAD response MUST NOT include a - * body + * NOTE: by HTML specification, HEAD response MUST NOT include a body */ response.setContentType("text/xml"); response.setStatus(HttpServletResponse.SC_OK); @@ -237,7 +222,7 @@ public abstract class BaseJettyTest { @SuppressWarnings("unchecked") public static InputSupplier getTestDataSupplier() throws IOException { byte[] oneConstitution = ByteStreams.toByteArray(new GZIPInputStream(BaseJettyTest.class - .getResourceAsStream("/const.txt.gz"))); + .getResourceAsStream("/const.txt.gz"))); InputSupplier constitutionSupplier = ByteStreams.newInputStreamSupplier(oneConstitution); InputSupplier temp = ByteStreams.join(constitutionSupplier); @@ -249,11 +234,11 @@ public abstract class BaseJettyTest { } public static RestContextBuilder newBuilder(int testPort, - Properties properties, Module... connectionModules) { + Properties properties, Module... connectionModules) { ContextSpec contextSpec = contextSpec("test", - "http://localhost:" + testPort, "1", "identity", null, IntegrationTestClient.class, - IntegrationTestAsyncClient.class, ImmutableSet. copyOf(connectionModules)); + "http://localhost:" + testPort, "1", "identity", null, IntegrationTestClient.class, + IntegrationTestAsyncClient.class, ImmutableSet. copyOf(connectionModules)); return createContextBuilder(contextSpec, properties); } @@ -287,7 +272,7 @@ public abstract class BaseJettyTest { } protected boolean redirectEveryTwentyRequests(HttpServletRequest request, HttpServletResponse response) - throws IOException { + throws IOException { if (cycle.incrementAndGet() % 20 == 0) { response.sendRedirect("http://localhost:" + (testPort + 1) + "/"); ((Request) request).setHandled(true); diff --git a/core/src/test/java/org/jclouds/http/filters/BasicAuthenticationTest.java b/core/src/test/java/org/jclouds/http/filters/BasicAuthenticationTest.java index 5c064fb17d..23a6fa8274 100755 --- a/core/src/test/java/org/jclouds/http/filters/BasicAuthenticationTest.java +++ b/core/src/test/java/org/jclouds/http/filters/BasicAuthenticationTest.java @@ -23,6 +23,7 @@ import static org.testng.Assert.assertEquals; import java.io.UnsupportedEncodingException; import java.net.URI; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import javax.ws.rs.core.HttpHeaders; @@ -40,7 +41,7 @@ public class BasicAuthenticationTest { private static final String USER = "Aladdin"; private static final String PASSWORD = "open sesame"; - public void testAuth() throws UnsupportedEncodingException, NoSuchAlgorithmException { + public void testAuth() throws UnsupportedEncodingException, NoSuchAlgorithmException, CertificateException { BasicAuthentication filter = new BasicAuthentication(USER, PASSWORD, new JCEEncryptionService()); HttpRequest request = new HttpRequest("GET", URI.create("http://localhost")); filter.filter(request); diff --git a/core/src/test/java/org/jclouds/http/internal/WireLiveTest.java b/core/src/test/java/org/jclouds/http/internal/WireLiveTest.java index 487dc5162e..d65b4ac475 100644 --- a/core/src/test/java/org/jclouds/http/internal/WireLiveTest.java +++ b/core/src/test/java/org/jclouds/http/internal/WireLiveTest.java @@ -28,6 +28,7 @@ import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -58,6 +59,9 @@ public class WireLiveTest { } catch (NoSuchAlgorithmException e) { Throwables.propagate(e); encryptionService = null; + } catch (CertificateException e) { + Throwables.propagate(e); + encryptionService = null; } } diff --git a/core/src/test/resources/const.txt b/core/src/test/resources/const.txt new file mode 100644 index 0000000000..9b5ffaa9e0 --- /dev/null +++ b/core/src/test/resources/const.txt @@ -0,0 +1,872 @@ +Provided by USConstitution.net +------------------------------ + +[Note: Repealed text is not noted in this version. Spelling errors have been +corrected in this version. For an uncorrected, annotated version of the +Constitution, visit http://www.usconstitution.net/const.html ] + +We the People of the United States, in Order to form a more perfect Union, +establish Justice, insure domestic Tranquility, provide for the common +defence, promote the general Welfare, and secure the Blessings of Liberty to +ourselves and our Posterity, do ordain and establish this Constitution for the +United States of America. + +Article 1. + +Section 1 +All legislative Powers herein granted shall be vested in a Congress of the +United States, which shall consist of a Senate and House of Representatives. + +Section 2 +The House of Representatives shall be composed of Members chosen every second +Year by the People of the several States, and the Electors in each State shall +have the Qualifications requisite for Electors of the most numerous Branch of +the State Legislature. + +No Person shall be a Representative who shall not have attained to the Age of +twenty five Years, and been seven Years a Citizen of the United States, and who +shall not, when elected, be an Inhabitant of that State in which he shall be +chosen. + +Representatives and direct Taxes shall be apportioned among the several States +which may be included within this Union, according to their respective Numbers, +which shall be determined by adding to the whole Number of free Persons, +including those bound to Service for a Term of Years, and excluding Indians not +taxed, three fifths of all other Persons. + +The actual Enumeration shall be made within three Years after the first Meeting +of the Congress of the United States, and within every subsequent Term of ten +Years, in such Manner as they shall by Law direct. The Number of +Representatives shall not exceed one for every thirty Thousand, but each State +shall have at Least one Representative; and until such enumeration shall be +made, the State of New Hampshire shall be entitled to choose three, +Massachusetts eight, Rhode Island and Providence Plantations one, Connecticut +five, New York six, New Jersey four, Pennsylvania eight, Delaware one, Maryland +six, Virginia ten, North Carolina five, South Carolina five and Georgia three. + +When vacancies happen in the Representation from any State, the Executive +Authority thereof shall issue Writs of Election to fill such Vacancies. + +The House of Representatives shall choose their Speaker and other Officers; and +shall have the sole Power of Impeachment. + +Section 3 +The Senate of the United States shall be composed of two Senators from each +State, chosen by the Legislature thereof, for six Years; and each Senator shall +have one Vote. + +Immediately after they shall be assembled in Consequence of the first Election, +they shall be divided as equally as may be into three Classes. The Seats of the +Senators of the first Class shall be vacated at the Expiration of the second +Year, of the second Class at the Expiration of the fourth Year, and of the +third Class at the Expiration of the sixth Year, so that one third may be +chosen every second Year; and if Vacancies happen by Resignation, or otherwise, +during the Recess of the Legislature of any State, the Executive thereof may +make temporary Appointments until the next Meeting of the Legislature, which +shall then fill such Vacancies. + +No person shall be a Senator who shall not have attained to the Age of thirty +Years, and been nine Years a Citizen of the United States, and who shall not, +when elected, be an Inhabitant of that State for which he shall be chosen. + +The Vice President of the United States shall be President of the Senate, but +shall have no Vote, unless they be equally divided. + +The Senate shall choose their other Officers, and also a President pro tempore, +in the absence of the Vice President, or when he shall exercise the Office of +President of the United States. + +The Senate shall have the sole Power to try all Impeachments. When sitting for +that Purpose, they shall be on Oath or Affirmation. When the President of the +United States is tried, the Chief Justice shall preside: And no Person shall be +convicted without the Concurrence of two thirds of the Members present. + +Judgment in Cases of Impeachment shall not extend further than to removal from +Office, and disqualification to hold and enjoy any Office of honor, Trust or +Profit under the United States: but the Party convicted shall nevertheless be +liable and subject to Indictment, Trial, Judgment and Punishment, according to +Law. + +Section 4 +The Times, Places and Manner of holding Elections for Senators and +Representatives, shall be prescribed in each State by the Legislature thereof; +but the Congress may at any time by Law make or alter such Regulations, except +as to the Place of Choosing Senators. + +The Congress shall assemble at least once in every Year, and such Meeting shall +be on the first Monday in December, unless they shall by Law appoint a +different Day. + +Section 5 +Each House shall be the Judge of the Elections, Returns and Qualifications of +its own Members, and a Majority of each shall constitute a Quorum to do +Business; but a smaller number may adjourn from day to day, and may be +authorized to compel the Attendance of absent Members, in such Manner, and +under such Penalties as each House may provide. + +Each House may determine the Rules of its Proceedings, punish its Members for +disorderly Behavior, and, with the Concurrence of two-thirds, expel a Member. + +Each House shall keep a Journal of its Proceedings, and from time to time +publish the same, excepting such Parts as may in their Judgment require +Secrecy; and the Yeas and Nays of the Members of either House on any question +shall, at the Desire of one fifth of those Present, be entered on the Journal. + +Neither House, during the Session of Congress, shall, without the Consent of +the other, adjourn for more than three days, nor to any other Place than that +in which the two Houses shall be sitting. + +Section 6 +The Senators and Representatives shall receive a Compensation for their +Services, to be ascertained by Law, and paid out of the Treasury of the United +States. They shall in all Cases, except Treason, Felony and Breach of the +Peace, be privileged from Arrest during their Attendance at the Session of +their respective Houses, and in going to and returning from the same; and for +any Speech or Debate in either House, they shall not be questioned in any other +Place. + +No Senator or Representative shall, during the Time for which he was elected, +be appointed to any civil Office under the Authority of the United States which +shall have been created, or the Emoluments whereof shall have been increased +during such time; and no Person holding any Office under the United States, +shall be a Member of either House during his Continuance in Office. + + +Section 7 +All bills for raising Revenue shall originate in the House of Representatives; +but the Senate may propose or concur with Amendments as on other Bills. + +Every Bill which shall have passed the House of Representatives and the Senate, +shall, before it become a Law, be presented to the President of the United +States; If he approve he shall sign it, but if not he shall return it, with his +Objections to that House in which it shall have originated, who shall enter the +Objections at large on their Journal, and proceed to reconsider it. If after +such Reconsideration two thirds of that House shall agree to pass the Bill, it +shall be sent, together with the Objections, to the other House, by which it +shall likewise be reconsidered, and if approved by two thirds of that House, it +shall become a Law. But in all such Cases the Votes of both Houses shall be +determined by Yeas and Nays, and the Names of the Persons voting for and +against the Bill shall be entered on the Journal of each House respectively. If +any Bill shall not be returned by the President within ten Days (Sundays +excepted) after it shall have been presented to him, the Same shall be a Law, +in like Manner as if he had signed it, unless the Congress by their Adjournment +prevent its Return, in which Case it shall not be a Law. + +Every Order, Resolution, or Vote to which the Concurrence of the Senate and +House of Representatives may be necessary (except on a question of Adjournment) +shall be presented to the President of the United States; and before the Same +shall take Effect, shall be approved by him, or being disapproved by him, shall +be repassed by two thirds of the Senate and House of Representatives, according +to the Rules and Limitations prescribed in the Case of a Bill. + + +Section 8 +The Congress shall have Power To lay and collect Taxes, Duties, Imposts and +Excises, to pay the Debts and provide for the common Defence and general +Welfare of the United States; but all Duties, Imposts and Excises shall be +uniform throughout the United States; + +To borrow money on the credit of the United States; + +To regulate Commerce with foreign Nations, and among the several States, and +with the Indian Tribes; + +To establish an uniform Rule of Naturalization, and uniform Laws on the subject +of Bankruptcies throughout the United States; + +To coin Money, regulate the Value thereof, and of foreign Coin, and fix the +Standard of Weights and Measures; + +To provide for the Punishment of counterfeiting the Securities and current Coin +of the United States; + +To establish Post Offices and Post Roads; + +To promote the Progress of Science and useful Arts, by securing for limited +Times to Authors and Inventors the exclusive Right to their respective Writings +and Discoveries; + +To constitute Tribunals inferior to the supreme Court; + +To define and punish Piracies and Felonies committed on the high Seas, and +Offenses against the Law of Nations; + +To declare War, grant Letters of Marque and Reprisal, and make Rules concerning +Captures on Land and Water; + +To raise and support Armies, but no Appropriation of Money to that Use shall be +for a longer Term than two Years; + +To provide and maintain a Navy; + +To make Rules for the Government and Regulation of the land and naval Forces; + +To provide for calling forth the Militia to execute the Laws of the Union, +suppress Insurrections and repel Invasions; + +To provide for organizing, arming, and disciplining the Militia, and for +governing such Part of them as may be employed in the Service of the United +States, reserving to the States respectively, the Appointment of the Officers, +and the Authority of training the Militia according to the discipline +prescribed by Congress; + +To exercise exclusive Legislation in all Cases whatsoever, over such District +(not exceeding ten Miles square) as may, by Cession of particular States, and +the acceptance of Congress, become the Seat of the Government of the United +States, and to exercise like Authority over all Places purchased by the Consent +of the Legislature of the State in which the Same shall be, for the Erection of +Forts, Magazines, Arsenals, dock-Yards, and other needful Buildings; And + +To make all Laws which shall be necessary and proper for carrying into +Execution the foregoing Powers, and all other Powers vested by this +Constitution in the Government of the United States, or in any Department or +Officer thereof. + +Section 9 +The Migration or Importation of such Persons as any of the States now existing +shall think proper to admit, shall not be prohibited by the Congress prior to +the Year one thousand eight hundred and eight, but a tax or duty may be imposed +on such Importation, not exceeding ten dollars for each Person. + +The privilege of the Writ of Habeas Corpus shall not be suspended, unless when +in Cases of Rebellion or Invasion the public Safety may require it. + +No Bill of Attainder or ex post facto Law shall be passed. + +No capitation, or other direct, Tax shall be laid, unless in Proportion to the +Census or Enumeration herein before directed to be taken. + +No Tax or Duty shall be laid on Articles exported from any State. + +No Preference shall be given by any Regulation of Commerce or Revenue to the +Ports of one State over those of another: nor shall Vessels bound to, or from, +one State, be obliged to enter, clear, or pay Duties in another. + +No Money shall be drawn from the Treasury, but in Consequence of Appropriations +made by Law; and a regular Statement and Account of the Receipts and +Expenditures of all public Money shall be published from time to time. + +No Title of Nobility shall be granted by the United States: And no Person +holding any Office of Profit or Trust under them, shall, without the Consent of +the Congress, accept of any present, Emolument, Office, or Title, of any kind +whatever, from any King, Prince or foreign State. + +Section 10 +No State shall enter into any Treaty, Alliance, or Confederation; grant Letters +of Marque and Reprisal; coin Money; emit Bills of Credit; make any Thing but +gold and silver Coin a Tender in Payment of Debts; pass any Bill of Attainder, +ex post facto Law, or Law impairing the Obligation of Contracts, or grant any +Title of Nobility. + +No State shall, without the Consent of the Congress, lay any Imposts or Duties +on Imports or Exports, except what may be absolutely necessary for executing +its inspection Laws: and the net Produce of all Duties and Imposts, laid by +any State on Imports or Exports, shall be for the Use of the Treasury of the +United States; and all such Laws shall be subject to the Revision and Control +of the Congress. + +No State shall, without the Consent of Congress, lay any duty of Tonnage, keep +Troops, or Ships of War in time of Peace, enter into any Agreement or Compact +with another State, or with a foreign Power, or engage in War, unless actually +invaded, or in such imminent Danger as will not admit of delay. + +Article 2. + +Section 1 +The executive Power shall be vested in a President of the United States of +America. He shall hold his Office during the Term of four Years, and, together +with the Vice-President chosen for the same Term, be elected, as follows: + +Each State shall appoint, in such Manner as the Legislature thereof may direct, +a Number of Electors, equal to the whole Number of Senators and Representatives +to which the State may be entitled in the Congress: but no Senator or +Representative, or Person holding an Office of Trust or Profit under the United +States, shall be appointed an Elector. + +The Electors shall meet in their respective States, and vote by Ballot for two +persons, of whom one at least shall not lie an Inhabitant of the same State +with themselves. And they shall make a List of all the Persons voted for, and +of the Number of Votes for each; which List they shall sign and certify, and +transmit sealed to the Seat of the Government of the United States, directed to +the President of the Senate. The President of the Senate shall, in the Presence +of the Senate and House of Representatives, open all the Certificates, and the +Votes shall then be counted. The Person having the greatest Number of Votes +shall be the President, if such Number be a Majority of the whole Number of +Electors appointed; and if there be more than one who have such Majority, and +have an equal Number of Votes, then the House of Representatives shall +immediately choose by Ballot one of them for President; and if no Person have a +Majority, then from the five highest on the List the said House shall in like +Manner choose the President. But in choosing the President, the Votes shall be +taken by States, the Representation from each State having one Vote; a quorum +for this Purpose shall consist of a Member or Members from two-thirds of the +States, and a Majority of all the States shall be necessary to a Choice. In +every Case, after the Choice of the President, the Person having the greatest +Number of Votes of the Electors shall be the Vice President. But if there +should remain two or more who have equal Votes, the Senate shall choose from +them by Ballot the Vice-President. + +The Congress may determine the Time of choosing the Electors, and the Day on +which they shall give their Votes; which Day shall be the same throughout the +United States. + +No person except a natural born Citizen, or a Citizen of the United States, at +the time of the Adoption of this Constitution, shall be eligible to the Office +of President; neither shall any Person be eligible to that Office who shall not +have attained to the Age of thirty-five Years, and been fourteen Years a +Resident within the United States. + +In Case of the Removal of the President from Office, or of his Death, +Resignation, or Inability to discharge the Powers and Duties of the said +Office, the same shall devolve on the Vice President, and the Congress may by +Law provide for the Case of Removal, Death, Resignation or Inability, both of +the President and Vice President, declaring what Officer shall then act as +President, and such Officer shall act accordingly, until the Disability be +removed, or a President shall be elected. + +The President shall, at stated Times, receive for his Services, a Compensation, +which shall neither be increased nor diminished during the Period for which he +shall have been elected, and he shall not receive within that Period any other +Emolument from the United States, or any of them. + +Before he enter on the Execution of his Office, he shall take the following +Oath or Affirmation: + +"I do solemnly swear (or affirm) that I will faithfully execute the Office of +President of the United States, and will to the best of my Ability, preserve, +protect and defend the Constitution of the United States." + +Section 2 +The President shall be Commander in Chief of the Army and Navy of the United +States, and of the Militia of the several States, when called into the actual +Service of the United States; he may require the Opinion, in writing, of the +principal Officer in each of the executive Departments, upon any subject +relating to the Duties of their respective Offices, and he shall have Power to +Grant Reprieves and Pardons for Offenses against the United States, except in +Cases of Impeachment. + +He shall have Power, by and with the Advice and Consent of the Senate, to make +Treaties, provided two thirds of the Senators present concur; and he shall +nominate, and by and with the Advice and Consent of the Senate, shall appoint +Ambassadors, other public Ministers and Consuls, Judges of the supreme Court, +and all other Officers of the United States, whose Appointments are not herein +otherwise provided for, and which shall be established by Law: but the Congress +may by Law vest the Appointment of such inferior Officers, as they think +proper, in the President alone, in the Courts of Law, or in the Heads of +Departments. + +The President shall have Power to fill up all Vacancies that may happen during +the Recess of the Senate, by granting Commissions which shall expire at the End +of their next Session. + +Section 3 +He shall from time to time give to the Congress Information of the State of the +Union, and recommend to their Consideration such Measures as he shall judge +necessary and expedient; he may, on extraordinary Occasions, convene both +Houses, or either of them, and in Case of Disagreement between them, with +Respect to the Time of Adjournment, he may adjourn them to such Time as he +shall think proper; he shall receive Ambassadors and other public Ministers; he +shall take Care that the Laws be faithfully executed, and shall Commission all +the Officers of the United States. + +Section 4 +The President, Vice President and all civil Officers of the United States, +shall be removed from Office on Impeachment for, and Conviction of, Treason, +Bribery, or other high Crimes and Misdemeanors. + +Article 3. + +Section 1 +The judicial Power of the United States, shall be vested in one supreme Court, +and in such inferior Courts as the Congress may from time to time ordain and +establish. The Judges, both of the supreme and inferior Courts, shall hold +their Offices during good Behavior, and shall, at stated Times, receive for +their Services a Compensation which shall not be diminished during their +Continuance in Office. + +Section 2 +The judicial Power shall extend to all Cases, in Law and Equity, arising under +this Constitution, the Laws of the United States, and Treaties made, or which +shall be made, under their Authority; to all Cases affecting Ambassadors, other +public Ministers and Consuls; to all Cases of admiralty and maritime +Jurisdiction; to Controversies to which the United States shall be a Party; to +Controversies between two or more States; between a State and Citizens of +another State; between Citizens of different States; between Citizens of the +same State claiming Lands under Grants of different States, and between a +State, or the Citizens thereof, and foreign States, Citizens or Subjects. + +In all Cases affecting Ambassadors, other public Ministers and Consuls, and +those in which a State shall be Party, the supreme Court shall have original +Jurisdiction. In all the other Cases before mentioned, the supreme Court shall +have appellate Jurisdiction, both as to Law and Fact, with such Exceptions, and +under such Regulations as the Congress shall make. + +The Trial of all Crimes, except in Cases of Impeachment, shall be by Jury; and +such Trial shall be held in the State where the said Crimes shall have been +committed; but when not committed within any State, the Trial shall be at such +Place or Places as the Congress may by Law have directed. + +Section 3 +Treason against the United States, shall consist only in levying War against +them, or in adhering to their Enemies, giving them Aid and Comfort. No Person +shall be convicted of Treason unless on the Testimony of two Witnesses to the +same overt Act, or on Confession in open Court. + +The Congress shall have power to declare the Punishment of Treason, but no +Attainder of Treason shall work Corruption of Blood, or Forfeiture except +during the Life of the Person attainted. + +Article 4. + +Section 1 +Full Faith and Credit shall be given in each State to the public Acts, Records, +and judicial Proceedings of every other State. And the Congress may by general +Laws prescribe the Manner in which such Acts, Records and Proceedings shall be +proved, and the Effect thereof. + +Section 2 +The Citizens of each State shall be entitled to all Privileges and Immunities +of Citizens in the several States. + +A Person charged in any State with Treason, Felony, or other Crime, who shall +flee from Justice, and be found in another State, shall on demand of the +executive Authority of the State from which he fled, be delivered up, to be +removed to the State having Jurisdiction of the Crime. + +No Person held to Service or Labour in one State, under the Laws thereof, +escaping into another, shall, in Consequence of any Law or Regulation therein, +be discharged from such Service or Labour, But shall be delivered up on Claim +of the Party to whom such Service or Labour may be due. + +Section 3 +New States may be admitted by the Congress into this Union; but no new States +shall be formed or erected within the Jurisdiction of any other State; nor any +State be formed by the Junction of two or more States, or parts of States, +without the Consent of the Legislatures of the States concerned as well as of +the Congress. + +The Congress shall have Power to dispose of and make all needful Rules and +Regulations respecting the Territory or other Property belonging to the United +States; and nothing in this Constitution shall be so construed as to Prejudice +any Claims of the United States, or of any particular State. + +Section 4 +The United States shall guarantee to every State in this Union a Republican +Form of Government, and shall protect each of them against Invasion; and on +Application of the Legislature, or of the Executive (when the Legislature +cannot be convened) against domestic Violence. + +Article 5. + +The Congress, whenever two thirds of both Houses shall deem it necessary, shall +propose Amendments to this Constitution, or, on the Application of the +Legislatures of two thirds of the several States, shall call a Convention for +proposing Amendments, which, in either Case, shall be valid to all Intents and +Purposes, as part of this Constitution, when ratified by the Legislatures of +three fourths of the several States, or by Conventions in three fourths +thereof, as the one or the other Mode of Ratification may be proposed by the +Congress; Provided that no Amendment which may be made prior to the Year One +thousand eight hundred and eight shall in any Manner affect the first and +fourth Clauses in the Ninth Section of the first Article; and that no State, +without its Consent, shall be deprived of its equal Suffrage in the Senate. + +Article 6. + +All Debts contracted and Engagements entered into, before the Adoption of this +Constitution, shall be as valid against the United States under this +Constitution, as under the Confederation. + +This Constitution, and the Laws of the United States which shall be made in +Pursuance thereof; and all Treaties made, or which shall be made, under the +Authority of the United States, shall be the supreme Law of the Land; and the +Judges in every State shall be bound thereby, any Thing in the Constitution or +Laws of any State to the Contrary notwithstanding. + +The Senators and Representatives before mentioned, and the Members of the +several State Legislatures, and all executive and judicial Officers, both of +the United States and of the several States, shall be bound by Oath or +Affirmation, to support this Constitution; but no religious Test shall ever be +required as a Qualification to any Office or public Trust under the United +States. + +Article 7. + +The Ratification of the Conventions of nine States, shall be sufficient for the +Establishment of this Constitution between the States so ratifying the Same. + +Done in Convention by the Unanimous Consent of the States present the +Seventeenth Day of September in the Year of our Lord one thousand seven hundred +and Eighty seven and of the Independence of the United States of America the +Twelfth. In Witness whereof We have hereunto subscribed our Names. + +George Washington - President and deputy from Virginia + +New Hampshire - John Langdon, Nicholas Gilman + +Massachusetts - Nathaniel Gorham, Rufus King + +Connecticut - William Samuel Johnson, Roger Sherman + +New York - Alexander Hamilton + +New Jersey - William Livingston, David Brearley, William Paterson, Jonathan +Dayton + +Pennsylvania - Benjamin Franklin, Thomas Mifflin, Robert Morris, George Clymer, +Thomas Fitzsimons, Jared Ingersoll, James Wilson, Gouvernour Morris + +Delaware - George Read, Gunning Bedford Jr., John Dickinson, Richard Bassett, +Jacob Broom + +Maryland - James McHenry, Daniel of St Thomas Jenifer, Daniel Carroll + +Virginia - John Blair, James Madison Jr. + +North Carolina - William Blount, Richard Dobbs Spaight, Hugh Williamson + +South Carolina - John Rutledge, Charles Cotesworth Pinckney, Charles Pinckney, +Pierce Butler + +Georgia - William Few, Abraham Baldwin + +Attest: William Jackson, Secretary + + +Amendment 1 +Congress shall make no law respecting an establishment of religion, or +prohibiting the free exercise thereof; or abridging the freedom of speech, or +of the press; or the right of the people peaceably to assemble, and to petition +the Government for a redress of grievances. + +Amendment 2 +A well regulated Militia, being necessary to the security of a free State, the +right of the people to keep and bear Arms, shall not be infringed. + +Amendment 3 +No Soldier shall, in time of peace be quartered in any house, without the +consent of the Owner, nor in time of war, but in a manner to be prescribed by +law. + +Amendment 4 +The right of the people to be secure in their persons, houses, papers, and +effects, against unreasonable searches and seizures, shall not be violated, and +no Warrants shall issue, but upon probable cause, supported by Oath or +affirmation, and particularly describing the place to be searched, and the +persons or things to be seized. + +Amendment 5 +No person shall be held to answer for a capital, or otherwise infamous crime, +unless on a presentment or indictment of a Grand Jury, except in cases arising +in the land or naval forces, or in the Militia, when in actual service in time +of War or public danger; nor shall any person be subject for the same offense +to be twice put in jeopardy of life or limb; nor shall be compelled in any +criminal case to be a witness against himself, nor be deprived of life, +liberty, or property, without due process of law; nor shall private property be +taken for public use, without just compensation. + +Amendment 6 +In all criminal prosecutions, the accused shall enjoy the right to a speedy and +public trial, by an impartial jury of the State and district wherein the crime +shall have been committed, which district shall have been previously +ascertained by law, and to be informed of the nature and cause of the +accusation; to be confronted with the witnesses against him; to have compulsory +process for obtaining witnesses in his favor, and to have the Assistance of +Counsel for his defence. + +Amendment 7 +In Suits at common law, where the value in controversy shall exceed twenty +dollars, the right of trial by jury shall be preserved, and no fact tried by a +jury, shall be otherwise re-examined in any Court of the United States, than +according to the rules of the common law. + +Amendment 8 +Excessive bail shall not be required, nor excessive fines imposed, nor cruel +and unusual punishments inflicted. + +Amendment 9 +The enumeration in the Constitution, of certain rights, shall not be construed +to deny or disparage others retained by the people. + +Amendment 10 +The powers not delegated to the United States by the Constitution, nor +prohibited by it to the States, are reserved to the States respectively, or to +the people. + +Amendment 11 +The Judicial power of the United States shall not be construed to extend to any +suit in law or equity, commenced or prosecuted against one of the United States +by Citizens of another State, or by Citizens or Subjects of any Foreign State. + +Amendment 12 +The Electors shall meet in their respective states, and vote by ballot for +President and Vice-President, one of whom, at least, shall not be an inhabitant +of the same state with themselves; they shall name in their ballots the person +voted for as President, and in distinct ballots the person voted for as +Vice-President, and they shall make distinct lists of all persons voted for as +President, and of all persons voted for as Vice-President and of the number of +votes for each, which lists they shall sign and certify, and transmit sealed to +the seat of the government of the United States, directed to the President of +the Senate; + +The President of the Senate shall, in the presence of the Senate and House of +Representatives, open all the certificates and the votes shall then be counted; + +The person having the greatest Number of votes for President, shall be the +President, if such number be a majority of the whole number of Electors +appointed; and if no person have such majority, then from the persons having +the highest numbers not exceeding three on the list of those voted for as +President, the House of Representatives shall choose immediately, by ballot, +the President. But in choosing the President, the votes shall be taken by +states, the representation from each state having one vote; a quorum for this +purpose shall consist of a member or members from two-thirds of the states, and +a majority of all the states shall be necessary to a choice. And if the House +of Representatives shall not choose a President whenever the right of choice +shall devolve upon them, before the fourth day of March next following, then +the Vice-President shall act as President, as in the case of the death or other +constitutional disability of the President. + +The person having the greatest number of votes as Vice-President, shall be the +Vice-President, if such number be a majority of the whole number of Electors +appointed, and if no person have a majority, then from the two highest numbers +on the list, the Senate shall choose the Vice-President; a quorum for the +purpose shall consist of two-thirds of the whole number of Senators, and a +majority of the whole number shall be necessary to a choice. But no person +constitutionally ineligible to the office of President shall be eligible to +that of Vice-President of the United States. + +Amendment 13 +1. Neither slavery nor involuntary servitude, except as a punishment for crime +whereof the party shall have been duly convicted, shall exist within the United +States, or any place subject to their jurisdiction. + +2. Congress shall have power to enforce this article by appropriate +legislation. + +Amendment 14 +1. All persons born or naturalized in the United States, and subject to the +jurisdiction thereof, are citizens of the United States and of the State +wherein they reside. No State shall make or enforce any law which shall abridge +the privileges or immunities of citizens of the United States; nor shall any +State deprive any person of life, liberty, or property, without due process of +law; nor deny to any person within its jurisdiction the equal protection of the +laws. + +2. Representatives shall be apportioned among the several States according to +their respective numbers, counting the whole number of persons in each State, +excluding Indians not taxed. But when the right to vote at any election for the +choice of electors for President and Vice-President of the United States, +Representatives in Congress, the Executive and Judicial officers of a State, or +the members of the Legislature thereof, is denied to any of the male +inhabitants of such State, being twenty-one years of age, and citizens of the +United States, or in any way abridged, except for participation in rebellion, +or other crime, the basis of representation therein shall be reduced in the +proportion which the number of such male citizens shall bear to the whole +number of male citizens twenty-one years of age in such State. + +3. No person shall be a Senator or Representative in Congress, or elector of +President and Vice-President, or hold any office, civil or military, under the +United States, or under any State, who, having previously taken an oath, as a +member of Congress, or as an officer of the United States, or as a member of +any State legislature, or as an executive or judicial officer of any State, to +support the Constitution of the United States, shall have engaged in +insurrection or rebellion against the same, or given aid or comfort to the +enemies thereof. But Congress may by a vote of two-thirds of each House, remove +such disability. + +4. The validity of the public debt of the United States, authorized by law, +including debts incurred for payment of pensions and bounties for services in +suppressing insurrection or rebellion, shall not be questioned. But neither the +United States nor any State shall assume or pay any debt or obligation incurred +in aid of insurrection or rebellion against the United States, or any claim for +the loss or emancipation of any slave; but all such debts, obligations and +claims shall be held illegal and void. + +5. The Congress shall have power to enforce, by appropriate legislation, the +provisions of this article. + +Amendment 15 +1. The right of citizens of the United States to vote shall not be denied or +abridged by the United States or by any State on account of race, color, or +previous condition of servitude. + +2. The Congress shall have power to enforce this article by appropriate +legislation. + +Amendment 16 +The Congress shall have power to lay and collect taxes on incomes, from +whatever source derived, without apportionment among the several States, and +without regard to any census or enumeration. + +Amendment 17 +The Senate of the United States shall be composed of two Senators from each +State, elected by the people thereof, for six years; and each Senator shall +have one vote. The electors in each State shall have the qualifications +requisite for electors of the most numerous branch of the State legislatures. + +When vacancies happen in the representation of any State in the Senate, the +executive authority of such State shall issue writs of election to fill such +vacancies: Provided, That the legislature of any State may empower the +executive thereof to make temporary appointments until the people fill the +vacancies by election as the legislature may direct. + +This amendment shall not be so construed as to affect the election or term of +any Senator chosen before it becomes valid as part of the Constitution. + +Amendment 18 +1. After one year from the ratification of this article the manufacture, sale, +or transportation of intoxicating liquors within, the importation thereof into, +or the exportation thereof from the United States and all territory subject to +the jurisdiction thereof for beverage purposes is hereby prohibited. + +2. The Congress and the several States shall have concurrent power to enforce +this article by appropriate legislation. + +3. This article shall be inoperative unless it shall have been ratified as an +amendment to the Constitution by the legislatures of the several States, as +provided in the Constitution, within seven years from the date of the +submission hereof to the States by the Congress. + +Amendment 19 +The right of citizens of the United States to vote shall not be denied or +abridged by the United States or by any State on account of sex. + +Congress shall have power to enforce this article by appropriate legislation. + +Amendment 20 +1. The terms of the President and Vice President shall end at noon on the 20th +day of January, and the terms of Senators and Representatives at noon on the 3d +day of January, of the years in which such terms would have ended if this +article had not been ratified; and the terms of their successors shall then +begin. + +2. The Congress shall assemble at least once in every year, and such meeting +shall begin at noon on the 3d day of January, unless they shall by law appoint +a different day. + +3. If, at the time fixed for the beginning of the term of the President, the +President elect shall have died, the Vice President elect shall become +President. If a President shall not have been chosen before the time fixed for +the beginning of his term, or if the President elect shall have failed to +qualify, then the Vice President elect shall act as President until a President +shall have qualified; and the Congress may by law provide for the case wherein +neither a President elect nor a Vice President elect shall have qualified, +declaring who shall then act as President, or the manner in which one who is to +act shall be selected, and such person shall act accordingly until a President +or Vice President shall have qualified. + +4. The Congress may by law provide for the case of the death of any of the +persons from whom the House of Representatives may choose a President whenever +the right of choice shall have devolved upon them, and for the case of the +death of any of the persons from whom the Senate may choose a Vice President +whenever the right of choice shall have devolved upon them. + +5. Sections 1 and 2 shall take effect on the 15th day of October following the +ratification of this article. + +6. This article shall be inoperative unless it shall have been ratified as an +amendment to the Constitution by the legislatures of three-fourths of the +several States within seven years from the date of its submission. + +Amendment 21 +1. The eighteenth article of amendment to the Constitution of the United States +is hereby repealed. + +2. The transportation or importation into any State, Territory, or possession +of the United States for delivery or use therein of intoxicating liquors, in +violation of the laws thereof, is hereby prohibited. + +3. The article shall be inoperative unless it shall have been ratified as an +amendment to the Constitution by conventions in the several States, as provided +in the Constitution, within seven years from the date of the submission hereof +to the States by the Congress. + +Amendment 22 +1. No person shall be elected to the office of the President more than twice, +and no person who has held the office of President, or acted as President, for +more than two years of a term to which some other person was elected President +shall be elected to the office of the President more than once. But this +Article shall not apply to any person holding the office of President, when this +Article was proposed by the Congress, and shall not prevent any person who may +be holding the office of President, or acting as President, during the term +within which this Article becomes operative from holding the office of +President or acting as President during the remainder of such term. + +2. This article shall be inoperative unless it shall have been ratified as an +amendment to the Constitution by the legislatures of three-fourths of the +several States within seven years from the date of its submission to the States +by the Congress. + +Amendment 23 +1. The District constituting the seat of Government of the United States shall +appoint in such manner as the Congress may direct: A number of electors of +President and Vice President equal to the whole number of Senators and +Representatives in Congress to which the District would be entitled if it were +a State, but in no event more than the least populous State; they shall be in +addition to those appointed by the States, but they shall be considered, for +the purposes of the election of President and Vice President, to be electors +appointed by a State; and they shall meet in the District and perform such +duties as provided by the twelfth article of amendment. + +2. The Congress shall have power to enforce this article by appropriate +legislation. + +Amendment 24 +1. The right of citizens of the United States to vote in any primary or other +election for President or Vice President, for electors for President or +Vice President, or for Senator or Representative in Congress, shall not be +denied or abridged by the United States or any State by reason of failure to +pay any poll tax or other tax. + +2. The Congress shall have power to enforce this article by appropriate +legislation. + +Amendment 25 +1. In case of the removal of the President from office or of his death or +resignation, the Vice President shall become President. + +2. Whenever there is a vacancy in the office of the Vice President, the +President shall nominate a Vice President who shall take office upon +confirmation by a majority vote of both Houses of Congress. + +3. Whenever the President transmits to the President pro tempore of the Senate +and the Speaker of the House of Representatives his written declaration that he +is unable to discharge the powers and duties of his office, and until he +transmits to them a written declaration to the contrary, such powers and duties +shall be discharged by the Vice President as Acting President. + +4. Whenever the Vice President and a majority of either the principal officers +of the executive departments or of such other body as Congress may by law +provide, transmit to the President pro tempore of the Senate and the Speaker of +the House of Representatives their written declaration that the President is +unable to discharge the powers and duties of his office, the Vice President +shall immediately assume the powers and duties of the office as Acting +President. + +Thereafter, when the President transmits to the President pro tempore of the +Senate and the Speaker of the House of Representatives his written declaration +that no inability exists, he shall resume the powers and duties of his office +unless the Vice President and a majority of either the principal officers of +the executive department or of such other body as Congress may by law provide, +transmit within four days to the President pro tempore of the Senate and the +Speaker of the House of Representatives their written declaration that the +President is unable to discharge the powers and duties of his office. Thereupon +Congress shall decide the issue, assembling within forty eight hours for that +purpose if not in session. If the Congress, within twenty one days after +receipt of the latter written declaration, or, if Congress is not in session, +within twenty one days after Congress is required to assemble, determines by +two thirds vote of both Houses that the President is unable to discharge the +powers and duties of his office, the Vice President shall continue to discharge +the same as Acting President; otherwise, the President shall resume the powers +and duties of his office. + +Amendment 26 +1. The right of citizens of the United States, who are eighteen years of age or +older, to vote shall not be denied or abridged by the United States or by any +State on account of age. + +2. The Congress shall have power to enforce this article by appropriate +legislation. + +Amendment 27 +No law, varying the compensation for the services of the Senators and +Representatives, shall take effect, until an election of Representatives shall +have intervened. diff --git a/extensions/bouncycastle/src/main/java/org/jclouds/encryption/bouncycastle/BouncyCastleEncryptionService.java b/extensions/bouncycastle/src/main/java/org/jclouds/encryption/bouncycastle/BouncyCastleEncryptionService.java index d2e498e048..38e27ea78c 100644 --- a/extensions/bouncycastle/src/main/java/org/jclouds/encryption/bouncycastle/BouncyCastleEncryptionService.java +++ b/extensions/bouncycastle/src/main/java/org/jclouds/encryption/bouncycastle/BouncyCastleEncryptionService.java @@ -26,11 +26,11 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.security.Key; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; -import javax.crypto.Cipher; import javax.inject.Singleton; import org.bouncycastle.crypto.Digest; @@ -50,12 +50,12 @@ import org.jclouds.io.payloads.ByteArrayPayload; */ @Singleton public class BouncyCastleEncryptionService extends BaseEncryptionService { - public BouncyCastleEncryptionService(KeyFactory rsaKeyFactory) { - super(rsaKeyFactory); + public BouncyCastleEncryptionService(KeyFactory rsaKeyFactory, CertificateFactory certFactory) { + super(rsaKeyFactory, certFactory); } - public BouncyCastleEncryptionService() throws NoSuchAlgorithmException { - super(KeyFactory.getInstance("RSA")); + public BouncyCastleEncryptionService() throws NoSuchAlgorithmException, CertificateException { + this(KeyFactory.getInstance("RSA"), CertificateFactory.getInstance("X.509")); } @Override @@ -189,17 +189,4 @@ public class BouncyCastleEncryptionService extends BaseEncryptionService { return resBuf; } - @Override - public byte[] rsaSign(String toSign, Key key) { - // TODO convert this to BC code - try { - Cipher cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.ENCRYPT_MODE, key); - return cipher.doFinal(toSign.getBytes()); - } catch (Exception e) { - propagate(e); - return null; - } - } - } diff --git a/extensions/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java b/extensions/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java index 12ee51c30d..70d068df38 100644 --- a/extensions/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java +++ b/extensions/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.Date; import javax.ws.rs.HttpMethod; @@ -60,6 +61,8 @@ public class ConvertToGaeRequestTest { encryptionService = new JCEEncryptionService(); } catch (NoSuchAlgorithmException e) { Throwables.propagate(e); + } catch (CertificateException e) { + Throwables.propagate(e); } } @@ -152,7 +155,7 @@ public class ConvertToGaeRequestTest { builder.append(header.getName()).append(": ").append(header.getValue()).append("\n"); } assertEquals(builder.toString(), - "User-Agent: jclouds/1.0 urlfetch/1.3.5\nContent-MD5: AQIDBA==\nContent-Type: text/plain\nContent-Length: 5\n"); + "User-Agent: jclouds/1.0 urlfetch/1.3.5\nContent-MD5: AQIDBA==\nContent-Type: text/plain\nContent-Length: 5\n"); assertEquals(new String(gaeRequest.getPayload()), "hoot!"); } } diff --git a/extensions/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java b/extensions/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java index b4088b8628..68827cb82b 100644 --- a/extensions/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java +++ b/extensions/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.List; @@ -59,6 +60,8 @@ public class ConvertToJcloudsResponseTest { encryptionService = new JCEEncryptionService(); } catch (NoSuchAlgorithmException e) { Throwables.propagate(e); + } catch (CertificateException e) { + Throwables.propagate(e); } } diff --git a/opscodeplatform/src/main/java/org/jclouds/opscodeplatform/OpscodePlatformAsyncClient.java b/opscodeplatform/src/main/java/org/jclouds/opscodeplatform/OpscodePlatformAsyncClient.java index ec88bba4c4..c85a23bc40 100644 --- a/opscodeplatform/src/main/java/org/jclouds/opscodeplatform/OpscodePlatformAsyncClient.java +++ b/opscodeplatform/src/main/java/org/jclouds/opscodeplatform/OpscodePlatformAsyncClient.java @@ -38,14 +38,12 @@ import org.jclouds.chef.domain.Organization; import org.jclouds.chef.domain.User; import org.jclouds.chef.filters.SignedHeaderAuth; import org.jclouds.chef.functions.OrganizationName; -import org.jclouds.chef.functions.ParseKeyFromJson; import org.jclouds.chef.functions.Username; import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.ParamParser; import org.jclouds.rest.annotations.RequestFilters; -import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.binders.BindToJsonPayload; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; @@ -74,9 +72,7 @@ public interface OpscodePlatformAsyncClient { */ @POST @Path("/users") - @ResponseParser(ParseKeyFromJson.class) - ListenableFuture createUser( - @BinderParam(BindToJsonPayload.class) User user); + ListenableFuture createUser(@BinderParam(BindToJsonPayload.class) User user); /** * @see ChefClient#updateUser @@ -85,7 +81,7 @@ public interface OpscodePlatformAsyncClient { @Path("/users/{username}") @Consumes(MediaType.APPLICATION_JSON) ListenableFuture updateUser( - @PathParam("username") @ParamParser(Username.class) @BinderParam(BindToJsonPayload.class) User user); + @PathParam("username") @ParamParser(Username.class) @BinderParam(BindToJsonPayload.class) User user); /** * @see ChefClient#getUser @@ -110,9 +106,7 @@ public interface OpscodePlatformAsyncClient { */ @POST @Path("/organizations") - @ResponseParser(ParseKeyFromJson.class) - ListenableFuture createOrg( - @BinderParam(BindToJsonPayload.class) Organization org); + ListenableFuture createOrg(@BinderParam(BindToJsonPayload.class) Organization org); /** * @see ChefClient#updateOrg @@ -121,7 +115,7 @@ public interface OpscodePlatformAsyncClient { @Path("/organizations/{orgname}") @Consumes(MediaType.APPLICATION_JSON) ListenableFuture updateOrg( - @PathParam("orgname") @ParamParser(OrganizationName.class) @BinderParam(BindToJsonPayload.class) Organization org); + @PathParam("orgname") @ParamParser(OrganizationName.class) @BinderParam(BindToJsonPayload.class) Organization org); /** * @see ChefClient#getOrg diff --git a/opscodeplatform/src/main/java/org/jclouds/opscodeplatform/OpscodePlatformClient.java b/opscodeplatform/src/main/java/org/jclouds/opscodeplatform/OpscodePlatformClient.java index 4fc0f83f76..5af76e66cb 100644 --- a/opscodeplatform/src/main/java/org/jclouds/opscodeplatform/OpscodePlatformClient.java +++ b/opscodeplatform/src/main/java/org/jclouds/opscodeplatform/OpscodePlatformClient.java @@ -64,7 +64,7 @@ public interface OpscodePlatformClient { *

* "403 Forbidden" if the caller is not authorized to create a user. */ - String createUser(User user); + User createUser(User user); /** * updates an existing user. Note: you must have update rights on the user. @@ -109,7 +109,7 @@ public interface OpscodePlatformClient { *

* "403 Forbidden" if the caller is not authorized to create a organization. */ - String createOrg(Organization organization); + Organization createOrg(Organization organization); /** * updates an existing organization. Note: you must have update rights on the organization. diff --git a/opscodeplatform/src/test/java/org/jclouds/opscodeplatform/OpscodePlatformAsyncClientTest.java b/opscodeplatform/src/test/java/org/jclouds/opscodeplatform/OpscodePlatformAsyncClientTest.java index 272a4fa1e5..8d43a750d2 100644 --- a/opscodeplatform/src/test/java/org/jclouds/opscodeplatform/OpscodePlatformAsyncClientTest.java +++ b/opscodeplatform/src/test/java/org/jclouds/opscodeplatform/OpscodePlatformAsyncClientTest.java @@ -23,9 +23,9 @@ */ package org.jclouds.opscodeplatform; -import static org.jclouds.concurrent.ConcurrentUtils.sameThreadExecutor; import static org.easymock.classextension.EasyMock.createMock; import static org.easymock.classextension.EasyMock.replay; +import static org.jclouds.concurrent.ConcurrentUtils.sameThreadExecutor; import static org.jclouds.rest.RestContextFactory.createContextBuilder; import static org.testng.Assert.assertEquals; @@ -38,7 +38,6 @@ import org.jclouds.chef.domain.Organization; import org.jclouds.chef.domain.User; import org.jclouds.chef.filters.SignedHeaderAuth; import org.jclouds.chef.filters.SignedHeaderAuthTest; -import org.jclouds.chef.functions.ParseKeyFromJson; import org.jclouds.concurrent.config.ConfiguresExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.date.TimeStamp; @@ -75,12 +74,12 @@ import com.google.inject.TypeLiteral; public class OpscodePlatformAsyncClientTest extends RestClientTest { public void testDelegatedOpscodePlatformCallsResolveProperly() throws SecurityException, NoSuchMethodException, - InterruptedException, ExecutionException { + InterruptedException, ExecutionException { final TransformingHttpCommandExecutorService httpExecutor = createMock(TransformingHttpCommandExecutorService.class); Injector injector = createContextBuilder(createContextSpec(), - ImmutableSet.of(new HttpExecutorModule(httpExecutor), new NullLoggingModule(), createModule())) - .buildInjector(); + ImmutableSet.of(new HttpExecutorModule(httpExecutor), new NullLoggingModule(), createModule())) + .buildInjector(); replay(httpExecutor); @@ -91,7 +90,7 @@ public class OpscodePlatformAsyncClientTest extends RestClientTest httpRequest = processor - .createRequest(method, new User("myuser")); + .createRequest(method, new User("myuser")); assertRequestLineEquals(httpRequest, "POST https://api.opscode.com/users HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); @@ -111,18 +110,21 @@ public class OpscodePlatformAsyncClientTest extends RestClientTest httpRequest = processor - .createRequest(method, new User("myuser")); + .createRequest(method, new User("myuser")); assertRequestLineEquals(httpRequest, "PUT https://api.opscode.com/users/myuser HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); @@ -182,13 +184,13 @@ public class OpscodePlatformAsyncClientTest extends RestClientTest httpRequest = processor.createRequest(method, new Organization( - "myorganization")); + "myorganization")); assertRequestLineEquals(httpRequest, "POST https://api.opscode.com/organizations HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); assertPayloadEquals(httpRequest, "{\"name\":\"myorganization\"}", "application/json", false); - assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class); + assertResponseParserClassEquals(method, httpRequest, ParseJson.class); assertSaxResponseParserClassEquals(method, null); assertExceptionParserClassEquals(method, null); @@ -199,7 +201,7 @@ public class OpscodePlatformAsyncClientTest extends RestClientTest httpRequest = processor.createRequest(method, new Organization( - "myorganization")); + "myorganization")); assertRequestLineEquals(httpRequest, "PUT https://api.opscode.com/organizations/myorganization HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); @@ -291,6 +293,6 @@ public class OpscodePlatformAsyncClientTest extends RestClientTest createContextSpec() { return new RestContextFactory().createContextSpec("opscodeplatform", "user", SignedHeaderAuthTest.PRIVATE_KEY, - new Properties()); + new Properties()); } } diff --git a/opscodeplatform/src/test/java/org/jclouds/opscodeplatform/OpscodePlatformClientLiveTest.java b/opscodeplatform/src/test/java/org/jclouds/opscodeplatform/OpscodePlatformClientLiveTest.java index 60b15f3dda..e810612683 100644 --- a/opscodeplatform/src/test/java/org/jclouds/opscodeplatform/OpscodePlatformClientLiveTest.java +++ b/opscodeplatform/src/test/java/org/jclouds/opscodeplatform/OpscodePlatformClientLiveTest.java @@ -67,12 +67,11 @@ public class OpscodePlatformClientLiveTest { String keyfile = System.getProperty("jclouds.test.credential"); if (keyfile == null || keyfile.equals("")) keyfile = "/etc/chef/validation.pem"; - validatorConnection = createConnection(orgname + "-validator", Files.toString(new File( - keyfile), Charsets.UTF_8)); + validatorConnection = createConnection(orgname + "-validator", Files.toString(new File(keyfile), Charsets.UTF_8)); } - private RestContext createConnection( - String identity, String key) throws IOException { + private RestContext createConnection(String identity, String key) + throws IOException { Properties props = new Properties(); return new RestContextFactory().createContext("opscodeplatform", identity, key, ImmutableSet . of(new Log4JLoggingModule()), props); @@ -88,7 +87,8 @@ public class OpscodePlatformClientLiveTest { @Test(dependsOnMethods = "testListClientsInOrg") public void testCreateClientInOrg() throws Exception { validatorConnection.getApi().getChefClientForOrg(orgname).deleteClient(PREFIX); - clientKey = validatorConnection.getApi().getChefClientForOrg(orgname).createClient(PREFIX); + clientKey = validatorConnection.utils().encryption().toPem( + validatorConnection.getApi().getChefClientForOrg(orgname).createClient(PREFIX).getPrivateKey()); assertNotNull(clientKey); System.out.println(clientKey); clientConnection = createConnection(PREFIX, clientKey); @@ -97,8 +97,8 @@ public class OpscodePlatformClientLiveTest { @Test(dependsOnMethods = "testCreateClientInOrg") public void testGenerateKeyForClientInOrg() throws Exception { - clientKey = validatorConnection.getApi().getChefClientForOrg(orgname).generateKeyForClient( - PREFIX); + clientKey = validatorConnection.utils().encryption().toPem( + validatorConnection.getApi().getChefClientForOrg(orgname).createClient(PREFIX).getPrivateKey()); assertNotNull(clientKey); clientConnection.close(); clientConnection = createConnection(PREFIX, clientKey); diff --git a/rackspace/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesClientLiveTest.java b/rackspace/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesClientLiveTest.java index 7219cc9a85..bd548d9137 100644 --- a/rackspace/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesClientLiveTest.java +++ b/rackspace/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesClientLiveTest.java @@ -27,7 +27,6 @@ import static org.testng.Assert.fail; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; -import java.security.NoSuchAlgorithmException; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -36,8 +35,6 @@ import java.util.concurrent.TimeoutException; import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.domain.PageSet; import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest; -import org.jclouds.encryption.EncryptionService; -import org.jclouds.encryption.internal.JCEEncryptionService; import org.jclouds.http.HttpResponseException; import org.jclouds.http.options.GetOptions; import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata; @@ -52,7 +49,6 @@ import org.jclouds.util.Utils; import org.testng.annotations.Test; import com.google.common.base.Predicate; -import com.google.common.base.Throwables; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; @@ -64,15 +60,6 @@ import com.google.common.collect.Maps; @Test(groups = "live", testName = "cloudfiles.CloudFilesClientLiveTest") public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { - protected volatile static EncryptionService encryptionService; - static { - try { - encryptionService = new JCEEncryptionService(); - } catch (NoSuchAlgorithmException e) { - Throwables.propagate(e); - } - } - public CloudFilesClient getApi() { return (CloudFilesClient) context.getProviderSpecificContext().getApi(); } @@ -116,7 +103,7 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { try { cdnMetadata = getApi().getCDNMetadata(containerNameWithoutCDN); assert cdnMetadata == null || !cdnMetadata.isCDNEnabled() : containerNameWithoutCDN - + " should not have metadata"; + + " should not have metadata"; } catch (ContainerNotFoundException e) { } catch (HttpResponseException e) { } @@ -144,8 +131,8 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { })); cdnMetadataList = getApi().listCDNContainers( - ListCdnContainerOptions.Builder.afterMarker( - containerNameWithCDN.substring(0, containerNameWithCDN.length() - 1)).maxResults(1)); + ListCdnContainerOptions.Builder.afterMarker( + containerNameWithCDN.substring(0, containerNameWithCDN.length() - 1)).maxResults(1)); assertEquals(cdnMetadataList.size(), 1); // Enable CDN with PUT for the same container, this time with a custom @@ -196,7 +183,7 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { // Create test containers String[] containerNames = new String[] { containerPrefix + ".testListOwnedContainers1", - containerPrefix + ".testListOwnedContainers2" }; + containerPrefix + ".testListOwnedContainers2" }; assertTrue(getApi().createContainer(containerNames[0])); assertTrue(getApi().createContainer(containerNames[1])); @@ -208,8 +195,8 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { // Test listing with options response = getApi().listContainers( - ListContainerOptions.Builder.afterMarker(containerNames[0].substring(0, containerNames[0].length() - 1)) - .maxResults(1)); + ListContainerOptions.Builder.afterMarker( + containerNames[0].substring(0, containerNames[0].length() - 1)).maxResults(1)); assertEquals(response.size(), 1); assertEquals(Iterables.get(response, 0).getName(), containerNames[0]); @@ -259,8 +246,8 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { // List only the container just created, using a marker with the // container name less 1 char Set response = getApi().listContainers( - ListContainerOptions.Builder.afterMarker(containerName1.substring(0, containerName1.length() - 1)) - .maxResults(1)); + ListContainerOptions.Builder.afterMarker(containerName1.substring(0, containerName1.length() - 1)) + .maxResults(1)); assertNotNull(response); assertEquals(response.size(), 1); assertEquals(Iterables.get(response, 0).getName(), containerName + ".hello"); @@ -316,7 +303,8 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { byte[] md5 = object.getPayload().getContentMD5(); String newEtag = getApi().putObject(containerName, object); assert newEtag != null; - assertEquals(encryptionService.hex(md5), encryptionService.hex(object.getPayload().getContentMD5())); + assertEquals(context.utils().encryption().hex(md5), context.utils().encryption().hex( + object.getPayload().getContentMD5())); // Test HEAD of missing object assert getApi().getObjectInfo(containerName, "non-existent-object") == null; @@ -332,8 +320,8 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { // assertEquals(metadata.getBytes(), new Long(data.length())); // assertEquals(metadata.getContentType(), "text/plain"); - assertEquals(encryptionService.hex(md5), encryptionService.hex(metadata.getHash())); - assertEquals(metadata.getHash(), encryptionService.fromHex(newEtag)); + assertEquals(context.utils().encryption().hex(md5), context.utils().encryption().hex(metadata.getHash())); + assertEquals(metadata.getHash(), context.utils().encryption().fromHex(newEtag)); assertEquals(metadata.getMetadata().entrySet().size(), 1); assertEquals(metadata.getMetadata().get("metadata"), "metadata-value"); @@ -352,8 +340,9 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { // object.getMetadata().getName()); assertEquals(getBlob.getInfo().getBytes(), new Long(data.length())); assertEquals(getBlob.getInfo().getContentType(), "text/plain"); - assertEquals(encryptionService.hex(md5), encryptionService.hex(getBlob.getInfo().getHash())); - assertEquals(encryptionService.fromHex(newEtag), getBlob.getInfo().getHash()); + assertEquals(context.utils().encryption().hex(md5), context.utils().encryption().hex( + getBlob.getInfo().getHash())); + assertEquals(context.utils().encryption().fromHex(newEtag), getBlob.getInfo().getHash()); assertEquals(getBlob.getInfo().getMetadata().entrySet().size(), 2); assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-1"), "value-1"); assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-2"), "value-2"); @@ -362,7 +351,7 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { // transit) String correctEtag = newEtag; String incorrectEtag = "0" + correctEtag.substring(1); - object.getInfo().setHash(encryptionService.fromHex(incorrectEtag)); + object.getInfo().setHash(context.utils().encryption().fromHex(incorrectEtag)); try { getApi().putObject(containerName, object); } catch (HttpResponseException e) { @@ -375,21 +364,23 @@ public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { blob.getInfo().setName("chunked-object"); blob.setPayload(bais); newEtag = getApi().putObject(containerName, blob); - assertEquals(encryptionService.hex(md5), encryptionService.hex(getBlob.getInfo().getHash())); + assertEquals(context.utils().encryption().hex(md5), context.utils().encryption().hex( + getBlob.getInfo().getHash())); // Test GET with options // Non-matching ETag try { getApi() - .getObject(containerName, object.getInfo().getName(), GetOptions.Builder.ifETagDoesntMatch(newEtag)); + .getObject(containerName, object.getInfo().getName(), + GetOptions.Builder.ifETagDoesntMatch(newEtag)); } catch (HttpResponseException e) { assertEquals(e.getResponse().getStatusCode(), 304); } // Matching ETag getBlob = getApi().getObject(containerName, object.getInfo().getName(), - GetOptions.Builder.ifETagMatches(newEtag)); - assertEquals(getBlob.getInfo().getHash(), encryptionService.fromHex(newEtag)); + GetOptions.Builder.ifETagMatches(newEtag)); + assertEquals(getBlob.getInfo().getHash(), context.utils().encryption().fromHex(newEtag)); getBlob = getApi().getObject(containerName, object.getInfo().getName(), GetOptions.Builder.startAt(8)); assertEquals(Utils.toStringAndClose(getBlob.getPayload().getInput()), data.substring(8));