Add support for credentials of type "certificate".

This commit is contained in:
Scott Frederick
2017-05-22 17:26:55 -05:00
parent 0cfe0385b8
commit ec17ba7ba7
9 changed files with 442 additions and 16 deletions

View File

@@ -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())
);
}

View File

@@ -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;
}
}

View File

@@ -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<CertificateCredential> {
/**
* 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<CertificateCredential, CertificateWriteRequest, CertificateWriteRequestBuilder> {
@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;
}
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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");

View File

@@ -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<CertificateCredential> {
private static final CertificateCredential CREDENTIAL =
new CertificateCredential("certificate", "authority", "private-key");
@DataPoints("detail-responses")
public static List<ResponseEntity<CredentialDetails<CertificateCredential>>> buildDetailResponses() {
return buildDetailResponses(ValueType.CERTIFICATE, CREDENTIAL);
}
@DataPoints("data-responses")
public static List<ResponseEntity<CredentialDetailsData<CertificateCredential>>> buildDataResponses() {
return buildDataResponses(ValueType.CERTIFICATE, CREDENTIAL);
}
@Override
public WriteRequest<CertificateCredential> getRequest() {
return CertificateWriteRequest.builder()
.name(NAME)
.value(CREDENTIAL)
.build();
}
@Override
public Class<CertificateCredential> getType() {
return CertificateCredential.class;
}
@Theory
public void write(@FromDataPoints("detail-responses")
ResponseEntity<CredentialDetails<CertificateCredential>> expectedResponse) {
verifyWrite(expectedResponse);
}
@Theory
public void getById(@FromDataPoints("detail-responses")
ResponseEntity<CredentialDetails<CertificateCredential>> expectedResponse) {
verifyGetById(expectedResponse);
}
@Theory
public void getByNameWithString(@FromDataPoints("data-responses")
ResponseEntity<CredentialDetailsData<CertificateCredential>> expectedResponse) {
verifyGetByNameWithString(expectedResponse);
}
@Theory
public void getByNameWithCredentialName(@FromDataPoints("data-responses")
ResponseEntity<CredentialDetailsData<CertificateCredential>> expectedResponse) {
verifyGetByNameWithCredentialName(expectedResponse);
}
}

View File

@@ -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<CertificateCredential> 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<CertificateCredential> 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<CertificateCredential> data = parseDetails(credentials, CertificateCredential.class);
assertDetails(data, null, "authority", "private-key");
}
@Test
public void deserializeDetailsData() throws Exception {
CredentialDetailsData<CertificateCredential> response = parseDetailsData(CERT_CREDENTIALS, CertificateCredential.class);
assertThat(response.getData().size(), equalTo(1));
CredentialDetails<CertificateCredential> data = response.getData().get(0);
assertDetails(data, "cert", "authority", "private-key");
}
private void assertDetails(CredentialDetails<CertificateCredential> 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));
}
}

View File

@@ -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);
}
}