Removing security-credhub module

This commit is contained in:
Alberto C. Ríos
2023-04-17 11:21:40 +02:00
parent 09d4736ceb
commit 135e3496d1
28 changed files with 0 additions and 1696 deletions

View File

@@ -31,6 +31,5 @@ include "spring-cloud-app-broker-core"
include "spring-cloud-app-broker-autoconfigure"
include "spring-cloud-app-broker-integration-tests"
include "spring-cloud-app-broker-acceptance-tests"
include "spring-cloud-app-broker-security-credhub"
include "spring-cloud-starter-app-broker"
include "spring-cloud-starter-app-broker-cloudfoundry"

View File

@@ -31,11 +31,9 @@ dependencies {
api project(":spring-cloud-app-broker-core")
api project(":spring-cloud-app-broker-deployer")
api project(":spring-cloud-app-broker-deployer-cloudfoundry")
api project(":spring-cloud-app-broker-security-credhub")
api "org.springframework.boot:spring-boot-starter"
api "org.cloudfoundry:cloudfoundry-client-reactor:${cfJavaClientVersion}"
api "org.cloudfoundry:cloudfoundry-operations:${cfJavaClientVersion}"
api "org.springframework.credhub:spring-credhub-starter:${springCredhubVersion}"
testImplementation "org.springframework.boot:spring-boot-starter-test"
testImplementation "org.springframework.boot:spring-boot-starter-webflux"

View File

@@ -1,82 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.autoconfigure;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
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;
import org.springframework.credhub.core.ReactiveCredHubOperations;
/**
* CredHub auto-configuration
*/
@Configuration
@AutoConfigureBefore(AppBrokerAutoConfiguration.class)
@AutoConfigureAfter(CredHubTemplateAutoConfiguration.class)
@ConditionalOnClass(ReactiveCredHubOperations.class)
@ConditionalOnBean(ReactiveCredHubOperations.class)
public class CredHubAutoConfiguration {
@Value("${spring.application.name}")
private String appName;
/**
* Provide a {@link CreateServiceInstanceAppBindingWorkflow} bean
*
* @param credHubOperations the ReactiveCredHubOperations bean
* @return the bean
*/
@Bean
public CreateServiceInstanceAppBindingWorkflow credhubPersistingCreateServiceInstanceAppBindingWorkflow(
ReactiveCredHubOperations credHubOperations) {
return new CredHubPersistingCreateServiceInstanceAppBindingWorkflow(credHubOperations, appName);
}
/**
* Provide a {@link DeleteServiceInstanceBindingWorkflow} bean
*
* @param credHubOperations the ReactiveCredHubOperations bean
* @return the bean
*/
@Bean
public DeleteServiceInstanceBindingWorkflow credhubPersistingDeleteServiceInstanceAppBindingWorkflow(
ReactiveCredHubOperations credHubOperations) {
return new CredHubPersistingDeleteServiceInstanceBindingWorkflow(credHubOperations, appName);
}
/**
* Provide a {@link CredHubCredentialsGenerator} bean
*
* @param credHubOperations the ReactiveCredHubOperations bean
* @return the bean
*/
@Bean
public CredHubCredentialsGenerator credHubCredentialsGenerator(ReactiveCredHubOperations credHubOperations) {
return new CredHubCredentialsGenerator(credHubOperations);
}
}

View File

@@ -1,3 +1,2 @@
org.springframework.cloud.appbroker.autoconfigure.AppBrokerAutoConfiguration
org.springframework.cloud.appbroker.autoconfigure.CloudFoundryAppDeployerAutoConfiguration
org.springframework.cloud.appbroker.autoconfigure.CredHubAutoConfiguration

View File

@@ -1,93 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.autoconfigure;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.appbroker.extensions.credentials.CredHubCredentialsGenerator;
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.ReactiveCredHubOperations;
import org.springframework.credhub.core.ReactiveCredHubTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
class CredHubAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
CloudFoundryAppDeployerAutoConfiguration.class,
AppBrokerAutoConfiguration.class,
CredHubAutoConfiguration.class
))
.withUserConfiguration(CredHubConfiguration.class)
.withPropertyValues(
"spring.cloud.appbroker.deployer.cloudfoundry.api-host=https://api.example.local",
"spring.cloud.appbroker.deployer.cloudfoundry.username=user",
"spring.cloud.appbroker.deployer.cloudfoundry.password=secret"
);
@Test
void servicesAreNotCreatedWithoutCredHubOnClasspath() {
contextRunner
.withClassLoader(new FilteredClassLoader(ReactiveCredHubOperations.class))
.run((context) -> {
assertThat(context)
.hasSingleBean(CredentialGenerator.class)
.getBean(CredentialGenerator.class)
.isExactlyInstanceOf(SimpleCredentialGenerator.class);
assertThat(context)
.doesNotHaveBean(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class)
.doesNotHaveBean(CredHubPersistingDeleteServiceInstanceBindingWorkflow.class);
});
}
@Test
void servicesAreCreatedWithCredHubConfigured() {
contextRunner
.run((context) -> {
assertThat(context)
.hasSingleBean(CredentialGenerator.class)
.getBean(CredentialGenerator.class)
.isExactlyInstanceOf(CredHubCredentialsGenerator.class);
assertThat(context)
.hasSingleBean(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class)
.hasSingleBean(CredHubPersistingDeleteServiceInstanceBindingWorkflow.class);
});
}
@TestConfiguration
public static class CredHubConfiguration {
@Bean
public ReactiveCredHubOperations credHubOperations() {
return new ReactiveCredHubTemplate(WebClient.create());
}
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.CreateBindingWithCredHubComponentTest.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 {
protected static final String APP_NAME = "create-binding-credhub";
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}")
private 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());
}
}

View File

@@ -1,111 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.UaaStubFixture;
import org.springframework.cloud.servicebroker.model.instance.OperationState;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.TestPropertySource;
import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithCredHubCredentialsComponentTest.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.cloud.appbroker.services[0].apps[0].credential-providers[0].name=SpringSecurityBasicAuth",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.length=14",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-uppercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-lowercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-numeric=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-special=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].name=SpringSecurityOAuth2",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.registration=example-app-client",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.client-id=test-client",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.grant-types=[\"client_credentials\"]",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.length=12",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-uppercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-lowercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-numeric=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-special=false",
"spring.credhub.url=http://localhost:8888"
})
@Disabled
// See https://github.com/spring-cloud/spring-cloud-app-broker/issues/315
class CreateInstanceWithCredHubCredentialsComponentTest extends WiremockComponentTest {
protected static final String APP_NAME = "app-with-credentials";
private static final String SERVICE_INSTANCE_ID = "instance-id";
@Autowired
private OpenServiceBrokerApiFixture brokerFixture;
@Autowired
private CloudControllerStubFixture cloudControllerFixture;
@Autowired
private UaaStubFixture uaaFixture;
@Autowired
private CredHubStubFixture credHubFixture;
@Test
void pushAppWithCredentials() {
cloudControllerFixture.stubAppDoesNotExist(APP_NAME);
cloudControllerFixture.stubPushApp(APP_NAME);
uaaFixture.stubCreateClient("test-client");
credHubFixture.stubGenerateUser(APP_NAME, SERVICE_INSTANCE_ID, "basic", 14);
credHubFixture.stubGeneratePassword(APP_NAME, SERVICE_INSTANCE_ID, "oauth2", 12);
// when a service instance is created
given(brokerFixture.serviceInstanceRequest())
.when()
.put(brokerFixture.createServiceInstanceUrl(), SERVICE_INSTANCE_ID)
.then()
.statusCode(HttpStatus.ACCEPTED.value());
// when the "last_operation" API is polled
given(brokerFixture.serviceInstanceRequest())
.when()
.get(brokerFixture.getLastInstanceOperationUrl(), SERVICE_INSTANCE_ID)
.then()
.statusCode(HttpStatus.OK.value())
.body("state", is(equalTo(OperationState.IN_PROGRESS.toString())));
String state = brokerFixture.waitForAsyncOperationComplete(SERVICE_INSTANCE_ID);
assertThat(state).isEqualTo(OperationState.SUCCEEDED.toString());
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.DeleteBindingWithCredHubComponentTest.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 {
protected static final String APP_NAME = "delete-binding-credhub";
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}")
private String serviceDefinitionId;
@Test
void deleteAppBindingWithCredHub() {
String credentialName = credHubFixture.bindingCredentialName(brokerAppName, serviceDefinitionId,
BINDING_ID, CREDENTIAL_NAME);
credHubFixture.stubFindCredential(credentialName);
credHubFixture.stubDeleteCredential(credentialName);
credHubFixture.stubDeletePermission(credentialName);
// when a service binding is deleted
given(brokerFixture.serviceAppBindingRequest())
.when()
.delete(brokerFixture.deleteBindingUrl(), SERVICE_INSTANCE_ID, BINDING_ID)
.then()
.statusCode(HttpStatus.OK.value());
}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.UaaStubFixture;
import org.springframework.cloud.servicebroker.model.instance.OperationState;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.TestPropertySource;
import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.springframework.cloud.appbroker.integration.DeleteInstanceWithCredHubCredentialsComponentTest.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.cloud.appbroker.services[0].apps[0].credential-providers[0].name=SpringSecurityBasicAuth",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.length=14",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-uppercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-lowercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-numeric=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-special=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].name=SpringSecurityOAuth2",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.registration=example-app-client",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.client-id=test-client",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.grant-types=[\"client_credentials\"]",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.length=12",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-uppercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-lowercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-numeric=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-special=false",
"spring.credhub.url=http://localhost:8888"
})
@Disabled
// See https://github.com/spring-cloud/spring-cloud-app-broker/issues/315
class DeleteInstanceWithCredHubCredentialsComponentTest extends WiremockComponentTest {
protected static final String APP_NAME = "first-app";
private static final String SERVICE_INSTANCE_ID = "instance-id";
@Autowired
private OpenServiceBrokerApiFixture brokerFixture;
@Autowired
private CloudControllerStubFixture cloudControllerFixture;
@Autowired
private UaaStubFixture uaaFixture;
@Autowired
private CredHubStubFixture credHubFixture;
@Test
void deleteAppWithCredentials() {
cloudControllerFixture.stubAppExists(APP_NAME);
cloudControllerFixture.stubServiceBindingDoesNotExist(APP_NAME);
cloudControllerFixture.stubDeleteApp(APP_NAME);
uaaFixture.stubDeleteClient("test-client");
credHubFixture.stubDeleteCredential(APP_NAME, SERVICE_INSTANCE_ID, "basic");
credHubFixture.stubDeleteCredential(APP_NAME, SERVICE_INSTANCE_ID, "oauth2");
// when the service instance is deleted
given(brokerFixture.serviceInstanceRequest())
.when()
.delete(brokerFixture.deleteServiceInstanceUrl(), SERVICE_INSTANCE_ID)
.then()
.statusCode(HttpStatus.ACCEPTED.value());
// when the "last_operation" API is polled
given(brokerFixture.serviceInstanceRequest())
.when()
.get(brokerFixture.getLastInstanceOperationUrl(), SERVICE_INSTANCE_ID)
.then()
.statusCode(HttpStatus.OK.value())
.body("state", is(equalTo(OperationState.IN_PROGRESS.toString())));
String state = brokerFixture.waitForAsyncOperationComplete(SERVICE_INSTANCE_ID);
assertThat(state).isEqualTo(OperationState.SUCCEEDED.toString());
}
}

View File

@@ -25,7 +25,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
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.UaaStubFixture;
import org.springframework.cloud.appbroker.integration.fixtures.WiremockServerFixture;
@@ -39,13 +38,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
WiremockServerFixture.class,
OpenServiceBrokerApiFixture.class,
CloudControllerStubFixture.class,
<<<<<<< Updated upstream
UaaStubFixture.class,
CredHubStubFixture.class,
TestBindingCredentialsProviderFixture.class},
=======
UaaStubFixture.class},
>>>>>>> Stashed changes
properties = {
"spring.cloud.appbroker.deployer.cloudfoundry.api-host=localhost",
"spring.cloud.appbroker.deployer.cloudfoundry.api-port=8080",

View File

@@ -1,142 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.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.containing;
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
public class CredHubStubFixture extends WiremockStubFixture {
protected CredHubStubFixture() {
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(get(urlPathEqualTo("/api/v1/permissions"))
.withQueryParam("credential_name", equalTo(credentialName))
.willReturn(ok()
.withHeader("Content-type", "application/json")
.withBody(credhub("list-permissions"))));
stubFor(get(urlPathEqualTo("/api/v2/permissions"))
.withQueryParam("path", equalTo(credentialName))
.withQueryParam("actor", containing("uaa-user:"))
.willReturn(ok()
.withHeader("Content-type", "application/json")
.withBody(credhub("get-permission"))));
stubFor(delete(urlPathEqualTo("/api/v2/permissions/6e9a275a-6a4b-4634-9e97-b838fd0aa2c7"))
.willReturn(noContent()
.withHeader("Content-type", "application/json")
.withBody(credhub("put-data-json"))));
}
public void stubDeletePermission(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");
}
public void stubGeneratePassword(String appId, String serviceInstanceId,
String descriptor, int length) {
stubGenerate(appId, serviceInstanceId, descriptor, length, "password", "post-data-password");
}
private void stubGenerate(String appId, String serviceInstanceId,
String descriptor, int length,
String type, String responseBody) {
stubFor(post(urlPathEqualTo("/api/v1/data"))
.withRequestBody(matchingJsonPath("$.[?(@.name == '" +
"/" + appId +
"/" + serviceInstanceId +
"/" + descriptor + "')]"))
.withRequestBody(matchingJsonPath("$.[?(@.type == '" + type + "')]"))
.withRequestBody(matchingJsonPath("$.[?(@.parameters.length == '" + length + "')]"))
.willReturn(ok()
.withHeader("Content-type", "application/json")
.withBody(credhub(responseBody))));
}
public void stubDeleteCredential(String appId, String serviceInstanceId, String descriptor) {
String credentialName = "/" + appId + "/" + serviceInstanceId + "/" + descriptor;
stubFor(delete(urlPathEqualTo("/api/v1/data"))
.withQueryParam("name", equalTo(credentialName))
.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");
}
}

View File

@@ -50,8 +50,6 @@ public class WiremockServerFixture {
private WireMockServer ccUaaWiremockServer;
private WireMockServer credHubWiremockServer;
@PostConstruct
public void startWiremock() {
ccUaaWiremockServer = new WireMockServer(wireMockConfig()
@@ -59,28 +57,20 @@ public class WiremockServerFixture {
.usingFilesUnderClasspath("recordings"));
ccUaaWiremockServer.start();
credHubWiremockServer = new WireMockServer(wireMockConfig()
.port(8888)
.usingFilesUnderClasspath("recordings"));
credHubWiremockServer.start();
uaaFixture.stubCommonUaaRequests();
cloudFoundryFixture.stubCommonCloudControllerRequests();
}
public void stopWiremock() {
ccUaaWiremockServer.stop();
credHubWiremockServer.stop();
}
public void resetWiremock() {
ccUaaWiremockServer.resetAll();
credHubWiremockServer.resetAll();
}
public void verifyAllRequiredStubsUsed() {
verifyStubs(ccUaaWiremockServer);
verifyStubs(credHubWiremockServer);
}
private void verifyStubs(WireMockServer wireMockServer) {

View File

@@ -1,8 +0,0 @@
{
"credentials": [
{
"version_created_at": "2017-05-09T21:09:26Z",
"name": "/example-json"
}
]
}

View File

@@ -1,6 +0,0 @@
{
"path" : "/some-path/*",
"operations" : [ "read", "write" ],
"actor" : "uaa-user:106f52e2-5d01-4675-8d7a-c05ff9a2c081",
"uuid" : "6e9a275a-6a4b-4634-9e97-b838fd0aa2c7"
}

View File

@@ -1,8 +0,0 @@
{
"credential_name" : "/some-credential-name",
"permissions" : [ {
"actor" : "uaa-user:106f52e2-5d01-4675-8d7a-c05ff9a2c081",
"path" : "some-path",
"operations" : [ "read" ]
} ]
}

View File

@@ -1,7 +0,0 @@
{
"type": "password",
"version_created_at": "2017-01-05T01:01:01Z",
"id": "67fc3def-bbfb-4953-83f8-4ab0682ad675",
"name": "/example-password",
"value": "mRPZBbAfblRpac"
}

View File

@@ -1,10 +0,0 @@
{
"type": "user",
"version_created_at": "2017-01-05T01:01:01Z",
"id": "67fc3def-bbfb-4953-83f8-4ab0682ad675",
"name": "/example-user",
"value": {
"username": "FQnwWoxgSrDuqD",
"password": "mRPZBbAfblRpac"
}
}

View File

@@ -1,9 +0,0 @@
{
"uuid": "example-uuid",
"path": "/example-directory/*",
"actor": "uaa-user:106f52e2-5d01-4675-8d7a-c05ff9a2c081",
"operations": [
"read",
"write"
]
}

View File

@@ -1,14 +0,0 @@
{
"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
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.
*/
import org.springframework.boot.gradle.plugin.SpringBootPlugin
plugins {
id 'org.springframework.boot' apply false
}
description = "Spring Cloud App Broker Security CredHub"
dependencies {
api platform(SpringBootPlugin.BOM_COORDINATES)
api project(":spring-cloud-app-broker-core")
api "org.springframework.credhub:spring-credhub-starter:${springCredhubVersion}"
api "org.springframework.cloud:spring-cloud-open-service-broker-core:${openServiceBrokerVersion}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher"
testImplementation "org.springframework.boot:spring-boot-starter-test"
testImplementation "org.springframework.boot:spring-boot-starter-webflux"
testImplementation "io.projectreactor:reactor-test"
testImplementation "io.projectreactor.tools:blockhound-junit-platform:${blockHoundVersion}"
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.extensions.credentials;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
import org.springframework.credhub.core.ReactiveCredHubOperations;
import org.springframework.credhub.support.CredentialDetails;
import org.springframework.credhub.support.SimpleCredentialName;
import org.springframework.credhub.support.password.PasswordCredential;
import org.springframework.credhub.support.password.PasswordParameters;
import org.springframework.credhub.support.password.PasswordParametersRequest;
import org.springframework.credhub.support.user.UserCredential;
import org.springframework.credhub.support.user.UserParametersRequest;
public class CredHubCredentialsGenerator implements CredentialGenerator {
private final ReactiveCredHubOperations credHubOperations;
public CredHubCredentialsGenerator(ReactiveCredHubOperations credHubOperations) {
this.credHubOperations = credHubOperations;
}
@Override
public Mono<Tuple2<String, String>> generateUser(String applicationId, String serviceInstanceId, String descriptor,
int length, boolean includeUppercaseAlpha, boolean includeLowercaseAlpha, boolean includeNumeric,
boolean includeSpecial) {
return credHubOperations.credentials().generate(UserParametersRequest.builder()
.name(new SimpleCredentialName(applicationId, serviceInstanceId, descriptor))
.parameters(passwordParameters(length, includeUppercaseAlpha, includeLowercaseAlpha, includeNumeric,
includeSpecial))
.build(), UserCredential.class)
.map(CredentialDetails::getValue)
.map(userCredential -> Tuples.of(userCredential.getUsername(), userCredential.getPassword()));
}
@Override
public Mono<String> generateString(String applicationId, String serviceInstanceId, String descriptor, int length,
boolean includeUppercaseAlpha, boolean includeLowercaseAlpha, boolean includeNumeric,
boolean includeSpecial) {
return credHubOperations.credentials()
.generate(PasswordParametersRequest.builder()
.name(new SimpleCredentialName(applicationId, serviceInstanceId, descriptor))
.parameters(passwordParameters(length, includeUppercaseAlpha, includeLowercaseAlpha, includeNumeric,
includeSpecial))
.build(), PasswordCredential.class)
.map(CredentialDetails::getValue)
.map(PasswordCredential::getPassword);
}
@Override
public Mono<Void> deleteUser(String applicationId, String serviceInstanceId, String descriptor) {
return credHubOperations.credentials()
.deleteByName(new SimpleCredentialName(applicationId, serviceInstanceId, descriptor));
}
@Override
public Mono<Void> deleteString(String applicationId, String serviceInstanceId, String descriptor) {
return credHubOperations.credentials()
.deleteByName(new SimpleCredentialName(applicationId, serviceInstanceId, descriptor));
}
private PasswordParameters passwordParameters(int length, boolean includeUppercaseAlpha,
boolean includeLowercaseAlpha, boolean includeNumeric, boolean includeSpecial) {
return PasswordParameters.builder()
.length(length)
.excludeUpper(!includeUppercaseAlpha)
.excludeLower(!includeLowercaseAlpha)
.excludeNumber(!includeNumeric)
.includeSpecial(includeSpecial)
.build();
}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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 reactor.core.publisher.Mono;
import reactor.util.Logger;
import reactor.util.Loggers;
import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindingWorkflow;
import org.springframework.cloud.servicebroker.model.binding.BindResource;
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.ReactiveCredHubOperations;
import org.springframework.credhub.support.CredentialName;
import org.springframework.credhub.support.json.JsonCredentialRequest;
import org.springframework.credhub.support.permissions.Operation;
import org.springframework.credhub.support.permissions.Permission;
import org.springframework.util.CollectionUtils;
@Order(50)
public class CredHubPersistingCreateServiceInstanceAppBindingWorkflow extends CredHubPersistingWorkflow
implements CreateServiceInstanceAppBindingWorkflow {
private static final Logger LOG = Loggers.getLogger(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class);
private static final String CREDHUB_REF_KEY = "credhub-ref";
private static final String CREDENTIAL_CLIENT_ID = "credential_client_id";
private final ReactiveCredHubOperations credHubOperations;
public CredHubPersistingCreateServiceInstanceAppBindingWorkflow(ReactiveCredHubOperations credHubOperations,
String appName) {
super(appName);
this.credHubOperations = credHubOperations;
}
@Override
public Mono<CreateServiceInstanceAppBindingResponseBuilder> buildResponse(
CreateServiceInstanceBindingRequest request, CreateServiceInstanceAppBindingResponseBuilder responseBuilder) {
return Mono.just(responseBuilder.build())
.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(String
.format("Error storing binding credentials with name '%s' in CredHub with error: '%s'",
credentialName.getName(), exception.getMessage()), exception)));
}
return Mono.just(responseBuilder);
});
}
private Mono<CreateServiceInstanceAppBindingResponseBuilder> persistBindingCredentials(
CreateServiceInstanceBindingRequest request, CreateServiceInstanceAppBindingResponse response,
CredentialName credentialName) {
return writeCredential(response, credentialName)
.then(writePermissions(request, credentialName))
.thenReturn(buildReplacementBindingResponse(response, credentialName));
}
private Mono<Void> writeCredential(CreateServiceInstanceAppBindingResponse response,
CredentialName credentialName) {
return credHubOperations.credentials()
.write(JsonCredentialRequest.builder()
.name(credentialName)
.value(response.getCredentials())
.build())
.then();
}
private Mono<Void> writePermissions(CreateServiceInstanceBindingRequest request,
CredentialName credentialName) {
BindResource bindResource = request.getBindResource();
return Mono.defer(() -> {
if (bindResource.getAppGuid() != null) {
return credHubOperations.permissionsV2()
.addPermissions(credentialName, Permission.builder()
.app(bindResource.getAppGuid())
.operation(Operation.READ)
.build())
.then();
}
if (bindResource.getProperty(CREDENTIAL_CLIENT_ID) != null) {
return credHubOperations.permissionsV2()
.addPermissions(credentialName, Permission.builder()
.client(bindResource.getProperty(CREDENTIAL_CLIENT_ID).toString())
.operation(Operation.READ)
.build())
.then();
}
return Mono.empty();
});
}
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());
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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 reactor.core.publisher.Mono;
import reactor.util.Logger;
import reactor.util.Loggers;
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.ReactiveCredHubOperations;
import org.springframework.credhub.support.CredentialName;
import org.springframework.credhub.support.ServiceInstanceCredentialName;
@Order(50)
public class CredHubPersistingDeleteServiceInstanceBindingWorkflow extends CredHubPersistingWorkflow
implements DeleteServiceInstanceBindingWorkflow {
private static final Logger LOG = Loggers.getLogger(CredHubPersistingDeleteServiceInstanceBindingWorkflow.class);
private final ReactiveCredHubOperations credHubOperations;
public CredHubPersistingDeleteServiceInstanceBindingWorkflow(ReactiveCredHubOperations credHubOperations,
String appName) {
super(appName);
this.credHubOperations = credHubOperations;
}
@Override
public Mono<DeleteServiceInstanceBindingResponseBuilder> buildResponse(DeleteServiceInstanceBindingRequest request,
DeleteServiceInstanceBindingResponseBuilder responseBuilder) {
return buildCredentialName(request.getServiceDefinitionId(), request.getBindingId())
.filterWhen(this::credentialExists)
.delayUntil(this::deletePermission)
.delayUntil(this::deleteCredential)
.thenReturn(responseBuilder);
}
private Mono<Void> deleteCredential(ServiceInstanceCredentialName credentialName) {
return credHubOperations.credentials()
.deleteByName(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(
String.format("Error deleting binding credentials with name '%s' in CredHub with error: '%s'",
credentialName.getName(), exception.getMessage()), exception));
}
private Mono<Void> deletePermission(ServiceInstanceCredentialName credentialName) {
return credHubOperations.permissions()
.getPermissions(credentialName)
.flatMap(permission -> credHubOperations.permissionsV2()
.getPermissionsByPathAndActor(credentialName, permission.getActor()))
.flatMap(credentialPermission -> credHubOperations.permissionsV2()
.deletePermission(credentialPermission.getId())
.doOnRequest(
l -> LOG.debug("Deleting binding permission for credential with name '{}' in CredHub",
credentialName.getName()))
.doOnSuccess(r -> LOG
.debug("Finished deleting binding permission for credential with name '{}' in CredHub",
credentialName.getName()))
.doOnError(exception -> LOG.error(
String.format(
"Error deleting binding permission for credential with name '%s' in CredHub with error: '%s'",
credentialName.getName(), exception.getMessage()), exception)))
.then();
}
private Mono<Boolean> credentialExists(CredentialName credentialName) {
return credHubOperations.credentials()
.findByName(credentialName)
.collectList()
.map(credentialSummaries -> !credentialSummaries.isEmpty());
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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 reactor.core.publisher.Mono;
import org.springframework.credhub.support.ServiceInstanceCredentialName;
public class CredHubPersistingWorkflow {
private static final String CREDENTIALS_NAME = "credentials-json";
private final String appName;
protected CredHubPersistingWorkflow(String appName) {
this.appName = appName;
}
protected Mono<ServiceInstanceCredentialName> buildCredentialName(String serviceDefinitionId, String bindingId) {
return Mono.just(ServiceInstanceCredentialName.builder()
.serviceBrokerName(this.appName)
.serviceOfferingName(serviceDefinitionId)
.serviceBindingId(bindingId)
.credentialName(CREDENTIALS_NAME)
.build());
}
}

View File

@@ -1,140 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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.extensions.credentials;
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 reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.credhub.core.ReactiveCredHubOperations;
import org.springframework.credhub.core.credential.ReactiveCredHubCredentialOperations;
import org.springframework.credhub.support.CredentialDetails;
import org.springframework.credhub.support.CredentialType;
import org.springframework.credhub.support.SimpleCredentialName;
import org.springframework.credhub.support.password.PasswordCredential;
import org.springframework.credhub.support.password.PasswordParameters;
import org.springframework.credhub.support.user.UserCredential;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
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 CredHubCredentialsGeneratorTest {
@Mock
private ReactiveCredHubOperations credHubOperations;
@Mock
private ReactiveCredHubCredentialOperations credHubCredentialOperations;
private CredHubCredentialsGenerator generator;
@BeforeEach
void setUp() {
this.generator = new CredHubCredentialsGenerator(credHubOperations);
}
@Test
void passwordParameters() {
PasswordParameters params = ReflectionTestUtils
.invokeMethod(generator, "passwordParameters", 42, false, false, false, false);
assertThat(params.getLength()).isEqualTo(42);
assertThat(params.getExcludeUpper()).isTrue();
assertThat(params.getExcludeLower()).isTrue();
assertThat(params.getExcludeNumber()).isTrue();
assertThat(params.getIncludeSpecial()).isFalse();
}
@Test
void generateUser() {
given(this.credHubOperations.credentials())
.willReturn(credHubCredentialOperations);
CredentialDetails<UserCredential> creds = new CredentialDetails<>("id",
new SimpleCredentialName("app-service"), CredentialType.PASSWORD,
new UserCredential("username", "password"));
given(this.credHubCredentialOperations.generate(any(), eq(UserCredential.class)))
.willReturn(Mono.just(creds));
StepVerifier.create(generator.generateUser("foo", "bar", "hello", 12, false, false, false, false))
.assertNext(tuple2 -> {
assertThat(tuple2.getT1()).isEqualTo("username");
assertThat(tuple2.getT2()).isEqualTo("password");
})
.verifyComplete();
}
@Test
void generateString() {
given(this.credHubOperations.credentials())
.willReturn(credHubCredentialOperations);
CredentialDetails<PasswordCredential> creds = new CredentialDetails<>("id",
new SimpleCredentialName("app-service"), CredentialType.PASSWORD,
new PasswordCredential("password"));
given(this.credHubCredentialOperations.generate(any(), eq(PasswordCredential.class)))
.willReturn(Mono.just(creds));
StepVerifier.create(generator.generateString("foo", "bar", "hello", 12, false, false, false, false))
.assertNext(password -> assertThat(password).isEqualTo("password"))
.verifyComplete();
}
@Test
void deleteUser() {
given(this.credHubOperations.credentials())
.willReturn(credHubCredentialOperations);
given(this.credHubCredentialOperations.deleteByName(any()))
.willReturn(Mono.empty());
StepVerifier.create(generator.deleteUser("foo", "bar", "hello"))
.verifyComplete();
verify(credHubCredentialOperations).deleteByName(new SimpleCredentialName("foo", "bar", "hello"));
verifyNoMoreInteractions(credHubOperations);
verifyNoMoreInteractions(credHubCredentialOperations);
}
@Test
void deleteString() {
given(this.credHubOperations.credentials())
.willReturn(credHubCredentialOperations);
given(this.credHubCredentialOperations.deleteByName(any()))
.willReturn(Mono.empty());
StepVerifier.create(generator.deleteString("foo", "bar", "hello"))
.verifyComplete();
verify(credHubCredentialOperations).deleteByName(new SimpleCredentialName("foo", "bar", "hello"));
verifyNoMoreInteractions(credHubOperations);
verifyNoMoreInteractions(credHubCredentialOperations);
}
}

View File

@@ -1,182 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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 java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
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.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.cloud.servicebroker.model.binding.BindResource;
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.cloud.servicebroker.model.binding.VolumeMount;
import org.springframework.credhub.core.ReactiveCredHubOperations;
import org.springframework.credhub.core.credential.ReactiveCredHubCredentialOperations;
import org.springframework.credhub.core.permissionV2.ReactiveCredHubPermissionV2Operations;
import org.springframework.credhub.support.CredentialDetails;
import org.springframework.credhub.support.CredentialName;
import org.springframework.credhub.support.CredentialPermission;
import org.springframework.credhub.support.ServiceInstanceCredentialName;
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.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@ExtendWith(MockitoExtension.class)
class CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest {
@Mock
private ReactiveCredHubOperations credHubOperations;
@Mock
private ReactiveCredHubCredentialOperations credHubCredentialOperations;
@Mock
private ReactiveCredHubPermissionV2Operations credHubPermissionOperations;
private CredHubPersistingCreateServiceInstanceAppBindingWorkflow workflow;
@BeforeEach
void setUp() {
this.workflow = new CredHubPersistingCreateServiceInstanceAppBindingWorkflow(credHubOperations,
"test-app-name");
}
@Test
void noBindingCredentials() {
CreateServiceInstanceBindingRequest request = CreateServiceInstanceBindingRequest
.builder()
.bindingId("foo-binding-id")
.serviceInstanceId("foo-instance-id")
.serviceDefinitionId("foo-definition-id")
.build();
CreateServiceInstanceAppBindingResponseBuilder responseBuilder = CreateServiceInstanceAppBindingResponse
.builder()
.bindingExisted(true)
.syslogDrainUrl("https://logs.example.local")
.volumeMounts(VolumeMount.builder().build())
.volumeMounts(VolumeMount.builder().build())
.volumeMounts(Arrays.asList(
VolumeMount.builder().build(),
VolumeMount.builder().build()
));
StepVerifier
.create(this.workflow.buildResponse(request, responseBuilder))
.assertNext(createServiceInstanceAppBindingResponseBuilder -> {
CreateServiceInstanceAppBindingResponse response = createServiceInstanceAppBindingResponseBuilder
.build();
assertThat(response.isBindingExisted()).isEqualTo(true);
assertThat(response.getCredentials()).hasSize(0);
assertThat(response.getSyslogDrainUrl()).isEqualTo("https://logs.example.local");
assertThat(response.getVolumeMounts()).hasSize(4);
})
.verifyComplete();
verifyNoInteractions(this.credHubCredentialOperations);
}
@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<String, Object> credentials = new HashMap<String, Object>() {{
put("credential4", "value4");
put("credential5", "value5");
}};
CreateServiceInstanceAppBindingResponseBuilder responseBuilder = CreateServiceInstanceAppBindingResponse
.builder()
.bindingExisted(true)
.credentials("credential1", "value1")
.credentials("credential2", 2)
.credentials("credential3", true)
.credentials(credentials)
.syslogDrainUrl("https://logs.example.local")
.volumeMounts(VolumeMount.builder().build())
.volumeMounts(VolumeMount.builder().build())
.volumeMounts(Arrays.asList(
VolumeMount.builder().build(),
VolumeMount.builder().build()
));
given(this.credHubOperations.credentials())
.willReturn(credHubCredentialOperations);
given(this.credHubOperations.permissionsV2())
.willReturn(credHubPermissionOperations);
given(this.credHubCredentialOperations.write(any()))
.willReturn(Mono.just(new CredentialDetails<>()));
given(this.credHubPermissionOperations.addPermissions(any(), any()))
.willReturn(Mono.just(Mockito.mock(CredentialPermission.class)));
StepVerifier
.create(this.workflow.buildResponse(request, responseBuilder))
.assertNext(createServiceInstanceAppBindingResponseBuilder -> {
CreateServiceInstanceAppBindingResponse response = createServiceInstanceAppBindingResponseBuilder
.build();
assertThat(response.isBindingExisted()).isEqualTo(true);
assertThat(response.getCredentials()).hasSize(1);
assertThat(response.getCredentials().get("credhub-ref")).isEqualTo(credentialName.getName());
assertThat(response.getSyslogDrainUrl()).isEqualTo("https://logs.example.local");
assertThat(response.getVolumeMounts()).hasSize(4);
})
.verifyComplete();
verify(this.credHubCredentialOperations, times(1))
.write(any());
verify(this.credHubPermissionOperations, times(1))
.addPermissions(any(), any());
verifyNoMoreInteractions(this.credHubCredentialOperations);
}
}

View File

@@ -1,167 +0,0 @@
/*
* Copyright 2002-2020 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
*
* https://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 java.util.Collections;
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 reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
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.ReactiveCredHubOperations;
import org.springframework.credhub.core.credential.ReactiveCredHubCredentialOperations;
import org.springframework.credhub.core.permission.ReactiveCredHubPermissionOperations;
import org.springframework.credhub.core.permissionV2.ReactiveCredHubPermissionV2Operations;
import org.springframework.credhub.support.CredentialName;
import org.springframework.credhub.support.CredentialPermission;
import org.springframework.credhub.support.CredentialSummary;
import org.springframework.credhub.support.ServiceInstanceCredentialName;
import org.springframework.credhub.support.permissions.Actor;
import org.springframework.credhub.support.permissions.Operation;
import org.springframework.credhub.support.permissions.Permission;
import org.springframework.test.util.ReflectionTestUtils;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@ExtendWith(MockitoExtension.class)
class CredHubPersistingDeleteServiceInstanceBindingWorkflowTest {
@Mock
private ReactiveCredHubOperations credHubOperations;
@Mock
private ReactiveCredHubCredentialOperations credHubCredentialOperations;
@Mock
private ReactiveCredHubPermissionV2Operations credHubPermissionV2Operations;
@Mock
private ReactiveCredHubPermissionOperations credHubPermissionOperations;
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.credHubOperations.permissionsV2())
.willReturn(credHubPermissionV2Operations);
given(this.credHubOperations.permissions())
.willReturn(credHubPermissionOperations);
given(this.credHubCredentialOperations.findByName(credentialName))
.willReturn(Flux.fromIterable(Collections.singletonList(new CredentialSummary(credentialName))));
CredentialPermission credentialPermission = new CredentialPermission(credentialName,
Permission.builder().app("app-id").operation(Operation.READ).build());
ReflectionTestUtils.setField(credentialPermission, "uuid", "permission-uuid");
given(this.credHubPermissionV2Operations.getPermissionsByPathAndActor(any(), any()))
.willReturn(Mono.just(credentialPermission));
given(this.credHubPermissionV2Operations.deletePermission("permission-uuid"))
.willReturn(Mono.empty());
Permission permission = Permission.builder().operation(Operation.READ).client("client-id").build();
given(this.credHubPermissionOperations.getPermissions(any()))
.willReturn(Flux.just(permission));
given(this.credHubCredentialOperations.deleteByName(any()))
.willReturn(Mono.empty());
StepVerifier
.create(this.workflow.buildResponse(request, responseBuilder))
.expectNext(responseBuilder)
.verifyComplete();
verify(this.credHubCredentialOperations, times(1))
.deleteByName(eq(credentialName));
verify(this.credHubPermissionV2Operations, times(1))
.getPermissionsByPathAndActor(eq(credentialName), eq(Actor.client("client-id")));
verify(this.credHubPermissionV2Operations, times(1))
.deletePermission(eq("permission-uuid"));
verifyNoMoreInteractions(this.credHubCredentialOperations);
verifyNoMoreInteractions(this.credHubPermissionOperations);
verifyNoMoreInteractions(this.credHubPermissionV2Operations);
}
@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(Flux.fromIterable(Collections.emptyList()));
StepVerifier
.create(this.workflow.buildResponse(request, responseBuilder))
.expectNext(responseBuilder)
.verifyComplete();
verifyNoMoreInteractions(this.credHubCredentialOperations);
verifyNoMoreInteractions(this.credHubPermissionOperations);
}
}

View File

@@ -19,7 +19,6 @@ description = "Spring Cloud App Broker Starter for Cloud Foundry"
dependencies {
api project(":spring-cloud-starter-app-broker")
api project(":spring-cloud-app-broker-deployer-cloudfoundry")
api project(":spring-cloud-app-broker-security-credhub")
api "org.cloudfoundry:cloudfoundry-client-reactor:${cfJavaClientVersion}"
api "org.cloudfoundry:cloudfoundry-operations:${cfJavaClientVersion}"
}