diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/core/JsonUtils.java b/spring-credhub-core/src/main/java/org/springframework/credhub/core/JsonUtils.java index 977bdd1..6a4ca03 100644 --- a/spring-credhub-core/src/main/java/org/springframework/credhub/core/JsonUtils.java +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/core/JsonUtils.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.util.ISO8601DateFormat; +import org.springframework.credhub.support.CertificateCredential; import org.springframework.credhub.support.JsonCredential; import org.springframework.credhub.support.PasswordCredential; import org.springframework.credhub.support.RsaCredential; @@ -68,6 +69,7 @@ public class JsonUtils { new NamedType(UserCredential.class, ValueType.USER.type()), new NamedType(RsaCredential.class, ValueType.RSA.type()), new NamedType(SshCredential.class, ValueType.SSH.type()), + new NamedType(CertificateCredential.class, ValueType.CERTIFICATE.type()), new NamedType(JsonCredential.class, ValueType.JSON.type()) ); } diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/CertificateCredential.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CertificateCredential.java new file mode 100644 index 0000000..83a2d57 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CertificateCredential.java @@ -0,0 +1,90 @@ +/* + * Copyright 2016-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.credhub.support; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.springframework.util.Assert; + +/** + * A certificate credential consists of a certificate, a certificate authority, and a private key. At least + * one of these three values must be provided. + * + * @author Scott Frederick + */ +public class CertificateCredential { + private final String certificate; + @JsonProperty("ca") + private final String certificateAuthority; + private final String privateKey; + + /** + * Create an empty {@link CertificateCredential}. Intended to be used internally for deserialization of responses. + */ + private CertificateCredential() { + certificate = null; + certificateAuthority = null; + privateKey = null; + } + + /** + * Create an {@link CertificateCredential} from the provided public and private key. At least one of the key + * values must not be {@literal null}. + * + * @param certificate the certificate value; may be {@literal null} if one of the other parameters + * is not {@literal null} + * @param certificateAuthority the certificate authority value; may be {@literal null} if one of + * the other parameters is not {@literal null} + * @param privateKey the private key; may be {@literal null} if one of the other parameters is + * not {@literal null} + */ + public CertificateCredential(String certificate, String certificateAuthority, String privateKey) { + Assert.isTrue(certificate != null || certificateAuthority != null || privateKey != null, + "at least one of certificate, certificateAuthority, or privateKey must not be null"); + this.certificate = certificate; + this.certificateAuthority = certificateAuthority; + this.privateKey = privateKey; + } + + + /** + * Get the certificate value. + * + * @return the certificate + */ + public String getCertificate() { + return certificate; + } + + /** + * Get the certificate authority value. + * + * @return the certificate authority + */ + public String getCertificateAuthority() { + return certificateAuthority; + } + + /** + * Get the private key value. + * + * @return the private key + */ + public String getPrivateKey() { + return privateKey; + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/CertificateWriteRequest.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CertificateWriteRequest.java new file mode 100644 index 0000000..25b71c2 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CertificateWriteRequest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.credhub.support; + +import org.springframework.util.Assert; + +import static org.springframework.credhub.support.ValueType.CERTIFICATE; + +/** + * The details of a request to write a new or update an existing {@link CertificateCredential} in CredHub. + * + * @author Scott Frederick + */ +public class CertificateWriteRequest extends WriteRequest { + /** + * Create a builder that provides a fluent API for providing the values required + * to construct a {@link CertificateWriteRequest}. + * + * @return a builder + */ + public static CertificateWriteRequestBuilder builder() { + return new CertificateWriteRequestBuilder(); + } + + /** + * A builder that provides a fluent API for constructing {@link CertificateWriteRequest}s. + */ + public static class CertificateWriteRequestBuilder + extends WriteRequestBuilder { + @Override + protected CertificateWriteRequest createTarget() { + return new CertificateWriteRequest(); + } + + @Override + protected CertificateWriteRequestBuilder createBuilder() { + return this; + } + + /** + * Set the value of an certificate credential. + * + * @param value the credential value; must not be {@literal null} + * @return the builder + */ + public CertificateWriteRequestBuilder value(CertificateCredential value) { + Assert.notNull(value, "value must not be null"); + targetObj.setType(CERTIFICATE); + targetObj.setValue(value); + return this; + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/RsaCredential.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/RsaCredential.java index dd0a84f..8f6ccc5 100644 --- a/spring-credhub-core/src/main/java/org/springframework/credhub/support/RsaCredential.java +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/RsaCredential.java @@ -33,8 +33,8 @@ public class RsaCredential extends KeyPairCredential { * Create an {@link RsaCredential} from the provided public and private key. At least one of the key * values must not be {@literal null}. * - * @param publicKey the public key - * @param privateKey the private key + * @param publicKey the public key; may be {@literal null} only if {@literal privateKey} is not null + * @param privateKey the private key; may be {@literal null} only if {@literal publicKey} is not null */ public RsaCredential(String publicKey, String privateKey) { super(publicKey, privateKey); diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/SshCredential.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/SshCredential.java index c4a5b7b..f5e95d5 100644 --- a/spring-credhub-core/src/main/java/org/springframework/credhub/support/SshCredential.java +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/SshCredential.java @@ -33,8 +33,8 @@ public class SshCredential extends KeyPairCredential { * Create an {@link SshCredential} from the provided public and private key. At least one of the key * values must not be {@literal null}. * - * @param publicKey the public key - * @param privateKey the private key + * @param publicKey the public key; may be {@literal null} only if {@literal privateKey} is not null + * @param privateKey the private key; may be {@literal null} only if {@literal publicKey} is not null */ public SshCredential(String publicKey, String privateKey) { super(publicKey, privateKey); diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/ValueType.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ValueType.java index 156804b..56e3e39 100644 --- a/spring-credhub-core/src/main/java/org/springframework/credhub/support/ValueType.java +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ValueType.java @@ -25,38 +25,37 @@ import com.fasterxml.jackson.annotation.JsonCreator; */ public enum ValueType { /** - * A password credential consists of a single string value. The password value - * is provided by the client (i.e. not generated by CredHub). + * Indicates a credential of type {@link PasswordCredential}. */ PASSWORD("password"), /** - * A value credential consists of a single string value. The value - * is provided by the client (i.e. not generated by CredHub). + * Indicates a credential of type {@link ValueCredential}. */ VALUE("value"), /** - * A user credential consists of a username and password. The values - * are provided by the client. + * Indicates a credential of type {@link UserCredential}. */ USER("user"), /** - * An RSA credential consists of a private key and/or public key. The values - * are provided by the client. + * Indicates a credential of type {@link RsaCredential}. */ RSA("rsa"), /** - * An SSH credential consists of a private key and/or public key. The values - * are provided by the client. + * Indicates a credential of type {@link SshCredential}. */ SSH("ssh"), /** - * A JSON credential consists of one or more fields in a JSON document. The keys and - * values in the JSON document are determined by the client. + * Indicates a credential of type {@link CertificateCredential}. + */ + CERTIFICATE("certificate"), + + /** + * Indicates a credential of type {@link JsonCredential}. */ JSON("json"); diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubTemplateDetailCertificateUnitTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubTemplateDetailCertificateUnitTests.java new file mode 100644 index 0000000..82e0922 --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubTemplateDetailCertificateUnitTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2016-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.credhub.core; + +import java.util.List; + +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.FromDataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +import org.springframework.credhub.support.CertificateWriteRequest; +import org.springframework.credhub.support.CredentialDetails; +import org.springframework.credhub.support.CredentialDetailsData; +import org.springframework.credhub.support.CertificateCredential; +import org.springframework.credhub.support.ValueType; +import org.springframework.credhub.support.WriteRequest; +import org.springframework.http.ResponseEntity; + +@RunWith(Theories.class) +public class CredHubTemplateDetailCertificateUnitTests + extends CredHubTemplateDetailUnitTestsBase { + private static final CertificateCredential CREDENTIAL = + new CertificateCredential("certificate", "authority", "private-key"); + + @DataPoints("detail-responses") + public static List>> buildDetailResponses() { + return buildDetailResponses(ValueType.CERTIFICATE, CREDENTIAL); + } + + @DataPoints("data-responses") + public static List>> buildDataResponses() { + return buildDataResponses(ValueType.CERTIFICATE, CREDENTIAL); + } + + @Override + public WriteRequest getRequest() { + return CertificateWriteRequest.builder() + .name(NAME) + .value(CREDENTIAL) + .build(); + } + + @Override + public Class getType() { + return CertificateCredential.class; + } + + @Theory + public void write(@FromDataPoints("detail-responses") + ResponseEntity> expectedResponse) { + verifyWrite(expectedResponse); + } + + @Theory + public void getById(@FromDataPoints("detail-responses") + ResponseEntity> expectedResponse) { + verifyGetById(expectedResponse); + } + + @Theory + public void getByNameWithString(@FromDataPoints("data-responses") + ResponseEntity> expectedResponse) { + verifyGetByNameWithString(expectedResponse); + } + + @Theory + public void getByNameWithCredentialName(@FromDataPoints("data-responses") + ResponseEntity> expectedResponse) { + verifyGetByNameWithCredentialName(expectedResponse); + } +} \ No newline at end of file diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/support/CertificateCredentialDetailsUnitTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/support/CertificateCredentialDetailsUnitTests.java new file mode 100644 index 0000000..33060e9 --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/support/CertificateCredentialDetailsUnitTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.credhub.support; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +public class CertificateCredentialDetailsUnitTests extends JsonParsingUnitTestsBase { + private static final String CERT_CREDENTIALS = + " \"type\": \"certificate\"," + + " \"value\": {" + + " \"certificate\": \"cert\"," + + " \"ca\": \"authority\"," + + " \"private_key\": \"private-key\"" + + " }"; + + @Test + public void deserializeDetailsWithAllValues() throws Exception { + CredentialDetails data = parseDetails(CERT_CREDENTIALS, CertificateCredential.class); + + assertDetails(data, "cert", "authority", "private-key"); + } + + @Test + public void deserializeDetailsCertOnly() throws Exception { + final String credentials = + " \"type\": \"certificate\"," + + " \"value\": {" + + " \"certificate\": \"cert\"" + + " }"; + CredentialDetails data = parseDetails(credentials, CertificateCredential.class); + + assertDetails(data, "cert", null, null); + } + + @Test + public void deserializeDetailsWithNoCert() throws Exception { + final String credentials = + " \"type\": \"certificate\"," + + " \"value\": {" + + " \"ca\": \"authority\"," + + " \"private_key\": \"private-key\"" + + " }"; + CredentialDetails data = parseDetails(credentials, CertificateCredential.class); + + assertDetails(data, null, "authority", "private-key"); + } + + @Test + public void deserializeDetailsData() throws Exception { + CredentialDetailsData response = parseDetailsData(CERT_CREDENTIALS, CertificateCredential.class); + + assertThat(response.getData().size(), equalTo(1)); + + CredentialDetails data = response.getData().get(0); + + assertDetails(data, "cert", "authority", "private-key"); + } + + private void assertDetails(CredentialDetails data, + String certificate, String ca, String privateKey) { + assertCommonDetails(data); + + assertThat(data.getValueType(), equalTo(ValueType.CERTIFICATE)); + assertThat(data.getValue().getCertificate(), equalTo(certificate)); + assertThat(data.getValue().getCertificateAuthority(), equalTo(ca)); + assertThat(data.getValue().getPrivateKey(), equalTo(privateKey)); + } +} diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/support/CertificateWriteRequestUnitTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/support/CertificateWriteRequestUnitTests.java new file mode 100644 index 0000000..001f139 --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/support/CertificateWriteRequestUnitTests.java @@ -0,0 +1,96 @@ +/* + * Copyright 2016-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.credhub.support; + +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.valid4j.matchers.jsonpath.JsonPathMatchers.hasJsonPath; +import static org.valid4j.matchers.jsonpath.JsonPathMatchers.hasNoJsonPath; + +public class CertificateWriteRequestUnitTests extends WriteRequestUnitTestsBase { + @Before + public void setUp() { + buildRequest(new CertificateCredential("cert", "ca", "private-key")); + } + + @Test + public void serializeWithAllValues() throws Exception { + String jsonValue = serializeToJson(requestBuilder); + + assertThat(jsonValue, + allOf(hasJsonPath("$.overwrite", equalTo(true)), + hasJsonPath("$.name", equalTo("/c/example/credential")), + hasJsonPath("$.type", equalTo("certificate")), + hasJsonPath("$.value.certificate", equalTo("cert")), + hasJsonPath("$.value.ca", equalTo("ca")), + hasJsonPath("$.value.private_key", equalTo("private-key")))); + + assertThat(jsonValue, hasNoJsonPath("$.additional_permissions")); + } + + @Test + public void serializeWithCertOnly() throws Exception { + buildRequest(new CertificateCredential("cert", null, null)); + + String jsonValue = serializeToJson(requestBuilder); + + assertThat(jsonValue, + allOf(hasJsonPath("$.overwrite", equalTo(true)), + hasJsonPath("$.name", equalTo("/c/example/credential")), + hasJsonPath("$.type", equalTo("certificate")), + hasJsonPath("$.value.certificate", equalTo("cert")), + hasNoJsonPath("$.value.ca"), + hasNoJsonPath("$.value.private_key"))); + + assertThat(jsonValue, hasNoJsonPath("$.additional_permissions")); + } + + @Test + public void serializeWithNoCert() throws Exception { + buildRequest(new CertificateCredential(null, "ca", "private-key")); + + String jsonValue = serializeToJson(requestBuilder); + + assertThat(jsonValue, + allOf(hasJsonPath("$.overwrite", equalTo(true)), + hasJsonPath("$.name", equalTo("/c/example/credential")), + hasJsonPath("$.type", equalTo("certificate")), + hasNoJsonPath("$.value.certificate"), + hasJsonPath("$.value.ca", equalTo("ca")), + hasJsonPath("$.value.private_key", equalTo("private-key")))); + + assertThat(jsonValue, hasNoJsonPath("$.additional_permissions")); + } + + @Test(expected = IllegalArgumentException.class) + public void serializeWithNoValues() throws Exception { + buildRequest(new CertificateCredential(null, null, null)); + + String jsonValue = serializeToJson(requestBuilder); + } + + private void buildRequest(CertificateCredential value) { + requestBuilder = CertificateWriteRequest.builder() + .name(new SimpleCredentialName("example", "credential")) + .overwrite(true) + .value(value); + } +} \ No newline at end of file