From ec01ee143bde1e78ceb5493bd297d4c4f5ec60e7 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Mon, 14 Jan 2019 14:45:56 -0600 Subject: [PATCH] Set binding credential permissions in CredHub. Delete credentials on unbind. Fixes #190 --- .../CredHubAutoConfiguration.java | 7 ++ .../CredHubAutoConfigurationTest.java | 7 +- ...WorkflowServiceInstanceBindingService.java | 2 - ...CreateBindingWithCredHubComponentTest.java | 108 ++++++++++++++++ ...DeleteBindingWithCredHubComponentTest.java | 71 +++++++++++ .../integration/WiremockComponentTest.java | 4 +- .../fixtures/CredHubStubFixture.java | 48 +++++++ .../fixtures/OpenServiceBrokerApiFixture.java | 60 ++++++--- ...TestBindingCredentialsProviderFixture.java | 55 ++++++++ .../fixtures/WiremockServerFixture.java | 16 ++- .../responses/credhub/get-data-find.json | 8 ++ .../responses/credhub/post-permission.json | 6 + .../responses/credhub/put-data-json.json | 14 +++ ...eateServiceInstanceAppBindingWorkflow.java | 105 +++++++++++----- ...gDeleteServiceInstanceBindingWorkflow.java | 67 ++++++++++ .../binding/CredHubPersistingWorkflow.java | 38 ++++++ ...ServiceInstanceAppBindingWorkflowTest.java | 24 +++- ...eteServiceInstanceBindingWorkflowTest.java | 117 ++++++++++++++++++ 18 files changed, 697 insertions(+), 60 deletions(-) create mode 100644 spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/CreateBindingWithCredHubComponentTest.java create mode 100644 spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/DeleteBindingWithCredHubComponentTest.java create mode 100644 spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/TestBindingCredentialsProviderFixture.java create mode 100644 spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/get-data-find.json create mode 100644 spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/post-permission.json create mode 100644 spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/put-data-json.json create mode 100644 spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingDeleteServiceInstanceBindingWorkflow.java create mode 100644 spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingWorkflow.java create mode 100644 spring-cloud-app-broker-security-credhub/src/test/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingDeleteServiceInstanceBindingWorkflowTest.java diff --git a/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/CredHubAutoConfiguration.java b/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/CredHubAutoConfiguration.java index 6b702f5..88d9395 100644 --- a/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/CredHubAutoConfiguration.java +++ b/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/CredHubAutoConfiguration.java @@ -23,7 +23,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.cloud.appbroker.extensions.credentials.CredHubCredentialsGenerator; import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindingWorkflow; +import org.springframework.cloud.appbroker.service.DeleteServiceInstanceBindingWorkflow; import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingCreateServiceInstanceAppBindingWorkflow; +import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingDeleteServiceInstanceBindingWorkflow; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.credhub.autoconfig.CredHubTemplateAutoConfiguration; @@ -44,6 +46,11 @@ public class CredHubAutoConfiguration { return new CredHubPersistingCreateServiceInstanceAppBindingWorkflow(credHubOperations, appName); } + @Bean + public DeleteServiceInstanceBindingWorkflow credhubPersistingDeleteServiceInstanceAppBindingWorkflow(CredHubOperations credHubOperations) { + return new CredHubPersistingDeleteServiceInstanceBindingWorkflow(credHubOperations, appName); + } + @Bean public CredHubCredentialsGenerator credHubCredentialsGenerator(CredHubOperations credHubOperations) { return new CredHubCredentialsGenerator(credHubOperations); diff --git a/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/CredHubAutoConfigurationTest.java b/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/CredHubAutoConfigurationTest.java index 342f1d4..69b66bf 100644 --- a/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/CredHubAutoConfigurationTest.java +++ b/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/CredHubAutoConfigurationTest.java @@ -25,6 +25,7 @@ import org.springframework.cloud.appbroker.extensions.credentials.CredHubCredent import org.springframework.cloud.appbroker.extensions.credentials.CredentialGenerator; import org.springframework.cloud.appbroker.extensions.credentials.SimpleCredentialGenerator; import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingCreateServiceInstanceAppBindingWorkflow; +import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingDeleteServiceInstanceBindingWorkflow; import org.springframework.context.annotation.Bean; import org.springframework.credhub.core.CredHubOperations; @@ -57,7 +58,8 @@ class CredHubAutoConfigurationTest { .isExactlyInstanceOf(SimpleCredentialGenerator.class); assertThat(context) - .doesNotHaveBean(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class); + .doesNotHaveBean(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class) + .doesNotHaveBean(CredHubPersistingDeleteServiceInstanceBindingWorkflow.class); }); } @@ -71,7 +73,8 @@ class CredHubAutoConfigurationTest { .isExactlyInstanceOf(CredHubCredentialsGenerator.class); assertThat(context) - .hasSingleBean(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class); + .hasSingleBean(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class) + .hasSingleBean(CredHubPersistingDeleteServiceInstanceBindingWorkflow.class); }); } diff --git a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/service/WorkflowServiceInstanceBindingService.java b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/service/WorkflowServiceInstanceBindingService.java index 861aabb..3e2d04a 100644 --- a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/service/WorkflowServiceInstanceBindingService.java +++ b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/service/WorkflowServiceInstanceBindingService.java @@ -111,13 +111,11 @@ public class WorkflowServiceInstanceBindingService implements ServiceInstanceBin private boolean isAppBindingRequest(CreateServiceInstanceBindingRequest request) { return request.getBindResource() != null - && StringUtils.hasText(request.getBindResource().getAppGuid()) && StringUtils.isEmpty(request.getBindResource().getRoute()); } private boolean isRouteBindingRequest(CreateServiceInstanceBindingRequest request) { return request.getBindResource() != null - && StringUtils.isEmpty(request.getBindResource().getAppGuid()) && StringUtils.hasText(request.getBindResource().getRoute()); } diff --git a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/CreateBindingWithCredHubComponentTest.java b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/CreateBindingWithCredHubComponentTest.java new file mode 100644 index 0000000..0594ef1 --- /dev/null +++ b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/CreateBindingWithCredHubComponentTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 2016-2018 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.cloud.appbroker.integration; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.appbroker.integration.fixtures.CredHubStubFixture; +import org.springframework.cloud.appbroker.integration.fixtures.OpenServiceBrokerApiFixture; +import org.springframework.cloud.appbroker.integration.fixtures.TestBindingCredentialsProviderFixture; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.TestPropertySource; + +import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; +import static io.restassured.RestAssured.given; +import static org.springframework.cloud.appbroker.integration.CreateInstanceWithBasicAuthCredentialsComponentTest.APP_NAME; + +@TestPropertySource(properties = { + "spring.cloud.appbroker.services[0].service-name=example", + "spring.cloud.appbroker.services[0].plan-name=standard", + "spring.cloud.appbroker.services[0].apps[0].path=classpath:demo.jar", + "spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME, + + "spring.credhub.url=http://localhost:8888" +}) +class CreateBindingWithCredHubComponentTest extends WiremockComponentTest { + + private static final String SERVICE_INSTANCE_ID = "instance-id"; + private static final String BINDING_ID = "binding-id"; + private static final String CREDENTIAL_NAME = "credentials-json"; + private static final String APP_ID = "app-id"; + + @Autowired + private OpenServiceBrokerApiFixture brokerFixture; + + @Autowired + private CredHubStubFixture credHubFixture; + + @Autowired + private TestBindingCredentialsProviderFixture bindingFixture; + + @Value("${spring.application.name}") + private String brokerAppName; + + @Value("${spring.cloud.openservicebroker.catalog.services[0].id}") + String serviceDefinitionId; + + @Test + void createAppBindingWithCredHub() { + String credentialName = credHubFixture.bindingCredentialName(brokerAppName, serviceDefinitionId, + BINDING_ID, CREDENTIAL_NAME); + + credHubFixture.stubWriteCredential(credentialName, + matchingJsonPath("$.[?(@.value.credential1 == '" + + bindingFixture.getCredentials().get("credential1") + + "')]"), + matchingJsonPath("$.[?(@.value.credential2 == '" + + bindingFixture.getCredentials().get("credential2") + + "')]")); + + credHubFixture.stubAddAppPermission(credentialName, "mtls-app:" + APP_ID); + + // when a service binding is created + given(brokerFixture.serviceAppBindingRequest()) + .when() + .put(brokerFixture.createBindingUrl(), SERVICE_INSTANCE_ID, BINDING_ID) + .then() + .statusCode(HttpStatus.CREATED.value()); + } + + @Test + void createServiceKeyWithCredHub() { + String credentialName = credHubFixture.bindingCredentialName(brokerAppName, serviceDefinitionId, + BINDING_ID, CREDENTIAL_NAME); + + credHubFixture.stubWriteCredential(credentialName, + matchingJsonPath("$.[?(@.value.credential1 == '" + + bindingFixture.getCredentials().get("credential1") + + "')]"), + matchingJsonPath("$.[?(@.value.credential2 == '" + + bindingFixture.getCredentials().get("credential2") + + "')]")); + + credHubFixture.stubAddAppPermission(credentialName, "uaa-client:service-key-client-id"); + + // when a service binding is created + given(brokerFixture.serviceKeyRequest()) + .when() + .put(brokerFixture.createBindingUrl(), SERVICE_INSTANCE_ID, BINDING_ID) + .then() + .statusCode(HttpStatus.CREATED.value()); + } + +} \ No newline at end of file diff --git a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/DeleteBindingWithCredHubComponentTest.java b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/DeleteBindingWithCredHubComponentTest.java new file mode 100644 index 0000000..b1c7a31 --- /dev/null +++ b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/DeleteBindingWithCredHubComponentTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2016-2018 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.cloud.appbroker.integration; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.appbroker.integration.fixtures.CredHubStubFixture; +import org.springframework.cloud.appbroker.integration.fixtures.OpenServiceBrokerApiFixture; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.TestPropertySource; + +import static io.restassured.RestAssured.given; +import static org.springframework.cloud.appbroker.integration.CreateInstanceWithBasicAuthCredentialsComponentTest.APP_NAME; + +@TestPropertySource(properties = { + "spring.cloud.appbroker.services[0].service-name=example", + "spring.cloud.appbroker.services[0].plan-name=standard", + "spring.cloud.appbroker.services[0].apps[0].path=classpath:demo.jar", + "spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME, + + "spring.credhub.url=http://localhost:8888" +}) +class DeleteBindingWithCredHubComponentTest extends WiremockComponentTest { + + private static final String SERVICE_INSTANCE_ID = "instance-id"; + private static final String BINDING_ID = "binding-id"; + private static final String CREDENTIAL_NAME = "credentials-json"; + + @Autowired + private OpenServiceBrokerApiFixture brokerFixture; + + @Autowired + private CredHubStubFixture credHubFixture; + + @Value("${spring.application.name}") + private String brokerAppName; + + @Value("${spring.cloud.openservicebroker.catalog.services[0].id}") + String serviceDefinitionId; + + @Test + void deleteAppBindingWithCredHub() { + String credentialName = credHubFixture.bindingCredentialName(brokerAppName, serviceDefinitionId, + BINDING_ID, CREDENTIAL_NAME); + + credHubFixture.stubFindCredential(credentialName); + credHubFixture.stubDeleteCredential(credentialName); + + // when a service binding is deleted + given(brokerFixture.serviceAppBindingRequest()) + .when() + .delete(brokerFixture.deleteBindingUrl(), SERVICE_INSTANCE_ID, BINDING_ID) + .then() + .statusCode(HttpStatus.OK.value()); + } +} \ No newline at end of file diff --git a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/WiremockComponentTest.java b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/WiremockComponentTest.java index 5a5d973..4cda9af 100644 --- a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/WiremockComponentTest.java +++ b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/WiremockComponentTest.java @@ -27,6 +27,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.appbroker.integration.fixtures.CloudControllerStubFixture; import org.springframework.cloud.appbroker.integration.fixtures.CredHubStubFixture; import org.springframework.cloud.appbroker.integration.fixtures.OpenServiceBrokerApiFixture; +import org.springframework.cloud.appbroker.integration.fixtures.TestBindingCredentialsProviderFixture; import org.springframework.cloud.appbroker.integration.fixtures.UaaStubFixture; import org.springframework.cloud.appbroker.integration.fixtures.WiremockServerFixture; import org.springframework.test.context.ActiveProfiles; @@ -40,7 +41,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; OpenServiceBrokerApiFixture.class, CloudControllerStubFixture.class, UaaStubFixture.class, - CredHubStubFixture.class}, + CredHubStubFixture.class, + TestBindingCredentialsProviderFixture.class}, properties = { "spring.cloud.appbroker.deployer.cloudfoundry.api-host=localhost", "spring.cloud.appbroker.deployer.cloudfoundry.api-port=8080", diff --git a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/CredHubStubFixture.java b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/CredHubStubFixture.java index 82d1cd6..c6fe152 100644 --- a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/CredHubStubFixture.java +++ b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/CredHubStubFixture.java @@ -16,14 +16,18 @@ package org.springframework.cloud.appbroker.integration.fixtures; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.matching.ContentPattern; import org.springframework.boot.test.context.TestComponent; import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; import static com.github.tomakehurst.wiremock.client.WireMock.noContent; import static com.github.tomakehurst.wiremock.client.WireMock.ok; import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.put; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @TestComponent @@ -32,6 +36,45 @@ public class CredHubStubFixture extends WiremockStubFixture { super(8888); } + public void stubWriteCredential(String credentialName, ContentPattern... appMetadataPatterns) { + MappingBuilder mappingBuilder = put(urlPathEqualTo("/api/v1/data")) + .withRequestBody(matchingJsonPath("$.[?(@.name == '" + credentialName + "')]")) + .withRequestBody(matchingJsonPath("$.[?(@.type == 'json')]")); + for (ContentPattern appMetadataPattern : appMetadataPatterns) { + mappingBuilder.withRequestBody(appMetadataPattern); + } + stubFor(mappingBuilder + .willReturn(ok() + .withHeader("Content-type", "application/json") + .withBody(credhub("put-data-json")))); + } + + public void stubFindCredential(String credentialName) { + stubFor(get(urlPathEqualTo("/api/v1/data")) + .withQueryParam("name-like", equalTo(credentialName)) + .willReturn(ok() + .withHeader("Content-type", "application/json") + .withBody(credhub("get-data-find")))); + } + + public void stubDeleteCredential(String credentialName) { + stubFor(delete(urlPathEqualTo("/api/v1/data")) + .withQueryParam("name", equalTo(credentialName)) + .willReturn(noContent() + .withHeader("Content-type", "application/json") + .withBody(credhub("put-data-json")))); + } + + public void stubAddAppPermission(String path, String actor) { + stubFor(post(urlPathEqualTo("/api/v2/permissions")) + .withRequestBody(matchingJsonPath("$.[?(@.path == '" + path + "')]")) + .withRequestBody(matchingJsonPath("$.[?(@.actor == '" + actor + "')]")) + .withRequestBody(matchingJsonPath("$.[?(@.operations[0] == 'read')]")) + .willReturn(ok() + .withHeader("Content-type", "application/json") + .withBody(credhub("post-permission")))); + } + public void stubGenerateUser(String appId, String serviceInstanceId, String descriptor, int length) { stubGenerate(appId, serviceInstanceId, descriptor, length, "user", "post-data-user"); @@ -64,6 +107,11 @@ public class CredHubStubFixture extends WiremockStubFixture { .willReturn(noContent())); } + public String bindingCredentialName(String brokerAppName, String serviceDefinitionId, + String bindingId, String credentialName) { + return "/c/" + brokerAppName + "/" + serviceDefinitionId + "/" + bindingId + "/" + credentialName; + } + private String credhub(String fileRoot) { return readResponseFromFile(fileRoot, "credhub"); } diff --git a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/OpenServiceBrokerApiFixture.java b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/OpenServiceBrokerApiFixture.java index ff4f7a8..51f6332 100644 --- a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/OpenServiceBrokerApiFixture.java +++ b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/OpenServiceBrokerApiFixture.java @@ -36,6 +36,8 @@ import static io.restassured.RestAssured.with; public class OpenServiceBrokerApiFixture implements ApplicationListener { private static final String ORG_ID = "org-id"; private static final String SPACE_ID = "space-id"; + private static final String APP_ID = "app-id"; + private static final String SERVICE_KEY_CLIENT_ID = "service-key-client-id"; @Value("${spring.cloud.openservicebroker.catalog.services[0].plans[0].id}") String planId; @@ -59,20 +61,57 @@ public class OpenServiceBrokerApiFixture implements ApplicationListener params) { String stringParams = new JSONObject(params).toString(); return serviceBrokerSpecification() .body("{" + - " \"service_id\": \"" + serviceDefinitionId + "\"," + - " \"plan_id\": \"" + planId + "\"," + - " \"organization_guid\": \"" + ORG_ID + "\"," + - " \"space_guid\": \"" + SPACE_ID + "\"," + - " \"parameters\": " + stringParams + + "\"service_id\": \"" + serviceDefinitionId + "\"," + + "\"plan_id\": \"" + planId + "\"," + + "\"organization_guid\": \"" + ORG_ID + "\"," + + "\"space_guid\": \"" + SPACE_ID + "\"," + + "\"parameters\": " + stringParams + + "}"); + } + + public RequestSpecification serviceAppBindingRequest() { + return serviceBrokerSpecification() + .body("{" + + "\"service_id\": \"" + serviceDefinitionId + "\"," + + "\"plan_id\": \"" + planId + "\"," + + "\"bind_resource\": {" + + "\"app_guid\": \"" + APP_ID + "\"" + + "}" + + "}"); + } + + public RequestSpecification serviceKeyRequest() { + return serviceBrokerSpecification() + .body("{" + + "\"service_id\": \"" + serviceDefinitionId + "\"," + + "\"plan_id\": \"" + planId + "\"," + + "\"bind_resource\": {" + + "\"credential_client_id\": \"" + SERVICE_KEY_CLIENT_ID + "\"" + + "}" + "}"); } @@ -83,15 +122,6 @@ public class OpenServiceBrokerApiFixture implements ApplicationListener credentials; + + public TestBindingCredentialsProviderFixture() { + this.credentials = new HashMap<>(); + this.credentials.put("credential1", "value1"); + this.credentials.put("credential2", "value2"); + } + + @Override + public Mono accept(CreateServiceInstanceBindingRequest request) { + return Mono.just(true); + } + + @Override + public Mono buildResponse(CreateServiceInstanceBindingRequest request, + CreateServiceInstanceAppBindingResponseBuilder responseBuilder) { + return Mono.just(responseBuilder.credentials(this.credentials)); + } + + public Map getCredentials() { + return credentials; + } +} diff --git a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/WiremockServerFixture.java b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/WiremockServerFixture.java index a319bd4..89eac54 100644 --- a/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/WiremockServerFixture.java +++ b/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/fixtures/WiremockServerFixture.java @@ -60,14 +60,20 @@ public class WiremockServerFixture { } public void verifyAllRequiredStubsUsed() { - Set servedStubIds = ccUaaWiremockServer.getServeEvents().getRequests().stream() + // todo: cleanup CC and UAA stubbing so used/unused can be verified + // verifyStubs(ccUaaWiremockServer); + verifyStubs(credHubWiremockServer); + } + + private void verifyStubs(WireMockServer wireMockServer) { + Set servedStubIds = wireMockServer.getServeEvents().getRequests().stream() .filter(event -> event.getStubMapping() != null) .map(event -> event.getStubMapping().getId()) .collect(Collectors.toSet()); - List unusedStubs = ccUaaWiremockServer.listAllStubMappings().getMappings().stream() + List unusedStubs = wireMockServer.listAllStubMappings().getMappings().stream() .filter(stub -> !servedStubIds.contains(stub.getId())) - .filter(this::stubIsOptional) + .filter(this::stubIsRequired) .collect(Collectors.toList()); assertThat(unusedStubs.size()) @@ -75,10 +81,10 @@ public class WiremockServerFixture { .isEqualTo(0); } - private boolean stubIsOptional(StubMapping stub) { + private boolean stubIsRequired(StubMapping stub) { if (stub.getMetadata() != null) { return !stub.getMetadata().getBoolean("optional"); } - return false; + return true; } } diff --git a/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/get-data-find.json b/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/get-data-find.json new file mode 100644 index 0000000..c42f4b7 --- /dev/null +++ b/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/get-data-find.json @@ -0,0 +1,8 @@ +{ + "credentials": [ + { + "version_created_at": "2017-05-09T21:09:26Z", + "name": "/example-json" + } + ] +} \ No newline at end of file diff --git a/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/post-permission.json b/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/post-permission.json new file mode 100644 index 0000000..339d916 --- /dev/null +++ b/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/post-permission.json @@ -0,0 +1,6 @@ +{ + "uuid": "example-uuid", + "path": "/example-directory/*", + "actor": "uaa-user:106f52e2-5d01-4675-8d7a-c05ff9a2c081", + "operations": ["read","write"] +} \ No newline at end of file diff --git a/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/put-data-json.json b/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/put-data-json.json new file mode 100644 index 0000000..d19482d --- /dev/null +++ b/spring-cloud-app-broker-integration-tests/src/test/resources/responses/credhub/put-data-json.json @@ -0,0 +1,14 @@ +{ + "type": "json", + "version_created_at": "2017-01-01T04:07:18Z", + "id": "67fc3def-bbfb-4953-83f8-4ab0682ad675", + "name": "/example-json", + "value": { + "key": 123, + "key_list": [ + "val1", + "val2" + ], + "is_true": true + } +} \ No newline at end of file diff --git a/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingCreateServiceInstanceAppBindingWorkflow.java b/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingCreateServiceInstanceAppBindingWorkflow.java index af5def9..6792a58 100644 --- a/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingCreateServiceInstanceAppBindingWorkflow.java +++ b/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingCreateServiceInstanceAppBindingWorkflow.java @@ -16,75 +16,112 @@ package org.springframework.cloud.appbroker.workflow.binding; +import org.springframework.credhub.support.permissions.Operation; import reactor.core.publisher.Mono; import reactor.util.Logger; import reactor.util.Loggers; +import org.springframework.cloud.servicebroker.model.binding.BindResource; import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindingWorkflow; import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse; import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse.CreateServiceInstanceAppBindingResponseBuilder; import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest; import org.springframework.core.annotation.Order; import org.springframework.credhub.core.CredHubOperations; -import org.springframework.credhub.support.ServiceInstanceCredentialName; +import org.springframework.credhub.support.CredentialName; +import org.springframework.credhub.support.permissions.Permission; import org.springframework.credhub.support.json.JsonCredentialRequest; import org.springframework.util.CollectionUtils; @Order(50) -public class CredHubPersistingCreateServiceInstanceAppBindingWorkflow implements CreateServiceInstanceAppBindingWorkflow { +public class CredHubPersistingCreateServiceInstanceAppBindingWorkflow + extends CredHubPersistingWorkflow + implements CreateServiceInstanceAppBindingWorkflow { private static final Logger LOG = Loggers.getLogger(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class); - private static final String CREDENTIALS_KEY = "credhub-ref"; + private static final String CREDHUB_REF_KEY = "credhub-ref"; - private static final String CREDENTIALS_NAME = "credentials-json"; - - private final String appName; + private static final String CREDENTIAL_CLIENT_ID = "credential_client_id"; private final CredHubOperations credHubOperations; public CredHubPersistingCreateServiceInstanceAppBindingWorkflow(CredHubOperations credHubOperations, String appName) { + super(appName); this.credHubOperations = credHubOperations; - this.appName = appName; } @Override public Mono buildResponse(CreateServiceInstanceBindingRequest request, CreateServiceInstanceAppBindingResponseBuilder responseBuilder) { return Mono.just(responseBuilder.build()) - .flatMap(response -> { - if (!CollectionUtils.isEmpty(response.getCredentials())) { - return persistBindingCredentials(request, response) - .doOnRequest(l -> LOG.debug("Storing binding credentials in CredHub")) - .doOnSuccess(r -> LOG.debug("Finished storing binding credentials in CredHub")) - .doOnError(exception -> LOG.debug("Error storing binding credentials in CredHub with error {}", exception)); - } - return Mono.just(responseBuilder); - }); + .flatMap(response -> { + if (!CollectionUtils.isEmpty(response.getCredentials())) { + return buildCredentialName(request.getServiceDefinitionId(), request.getBindingId()) + .flatMap(credentialName -> persistBindingCredentials(request, response, credentialName) + .doOnRequest(l -> LOG.debug("Storing binding credentials with name '{}' in CredHub", credentialName.getName())) + .doOnSuccess(r -> LOG.debug("Finished storing binding credentials with name '{}' in CredHub", credentialName.getName())) + .doOnError(exception -> LOG.error("Error storing binding credentials with name '{}' in CredHub with error: {}", + credentialName.getName(), exception.getMessage()))); + } + return Mono.just(responseBuilder); + }); } private Mono persistBindingCredentials(CreateServiceInstanceBindingRequest request, - CreateServiceInstanceAppBindingResponse response) { - return Mono.just(ServiceInstanceCredentialName.builder() - .serviceBrokerName(this.appName) - .serviceOfferingName(request.getServiceDefinitionId()) - .serviceBindingId(request.getBindingId()) - .credentialName(CREDENTIALS_NAME) - .build()) - .flatMap(credentialName -> Mono.fromCallable(() -> credHubOperations - .credentials() + CreateServiceInstanceAppBindingResponse response, + CredentialName credentialName) { + return writeCredential(response, credentialName) + .then(writePermissions(request, credentialName)) + .thenReturn(buildReplacementBindingResponse(response, credentialName)); + } + + private Mono writeCredential(CreateServiceInstanceAppBindingResponse response, + CredentialName credentialName) { + return Mono.fromCallable(() -> { + credHubOperations.credentials() .write(JsonCredentialRequest.builder() .name(credentialName) .value(response.getCredentials()) - .build())) - .thenReturn(CreateServiceInstanceAppBindingResponse - .builder() - .async(response.isAsync()) - .bindingExisted(response.isBindingExisted()) - .credentials(CREDENTIALS_KEY, credentialName.getName()) - .operation(response.getOperation()) - .syslogDrainUrl(response.getSyslogDrainUrl()) - .volumeMounts(response.getVolumeMounts()))); + .build()); + return null; + }); + } + + private Mono writePermissions(CreateServiceInstanceBindingRequest request, + CredentialName credentialName) { + return Mono.fromCallable(() -> { + BindResource bindResource = request.getBindResource(); + + if (bindResource.getAppGuid() != null) { + Permission permission = Permission.builder() + .app(bindResource.getAppGuid()) + .operation(Operation.READ) + .build(); + credHubOperations.permissionsV2().addPermissions(credentialName, permission); + } + + if (bindResource.getProperty(CREDENTIAL_CLIENT_ID) != null) { + Permission permission = Permission.builder() + .client(bindResource.getProperty(CREDENTIAL_CLIENT_ID).toString()) + .operation(Operation.READ) + .build(); + credHubOperations.permissionsV2().addPermissions(credentialName, permission); + } + + return null; + }); + } + + private CreateServiceInstanceAppBindingResponseBuilder buildReplacementBindingResponse(CreateServiceInstanceAppBindingResponse response, + CredentialName credentialName) { + return CreateServiceInstanceAppBindingResponse.builder() + .async(response.isAsync()) + .bindingExisted(response.isBindingExisted()) + .credentials(CREDHUB_REF_KEY, credentialName.getName()) + .operation(response.getOperation()) + .syslogDrainUrl(response.getSyslogDrainUrl()) + .volumeMounts(response.getVolumeMounts()); } } diff --git a/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingDeleteServiceInstanceBindingWorkflow.java b/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingDeleteServiceInstanceBindingWorkflow.java new file mode 100644 index 0000000..d3df27f --- /dev/null +++ b/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingDeleteServiceInstanceBindingWorkflow.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016-2018 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.cloud.appbroker.workflow.binding; + +import org.springframework.cloud.appbroker.service.DeleteServiceInstanceBindingWorkflow; +import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingRequest; +import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingResponse.DeleteServiceInstanceBindingResponseBuilder; +import org.springframework.core.annotation.Order; +import org.springframework.credhub.core.CredHubOperations; +import org.springframework.credhub.support.CredentialName; +import reactor.core.publisher.Mono; +import reactor.util.Logger; +import reactor.util.Loggers; + +@Order(50) +public class CredHubPersistingDeleteServiceInstanceBindingWorkflow + extends CredHubPersistingWorkflow + implements DeleteServiceInstanceBindingWorkflow { + + private static final Logger LOG = Loggers.getLogger(CredHubPersistingDeleteServiceInstanceBindingWorkflow.class); + + private final CredHubOperations credHubOperations; + + public CredHubPersistingDeleteServiceInstanceBindingWorkflow(CredHubOperations credHubOperations, String appName) { + super(appName); + this.credHubOperations = credHubOperations; + } + + @Override + public Mono buildResponse(DeleteServiceInstanceBindingRequest request, + DeleteServiceInstanceBindingResponseBuilder responseBuilder) { + return buildCredentialName(request.getServiceDefinitionId(), request.getBindingId()) + .filter(this::credentialExists) + .flatMap(credentialName -> deleteBindingCredentials(credentialName) + .doOnRequest(l -> LOG.debug("Deleting binding credentials with name '{}' in CredHub", credentialName.getName())) + .doOnSuccess(r -> LOG.debug("Finished deleting binding credentials with name '{}' in CredHub", credentialName.getName())) + .doOnError(exception -> LOG.error("Error deleting binding credentials with name '{}' in CredHub with error: {}", + credentialName.getName(), exception.getMessage()))) + .thenReturn(responseBuilder); + } + + private boolean credentialExists(CredentialName credentialName) { + return !credHubOperations.credentials().findByName(credentialName).isEmpty(); + } + + private Mono deleteBindingCredentials(CredentialName credentialName) { + return Mono.fromCallable(() -> { + credHubOperations.credentials().deleteByName(credentialName); + return null; + }); + } + +} diff --git a/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingWorkflow.java b/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingWorkflow.java new file mode 100644 index 0000000..24892b6 --- /dev/null +++ b/spring-cloud-app-broker-security-credhub/src/main/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingWorkflow.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016-2018 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.cloud.appbroker.workflow.binding; + +import org.springframework.credhub.support.ServiceInstanceCredentialName; +import reactor.core.publisher.Mono; + +class CredHubPersistingWorkflow { + private static final String CREDENTIALS_NAME = "credentials-json"; + private final String appName; + + CredHubPersistingWorkflow(String appName) { + this.appName = appName; + } + + Mono buildCredentialName(String serviceDefinitionId, String bindingId) { + return Mono.just(ServiceInstanceCredentialName.builder() + .serviceBrokerName(this.appName) + .serviceOfferingName(serviceDefinitionId) + .serviceBindingId(bindingId) + .credentialName(CREDENTIALS_NAME) + .build()); + } +} diff --git a/spring-cloud-app-broker-security-credhub/src/test/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest.java b/spring-cloud-app-broker-security-credhub/src/test/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest.java index 3b26410..e472669 100644 --- a/spring-cloud-app-broker-security-credhub/src/test/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest.java +++ b/spring-cloud-app-broker-security-credhub/src/test/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest.java @@ -25,6 +25,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.cloud.servicebroker.model.binding.BindResource; +import org.springframework.credhub.core.permissionV2.CredHubPermissionV2Operations; +import org.springframework.credhub.support.CredentialName; +import org.springframework.credhub.support.ServiceInstanceCredentialName; import reactor.test.StepVerifier; import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse; @@ -38,6 +42,7 @@ import org.springframework.credhub.support.CredentialDetails; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -51,6 +56,9 @@ class CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest { @Mock private CredHubCredentialOperations credHubCredentialOperations; + @Mock + private CredHubPermissionV2Operations credHubPermissionOperations; + private CredHubPersistingCreateServiceInstanceAppBindingWorkflow workflow; @BeforeEach @@ -96,11 +104,22 @@ class CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest { @Test @SuppressWarnings("serial") void storeCredentialsInCredHub() { + CredentialName credentialName = ServiceInstanceCredentialName.builder() + .serviceBrokerName("test-app-name") + .serviceOfferingName("foo-definition-id") + .serviceBindingId("foo-binding-id") + .credentialName("credentials-json") + .build(); + CreateServiceInstanceBindingRequest request = CreateServiceInstanceBindingRequest .builder() .bindingId("foo-binding-id") .serviceInstanceId("foo-instance-id") .serviceDefinitionId("foo-definition-id") + .bindResource(BindResource.builder() + .appGuid("app-id") + .properties("credential_client_id", "client-id") + .build()) .build(); Map credentials = new HashMap() {{ @@ -125,6 +144,8 @@ class CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest { given(this.credHubOperations.credentials()) .willReturn(credHubCredentialOperations); + given(this.credHubOperations.permissionsV2()) + .willReturn(credHubPermissionOperations); given(this.credHubCredentialOperations.write(any())) .willReturn(new CredentialDetails<>()); @@ -135,7 +156,7 @@ class CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest { CreateServiceInstanceAppBindingResponse response = createServiceInstanceAppBindingResponseBuilder.build(); assertThat(response.isBindingExisted()).isEqualTo(true); assertThat(response.getCredentials()).hasSize(1); - assertThat(response.getCredentials().get("credhub-ref")).isEqualTo("/c/test-app-name/foo-definition-id/foo-binding-id/credentials-json"); + assertThat(response.getCredentials().get("credhub-ref")).isEqualTo(credentialName.getName()); assertThat(response.getSyslogDrainUrl()).isEqualTo("https://logs.example.com"); assertThat(response.getVolumeMounts()).hasSize(4); @@ -143,6 +164,7 @@ class CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest { .verifyComplete(); verify(this.credHubCredentialOperations).write(any()); + verify(this.credHubPermissionOperations, times(2)).addPermissions(any(), any()); verifyNoMoreInteractions(this.credHubCredentialOperations); } } \ No newline at end of file diff --git a/spring-cloud-app-broker-security-credhub/src/test/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingDeleteServiceInstanceBindingWorkflowTest.java b/spring-cloud-app-broker-security-credhub/src/test/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingDeleteServiceInstanceBindingWorkflowTest.java new file mode 100644 index 0000000..1b6174e --- /dev/null +++ b/spring-cloud-app-broker-security-credhub/src/test/java/org/springframework/cloud/appbroker/workflow/binding/CredHubPersistingDeleteServiceInstanceBindingWorkflowTest.java @@ -0,0 +1,117 @@ +/* + * Copyright 2016-2018 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.cloud.appbroker.workflow.binding; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingRequest; +import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingResponse; +import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingResponse.DeleteServiceInstanceBindingResponseBuilder; +import org.springframework.credhub.core.CredHubOperations; +import org.springframework.credhub.core.credential.CredHubCredentialOperations; +import org.springframework.credhub.support.CredentialName; +import org.springframework.credhub.support.CredentialSummary; +import org.springframework.credhub.support.ServiceInstanceCredentialName; +import reactor.test.StepVerifier; + +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +@ExtendWith(MockitoExtension.class) +class CredHubPersistingDeleteServiceInstanceBindingWorkflowTest { + + @Mock + private CredHubOperations credHubOperations; + + @Mock + private CredHubCredentialOperations credHubCredentialOperations; + + private CredHubPersistingDeleteServiceInstanceBindingWorkflow workflow; + + @BeforeEach + void setUp() { + this.workflow = new CredHubPersistingDeleteServiceInstanceBindingWorkflow(credHubOperations, "test-app-name"); + } + + @Test + void deleteCredentialsFromCredHubWhenFound() { + CredentialName credentialName = ServiceInstanceCredentialName.builder() + .serviceBrokerName("test-app-name") + .serviceOfferingName("foo-definition-id") + .serviceBindingId("foo-binding-id") + .credentialName("credentials-json") + .build(); + + DeleteServiceInstanceBindingRequest request = DeleteServiceInstanceBindingRequest + .builder() + .bindingId("foo-binding-id") + .serviceInstanceId("foo-instance-id") + .serviceDefinitionId("foo-definition-id") + .build(); + + DeleteServiceInstanceBindingResponseBuilder responseBuilder = + DeleteServiceInstanceBindingResponse.builder(); + + given(this.credHubOperations.credentials()) + .willReturn(credHubCredentialOperations); + + given(this.credHubCredentialOperations.findByName(credentialName)) + .willReturn(Collections.singletonList(new CredentialSummary(credentialName))); + + StepVerifier + .create(this.workflow.buildResponse(request, responseBuilder)) + .expectNext(responseBuilder) + .verifyComplete(); + + verify(this.credHubCredentialOperations).deleteByName(eq(credentialName)); + verifyNoMoreInteractions(this.credHubCredentialOperations); + } + + @Test + void deleteCredentialsFromCredHubWhenNotFound() { + DeleteServiceInstanceBindingRequest request = DeleteServiceInstanceBindingRequest + .builder() + .bindingId("foo-binding-id") + .serviceInstanceId("foo-instance-id") + .serviceDefinitionId("foo-definition-id") + .build(); + + DeleteServiceInstanceBindingResponseBuilder responseBuilder = + DeleteServiceInstanceBindingResponse.builder(); + + given(this.credHubOperations.credentials()) + .willReturn(credHubCredentialOperations); + + given(this.credHubCredentialOperations.findByName(any())) + .willReturn(Collections.emptyList()); + + StepVerifier + .create(this.workflow.buildResponse(request, responseBuilder)) + .expectNext(responseBuilder) + .verifyComplete(); + + verifyNoMoreInteractions(this.credHubCredentialOperations); + } +} \ No newline at end of file