commit b5708f0df8de67b97a6724bcf317e2c6a09441c0 Author: Scott Frederick Date: Fri Apr 21 16:16:41 2017 -0500 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2f370a --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +*.ipr +*.iws +.idea + +.classpath +.project +.settings + +.checkstyle + +bin +build +out +target \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3e58748 --- /dev/null +++ b/pom.xml @@ -0,0 +1,716 @@ + + + 4.0.0 + + org.springframework.credhub + spring-credhub-parent + 1.0.0.BUILD-SNAPSHOT + + Spring CredHub + Parent project for Spring CredHub + pom + + http://projects.spring.io/spring-credhub/ + + + spring-credhub-dependencies + spring-credhub-core + + + + UTF-8 + 4.3.7.RELEASE + 1.6 + multi + spring-credhub + ${basedir} + + + 2017 + + + Pivotal Software, Inc. + http://www.spring.io + + + + https://github.com/spring-projects/spring-credhub + scm:git:git://github.com/spring-projects/spring-credhub.git + scm:git:ssh://git@github.com/spring-projects/spring-credhub.git + HEAD + + + + GitHub + https://github.com/spring-projects/spring-credhub/issues + + + + Bamboo + https://build.spring.io/browse/SPRINGCREDHUB-CREDHUB + + + + + sfrederick + Scott Frederick + + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + + Copyright 2016 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. + + + + + + + + + org.springframework.credhub + spring-credhub-dependencies + 1.0.0.BUILD-SNAPSHOT + import + pom + + + + + org.springframework + spring-framework-bom + ${spring.version} + pom + import + + + + + junit + junit + 4.12 + test + + + + org.mockito + mockito-core + 2.7.22 + test + + + + org.springframework + spring-test + ${spring.version} + test + + + + org.assertj + assertj-core + 3.6.1 + test + + + + org.valid4j + json-path-matchers + 1.1 + test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*Tests.java + + + **/Abstract*.java + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + org.codehaus.mojo.signature + java16 + 1.0 + + + + + enforce-java-6 + test + + check + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + + org.apache.maven.plugins + maven-site-plugin + 3.5.1 + + + org.apache.maven.wagon + wagon-ssh + 1.0 + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.15 + + + + org.asciidoctor + asciidoctor-maven-plugin + 1.5.3 + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.6 + + + + org.codehaus.mojo + wagon-maven-plugin + 1.0 + + + + + + + + + + + springNext + + + 4.3.8.BUILD-SNAPSHOT + + + + + spring-libs-snapshot + https://repo.spring.io/libs-snapshot + + true + + + + + + + spring5 + + + 5.0.0.BUILD-SNAPSHOT + + + + + spring-libs-snapshot + https://repo.spring.io/libs-snapshot + + true + + + + + + + + milestone + + + + repo.spring.io + Spring Milestone Repository + http://repo.spring.io/libs-milestone-local + + + + + + + + + + ci + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + jar + + package + + + + + + + + + + + + + + distribute + + + ${project.build.directory}/shared-resources + true + true + + + + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-shared-resources + + unpack-dependencies + + generate-resources + + + + org.springframework.data + spring-data-build-resources + zip + true + ${shared.resources} + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + aggregate-javadoc + + aggregate + + package + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + copy-documentation-resources + generate-resources + + + + + + + + + + + + + + + + + + + + run + + + + + collect-schema-files + process-resources + + + + + + + + + + + + run + + + + + rename-reference-docs + process-resources + + + + + + + + run + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true +
${project.name}
+ ${java.version} + true + ${shared.resources}/javadoc + ${shared.resources}/javadoc/overview.html + ${shared.resources}/javadoc/spring-javadoc.css + + true + -Xdoclint:none + + http://docs.spring.io/spring/docs/current/javadoc-api/ + http://docs.oracle.com/javase/6/docs/api + +
+
+ + + + + org.asciidoctor + asciidoctor-maven-plugin + + + org.asciidoctor + asciidoctorj-pdf + 1.5.0-alpha.11 + + + org.asciidoctor + asciidoctorj-epub3 + 1.5.0-alpha.6 + + + + + + html + generate-resources + + process-asciidoc + + + html5 + ${project.root}/target/site/reference/html + false + prettify + + true + font + true + spring.css + + + + + + epub + generate-resources + + process-asciidoc + + + epub3 + coderay + + + + + pdf + generate-resources + + process-asciidoc + + + pdf + coderay + + + + + + + ${project.root}/src/main/asciidoc + index.adoc + book + + ${project.version} + ${project.name} + ${project.version} + ${spring.version} + true + 3 + true + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + static + + single + + prepare-package + + + ${shared.resources}/assemblies/static-resources.xml + + static-resources + false + + + + docs + + single + + package + + + src/assembly/docs.xml + + spring-credhub-${project.version}-docs + false + docs + true + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + false + + +
+
+
+ + + + spring-libs-snapshot + https://repo.spring.io/libs-snapshot + + true + + + + + + + spring-plugins-release + https://repo.spring.io/plugins-release + + +
+
+
diff --git a/spring-credhub-core/pom.xml b/spring-credhub-core/pom.xml new file mode 100644 index 0000000..e9b02a7 --- /dev/null +++ b/spring-credhub-core/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + + org.springframework.credhub + spring-credhub-parent + 1.0.0.BUILD-SNAPSHOT + + + spring-credhub-core + Spring CredHub Core + Spring CredHub Core Components + jar + + + + org.springframework + spring-core + + + + org.springframework + spring-beans + + + + org.springframework + spring-web + + + + com.fasterxml.jackson.core + jackson-databind + + + + org.apache.httpcomponents + httpclient + true + + + + org.bouncycastle + bcpkix-jdk15on + + + + org.springframework + spring-test + test + + + + junit + junit + test + + + + org.mockito + mockito-core + test + + + + org.assertj + assertj-core + test + + + + org.valid4j + json-path-matchers + test + + + + org.projectlombok + lombok + 1.16.10 + true + + + + diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/configuration/ClientHttpRequestFactoryFactory.java b/spring-credhub-core/src/main/java/org/springframework/credhub/configuration/ClientHttpRequestFactoryFactory.java new file mode 100644 index 0000000..ab08937 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/configuration/ClientHttpRequestFactoryFactory.java @@ -0,0 +1,115 @@ +/* + * 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.configuration; + +import org.apache.http.client.config.RequestConfig; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContexts; +import org.springframework.credhub.support.ClientOptions; +import org.springframework.credhub.support.SslConfiguration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.GeneralSecurityException; + +/** + * Factory for {@link ClientHttpRequestFactory} that supports Apache HTTP Components, + * OkHttp, Netty and the JDK HTTP client (in that order). This factory configures a + * {@link ClientHttpRequestFactory} depending on the available dependencies. + * + * @author Scott Frederick + * @author Mark Paluch + */ +public class ClientHttpRequestFactoryFactory { + + private static final boolean HTTP_COMPONENTS_PRESENT = ClassUtils.isPresent( + "org.apache.http.client.HttpClient", + ClientHttpRequestFactoryFactory.class.getClassLoader()); + + /** + * Create a {@link ClientHttpRequestFactory} for the given {@link ClientOptions} and + * {@link SslConfiguration}. + * + * @param options must not be {@literal null} + * @param sslConfiguration must not be {@literal null} + * @return a new {@link ClientHttpRequestFactory}. Lifecycle beans must be initialized + * after obtaining. + */ + public static ClientHttpRequestFactory create(ClientOptions options, + SslConfiguration sslConfiguration) { + + Assert.notNull(options, "ClientOptions must not be null"); + Assert.notNull(sslConfiguration, "SslConfiguration must not be null"); + + try { + if (HTTP_COMPONENTS_PRESENT) { + return HttpComponents.usingHttpComponents(options, sslConfiguration); + } + } catch (GeneralSecurityException e) { + throw new IllegalStateException(e); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + throw new IllegalStateException("Only Apache HTTP Components is supported."); + } + + private static boolean hasSslConfiguration(SslConfiguration sslConfiguration) { + return sslConfiguration.getTrustStore() != null + || sslConfiguration.getKeyStore() != null; + } + + /** + * {@link ClientHttpRequestFactory} for Apache HttpComponents. + */ + static class HttpComponents { + + static ClientHttpRequestFactory usingHttpComponents(ClientOptions options, SslConfiguration sslConfiguration) + throws GeneralSecurityException, IOException { + + HttpClientBuilder httpClientBuilder = HttpClients.custom(); + + if (hasSslConfiguration(sslConfiguration)) { + SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(sslConfiguration.getKeyStore(), new TrustSelfSignedStrategy()) + .loadKeyMaterial(sslConfiguration.getTrustStore(), sslConfiguration.getKeyPassword()) + .build(); + + SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext); + httpClientBuilder.setSSLSocketFactory(sslSocketFactory); + httpClientBuilder.setSSLContext(sslContext); + } + + RequestConfig requestConfig = RequestConfig.custom() + .setConnectTimeout(options.getConnectionTimeout()) + .setSocketTimeout(options.getReadTimeout()) + .setAuthenticationEnabled(true) + .build(); + + httpClientBuilder.setDefaultRequestConfig(requestConfig); + + return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build()); + } + } +} \ No newline at end of file diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/configuration/CredHubConfiguration.java b/spring-credhub-core/src/main/java/org/springframework/credhub/configuration/CredHubConfiguration.java new file mode 100644 index 0000000..ef38cdd --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/configuration/CredHubConfiguration.java @@ -0,0 +1,113 @@ +/* + * 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.configuration; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.credhub.core.CloudFoundryAppInstanceProperties; +import org.springframework.credhub.core.CredHubProperties; +import org.springframework.credhub.core.CredHubTemplate; +import org.springframework.credhub.support.ClientOptions; +import org.springframework.credhub.support.SslConfiguration; +import org.springframework.http.client.ClientHttpRequestFactory; + +@Configuration +public class CredHubConfiguration { + @Bean + public CredHubProperties credHubProperties() { + return new CredHubProperties(); + } + + @Bean + public CloudFoundryAppInstanceProperties cloudFoundryAppInstanceProperties() { + return new CloudFoundryAppInstanceProperties(); + } + + @Bean + public CredHubTemplate credHubTemplate() { + return new CredHubTemplate(credHubProperties().getApiUriBase(), + clientHttpRequestFactoryWrapper().getClientHttpRequestFactory()); + } + + /** + * Create a {@link ClientFactoryWrapper} containing a {@link ClientHttpRequestFactory}. + * {@link ClientHttpRequestFactory} is not exposed as root bean because + * {@link ClientHttpRequestFactory} is configured with {@link ClientOptions} and + * {@link SslConfiguration} which are not necessarily applicable for the whole + * application. + * + * @return the {@link ClientFactoryWrapper} to wrap a {@link ClientHttpRequestFactory} + * instance. + * @see #clientOptions() + * @see #sslConfiguration() + */ + @Bean + public ClientFactoryWrapper clientHttpRequestFactoryWrapper() { + ClientHttpRequestFactory clientHttpRequestFactory = + ClientHttpRequestFactoryFactory.create(clientOptions(), sslConfiguration()); + return new ClientFactoryWrapper(clientHttpRequestFactory); + } + + /** + * @return {@link ClientOptions} to configure communication parameters. + * @see ClientOptions + */ + private ClientOptions clientOptions() { + return new ClientOptions(); + } + + /** + * @return SSL configuration options. + * @see SslConfiguration + */ + private SslConfiguration sslConfiguration() { + CloudFoundryAppInstanceProperties properties = cloudFoundryAppInstanceProperties(); + return SslConfiguration.forContainerCert(properties.getInstanceCertLocation(), properties.getInstanceKeyLocation()); + } + + /** + * Wrapper for {@link ClientHttpRequestFactory} to not expose the bean globally. + */ + public static class ClientFactoryWrapper implements InitializingBean, DisposableBean { + + private final ClientHttpRequestFactory clientHttpRequestFactory; + + public ClientFactoryWrapper(ClientHttpRequestFactory clientHttpRequestFactory) { + this.clientHttpRequestFactory = clientHttpRequestFactory; + } + + @Override + public void destroy() throws Exception { + if (clientHttpRequestFactory instanceof DisposableBean) { + ((DisposableBean) clientHttpRequestFactory).destroy(); + } + } + + @Override + public void afterPropertiesSet() throws Exception { + if (clientHttpRequestFactory instanceof InitializingBean) { + ((InitializingBean) clientHttpRequestFactory).afterPropertiesSet(); + } + } + + public ClientHttpRequestFactory getClientHttpRequestFactory() { + return clientHttpRequestFactory; + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/core/CloudFoundryAppInstanceProperties.java b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CloudFoundryAppInstanceProperties.java new file mode 100644 index 0000000..5aa88a9 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CloudFoundryAppInstanceProperties.java @@ -0,0 +1,43 @@ +/* + * 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 org.springframework.beans.factory.annotation.Value; + +public class CloudFoundryAppInstanceProperties { + @Value("${CF_INSTANCE_CERT}") + private String instanceCertLocation; + + @Value("${CF_INSTANCE_KEY}") + private String instanceKeyLocation; + + public CloudFoundryAppInstanceProperties() { + } + + public CloudFoundryAppInstanceProperties(String instanceCertLocation, String instanceKeyLocation) { + this.instanceCertLocation = instanceCertLocation; + this.instanceKeyLocation = instanceKeyLocation; + } + + public String getInstanceCertLocation() { + return instanceCertLocation; + } + + public String getInstanceKeyLocation() { + return instanceKeyLocation; + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubClient.java b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubClient.java new file mode 100644 index 0000000..c686b92 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubClient.java @@ -0,0 +1,38 @@ +/* + * + * * Copyright 2013-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 org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriTemplateHandler; + +public class CredHubClient { + public static RestTemplate createRestTemplate(String baseUri, ClientHttpRequestFactory clientHttpRequestFactory) { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setUriTemplateHandler(createUriTemplateHandler(baseUri)); + restTemplate.setRequestFactory(clientHttpRequestFactory); + return restTemplate; + } + + private static DefaultUriTemplateHandler createUriTemplateHandler(String baseUri) { + DefaultUriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler(); + uriTemplateHandler.setBaseUrl(baseUri); + return uriTemplateHandler; + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubException.java b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubException.java new file mode 100644 index 0000000..daa262c --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubException.java @@ -0,0 +1,35 @@ +/* + * 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 org.springframework.core.NestedRuntimeException; +import org.springframework.http.HttpStatus; +import org.springframework.web.client.HttpStatusCodeException; + +public class CredHubException extends NestedRuntimeException { + public CredHubException(HttpStatusCodeException e) { + super("Error calling CredHub: " + e.getStatusCode() + ": " + e.getResponseBodyAsString()); + } + + public CredHubException(HttpStatus statusCode) { + super("Error calling CredHub: " + statusCode); + } + + public CredHubException(String message) { + super("Error returned from call to CredHub: " + message); + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubProperties.java b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubProperties.java new file mode 100644 index 0000000..c4525ec --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubProperties.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2013-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 org.springframework.beans.factory.annotation.Value; + +public class CredHubProperties { + @Value("${CREDHUB_API}") + private String apiUriBase; + + public CredHubProperties() { + } + + public CredHubProperties(String apiUriBase) { + this.apiUriBase = apiUriBase; + } + + public String getApiUriBase() { + return apiUriBase; + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubTemplate.java b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubTemplate.java new file mode 100644 index 0000000..b94c18e --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/core/CredHubTemplate.java @@ -0,0 +1,122 @@ +package org.springframework.credhub.core; + +import org.springframework.credhub.support.CredHubResponse; +import org.springframework.credhub.support.CredentialDataResponse; +import org.springframework.credhub.support.CredentialData; +import org.springframework.credhub.support.FindResponse; +import org.springframework.credhub.support.WriteRequest; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.util.Assert; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +public class CredHubTemplate { + static final String BASE_URL_PATH = "/api/v1/data"; + static final String ID_URL_PATH = BASE_URL_PATH + "/{id}"; + static final String NAME_URL_QUERY = BASE_URL_PATH + "?name={name}"; + static final String NAME_LIKE_URL_QUERY = BASE_URL_PATH + "?name-like={name}"; + static final String PATH_URL_QUERY = BASE_URL_PATH + "?path={path}"; + + private final RestTemplate restTemplate; + + CredHubTemplate(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public CredHubTemplate(String apiUriBase, ClientHttpRequestFactory clientHttpRequestFactory) { + this.restTemplate = CredHubClient.createRestTemplate(apiUriBase, clientHttpRequestFactory); + } + + public CredentialData write(final WriteRequest writeRequest) { + return doWithRest(new RestOperationsCallback() { + @Override + public CredentialData doWithRestOperations(RestOperations restOperations) { + ResponseEntity response = + restOperations.exchange(BASE_URL_PATH, HttpMethod.PUT, + new HttpEntity(writeRequest), + CredentialDataResponse.class); + + throwExceptionOnError(response); + + return response.getBody().getData().get(0); + } + }); + } + + public CredentialData getById(final String id) { + return doWithRest(new RestOperationsCallback() { + @Override + public CredentialData doWithRestOperations(RestOperations restOperations) { + ResponseEntity response = + restOperations.getForEntity(ID_URL_PATH, CredentialDataResponse.class, id); + + throwExceptionOnError(response); + + return response.getBody().getData().get(0); + } + }); + } + + public FindResponse findByName(final String name) { + return doWithRest(new RestOperationsCallback() { + @Override + public FindResponse doWithRestOperations(RestOperations restOperations) { + ResponseEntity response = + restOperations.getForEntity(NAME_LIKE_URL_QUERY, FindResponse.class, name); + + throwExceptionOnError(response); + + return response.getBody(); + } + }); + } + + public FindResponse findByPath(final String path) { + return doWithRest(new RestOperationsCallback() { + @Override + public FindResponse doWithRestOperations(RestOperations restOperations) { + ResponseEntity response = + restOperations.getForEntity(PATH_URL_QUERY, FindResponse.class, path); + + throwExceptionOnError(response); + + return response.getBody(); + } + }); + } + + public void deleteByName(final String name) { + doWithRest(new RestOperationsCallback() { + @Override + public Void doWithRestOperations(RestOperations restOperations) { + restOperations.delete(NAME_URL_QUERY, name); + return null; + } + }); + } + + public T doWithRest(RestOperationsCallback callback) { + Assert.notNull(callback, "Callback must not be null"); + + try { + return callback.doWithRestOperations(restTemplate); + } catch (HttpStatusCodeException e) { + throw new CredHubException(e); + } + } + + private void throwExceptionOnError(ResponseEntity response) { + if (!response.getStatusCode().equals(HttpStatus.OK)) { + throw new CredHubException(response.getStatusCode()); + } + + if (response.getBody().getErrorMessage() != null) { + throw new CredHubException(response.getBody().getErrorMessage()); + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/core/RestOperationsCallback.java b/spring-credhub-core/src/main/java/org/springframework/credhub/core/RestOperationsCallback.java new file mode 100644 index 0000000..91b4455 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/core/RestOperationsCallback.java @@ -0,0 +1,35 @@ +/* + * 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 org.springframework.web.client.RestOperations; + +/** + * A callback for executing arbitrary operations on {@link RestOperations}. + * + * @author Mark Paluch + */ +public interface RestOperationsCallback { + + /** + * Callback method providing a {@link RestOperations} that is configured to interact with the CredHub server. + * + * @param restOperations restOperations to use, must not be {@literal null}. + * @return a result object or null if none. + */ + T doWithRestOperations(RestOperations restOperations); +} \ No newline at end of file diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/AccessControlEntry.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/AccessControlEntry.java new file mode 100644 index 0000000..70cdd64 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/AccessControlEntry.java @@ -0,0 +1,70 @@ +/* + * + * * Copyright 2013-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 lombok.AccessLevel; +import lombok.Builder; +import lombok.Data; +import lombok.Setter; +import lombok.Singular; + +import java.util.ArrayList; +import java.util.List; + +@Data +@Builder +public class AccessControlEntry { + private static final String APP_ACTOR_PREFIX = "mtls-app:"; + + @Setter(AccessLevel.PRIVATE) + private String actor; + + @Singular + private List operations; + + public List getOperations() { + List operationValues = new ArrayList(operations.size()); + for (Operation operation : operations) { + operationValues.add(operation.operation()); + } + return operationValues; + } + + public static class AccessControlEntryBuilder { + public AccessControlEntryBuilder app(String appId) { + this.actor = APP_ACTOR_PREFIX + appId; + return this; + } + } + + public enum Operation { + READ("read"), + WRITE("write"); + + private final String operation; + + Operation(String operation) { + this.operation = operation; + } + + public String operation() { + return operation; + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/ClientOptions.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ClientOptions.java new file mode 100644 index 0000000..f5d2119 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ClientOptions.java @@ -0,0 +1,75 @@ +/* + * 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 java.util.concurrent.TimeUnit; + +/** + * Client options for CredHubConnectivity. + * + * @author Mark Paluch + * @author Scott Frederick + */ +public class ClientOptions { + + /** + * Connection timeout; + */ + private final int connectionTimeout; + + /** + * Read timeout; + */ + private final int readTimeout; + + /** + * Create new {@link ClientOptions} with default timeouts of {@literal 5} + * {@link TimeUnit#SECONDS} connection timeout and {@literal 15} + * {@link TimeUnit#SECONDS} read timeout. + */ + public ClientOptions() { + this((int) TimeUnit.SECONDS.toMillis(5), (int) TimeUnit.SECONDS.toMillis(15)); + } + + /** + * Create new {@link ClientOptions}. + * + * @param connectionTimeout connection timeout in {@link TimeUnit#MILLISECONDS}, must + * be greater {@literal 0}. + * @param readTimeout read timeout in {@link TimeUnit#MILLISECONDS}, must be greater + * {@literal 0}. + */ + public ClientOptions(int connectionTimeout, int readTimeout) { + this.connectionTimeout = connectionTimeout; + this.readTimeout = readTimeout; + } + + /** + * @return the connection timeout in {@link TimeUnit#MILLISECONDS}. + */ + public int getConnectionTimeout() { + return connectionTimeout; + } + + /** + * @return the read timeout in {@link TimeUnit#MILLISECONDS}. + */ + public int getReadTimeout() { + return readTimeout; + } + +} \ No newline at end of file diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredHubRequest.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredHubRequest.java new file mode 100644 index 0000000..65db233 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredHubRequest.java @@ -0,0 +1,59 @@ +/* + * + * * Copyright 2013-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.JsonInclude; + +public class CredHubRequest { + private CredentialName name; + + CredHubRequest(CredentialName name) { + this.name = name; + } + + CredHubRequest() { + } + + @JsonInclude + public String getName() { + return name.getName(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CredHubRequest)) return false; + + CredHubRequest that = (CredHubRequest) o; + + return name != null ? name.equals(that.name) : that.name == null; + } + + @Override + public int hashCode() { + return name != null ? name.hashCode() : 0; + } + + @Override + public String toString() { + return "CredHubRequest{" + + "name=" + name + + '}'; + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredHubResponse.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredHubResponse.java new file mode 100644 index 0000000..4d8b6b7 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredHubResponse.java @@ -0,0 +1,81 @@ +/* + * 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; + +public class CredHubResponse { + @JsonProperty("errorMessage") + protected final String errorMessage; + + CredHubResponse() { + errorMessage = null; + } + + CredHubResponse(String errorMessage) { + this.errorMessage = errorMessage; + } + + public String getErrorMessage() { + return this.errorMessage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CredHubResponse that = (CredHubResponse) o; + + return errorMessage != null ? errorMessage.equals(that.errorMessage) : that.errorMessage == null; + } + + @Override + public int hashCode() { + return errorMessage != null ? errorMessage.hashCode() : 0; + } + + @Override + public String toString() { + return "CredHubResponse{" + + "errorMessage='" + errorMessage + '\'' + + '}'; + } + + public static class CredHubResponseBuilder { + private String errorMessage; + + CredHubResponseBuilder() { + } + + public CredHubResponse.CredHubResponseBuilder errorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + public CredHubResponse build() { + return new CredHubResponse(errorMessage); + } + + @Override + public String toString() { + return "CredHubResponseBuilder{" + + "errorMessage='" + errorMessage + '\'' + + '}'; + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialData.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialData.java new file mode 100644 index 0000000..42d58af --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialData.java @@ -0,0 +1,156 @@ +/* + * 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 com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import java.util.Map; + +@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class) +public class CredentialData { + private String id; + private CredentialName name; + @JsonProperty("type") + private ValueType valueType; + private Object value; + private String versionCreatedAt; + + public CredentialData() { + } + + CredentialData(String id, CredentialName name, ValueType valueType, Object value, String versionCreatedAt) { + this.id = id; + this.name = name; + this.valueType = valueType; + this.value = value; + this.versionCreatedAt = versionCreatedAt; + } + + public static CredentialDataBuilder builder() { + return new CredentialDataBuilder(); + } + + public String getId() { + return this.id; + } + + public CredentialName getName() { + return this.name; + } + + public ValueType getValueType() { + return this.valueType; + } + + public Object getValue() { + return this.value; + } + + public String getVersionCreatedAt() { + return this.versionCreatedAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CredentialData)) return false; + + CredentialData that = (CredentialData) o; + + if (id != null ? !id.equals(that.id) : that.id != null) return false; + if (name != null ? !name.equals(that.name) : that.name != null) return false; + if (valueType != that.valueType) return false; + if (value != null ? !value.equals(that.value) : that.value != null) return false; + return versionCreatedAt != null ? versionCreatedAt.equals(that.versionCreatedAt) : that.versionCreatedAt == null; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (name != null ? name.hashCode() : 0); + result = 31 * result + (valueType != null ? valueType.hashCode() : 0); + result = 31 * result + (value != null ? value.hashCode() : 0); + result = 31 * result + (versionCreatedAt != null ? versionCreatedAt.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "CredentialData{" + + "id='" + id + '\'' + + ", name=" + name + + ", valueType=" + valueType + + ", value=" + value + + ", versionCreatedAt='" + versionCreatedAt + '\'' + + '}'; + } + + public static class CredentialDataBuilder { + private String id; + private CredentialName name; + private ValueType valueType; + private Object value; + private String versionCreatedAt; + + CredentialDataBuilder() { + } + + public CredentialData.CredentialDataBuilder id(String id) { + this.id = id; + return this; + } + + public CredentialData.CredentialDataBuilder name(CredentialName name) { + this.name = name; + return this; + } + + public CredentialData.CredentialDataBuilder passwordValue(String value) { + this.valueType = ValueType.PASSWORD; + this.value = value; + return this; + } + + public CredentialData.CredentialDataBuilder jsonValue(Map value) { + this.valueType = ValueType.JSON; + this.value = value; + return this; + } + + public CredentialData.CredentialDataBuilder versionCreatedAt(String versionCreatedAt) { + this.versionCreatedAt = versionCreatedAt; + return this; + } + + public CredentialData build() { + return new CredentialData(id, name, valueType, value, versionCreatedAt); + } + + @Override + public String toString() { + return "CredentialDataBuilder{" + + "id='" + id + '\'' + + ", name=" + name + + ", valueType=" + valueType + + ", value=" + value + + ", versionCreatedAt='" + versionCreatedAt + '\'' + + '}'; + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialDataResponse.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialDataResponse.java new file mode 100644 index 0000000..f1604eb --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialDataResponse.java @@ -0,0 +1,138 @@ +/* + * + * * Copyright 2013-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.JsonInclude; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; + +@JsonInclude(NON_EMPTY) +public class CredentialDataResponse extends CredHubResponse { + private List data; + + public CredentialDataResponse() { + } + + CredentialDataResponse(String errorMessage) { + super(errorMessage); + } + + CredentialDataResponse(List data) { + super(null); + this.data = data; + } + + CredentialDataResponse(String errorMessage, List data) { + super(errorMessage); + this.data = data; + } + + public static CredentialDataResponseBuilder builder() { + return new CredentialDataResponseBuilder(); + } + + public List getData() { + return this.data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CredentialDataResponse)) return false; + if (!super.equals(o)) return false; + + CredentialDataResponse that = (CredentialDataResponse) o; + + return data != null ? data.equals(that.data) : that.data == null; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (data != null ? data.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "CredentialDataResponse{" + + "errorMessage=" + errorMessage + + ", data=" + data + + '}'; + } + + public static class CredentialDataResponseBuilder { + private String errorMessage; + private ArrayList data; + + CredentialDataResponseBuilder() { + } + + public CredentialDataResponse.CredentialDataResponseBuilder errorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + public CredentialDataResponse.CredentialDataResponseBuilder datum(CredentialData datum) { + initData(); + this.data.add(datum); + return this; + } + + public CredentialDataResponse.CredentialDataResponseBuilder data(Collection data) { + initData(); + this.data.addAll(data); + return this; + } + + private void initData() { + if (this.data == null) { + this.data = new ArrayList(); + } + } + + public CredentialDataResponse build() { + List data; + switch (this.data == null ? 0 : this.data.size()) { + case 0: + data = java.util.Collections.emptyList(); + break; + case 1: + data = java.util.Collections.singletonList(this.data.get(0)); + break; + default: + data = java.util.Collections.unmodifiableList(new ArrayList(this.data)); + } + + return new CredentialDataResponse(errorMessage, data); + } + + @Override + public String toString() { + return "CredentialDataResponseBuilder{" + + "errorMessage=" + errorMessage + "," + + "data=" + data + + '}'; + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialName.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialName.java new file mode 100644 index 0000000..2dabaa0 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/CredentialName.java @@ -0,0 +1,45 @@ +/* + * 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.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import org.springframework.util.StringUtils; + +import java.util.Arrays; + +@Data +public class CredentialName { + @JsonIgnore + private final String[] segments; + + CredentialName(String name) { + String[] split = name.split("/"); + // remove the "/c/" prefix + this.segments = Arrays.copyOfRange(split, 2, split.length); + } + + CredentialName(String... segments) { + this.segments = segments; + } + + @JsonInclude + public String getName() { + return "/c/" + StringUtils.arrayToDelimitedString(segments, "/"); + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/FindResponse.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/FindResponse.java new file mode 100644 index 0000000..34a34cf --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/FindResponse.java @@ -0,0 +1,204 @@ +/* + * 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.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class) +public class FindResponse extends CredHubResponse { + private List foundCredentials; + + FindResponse(List foundCredentials) { + this.foundCredentials = foundCredentials; + } + + public FindResponse(String errorMessage, List foundCredentials) { + super(errorMessage); + this.foundCredentials = foundCredentials; + } + + public static FindResponseBuilder builder() { + return new FindResponseBuilder(); + } + + public List getFoundCredentials() { + return this.foundCredentials; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof FindResponse)) return false; + if (!super.equals(o)) return false; + + FindResponse that = (FindResponse) o; + + return foundCredentials != null ? foundCredentials.equals(that.foundCredentials) : that.foundCredentials == null; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (foundCredentials != null ? foundCredentials.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "FindResponse{" + + "errorMessage='" + errorMessage + '\'' + + ", foundCredentials=" + foundCredentials + + '}'; + } + + public static class FindResponseBuilder { + private String errorMessage; + private ArrayList foundCredentials; + + FindResponseBuilder() { + } + + public FindResponse.FindResponseBuilder foundCredential(FoundCredential foundCredential) { + initFoundCredentials(); + this.foundCredentials.add(foundCredential); + return this; + } + + public FindResponse.FindResponseBuilder foundCredentials(Collection foundCredentials) { + initFoundCredentials(); + this.foundCredentials.addAll(foundCredentials); + return this; + } + + private void initFoundCredentials() { + if (this.foundCredentials == null) { + this.foundCredentials = new ArrayList(); + } + } + + public FindResponse build() { + List foundCredentials; + switch (this.foundCredentials == null ? 0 : this.foundCredentials.size()) { + case 0: + foundCredentials = java.util.Collections.emptyList(); + break; + case 1: + foundCredentials = java.util.Collections.singletonList(this.foundCredentials.get(0)); + break; + default: + foundCredentials = java.util.Collections.unmodifiableList(new ArrayList(this.foundCredentials)); + } + + return new FindResponse(errorMessage, foundCredentials); + } + + @Override + public String toString() { + return "FindResponseBuilder{" + + "foundCredentials=" + foundCredentials + + '}'; + } + + public FindResponseBuilder errorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + } + + public static class FoundCredential { + private CredentialName name; + private String versionCreatedAt; + + FoundCredential(CredentialName name, String versionCreatedAt) { + this.name = name; + this.versionCreatedAt = versionCreatedAt; + } + + public static FoundCredentialBuilder builder() { + return new FoundCredentialBuilder(); + } + + public CredentialName getName() { + return this.name; + } + + public String getVersionCreatedAt() { + return this.versionCreatedAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof FoundCredential)) return false; + + FoundCredential that = (FoundCredential) o; + + if (name != null ? !name.equals(that.name) : that.name != null) return false; + return versionCreatedAt != null ? versionCreatedAt.equals(that.versionCreatedAt) : that.versionCreatedAt == null; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (versionCreatedAt != null ? versionCreatedAt.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "FoundCredential{" + + "name=" + name + + ", versionCreatedAt='" + versionCreatedAt + '\'' + + '}'; + } + + public static class FoundCredentialBuilder { + private CredentialName name; + private String versionCreatedAt; + + FoundCredentialBuilder() { + } + + public FoundCredential.FoundCredentialBuilder name(CredentialName name) { + this.name = name; + return this; + } + + public FoundCredential.FoundCredentialBuilder versionCreatedAt(String versionCreatedAt) { + this.versionCreatedAt = versionCreatedAt; + return this; + } + + public FoundCredential build() { + return new FoundCredential(name, versionCreatedAt); + } + + @Override + public String toString() { + return "FoundCredentialBuilder{" + + "name=" + name + + ", versionCreatedAt='" + versionCreatedAt + '\'' + + '}'; + } + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/ReadRequest.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ReadRequest.java new file mode 100644 index 0000000..fe75821 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ReadRequest.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2013-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 lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ReadRequest extends CredHubRequest { + @Builder + public ReadRequest(CredentialName name) { + super(name); + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/ServiceInstanceCredentialName.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ServiceInstanceCredentialName.java new file mode 100644 index 0000000..3682297 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ServiceInstanceCredentialName.java @@ -0,0 +1,33 @@ +/* + * 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 lombok.Builder; +import lombok.Data; + +@Data +public class ServiceInstanceCredentialName extends CredentialName { + @Builder + public ServiceInstanceCredentialName(String serviceBrokerName, String serviceOfferingName, String serviceBindingId, String credentialName) { + super(serviceBrokerName, serviceOfferingName, serviceBindingId, credentialName); + } + + @Override + public String getName() { + return null; + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/SimpleCredentialName.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/SimpleCredentialName.java new file mode 100644 index 0000000..0f25de0 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/SimpleCredentialName.java @@ -0,0 +1,37 @@ +/* + * 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 lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class SimpleCredentialName extends CredentialName { + @Builder + public SimpleCredentialName(String... segments) { + super(segments); + } + + public static class SimpleCredentialNameBuilder { + public SimpleCredentialNameBuilder segments(String... segments) { + this.segments = segments; + return this; + } + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/SslConfiguration.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/SslConfiguration.java new file mode 100644 index 0000000..0fdb857 --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/SslConfiguration.java @@ -0,0 +1,108 @@ +/* + * 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.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; + +import java.io.FileReader; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; + +public class SslConfiguration { + private static final char[] KEY_PASSWORD = "keystore".toCharArray(); + private static final String CERTIFICATE_NAME = "credhub-cert"; + private static final String KEY_NAME = "credhub-key"; + + private KeyStore trustStore; + private KeyStore keyStore; + + public SslConfiguration() { + } + + public static SslConfiguration forContainerCert(String instanceCertLocation, String instanceKeyLocation) { + SslConfiguration sslConfiguration = new SslConfiguration(); + KeyStore keyStore = sslConfiguration.buildKeyStore(instanceCertLocation, instanceKeyLocation); + sslConfiguration.setKeyStore(keyStore); + sslConfiguration.setTrustStore(keyStore); + return sslConfiguration; + } + + public KeyStore getKeyStore() { + return keyStore; + } + + private void setKeyStore(KeyStore keyStore) { + this.keyStore = keyStore; + } + + public KeyStore getTrustStore() { + return trustStore; + } + + private void setTrustStore(KeyStore keyStore) { + this.trustStore = keyStore; + } + + public char[] getKeyPassword() { + return KEY_PASSWORD; + } + + private KeyStore buildKeyStore(String instanceCertLocation, String instanceKeyLocation) { + Certificate cert = parseCertificate(instanceCertLocation); + PrivateKey key = parsePrivateKey(instanceKeyLocation); + return createKeyStore(cert, key); + } + + private Certificate parseCertificate(String certificateLocation) { + try { + PEMParser parser = new PEMParser(new FileReader(certificateLocation)); + X509CertificateHolder certHolder = (X509CertificateHolder) parser.readObject(); + JcaX509CertificateConverter converter = new JcaX509CertificateConverter(); + return converter.getCertificate(certHolder); + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing and loading certificate from location " + certificateLocation, e); + } + } + + private PrivateKey parsePrivateKey(String keyLocation) { + try { + PEMParser reader = new PEMParser(new FileReader(keyLocation)); + PEMKeyPair key = (PEMKeyPair) reader.readObject(); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + return converter.getKeyPair(key).getPrivate(); + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing and loading private key from location " + keyLocation, e); + } + } + + private KeyStore createKeyStore(Certificate cert, PrivateKey key) { + try { + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + keystore.load(null); + keystore.setCertificateEntry(CERTIFICATE_NAME, cert); + keystore.setKeyEntry(KEY_NAME, key, KEY_PASSWORD, new Certificate[]{cert}); + return keystore; + } catch (Exception e) { + throw new IllegalArgumentException("Error creating keystore ", e); + } + } +} 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 new file mode 100644 index 0000000..85a6abf --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/ValueType.java @@ -0,0 +1,46 @@ +/* + * + * * Copyright 2013-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.JsonCreator; + +public enum ValueType { + PASSWORD("password"), + JSON("json"); + + private final String type; + + ValueType(String type) { + this.type = type; + } + + public String type() { + return type; + } + + @JsonCreator + public static ValueType getTypeByString(String type) { + for (ValueType e : ValueType.values()) { + if (e.type().equals(type)) { + return e; + } + } + return null; + } +} diff --git a/spring-credhub-core/src/main/java/org/springframework/credhub/support/WriteRequest.java b/spring-credhub-core/src/main/java/org/springframework/credhub/support/WriteRequest.java new file mode 100644 index 0000000..d82cc1b --- /dev/null +++ b/spring-credhub-core/src/main/java/org/springframework/credhub/support/WriteRequest.java @@ -0,0 +1,183 @@ +/* + * + * * Copyright 2013-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.JsonInclude; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Singular; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; +import static org.springframework.credhub.support.ValueType.JSON; + +@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class) +public class WriteRequest extends CredHubRequest { + private boolean overwrite; + + private ValueType valueType; + + private Object value; + + @JsonInclude(NON_EMPTY) + private List accessControlEntries; + + private WriteRequest(CredentialName name, boolean overwrite, Object value, ValueType valueType, + @Singular List accessControlEntries) { + super(name); + this.overwrite = overwrite; + this.valueType = valueType; + this.value = value; + this.accessControlEntries = accessControlEntries; + } + + public static WriteRequestBuilder builder() { + return new WriteRequestBuilder(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof WriteRequest)) return false; + if (!super.equals(o)) return false; + + WriteRequest that = (WriteRequest) o; + + if (overwrite != that.overwrite) return false; + if (valueType != that.valueType) return false; + if (value != null ? !value.equals(that.value) : that.value != null) return false; + return accessControlEntries != null ? accessControlEntries.equals(that.accessControlEntries) : that.accessControlEntries == null; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (overwrite ? 1 : 0); + result = 31 * result + (valueType != null ? valueType.hashCode() : 0); + result = 31 * result + (value != null ? value.hashCode() : 0); + result = 31 * result + (accessControlEntries != null ? accessControlEntries.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "WriteRequest{" + + "overwrite=" + overwrite + + ", valueType=" + valueType + + ", value=" + value + + ", accessControlEntries=" + accessControlEntries + + '}'; + } + + public boolean isOverwrite() { + return this.overwrite; + } + + public Object getValue() { + return this.value; + } + + public String getType() { + return valueType.type(); + } + + public List getAccessControlEntries() { + return this.accessControlEntries; + } + + public static class WriteRequestBuilder { + private CredentialName name; + private boolean overwrite; + private Object value; + private ValueType valueType; + private ArrayList accessControlEntries; + + WriteRequestBuilder() { + } + + public WriteRequestBuilder passwordValue(String value) { + this.valueType = ValueType.PASSWORD; + this.value = value; + return this; + } + + public WriteRequestBuilder jsonValue(Map value) { + this.valueType = JSON; + this.value = value; + return this; + } + + public WriteRequestBuilder name(CredentialName name) { + this.name = name; + return this; + } + + public WriteRequestBuilder overwrite(boolean overwrite) { + this.overwrite = overwrite; + return this; + } + + public WriteRequestBuilder accessControlEntry(AccessControlEntry accessControlEntry) { + if (this.accessControlEntries == null) + this.accessControlEntries = new ArrayList(); + this.accessControlEntries.add(accessControlEntry); + return this; + } + + public WriteRequestBuilder accessControlEntries(Collection accessControlEntries) { + if (this.accessControlEntries == null) + this.accessControlEntries = new ArrayList(); + this.accessControlEntries.addAll(accessControlEntries); + return this; + } + + public WriteRequest build() { + List accessControlEntries; + switch (this.accessControlEntries == null ? 0 : this.accessControlEntries.size()) { + case 0: + accessControlEntries = java.util.Collections.emptyList(); + break; + case 1: + accessControlEntries = java.util.Collections.singletonList(this.accessControlEntries.get(0)); + break; + default: + accessControlEntries = java.util.Collections.unmodifiableList(new ArrayList(this.accessControlEntries)); + } + + return new WriteRequest(name, overwrite, value, valueType, accessControlEntries); + } + + @Override + public String toString() { + return "WriteRequestBuilder{" + + "name=" + name + + ", overwrite=" + overwrite + + ", value=" + value + + ", valueType=" + valueType + + ", accessControlEntries=" + accessControlEntries + + '}'; + } + } + +} diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/configuration/ClientHttpRequestFactoryFactoryTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/configuration/ClientHttpRequestFactoryFactoryTests.java new file mode 100644 index 0000000..b18e487 --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/configuration/ClientHttpRequestFactoryFactoryTests.java @@ -0,0 +1,47 @@ +/* + * 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.configuration; + +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.CloseableHttpClient; +import org.junit.Test; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.credhub.support.ClientOptions; +import org.springframework.credhub.support.SslConfiguration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; + +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertThat; + +public class ClientHttpRequestFactoryFactoryTests { + + @Test + public void httpComponentsClientCreated() throws Exception { + + ClientHttpRequestFactory factory = ClientHttpRequestFactoryFactory.HttpComponents.usingHttpComponents( + new ClientOptions(), new SslConfiguration()); + + assertThat(factory, instanceOf(HttpComponentsClientHttpRequestFactory.class)); + + HttpClient httpClient = ((HttpComponentsClientHttpRequestFactory) factory).getHttpClient(); + + assertThat(httpClient, instanceOf(CloseableHttpClient.class)); + + ((DisposableBean) factory).destroy(); + } +} \ No newline at end of file diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubClientUnitTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubClientUnitTests.java new file mode 100644 index 0000000..675043e --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubClientUnitTests.java @@ -0,0 +1,54 @@ +/* + * + * * Copyright 2013-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 org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.AbstractUriTemplateHandler; + +import java.net.URI; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertThat; + + +@RunWith(MockitoJUnitRunner.class) +public class CredHubClientUnitTests { + private static final String CREDHUB_URI = "https://credhub.cf.example.com:8844"; + + @Mock + private ClientHttpRequestFactory clientHttpRequestFactory; + + @Test + public void restTemplateIsCreated() throws Exception { + RestTemplate restTemplate = + CredHubClient.createRestTemplate(CREDHUB_URI, clientHttpRequestFactory); + + assertThat(restTemplate.getUriTemplateHandler(), instanceOf(AbstractUriTemplateHandler.class)); + + AbstractUriTemplateHandler uriTemplateHandler = (AbstractUriTemplateHandler) restTemplate.getUriTemplateHandler(); + assertThat(uriTemplateHandler.getBaseUrl(), equalTo(CREDHUB_URI)); + } +} \ No newline at end of file diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubTemplateTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubTemplateTests.java new file mode 100644 index 0000000..e358ca2 --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/core/CredHubTemplateTests.java @@ -0,0 +1,269 @@ +/* + * 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 org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.credhub.support.CredentialDataResponse; +import org.springframework.credhub.support.CredentialData; +import org.springframework.credhub.support.FindResponse; +import org.springframework.credhub.support.SimpleCredentialName; +import org.springframework.credhub.support.WriteRequest; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.credhub.core.CredHubTemplate.BASE_URL_PATH; +import static org.springframework.credhub.core.CredHubTemplate.NAME_LIKE_URL_QUERY; +import static org.springframework.credhub.core.CredHubTemplate.NAME_URL_QUERY; +import static org.springframework.credhub.core.CredHubTemplate.PATH_URL_QUERY; +import static org.springframework.credhub.core.CredHubTemplate.ID_URL_PATH; +import static org.springframework.http.HttpMethod.PUT; +import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.HttpStatus.UNAUTHORIZED; + +@RunWith(MockitoJUnitRunner.class) +public class CredHubTemplateTests { + private static final String CREDENTIAL_ID = "1111-1111-1111-1111"; + private static final String CREDENTIAL_VALUE = "secret"; + + @Mock + private RestTemplate restTemplate; + + private CredHubTemplate credHubTemplate; + + private SimpleCredentialName name; + + private CredentialDataResponse dataResponse; + private CredentialDataResponse dataResponseWithError; + + private FindResponse findResponse; + private FindResponse findResponseWithError; + + @Before + public void setUp() { + credHubTemplate = new CredHubTemplate(restTemplate); + + name = SimpleCredentialName.builder().segments("example", "credential").build(); + + dataResponse = CredentialDataResponse.builder() + .datum(CredentialData.builder() + .name(name) + .id(CREDENTIAL_ID) + .passwordValue(CREDENTIAL_VALUE) + .build()) + .build(); + + dataResponseWithError = CredentialDataResponse.builder() + .errorMessage("errorMessage message") + .build(); + + findResponse = FindResponse.builder() + .foundCredential(FindResponse.FoundCredential.builder() + .name(name) + .versionCreatedAt("") + .build()) + .build(); + + findResponseWithError = FindResponse.builder() + .errorMessage("errorMessage message") + .build(); + } + + @Test + public void writeWithSuccess() { + WriteRequest request = WriteRequest.builder() + .name(name) + .passwordValue("secret") + .build(); + + when(restTemplate.exchange(BASE_URL_PATH, PUT, new HttpEntity(request), CredentialDataResponse.class)) + .thenReturn(new ResponseEntity(dataResponse, OK)); + + CredentialData response = credHubTemplate.write(request); + + assertThat(response, notNullValue()); + assertThat(response, equalTo(dataResponse.getData().get(0))); + } + + @Test + public void writeWithErrorResponse() { + WriteRequest request = WriteRequest.builder() + .name(name) + .passwordValue("secret") + .build(); + + when(restTemplate.exchange(BASE_URL_PATH, PUT, new HttpEntity(request), CredentialDataResponse.class)) + .thenReturn(new ResponseEntity(dataResponseWithError, OK)); + + try { + credHubTemplate.write(request); + fail("Exception should have been thrown"); + } catch (CredHubException e) { + assertThat(e.getMessage(), containsString(": errorMessage message")); + } + } + + @Test + public void writeWithHttpError() { + WriteRequest request = WriteRequest.builder() + .name(name) + .passwordValue("secret") + .build(); + + when(restTemplate.exchange(BASE_URL_PATH, PUT, new HttpEntity(request), CredentialDataResponse.class)) + .thenReturn(new ResponseEntity(dataResponseWithError, HttpStatus.UNAUTHORIZED)); + + try { + credHubTemplate.write(request); + fail("Exception should have been thrown"); + } catch (CredHubException e) { + assertThat(e.getMessage(), containsString(": " + HttpStatus.UNAUTHORIZED.toString())); + } + } + + @Test + public void getByIdWithSuccess() { + when(restTemplate.getForEntity(ID_URL_PATH, CredentialDataResponse.class, CREDENTIAL_ID)) + .thenReturn(new ResponseEntity(dataResponse, OK)); + + CredentialData response = credHubTemplate.getById(CREDENTIAL_ID); + + assertThat(response, notNullValue()); + assertThat(response, equalTo(dataResponse.getData().get(0))); + } + + @Test + public void getByIdWithError() { + when(restTemplate.getForEntity(ID_URL_PATH, CredentialDataResponse.class, CREDENTIAL_ID)) + .thenReturn(new ResponseEntity(dataResponseWithError, OK)); + + try { + credHubTemplate.getById(CREDENTIAL_ID); + fail("Exception should have been thrown"); + } catch (CredHubException e) { + assertThat(e.getMessage(), containsString(": errorMessage message")); + } + } + + @Test + public void getByIdWithHttpError() { + when(restTemplate.getForEntity(ID_URL_PATH, CredentialDataResponse.class, CREDENTIAL_ID)) + .thenReturn(new ResponseEntity(dataResponseWithError, UNAUTHORIZED)); + + try { + credHubTemplate.getById(CREDENTIAL_ID); + fail("Exception should have been thrown"); + } catch (CredHubException e) { + assertThat(e.getMessage(), containsString(": " + HttpStatus.UNAUTHORIZED.toString())); + } + } + + @Test + public void findByNameWithSuccess() { + when(restTemplate.getForEntity(NAME_LIKE_URL_QUERY, FindResponse.class, name.getName())) + .thenReturn(new ResponseEntity(findResponse, OK)); + + FindResponse response = credHubTemplate.findByName(name.getName()); + + assertThat(response, notNullValue()); + assertThat(response.getFoundCredentials().size(), equalTo(1)); + assertThat(response.getFoundCredentials(), equalTo(findResponse.getFoundCredentials())); + } + + @Test + public void findByNameWithError() { + when(restTemplate.getForEntity(NAME_LIKE_URL_QUERY, FindResponse.class, name.getName())) + .thenReturn(new ResponseEntity(findResponseWithError, OK)); + + try { + credHubTemplate.findByName(name.getName()); + fail("Exception should have been thrown"); + } catch (CredHubException e) { + assertThat(e.getMessage(), containsString(": errorMessage message")); + } + } + + @Test + public void findByNameWithHttpError() { + when(restTemplate.getForEntity(NAME_LIKE_URL_QUERY, FindResponse.class, name.getName())) + .thenReturn(new ResponseEntity(findResponseWithError, UNAUTHORIZED)); + + try { + credHubTemplate.findByName(name.getName()); + fail("Exception should have been thrown"); + } catch (CredHubException e) { + assertThat(e.getMessage(), containsString(": " + HttpStatus.UNAUTHORIZED.toString())); + } + } + + @Test + public void findByPathWithSuccess() { + when(restTemplate.getForEntity(PATH_URL_QUERY, FindResponse.class, name.getName())) + .thenReturn(new ResponseEntity(findResponse, OK)); + + FindResponse response = credHubTemplate.findByPath(name.getName()); + + assertThat(response, notNullValue()); + assertThat(response.getFoundCredentials().size(), equalTo(1)); + assertThat(response.getFoundCredentials(), equalTo(findResponse.getFoundCredentials())); + } + + @Test + public void findByPathWithError() { + when(restTemplate.getForEntity(PATH_URL_QUERY, FindResponse.class, name.getName())) + .thenReturn(new ResponseEntity(findResponseWithError, OK)); + + try { + credHubTemplate.findByPath(name.getName()); + fail("Exception should have been thrown"); + } catch (CredHubException e) { + assertThat(e.getMessage(), containsString(": errorMessage message")); + } + } + + @Test + public void findByPathWithHttpError() { + when(restTemplate.getForEntity(PATH_URL_QUERY, FindResponse.class, name.getName())) + .thenReturn(new ResponseEntity(findResponseWithError, UNAUTHORIZED)); + + try { + credHubTemplate.findByPath(name.getName()); + fail("Exception should have been thrown"); + } catch (CredHubException e) { + assertThat(e.getMessage(), containsString(": " + HttpStatus.UNAUTHORIZED.toString())); + } + } + + @Test + public void deleteByName() { + credHubTemplate.deleteByName(name.getName()); + + verify(restTemplate).delete(NAME_URL_QUERY, name.getName()); + } +} \ No newline at end of file diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/support/CredHubRequestUnitTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/support/CredHubRequestUnitTests.java new file mode 100644 index 0000000..aa2512f --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/support/CredHubRequestUnitTests.java @@ -0,0 +1,34 @@ +/* + * + * * Copyright 2013-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 CredHubRequestUnitTests { + @Test + public void generateNameWithAllFields() { + CredHubRequest request = new CredHubRequest(new CredentialName("service-broker-name", "service-offering-name", + "service-binding-id", "credential-name")); + + assertThat(request.getName(), equalTo("/c/service-broker-name/service-offering-name/service-binding-id/credential-name")); + } +} \ No newline at end of file diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/support/CredentialDataResponseUnitTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/support/CredentialDataResponseUnitTests.java new file mode 100644 index 0000000..550d477 --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/support/CredentialDataResponseUnitTests.java @@ -0,0 +1,120 @@ +/* + * + * * Copyright 2013-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.databind.ObjectMapper; +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +import java.util.Map; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +public class CredentialDataResponseUnitTests { + private ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void deserializationWithPasswordValue() throws Exception { + String json = "{" + + " \"data\": [" + + " {" + + " \"type\": \"password\"," + + " \"version_created_at\": \"2017-04-14T19:37:28Z\"," + + " \"id\": \"80cbb13f-7562-4e72-92de-f3ccf69eaa59\"," + + " \"name\": \"/c/service-broker-name/service-instance-name/binding-id/credentials-json\"," + + " \"value\": \"secret\"" + + " }" + + " ]" + + "}"; + + CredentialDataResponse response = parseResponse(json); + + assertThat(response.getData().size(), equalTo(1)); + + CredentialData data = response.getData().get(0); + + assertThat(data.getValueType(), equalTo(ValueType.PASSWORD)); + assertThat(data.getVersionCreatedAt(), equalTo("2017-04-14T19:37:28Z")); + assertThat(data.getId(), equalTo("80cbb13f-7562-4e72-92de-f3ccf69eaa59")); + assertThat(data.getName().getName(), equalTo("/c/service-broker-name/service-instance-name/binding-id/credentials-json")); + + assertThat(data.getValue(), instanceOf(String.class)); + assertThat(data.getValue(), CoreMatchers.equalTo("secret")); + + assertThat(response.getErrorMessage(), nullValue()); + } + + @Test + public void deserializationWithJsonValue() throws Exception { + String json = "{" + + " \"data\": [" + + " {" + + " \"type\": \"json\"," + + " \"version_created_at\": \"2017-04-14T19:37:28Z\"," + + " \"id\": \"80cbb13f-7562-4e72-92de-f3ccf69eaa59\"," + + " \"name\": \"/c/service-broker-name/service-instance-name/binding-id/credentials-json\"," + + " \"value\": {" + + " \"client_id\": \"test-id\"," + + " \"client_secret\": \"test-secret\"," + + " \"uri\": \"https://example.com\"" + + " }" + + " }" + + " ]" + + "}"; + + CredentialDataResponse response = parseResponse(json); + + assertThat(response.getData().size(), equalTo(1)); + + CredentialData data = response.getData().get(0); + + assertThat(data.getValueType(), equalTo(ValueType.JSON)); + assertThat(data.getVersionCreatedAt(), equalTo("2017-04-14T19:37:28Z")); + assertThat(data.getId(), equalTo("80cbb13f-7562-4e72-92de-f3ccf69eaa59")); + assertThat(data.getName().getName(), equalTo("/c/service-broker-name/service-instance-name/binding-id/credentials-json")); + + assertThat(data.getValue(), instanceOf(Map.class)); + Map valueMap = (Map) data.getValue(); + assertThat(valueMap.get("client_id"), CoreMatchers.equalTo("test-id")); + assertThat(valueMap.get("client_secret"), CoreMatchers.equalTo("test-secret")); + assertThat(valueMap.get("uri"), CoreMatchers.equalTo("https://example.com")); + + + assertThat(response.getErrorMessage(), nullValue()); + } + + @Test + public void deserializationWithError() throws Exception { + String json = "{" + + " \"errorMessage\": \"some errorMessage text\"" + + "}"; + + CredentialDataResponse response = parseResponse(json); + assertThat(response.getErrorMessage(), equalTo("some errorMessage text")); + + assertThat(response.getData(), nullValue()); + } + + private CredentialDataResponse parseResponse(String json) throws java.io.IOException { + return objectMapper.readValue(json, CredentialDataResponse.class); + } +} diff --git a/spring-credhub-core/src/test/java/org/springframework/credhub/support/WriteRequestUnitTests.java b/spring-credhub-core/src/test/java/org/springframework/credhub/support/WriteRequestUnitTests.java new file mode 100644 index 0000000..5e54e94 --- /dev/null +++ b/spring-credhub-core/src/test/java/org/springframework/credhub/support/WriteRequestUnitTests.java @@ -0,0 +1,149 @@ +/* + * + * * Copyright 2013-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.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; + +import static org.springframework.credhub.support.AccessControlEntry.Operation.READ; +import static org.springframework.credhub.support.AccessControlEntry.Operation.WRITE; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.valid4j.matchers.jsonpath.JsonPathMatchers.hasJsonPath; +import static org.valid4j.matchers.jsonpath.JsonPathMatchers.hasNoJsonPath; +import static org.valid4j.matchers.jsonpath.JsonPathMatchers.isJson; + +public class WriteRequestUnitTests { + + private ObjectMapper mapper; + + private WriteRequest.WriteRequestBuilder requestBuilder; + + @Before + public void setUp() { + mapper = new ObjectMapper(); + + requestBuilder = WriteRequest.builder() + .name(SimpleCredentialName.builder() + .segments("example", "credential") + .build()); + } + + @Test + public void typeIsSerializable() { + assertTrue(mapper.canSerialize(WriteRequest.class)); + } + + @Test + public void serializationWithJsonValue() throws Exception { + requestBuilder + .jsonValue(new HashMap() {{ + put("data", "value"); + put("test", true); + }}); + + String jsonValue = serializeToJson(requestBuilder); + + assertThat(jsonValue, allOf( + hasJsonPath("$.overwrite", equalTo(false)), + hasJsonPath("$.name", equalTo("/c/example/credential")), + hasJsonPath("$.type", equalTo("json")), + hasJsonPath("$.value.data", equalTo("value")), + hasJsonPath("$.value.test", equalTo(true)) + )); + + assertThat(jsonValue, hasNoJsonPath("$.access_control_entries")); + } + + @Test + public void serializationWithPasswordValue() throws Exception { + requestBuilder + .overwrite(true) + .passwordValue("secret"); + + String jsonValue = serializeToJson(requestBuilder); + + assertThat(jsonValue, allOf( + hasJsonPath("$.overwrite", equalTo(true)), + hasJsonPath("$.name", equalTo("/c/example/credential")), + hasJsonPath("$.type", equalTo("password")), + hasJsonPath("$.value", equalTo("secret")) + )); + + assertThat(jsonValue, hasNoJsonPath("$.access_control_entries")); + } + + @Test + public void serializationWithOneAccessControl() throws Exception { + requestBuilder + .passwordValue("secret") + .accessControlEntry(AccessControlEntry.builder() + .app("app-id") + .operation(READ) + .build()); + + String jsonValue = serializeToJson(requestBuilder); + + assertThat(jsonValue, allOf( + hasJsonPath("$.access_control_entries[0].actor", equalTo("mtls-app:app-id")), + hasJsonPath("$.access_control_entries[0].operations[0]", equalTo("read")) + )); + } + + @Test + public void serializationWithTwoAccessControls() throws Exception { + requestBuilder + .passwordValue("secret") + .accessControlEntry(AccessControlEntry.builder() + .app("app1-id") + .operation(READ) + .operation(WRITE) + .build()) + .accessControlEntry(AccessControlEntry.builder() + .app("app2-id") + .operation(WRITE) + .operation(READ) + .build()); + + String jsonValue = serializeToJson(requestBuilder); + + assertThat(jsonValue, allOf( + hasJsonPath("$.access_control_entries[0].actor", equalTo("mtls-app:app1-id")), + hasJsonPath("$.access_control_entries[0].operations[0]", equalTo("read")), + hasJsonPath("$.access_control_entries[0].operations[1]", equalTo("write")), + hasJsonPath("$.access_control_entries[1].actor", equalTo("mtls-app:app2-id")), + hasJsonPath("$.access_control_entries[1].operations[0]", equalTo("write")), + hasJsonPath("$.access_control_entries[1].operations[1]", equalTo("read")) + )); + } + + private String serializeToJson(WriteRequest.WriteRequestBuilder requestBuilder) throws JsonProcessingException { + String jsonValue = mapper.writeValueAsString(requestBuilder.build()); + assertThat(jsonValue, isJson()); + return jsonValue; + } + +} \ No newline at end of file diff --git a/spring-credhub-dependencies/pom.xml b/spring-credhub-dependencies/pom.xml new file mode 100644 index 0000000..efcb7d1 --- /dev/null +++ b/spring-credhub-dependencies/pom.xml @@ -0,0 +1,128 @@ + + + 4.0.0 + + org.springframework.credhub + spring-credhub-dependencies + 1.0.0.BUILD-SNAPSHOT + pom + + spring-credhub-dependencies + Spring CredHub Dependencies + + http://projects.spring.io/spring-credhub/ + + 2017 + + + Pivotal Software, Inc. + http://www.spring.io + + + + https://github.com/spring-projects/spring-credhub + scm:git:git://github.com/spring-projects/spring-credhub.git + scm:git:ssh://git@github.com/spring-projects/spring-credhub.git + HEAD + + + + GitHub + https://github.com/spring-projects/spring-credhub/issues + + + + Bamboo + https://build.spring.io/browse/SPRINGCREDHUB-CREDHUB + + + + + sfrederick + Scott Frederick + + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + + + + + 2.8.7 + 4.5.3 + 1.56 + + + + + + + + + org.springframework.credhub + spring-credhub-core + 1.0.0.BUILD-SNAPSHOT + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + true + + + commons-logging + commons-logging + + + + + + org.bouncycastle + bcpkix-jdk15on + ${bouncycastle.version} + + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + + + + + milestone + + + + repo.spring.io + Spring Milestone Repository + http://repo.spring.io/libs-milestone-local + + + + + + +