From eafdeb5266f934b24ee5ec560606c76ae56a62c8 Mon Sep 17 00:00:00 2001 From: Adrian Bravo Date: Mon, 27 Jul 2015 11:29:44 -0700 Subject: [PATCH] JCLOUDS-967: Chef Client Model update with public key attribute JCLOUDS-967: Made PubKey nullable and added tests to verify serialization works with and without it --- .../java/org/jclouds/chef/domain/Client.java | 28 ++++++++-- .../functions/ParseClientFromJsonTest.java | 53 +++++++++++++++---- .../src/test/resources/client-no-pub-key.json | 8 +++ apis/chef/src/test/resources/client.json | 5 +- 4 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 apis/chef/src/test/resources/client-no-pub-key.json diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Client.java b/apis/chef/src/main/java/org/jclouds/chef/domain/Client.java index 733560987d..53bd1f2892 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/domain/Client.java +++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Client.java @@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.beans.ConstructorProperties; import java.security.PrivateKey; +import java.security.PublicKey; import java.security.cert.X509Certificate; import org.jclouds.javax.annotation.Nullable; @@ -41,6 +42,7 @@ public class Client { private String clientname; private String name; private boolean validator; + private PublicKey publicKey; public Builder certificate(X509Certificate certificate) { this.certificate = checkNotNull(certificate, "certificate"); @@ -52,6 +54,11 @@ public class Client { return this; } + public Builder publicKey(PublicKey publicKey) { + this.publicKey = checkNotNull(publicKey, "publicKey"); + return this; + } + public Builder orgname(String orgname) { this.orgname = checkNotNull(orgname, "orgname"); return this; @@ -73,27 +80,34 @@ public class Client { } public Client build() { - return new Client(certificate, orgname, clientname, name, validator, privateKey); + return new Client(certificate, orgname, clientname, name, validator, privateKey, publicKey); } } private final X509Certificate certificate; @SerializedName("private_key") private final PrivateKey privateKey; + @SerializedName("public_key") + private final PublicKey publicKey; private final String orgname; private final String clientname; private final String name; private final boolean validator; - @ConstructorProperties({ "certificate", "orgname", "clientname", "name", "validator", "private_key" }) + @ConstructorProperties({ "certificate", "orgname", "clientname", "name", "validator", "private_key", "public_key"}) protected Client(X509Certificate certificate, String orgname, String clientname, String name, boolean validator, - @Nullable PrivateKey privateKey) { + @Nullable PrivateKey privateKey, @Nullable PublicKey publicKey) { this.certificate = certificate; this.orgname = orgname; this.clientname = clientname; this.name = name; this.validator = validator; this.privateKey = privateKey; + this.publicKey = publicKey; + } + + public PublicKey getPublicKey() { + return publicKey; } public PrivateKey getPrivateKey() { @@ -129,6 +143,7 @@ public class Client { 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 + ((publicKey == null) ? 0 : publicKey.hashCode()); result = prime * result + (validator ? 1231 : 1237); return result; } @@ -162,6 +177,11 @@ public class Client { return false; } else if (!orgname.equals(other.orgname)) return false; + if (publicKey == null) { + if (other.publicKey != null) + return false; + } else if (!publicKey.equals(other.publicKey)) + return false; if (privateKey == null) { if (other.privateKey != null) return false; @@ -175,7 +195,7 @@ public class Client { @Override public String toString() { return "Client [name=" + name + ", clientname=" + clientname + ", orgname=" + orgname + ", isValidator=" - + validator + ", certificate=" + certificate + ", privateKey=" + (privateKey == null ? "not " : "") + + validator + ", certificate=" + certificate + ", publicKey=" + publicKey + ", privateKey=" + (privateKey == null ? "not " : "") + "present]"; } diff --git a/apis/chef/src/test/java/org/jclouds/chef/functions/ParseClientFromJsonTest.java b/apis/chef/src/test/java/org/jclouds/chef/functions/ParseClientFromJsonTest.java index e218af59a1..70a38a1204 100644 --- a/apis/chef/src/test/java/org/jclouds/chef/functions/ParseClientFromJsonTest.java +++ b/apis/chef/src/test/java/org/jclouds/chef/functions/ParseClientFromJsonTest.java @@ -21,6 +21,7 @@ import static org.testng.Assert.assertEquals; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.PublicKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; @@ -50,6 +51,9 @@ import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.spec.RSAPublicKeySpec; + /** * Tests behavior of {@code ParseClientFromJson} */ @@ -62,9 +66,10 @@ public class ParseClientFromJsonTest { private Crypto crypto; private PrivateKey privateKey; private X509Certificate certificate; + private PublicKey publicKey; @BeforeTest - protected void setUpInjector() throws IOException, CertificateException, InvalidKeySpecException { + protected void setUpInjector() throws IOException, CertificateException, InvalidKeySpecException, NoSuchAlgorithmException { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { @@ -77,23 +82,51 @@ public class ParseClientFromJsonTest { crypto = injector.getInstance(Crypto.class); certificate = Pems.x509Certificate(ByteSource.wrap(CERTIFICATE.getBytes(Charsets.UTF_8)), null); privateKey = crypto.rsaKeyFactory().generatePrivate(Pems.privateKeySpec(ByteSource.wrap(PRIVATE_KEY.getBytes(Charsets.UTF_8)))); + + RSAPrivateCrtKey privk = (RSAPrivateCrtKey)privateKey; + RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(), privk.getPublicExponent()); + publicKey = crypto.rsaKeyFactory().generatePublic(publicKeySpec); } @SuppressWarnings("resource") - public void test() throws IOException, CertificateException, NoSuchAlgorithmException { + public void testClientWithPubKey() throws IOException, CertificateException, NoSuchAlgorithmException { Client user = Client.builder().certificate(certificate).orgname("jclouds").clientname("adriancole-jcloudstest") - .name("adriancole-jcloudstest").isValidator(false).privateKey(privateKey).build(); - - byte[] encrypted = ByteStreams2.toByteArrayAndClose(new RSAEncryptingPayload(new JCECrypto(), Payloads.newPayload("fooya"), user - .getCertificate().getPublicKey()).openStream()); - - assertEquals( - ByteStreams2.toByteArrayAndClose(new RSADecryptingPayload(new JCECrypto(), Payloads.newPayload(encrypted), user.getPrivateKey()).openStream()), - "fooya".getBytes()); + .name("adriancole-jcloudstest").isValidator(false).privateKey(privateKey).publicKey(publicKey).build(); assertEquals( handler.apply(HttpResponse.builder().statusCode(200).message("ok") .payload(ParseClientFromJsonTest.class.getResourceAsStream("/client.json")).build()), user); } + + @SuppressWarnings("resource") + public void testClientWithoutPubKey() throws IOException, CertificateException, NoSuchAlgorithmException { + + Client user = Client.builder().certificate(certificate).orgname("jclouds").clientname("adriancole-jcloudstest") + .name("adriancole-jcloudstest").isValidator(false).privateKey(privateKey).build(); + + assertEquals( + handler.apply(HttpResponse.builder().statusCode(200).message("ok") + .payload(ParseClientFromJsonTest.class.getResourceAsStream("/client-no-pub-key.json")).build()), user); + } + + @SuppressWarnings("resource") + public void testEncryptionWithClientPriv() throws IOException, CertificateException, NoSuchAlgorithmException { + + Client user = Client.builder().certificate(certificate).orgname("jclouds").clientname("adriancole-jcloudstest") + .name("adriancole-jcloudstest").isValidator(false).privateKey(privateKey).publicKey(publicKey).build(); + + byte[] encrypted = ByteStreams2.toByteArrayAndClose(new RSAEncryptingPayload(new JCECrypto(), Payloads.newPayload("fooya"), user + .getCertificate().getPublicKey()).openStream()); + + assertEquals( + ByteStreams2.toByteArrayAndClose(new RSADecryptingPayload(new JCECrypto(), Payloads.newPayload(encrypted), user.getPrivateKey()).openStream()), + "fooya".getBytes()); + + assertEquals( + handler.apply(HttpResponse.builder().statusCode(200).message("ok") + .payload(ParseClientFromJsonTest.class.getResourceAsStream("/client.json")).build()), user); + } + + } diff --git a/apis/chef/src/test/resources/client-no-pub-key.json b/apis/chef/src/test/resources/client-no-pub-key.json new file mode 100644 index 0000000000..a9ba95229c --- /dev/null +++ b/apis/chef/src/test/resources/client-no-pub-key.json @@ -0,0 +1,8 @@ +{ "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", + "clientname" : "adriancole-jcloudstest", + "name" : "adriancole-jcloudstest", + "orgname" : "jclouds", + "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", + "uri" : "https://api.opscode.com/organizations/jclouds/clients/adriancole-jcloudstest", + "validator" : false +} diff --git a/apis/chef/src/test/resources/client.json b/apis/chef/src/test/resources/client.json index eef7c71574..afce282ee2 100644 --- a/apis/chef/src/test/resources/client.json +++ b/apis/chef/src/test/resources/client.json @@ -4,5 +4,6 @@ "orgname" : "jclouds", "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", "uri" : "https://api.opscode.com/organizations/jclouds/clients/adriancole-jcloudstest", - "validator" : false -} \ No newline at end of file + "validator" : false, + "public_key" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyb2ZJJqGm0KKR+8nfQJN\nsSd+F9tXNMV7CfOcW6jsqs8EZgiVR09hD1IYOj4YqM0qJONlgyg4xRWewdSG7QTP\nj1lJpVAida9sXy2+kzyagZA1Am0OZcbqb5hoeIDgcX+eDa79s0u0DomjcfO9EKhv\nHLBz+zM+3QqPRkPV8nYTbfs+HjVzzOU6D1B0XR3+IPZZl2AnWs2d0qhnStHcDUvn\nRVQ0P482YwN9VgceOZtpPz0DCKEJ5Tx5STub8k0/zt/VAMHQafLSuQMLd2s4ZLuO\nZptN//uAsTmxireqd37z+8ZTdBbJ8LEpJ+iCXuSfm5aUh7iw6oxvToY2AL53+jK2\nUQIDAQAB\n-----END PUBLIC KEY-----\n" +}