From 0685897dac6bacb524e4b2edc32b73f2c67abc89 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Wed, 14 Nov 2018 17:09:58 -0600 Subject: [PATCH] Add SpringSecurityOAuth2 credential provider. --- .../AppBrokerAutoConfiguration.java | 8 + ...udFoundryAppDeployerAutoConfiguration.java | 7 + .../AppBrokerAutoConfigurationTest.java | 28 ++- ...undryAppDeployerAutoConfigurationTest.java | 2 + .../CredentialGenerationConfig.java | 66 ++++++ ...ityBasicAuthCredentialProviderFactory.java | 57 +---- ...curityOAuth2CredentialProviderFactory.java | 220 ++++++++++++++++++ ...tyOAuth2CredentialProviderFactoryTest.java | 187 +++++++++++++++ .../CloudFoundryOAuth2Client.java | 117 ++++++++++ .../CloudFoundryOAuth2ClientTest.java | 146 ++++++++++++ .../CreateServiceInstanceRequest.java | 2 - .../oauth2/CreateOAuth2ClientRequest.java | 182 +++++++++++++++ .../oauth2/CreateOAuth2ClientResponse.java | 136 +++++++++++ .../oauth2/DeleteOAuth2ClientRequest.java | 102 ++++++++ .../oauth2/DeleteOAuth2ClientResponse.java | 134 +++++++++++ .../cloud/appbroker/oauth2/OAuth2Client.java | 29 +++ ...ithBasicAuthCredentialsComponentTest.java} | 6 +- ...nceWithOAuth2CredentialsComponentTest.java | 120 ++++++++++ .../sample/fixtures/UaaStubFixture.java | 13 ++ .../responses/uaa/delete-oauth-clients.json | 9 + .../responses/uaa/post-oauth-clients.json | 9 + 21 files changed, 1518 insertions(+), 62 deletions(-) create mode 100644 spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/CredentialGenerationConfig.java create mode 100644 spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityOAuth2CredentialProviderFactory.java create mode 100644 spring-cloud-app-broker-core/src/test/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityOAuth2CredentialProviderFactoryTest.java create mode 100644 spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryOAuth2Client.java create mode 100644 spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryOAuth2ClientTest.java create mode 100644 spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/CreateOAuth2ClientRequest.java create mode 100644 spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/CreateOAuth2ClientResponse.java create mode 100644 spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/DeleteOAuth2ClientRequest.java create mode 100644 spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/DeleteOAuth2ClientResponse.java create mode 100644 spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/OAuth2Client.java rename spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/{CreateInstanceWithCredentialsComponentTest.java => CreateInstanceWithBasicAuthCredentialsComponentTest.java} (95%) create mode 100644 spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithOAuth2CredentialsComponentTest.java create mode 100644 spring-cloud-app-broker-sample/src/test/resources/responses/uaa/delete-oauth-clients.json create mode 100644 spring-cloud-app-broker-sample/src/test/resources/responses/uaa/post-oauth-clients.json diff --git a/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfiguration.java b/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfiguration.java index 6c133b9..0b31e2a 100644 --- a/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfiguration.java +++ b/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfiguration.java @@ -35,6 +35,7 @@ import org.springframework.cloud.appbroker.extensions.credentials.CredentialProv import org.springframework.cloud.appbroker.extensions.credentials.CredentialProviderService; import org.springframework.cloud.appbroker.extensions.credentials.SimpleCredentialGenerator; import org.springframework.cloud.appbroker.extensions.credentials.SpringSecurityBasicAuthCredentialProviderFactory; +import org.springframework.cloud.appbroker.extensions.credentials.SpringSecurityOAuth2CredentialProviderFactory; import org.springframework.cloud.appbroker.extensions.parameters.BackingApplicationsParametersTransformationService; import org.springframework.cloud.appbroker.extensions.parameters.BackingServicesParametersTransformationService; import org.springframework.cloud.appbroker.extensions.parameters.EnvironmentMappingParametersTransformerFactory; @@ -44,6 +45,7 @@ import org.springframework.cloud.appbroker.extensions.parameters.PropertyMapping import org.springframework.cloud.appbroker.extensions.targets.SpacePerServiceInstance; import org.springframework.cloud.appbroker.extensions.targets.TargetFactory; import org.springframework.cloud.appbroker.extensions.targets.TargetService; +import org.springframework.cloud.appbroker.oauth2.OAuth2Client; import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindingWorkflow; import org.springframework.cloud.appbroker.service.CreateServiceInstanceRouteBindingWorkflow; import org.springframework.cloud.appbroker.service.CreateServiceInstanceWorkflow; @@ -133,6 +135,12 @@ public class AppBrokerAutoConfiguration { return new SpringSecurityBasicAuthCredentialProviderFactory(credentialGenerator); } + @Bean + public SpringSecurityOAuth2CredentialProviderFactory springSecurityOAuth2CredentialProvider(CredentialGenerator credentialGenerator, + OAuth2Client oAuth2Client) { + return new SpringSecurityOAuth2CredentialProviderFactory(credentialGenerator, oAuth2Client); + } + @Bean public CredentialProviderService credentialProviderService(List> providers) { return new CredentialProviderService(providers); diff --git a/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/CloudFoundryAppDeployerAutoConfiguration.java b/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/CloudFoundryAppDeployerAutoConfiguration.java index f85ac10..0a9bf3f 100644 --- a/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/CloudFoundryAppDeployerAutoConfiguration.java +++ b/spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/CloudFoundryAppDeployerAutoConfiguration.java @@ -34,8 +34,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.appbroker.deployer.AppDeployer; +import org.springframework.cloud.appbroker.oauth2.OAuth2Client; import org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryAppDeployer; import org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties; +import org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryOAuth2Client; import org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryTargetProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -71,6 +73,11 @@ public class CloudFoundryAppDeployerAutoConfiguration { targetProperties, resourceLoader); } + @Bean + OAuth2Client cloudFoundryOAuth2Client(UaaClient uaaClient) { + return new CloudFoundryOAuth2Client(uaaClient); + } + @Bean ReactorCloudFoundryClient cloudFoundryClient(ConnectionContext connectionContext, TokenProvider tokenProvider) { return ReactorCloudFoundryClient.builder() diff --git a/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfigurationTest.java b/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfigurationTest.java index bfb084e..3862658 100644 --- a/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfigurationTest.java +++ b/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfigurationTest.java @@ -26,8 +26,14 @@ import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionServ import org.springframework.cloud.appbroker.deployer.BrokeredServices; import org.springframework.cloud.appbroker.deployer.DeployerClient; import org.springframework.cloud.appbroker.extensions.credentials.CredentialProviderService; +import org.springframework.cloud.appbroker.extensions.credentials.SpringSecurityBasicAuthCredentialProviderFactory; +import org.springframework.cloud.appbroker.extensions.credentials.SpringSecurityOAuth2CredentialProviderFactory; import org.springframework.cloud.appbroker.extensions.parameters.BackingApplicationsParametersTransformationService; import org.springframework.cloud.appbroker.extensions.parameters.BackingServicesParametersTransformationService; +import org.springframework.cloud.appbroker.extensions.parameters.EnvironmentMappingParametersTransformerFactory; +import org.springframework.cloud.appbroker.extensions.parameters.ParameterMappingParametersTransformerFactory; +import org.springframework.cloud.appbroker.extensions.parameters.PropertyMappingParametersTransformerFactory; +import org.springframework.cloud.appbroker.extensions.targets.SpacePerServiceInstance; import org.springframework.cloud.appbroker.extensions.targets.TargetService; import org.springframework.cloud.appbroker.service.WorkflowServiceInstanceBindingService; import org.springframework.cloud.appbroker.service.WorkflowServiceInstanceService; @@ -97,16 +103,30 @@ class AppBrokerAutoConfigurationTest { assertThat(context).hasSingleBean(DeployerClient.class); assertThat(context).hasSingleBean(BrokeredServices.class); + assertThat(context).hasSingleBean(ServiceInstanceStateRepository.class); assertThat(context).hasSingleBean(ServiceInstanceBindingStateRepository.class); + assertThat(context).hasSingleBean(BackingAppDeploymentService.class); - assertThat(context).hasSingleBean(BackingApplicationsParametersTransformationService.class); - assertThat(context).hasSingleBean(BackingServicesParametersTransformationService.class); - assertThat(context).hasSingleBean(CredentialProviderService.class); - assertThat(context).hasSingleBean(TargetService.class); assertThat(context).hasSingleBean(BackingServicesProvisionService.class); + + assertThat(context).hasSingleBean(BackingApplicationsParametersTransformationService.class); + assertThat(context).hasSingleBean(EnvironmentMappingParametersTransformerFactory.class); + assertThat(context).hasSingleBean(PropertyMappingParametersTransformerFactory.class); + assertThat(context).hasSingleBean(ParameterMappingParametersTransformerFactory.class); + + assertThat(context).hasSingleBean(BackingServicesParametersTransformationService.class); + + assertThat(context).hasSingleBean(CredentialProviderService.class); + assertThat(context).hasSingleBean(SpringSecurityBasicAuthCredentialProviderFactory.class); + assertThat(context).hasSingleBean(SpringSecurityOAuth2CredentialProviderFactory.class); + + assertThat(context).hasSingleBean(TargetService.class); + assertThat(context).hasSingleBean(SpacePerServiceInstance.class); + assertThat(context).hasSingleBean(WorkflowServiceInstanceService.class); assertThat(context).hasSingleBean(WorkflowServiceInstanceBindingService.class); + assertThat(context).hasSingleBean(AppDeploymentCreateServiceInstanceWorkflow.class); assertThat(context).hasSingleBean(AppDeploymentDeleteServiceInstanceWorkflow.class); assertThat(context).hasSingleBean(AppDeploymentUpdateServiceInstanceWorkflow.class); diff --git a/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/CloudFoundryAppDeployerAutoConfigurationTest.java b/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/CloudFoundryAppDeployerAutoConfigurationTest.java index 7a2e5dd..0a9a76f 100644 --- a/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/CloudFoundryAppDeployerAutoConfigurationTest.java +++ b/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/CloudFoundryAppDeployerAutoConfigurationTest.java @@ -32,6 +32,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.appbroker.deployer.AppDeployer; import org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties; import org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryTargetProperties; +import org.springframework.cloud.appbroker.oauth2.OAuth2Client; import static org.assertj.core.api.Assertions.assertThat; @@ -73,6 +74,7 @@ class CloudFoundryAppDeployerAutoConfigurationTest { assertThat(deploymentProperties.getDomain()).isEqualTo("example.com"); assertThat(context).hasSingleBean(AppDeployer.class); + assertThat(context).hasSingleBean(OAuth2Client.class); assertThat(context).hasSingleBean(ReactorCloudFoundryClient.class); assertThat(context).hasSingleBean(ReactorDopplerClient.class); diff --git a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/CredentialGenerationConfig.java b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/CredentialGenerationConfig.java new file mode 100644 index 0000000..fa7c456 --- /dev/null +++ b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/CredentialGenerationConfig.java @@ -0,0 +1,66 @@ +/* + * 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.extensions.credentials; + +@SuppressWarnings("WeakerAccess") +public class CredentialGenerationConfig { + private int length; + private boolean includeUppercaseAlpha = true; + private boolean includeLowercaseAlpha = true; + private boolean includeNumeric = true; + private boolean includeSpecial = true; + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + + public boolean isIncludeUppercaseAlpha() { + return includeUppercaseAlpha; + } + + public void setIncludeUppercaseAlpha(boolean includeUppercaseAlpha) { + this.includeUppercaseAlpha = includeUppercaseAlpha; + } + + public boolean isIncludeLowercaseAlpha() { + return includeLowercaseAlpha; + } + + public void setIncludeLowercaseAlpha(boolean includeLowercaseAlpha) { + this.includeLowercaseAlpha = includeLowercaseAlpha; + } + + public boolean isIncludeNumeric() { + return includeNumeric; + } + + public void setIncludeNumeric(boolean includeNumeric) { + this.includeNumeric = includeNumeric; + } + + public boolean isIncludeSpecial() { + return includeSpecial; + } + + public void setIncludeSpecial(boolean includeSpecial) { + this.includeSpecial = includeSpecial; + } +} diff --git a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityBasicAuthCredentialProviderFactory.java b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityBasicAuthCredentialProviderFactory.java index 9151731..a6e9f09 100644 --- a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityBasicAuthCredentialProviderFactory.java +++ b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityBasicAuthCredentialProviderFactory.java @@ -21,7 +21,7 @@ import org.springframework.cloud.appbroker.deployer.BackingApplication; import reactor.core.publisher.Mono; public class SpringSecurityBasicAuthCredentialProviderFactory extends - CredentialProviderFactory { + CredentialProviderFactory { static final String SPRING_SECURITY_USER_NAME = "security.user.name"; static final String SPRING_SECURITY_USER_PASSWORD = "security.user.password"; @@ -29,12 +29,12 @@ public class SpringSecurityBasicAuthCredentialProviderFactory extends private final CredentialGenerator credentialGenerator; public SpringSecurityBasicAuthCredentialProviderFactory(CredentialGenerator credentialGenerator) { - super(Config.class); + super(CredentialGenerationConfig.class); this.credentialGenerator = credentialGenerator; } @Override - public CredentialProvider create(Config config) { + public CredentialProvider create(CredentialGenerationConfig config) { return new CredentialProvider() { @Override public Mono addCredentials(BackingApplication backingApplication, @@ -52,7 +52,7 @@ public class SpringSecurityBasicAuthCredentialProviderFactory extends }; } - private void generateCredentials(Config config, + private void generateCredentials(CredentialGenerationConfig config, BackingApplication backingApplication, String serviceInstanceGuid) { Pair user = @@ -63,53 +63,4 @@ public class SpringSecurityBasicAuthCredentialProviderFactory extends backingApplication.addEnvironment(SPRING_SECURITY_USER_NAME, user.getLeft()); backingApplication.addEnvironment(SPRING_SECURITY_USER_PASSWORD, user.getRight()); } - - @SuppressWarnings("WeakerAccess") - public static class Config { - private int length; - private boolean includeUppercaseAlpha = true; - private boolean includeLowercaseAlpha = true; - private boolean includeNumeric = true; - private boolean includeSpecial = true; - - public int getLength() { - return length; - } - - public void setLength(int length) { - this.length = length; - } - - public boolean isIncludeUppercaseAlpha() { - return includeUppercaseAlpha; - } - - public void setIncludeUppercaseAlpha(boolean includeUppercaseAlpha) { - this.includeUppercaseAlpha = includeUppercaseAlpha; - } - - public boolean isIncludeLowercaseAlpha() { - return includeLowercaseAlpha; - } - - public void setIncludeLowercaseAlpha(boolean includeLowercaseAlpha) { - this.includeLowercaseAlpha = includeLowercaseAlpha; - } - - public boolean isIncludeNumeric() { - return includeNumeric; - } - - public void setIncludeNumeric(boolean includeNumeric) { - this.includeNumeric = includeNumeric; - } - - public boolean isIncludeSpecial() { - return includeSpecial; - } - - public void setIncludeSpecial(boolean includeSpecial) { - this.includeSpecial = includeSpecial; - } - } } diff --git a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityOAuth2CredentialProviderFactory.java b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityOAuth2CredentialProviderFactory.java new file mode 100644 index 0000000..131129f --- /dev/null +++ b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityOAuth2CredentialProviderFactory.java @@ -0,0 +1,220 @@ +/* + * 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.extensions.credentials; + +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.cloud.appbroker.deployer.BackingApplication; +import org.springframework.cloud.appbroker.oauth2.CreateOAuth2ClientRequest; +import org.springframework.cloud.appbroker.oauth2.CreateOAuth2ClientResponse; +import org.springframework.cloud.appbroker.oauth2.DeleteOAuth2ClientRequest; +import org.springframework.cloud.appbroker.oauth2.DeleteOAuth2ClientResponse; +import org.springframework.cloud.appbroker.oauth2.OAuth2Client; +import reactor.core.publisher.Mono; + +public class SpringSecurityOAuth2CredentialProviderFactory extends + CredentialProviderFactory { + + private static final String SPRING_SECURITY_CLIENT_REGISTRATION = "spring.security.oauth2.client.registration."; + private static final String SPRING_SECURITY_CLIENT_ID = ".client-id"; + private static final String SPRING_SECURITY_CLIENT_SECRET = ".client-secret"; + + private final CredentialGenerator credentialGenerator; + private final OAuth2Client oAuth2Client; + + public SpringSecurityOAuth2CredentialProviderFactory(CredentialGenerator credentialGenerator, + OAuth2Client oAuth2Client) { + super(Config.class); + this.credentialGenerator = credentialGenerator; + this.oAuth2Client = oAuth2Client; + } + + @Override + public CredentialProvider create(Config config) { + return new CredentialProvider() { + @Override + public Mono addCredentials(BackingApplication backingApplication, + String serviceInstanceGuid) { + return generateCredentials(config, backingApplication, serviceInstanceGuid) + .flatMap(client -> createOAuth2Client(config, client)) + .flatMap(response -> Mono.just(backingApplication)); + } + + @Override + public Mono deleteCredentials(BackingApplication backingApplication, + String serviceInstanceGuid) { + credentialGenerator.deleteString(backingApplication.getName(), serviceInstanceGuid); + + String clientId = generateClientId(config, backingApplication, serviceInstanceGuid); + + return deleteOAuth2Client(config, clientId) + .flatMap(response -> Mono.just(backingApplication)); + } + }; + } + + private Mono> generateCredentials(Config config, + BackingApplication backingApplication, + String serviceInstanceGuid) { + String id = generateClientId(config, backingApplication, serviceInstanceGuid); + String secret = generateClientSecret(config, backingApplication, serviceInstanceGuid); + + Pair client = Pair.of(id, secret); + + backingApplication.addEnvironment(springSecurityClientIdKey(config.getRegistration()), + client.getLeft()); + backingApplication.addEnvironment(springSecurityClientSecretKey(config.getRegistration()), + client.getRight()); + + return Mono.just(client); + } + + private String generateClientId(Config config, BackingApplication backingApplication, + String serviceInstanceGuid) { + if (config.clientId == null) { + return backingApplication.getName() + "-" + serviceInstanceGuid; + } + return config.getClientId(); + } + + private String generateClientSecret(Config config, BackingApplication backingApplication, + String serviceInstanceGuid) { + return credentialGenerator.generateString(backingApplication.getName(), serviceInstanceGuid, + config.getLength(), config.isIncludeUppercaseAlpha(), config.isIncludeLowercaseAlpha(), + config.isIncludeNumeric(), config.isIncludeSpecial()); + } + + private Mono createOAuth2Client(Config config, Pair client) { + CreateOAuth2ClientRequest.CreateOAuth2ClientRequestBuilder builder = CreateOAuth2ClientRequest.builder() + .clientId(client.getLeft()) + .clientSecret(client.getRight()) + .clientName(config.getClientName()) + .identityZoneSubdomain(config.getIdentityZoneSubdomain()) + .identityZoneId(config.getIdentityZoneId()); + + if (config.getScopes() != null) { + builder.scopes(config.getScopes()); + } + + if (config.getAuthorities() != null) { + builder.authorities(config.getAuthorities()); + } + + if (config.getGrantTypes() != null) { + builder.grantTypes(config.getGrantTypes()); + } + + return oAuth2Client.createClient(builder.build()); + } + + private Mono deleteOAuth2Client(Config config, String clientId) { + DeleteOAuth2ClientRequest request = DeleteOAuth2ClientRequest.builder() + .clientId(clientId) + .identityZoneSubdomain(config.getIdentityZoneSubdomain()) + .identityZoneId(config.getIdentityZoneId()) + .build(); + + return oAuth2Client.deleteClient(request); + } + + String springSecurityClientIdKey(String registration) { + return SPRING_SECURITY_CLIENT_REGISTRATION + + registration + + SPRING_SECURITY_CLIENT_ID; + } + + String springSecurityClientSecretKey(String registration) { + return SPRING_SECURITY_CLIENT_REGISTRATION + + registration + + SPRING_SECURITY_CLIENT_SECRET; + } + + @SuppressWarnings("WeakerAccess") + public static class Config extends CredentialGenerationConfig { + private String registration; + private String clientId; + private String clientName; + private String[] scopes; + private String[] authorities; + private String[] grantTypes; + private String identityZoneSubdomain; + private String identityZoneId; + + public String getRegistration() { + return registration; + } + + public void setRegistration(String registration) { + this.registration = registration; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientName() { + return clientName; + } + + public void setClientName(String clientName) { + this.clientName = clientName; + } + + public String[] getScopes() { + return scopes; + } + + public void setScopes(String... scopes) { + this.scopes = scopes; + } + + public String[] getAuthorities() { + return authorities; + } + + public void setAuthorities(String... authorities) { + this.authorities = authorities; + } + + public String[] getGrantTypes() { + return grantTypes; + } + + public void setGrantTypes(String... grantTypes) { + this.grantTypes = grantTypes; + } + + public String getIdentityZoneSubdomain() { + return identityZoneSubdomain; + } + + public void setIdentityZoneSubdomain(String identityZoneSubdomain) { + this.identityZoneSubdomain = identityZoneSubdomain; + } + + public String getIdentityZoneId() { + return identityZoneId; + } + + public void setIdentityZoneId(String identityZoneId) { + this.identityZoneId = identityZoneId; + } + } +} diff --git a/spring-cloud-app-broker-core/src/test/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityOAuth2CredentialProviderFactoryTest.java b/spring-cloud-app-broker-core/src/test/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityOAuth2CredentialProviderFactoryTest.java new file mode 100644 index 0000000..f5b2bf8 --- /dev/null +++ b/spring-cloud-app-broker-core/src/test/java/org/springframework/cloud/appbroker/extensions/credentials/SpringSecurityOAuth2CredentialProviderFactoryTest.java @@ -0,0 +1,187 @@ +/* + * 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.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 org.springframework.cloud.appbroker.deployer.BackingApplication; +import org.springframework.cloud.appbroker.oauth2.CreateOAuth2ClientRequest; +import org.springframework.cloud.appbroker.oauth2.CreateOAuth2ClientResponse; +import org.springframework.cloud.appbroker.oauth2.DeleteOAuth2ClientRequest; +import org.springframework.cloud.appbroker.oauth2.DeleteOAuth2ClientResponse; +import org.springframework.cloud.appbroker.oauth2.OAuth2Client; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SpringSecurityOAuth2CredentialProviderFactoryTest { + private static final String CLIENT_REGISTRATION = "app-client"; + + @Mock + private CredentialGenerator credentialGenerator; + + @Mock + private OAuth2Client oAuth2Client; + + private CredentialProvider provider; + private SpringSecurityOAuth2CredentialProviderFactory factory; + + @BeforeEach + void setUp() { + factory = new SpringSecurityOAuth2CredentialProviderFactory(credentialGenerator, oAuth2Client); + } + + @Test + void addCredentialsWithClientId() { + createProvider("test-id"); + + BackingApplication backingApplication = BackingApplication.builder() + .build(); + + when(credentialGenerator.generateString(backingApplication.getName(), "service-instance-id", + 8, true, false, true, false)) + .thenReturn("test-secret"); + + when(oAuth2Client.createClient(buildCreateOAuth2Request("test-id"))) + .thenReturn(Mono.just(CreateOAuth2ClientResponse.builder().build())); + + StepVerifier + .create(provider.addCredentials(backingApplication, "service-instance-id")) + .expectNext(backingApplication) + .verifyComplete(); + + assertThat(backingApplication.getEnvironment()) + .containsEntry(factory.springSecurityClientIdKey(CLIENT_REGISTRATION), "test-id"); + assertThat(backingApplication.getEnvironment()) + .containsEntry(factory.springSecurityClientSecretKey(CLIENT_REGISTRATION), "test-secret"); + } + + @Test + void addCredentialsWithoutClientId() { + createProvider(null); + + BackingApplication backingApplication = BackingApplication.builder() + .name("test-app") + .build(); + String clientId = backingApplication.getName() + "-" + "service-instance-id"; + + when(credentialGenerator.generateString(backingApplication.getName(), "service-instance-id", + 8, true, false, true, false)) + .thenReturn("test-secret"); + + when(oAuth2Client.createClient(buildCreateOAuth2Request(clientId))) + .thenReturn(Mono.just(CreateOAuth2ClientResponse.builder().build())); + + StepVerifier + .create(provider.addCredentials(backingApplication, "service-instance-id")) + .expectNext(backingApplication) + .verifyComplete(); + + assertThat(backingApplication.getEnvironment()) + .containsEntry(factory.springSecurityClientIdKey(CLIENT_REGISTRATION), clientId); + assertThat(backingApplication.getEnvironment()) + .containsEntry(factory.springSecurityClientSecretKey(CLIENT_REGISTRATION), "test-secret"); + } + + @Test + void deleteCredentialsWithClientId() { + createProvider("test-id"); + + BackingApplication backingApplication = BackingApplication.builder() + .build(); + + when(oAuth2Client.deleteClient(buildDeleteOAuth2Request("test-id"))) + .thenReturn(Mono.just(DeleteOAuth2ClientResponse.builder().build())); + + StepVerifier + .create(provider.deleteCredentials(backingApplication, "service-instance-id")) + .expectNext(backingApplication) + .verifyComplete(); + + verify(credentialGenerator).deleteString(backingApplication.getName(), "service-instance-id"); + verifyNoMoreInteractions(credentialGenerator); + } + + @Test + void deleteCredentialsWithoutClientId() { + createProvider(null); + + BackingApplication backingApplication = BackingApplication.builder() + .name("test-app") + .build(); + String clientId = backingApplication.getName() + "-" + "service-instance-id"; + + when(oAuth2Client.deleteClient(buildDeleteOAuth2Request(clientId))) + .thenReturn(Mono.just(DeleteOAuth2ClientResponse.builder().build())); + + StepVerifier + .create(provider.deleteCredentials(backingApplication, "service-instance-id")) + .expectNext(backingApplication) + .verifyComplete(); + + verify(credentialGenerator).deleteString(backingApplication.getName(), "service-instance-id"); + verifyNoMoreInteractions(credentialGenerator); + } + + private void createProvider(String clientId) { + provider = factory.createWithConfig(config -> { + config.setRegistration(CLIENT_REGISTRATION); + config.setClientId(clientId); + config.setClientName("test-name"); + config.setScopes("scope1"); + config.setAuthorities("auth1"); + config.setGrantTypes("client_credentials"); + config.setIdentityZoneSubdomain("subdomain"); + config.setIdentityZoneId("zoneId"); + + config.setLength(8); + config.setIncludeUppercaseAlpha(true); + config.setIncludeLowercaseAlpha(false); + config.setIncludeNumeric(true); + config.setIncludeSpecial(false); + }); + } + + private CreateOAuth2ClientRequest buildCreateOAuth2Request(String clientId) { + return CreateOAuth2ClientRequest.builder() + .clientId(clientId) + .clientSecret("test-secret") + .clientName("test-name") + .scopes("scope1") + .authorities("auth1") + .grantTypes("client_credentials") + .identityZoneSubdomain("subdomain") + .identityZoneId("zoneId") + .build(); + } + + private DeleteOAuth2ClientRequest buildDeleteOAuth2Request(String clientId) { + return DeleteOAuth2ClientRequest.builder() + .clientId(clientId) + .identityZoneSubdomain("subdomain") + .identityZoneId("zoneId") + .build(); + } +} diff --git a/spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryOAuth2Client.java b/spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryOAuth2Client.java new file mode 100644 index 0000000..a3cd133 --- /dev/null +++ b/spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryOAuth2Client.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.deployer.cloudfoundry; + +import org.cloudfoundry.uaa.UaaClient; +import org.cloudfoundry.uaa.clients.CreateClientRequest; +import org.cloudfoundry.uaa.clients.CreateClientResponse; +import org.cloudfoundry.uaa.clients.DeleteClientRequest; +import org.cloudfoundry.uaa.clients.DeleteClientResponse; +import org.cloudfoundry.uaa.tokens.GrantType; +import org.springframework.cloud.appbroker.oauth2.CreateOAuth2ClientRequest; +import org.springframework.cloud.appbroker.oauth2.CreateOAuth2ClientResponse; +import org.springframework.cloud.appbroker.oauth2.DeleteOAuth2ClientRequest; +import org.springframework.cloud.appbroker.oauth2.DeleteOAuth2ClientResponse; +import org.springframework.cloud.appbroker.oauth2.OAuth2Client; +import org.springframework.util.CollectionUtils; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.stream.Collectors; + +public class CloudFoundryOAuth2Client implements OAuth2Client { + private final UaaClient uaaClient; + + public CloudFoundryOAuth2Client(UaaClient uaaClient) { + this.uaaClient = uaaClient; + } + + @Override + public Mono createClient(CreateOAuth2ClientRequest request) { + return uaaClient.clients() + .create(mapCreateRequest(request)) + .map(this::mapCreateResponse); + } + + @Override + public Mono deleteClient(DeleteOAuth2ClientRequest request) { + return uaaClient.clients() + .delete(mapDeleteRequest(request)) + .map(this::mapDeleteResponse); + } + + private CreateClientRequest mapCreateRequest(CreateOAuth2ClientRequest request) { + return CreateClientRequest.builder() + .clientId(request.getClientId()) + .clientSecret(request.getClientSecret()) + .name(request.getClientName()) + .scopes(request.getScopes()) + .authorities(request.getAuthorities()) + .authorizedGrantTypes(mapStringToGrantType(request.getGrantTypes())) + .identityZoneSubdomain(request.getIdentityZoneSubdomain()) + .identityZoneId(request.getIdentityZoneId()) + .build(); + } + + private CreateOAuth2ClientResponse mapCreateResponse(CreateClientResponse response) { + return CreateOAuth2ClientResponse.builder() + .clientId(response.getClientId()) + .clientName(response.getName()) + .scopes(response.getScopes()) + .authorities(response.getAuthorities()) + .grantTypes(mapGrantTypeToString(response.getAuthorizedGrantTypes())) + .build(); + } + + private DeleteClientRequest mapDeleteRequest(DeleteOAuth2ClientRequest request) { + return DeleteClientRequest.builder() + .clientId(request.getClientId()) + .identityZoneSubdomain(request.getIdentityZoneSubdomain()) + .identityZoneId(request.getIdentityZoneId()) + .build(); + } + + private DeleteOAuth2ClientResponse mapDeleteResponse(DeleteClientResponse response) { + return DeleteOAuth2ClientResponse.builder() + .clientId(response.getClientId()) + .clientName(response.getName()) + .scopes(response.getScopes()) + .authorities(response.getAuthorities()) + .grantTypes(mapGrantTypeToString(response.getAuthorizedGrantTypes())) + .build(); + } + + private List mapStringToGrantType(List grantTypes) { + if (CollectionUtils.isEmpty(grantTypes)) { + return null; + } + + return grantTypes.stream() + .map(GrantType::from) + .collect(Collectors.toList()); + } + + private List mapGrantTypeToString(List grantTypes) { + if (CollectionUtils.isEmpty(grantTypes)) { + return null; + } + + return grantTypes.stream() + .map(GrantType::getValue) + .collect(Collectors.toList()); + } +} diff --git a/spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryOAuth2ClientTest.java b/spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryOAuth2ClientTest.java new file mode 100644 index 0000000..d180030 --- /dev/null +++ b/spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryOAuth2ClientTest.java @@ -0,0 +1,146 @@ +/* + * 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.deployer.cloudfoundry; + +import org.cloudfoundry.uaa.UaaClient; +import org.cloudfoundry.uaa.clients.Clients; +import org.cloudfoundry.uaa.clients.CreateClientRequest; +import org.cloudfoundry.uaa.clients.CreateClientResponse; +import org.cloudfoundry.uaa.clients.DeleteClientRequest; +import org.cloudfoundry.uaa.clients.DeleteClientResponse; +import org.cloudfoundry.uaa.tokens.GrantType; +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.appbroker.oauth2.CreateOAuth2ClientRequest; +import org.springframework.cloud.appbroker.oauth2.DeleteOAuth2ClientRequest; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CloudFoundryOAuth2ClientTest { + + @Mock + private UaaClient uaaClient; + + @Mock + private Clients clients; + + private CloudFoundryOAuth2Client oAuth2Client; + + @BeforeEach + void setUp() { + when(uaaClient.clients()).thenReturn(clients); + + oAuth2Client = new CloudFoundryOAuth2Client(uaaClient); + } + + @Test + void createClient() { + CreateClientRequest clientsRequest = CreateClientRequest.builder() + .clientId("test-client") + .clientSecret("test-secret") + .name("test-name") + .scopes("auth1", "auth2") + .authorities("auth1", "auth2") + .authorizedGrantTypes(GrantType.CLIENT_CREDENTIALS, GrantType.AUTHORIZATION_CODE, + GrantType.PASSWORD, GrantType.IMPLICIT, GrantType.REFRESH_TOKEN) + .identityZoneSubdomain("subdomain") + .identityZoneId("zoneId") + .build(); + + CreateClientResponse clientsResponse = CreateClientResponse.builder() + .clientId("test-client") + .name("test-name") + .scopes("auth1", "auth2") + .authorities("auth1", "auth2") + .authorizedGrantTypes(GrantType.CLIENT_CREDENTIALS, GrantType.AUTHORIZATION_CODE, + GrantType.PASSWORD, GrantType.IMPLICIT, GrantType.REFRESH_TOKEN) + .build(); + + when(clients.create(clientsRequest)) + .thenReturn(Mono.just(clientsResponse)); + + CreateOAuth2ClientRequest request = CreateOAuth2ClientRequest.builder() + .clientId("test-client") + .clientSecret("test-secret") + .clientName("test-name") + .scopes("auth1", "auth2") + .authorities("auth1", "auth2") + .grantTypes("client_credentials", "authorization_code", "password", "implicit", "refresh_token") + .identityZoneSubdomain("subdomain") + .identityZoneId("zoneId") + .build(); + + StepVerifier.create(oAuth2Client.createClient(request)) + .assertNext(response -> assertResponse(response.getClientId(), response.getClientName(), + response.getScopes(), response.getAuthorities(), + response.getGrantTypes())) + .verifyComplete(); + } + + @Test + void deleteClient() { + DeleteClientRequest clientsRequest = DeleteClientRequest.builder() + .clientId("test-client") + .identityZoneSubdomain("subdomain") + .identityZoneId("zoneId") + .build(); + + DeleteClientResponse clientsResponse = DeleteClientResponse.builder() + .clientId("test-client") + .name("test-name") + .scopes("auth1", "auth2") + .authorities("auth1", "auth2") + .authorizedGrantTypes(GrantType.CLIENT_CREDENTIALS, GrantType.AUTHORIZATION_CODE, + GrantType.PASSWORD, GrantType.IMPLICIT, GrantType.REFRESH_TOKEN) + .build(); + + when(clients.delete(clientsRequest)) + .thenReturn(Mono.just(clientsResponse)); + + DeleteOAuth2ClientRequest request = DeleteOAuth2ClientRequest.builder() + .clientId("test-client") + .identityZoneSubdomain("subdomain") + .identityZoneId("zoneId") + .build(); + + StepVerifier.create(oAuth2Client.deleteClient(request)) + .assertNext(response -> assertResponse(response.getClientId(), response.getClientName(), + response.getScopes(), response.getAuthorities(), + response.getGrantTypes())) + .verifyComplete(); + } + + private void assertResponse(String clientId, String name, + List scopes, List authorities, + List authorizedGrantTypes) { + assertThat(clientId).isEqualTo("test-client"); + assertThat(name).isEqualTo("test-name"); + assertThat(scopes).contains("auth1", "auth2"); + assertThat(authorities).contains("auth1", "auth2"); + assertThat(authorizedGrantTypes).contains( + "client_credentials", "authorization_code", "password", "implicit", "refresh_token"); + } +} \ No newline at end of file diff --git a/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/deployer/CreateServiceInstanceRequest.java b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/deployer/CreateServiceInstanceRequest.java index 93d9a97..098c2b2 100644 --- a/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/deployer/CreateServiceInstanceRequest.java +++ b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/deployer/CreateServiceInstanceRequest.java @@ -39,8 +39,6 @@ public class CreateServiceInstanceRequest { this.properties = properties; } - - public static CreateServiceInstanceRequestBuilder builder() { return new CreateServiceInstanceRequestBuilder(); } diff --git a/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/CreateOAuth2ClientRequest.java b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/CreateOAuth2ClientRequest.java new file mode 100644 index 0000000..8fa901c --- /dev/null +++ b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/CreateOAuth2ClientRequest.java @@ -0,0 +1,182 @@ +/* + * 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.oauth2; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class CreateOAuth2ClientRequest { + + private final String clientId; + private final String clientSecret; + private final String clientName; + private final List scopes; + private final List authorities; + private final List grantTypes; + private final String identityZoneSubdomain; + private final String identityZoneId; + + CreateOAuth2ClientRequest(String clientId, String clientSecret, String clientName, + List scopes, List authorities, List grantTypes, + String identityZoneSubdomain, String identityZoneId) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.clientName = clientName; + this.scopes = scopes; + this.authorities = authorities; + this.grantTypes = grantTypes; + this.identityZoneSubdomain = identityZoneSubdomain; + this.identityZoneId = identityZoneId; + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public String getClientName() { + return clientName; + } + + public List getScopes() { + return scopes; + } + + public List getAuthorities() { + return authorities; + } + + public List getGrantTypes() { + return grantTypes; + } + + public String getIdentityZoneSubdomain() { + return identityZoneSubdomain; + } + + public String getIdentityZoneId() { + return identityZoneId; + } + + public static CreateOAuth2ClientRequestBuilder builder() { + return new CreateOAuth2ClientRequestBuilder(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CreateOAuth2ClientRequest)) { + return false; + } + CreateOAuth2ClientRequest that = (CreateOAuth2ClientRequest) o; + return Objects.equals(clientId, that.clientId) && + Objects.equals(clientSecret, that.clientSecret) && + Objects.equals(clientName, that.clientName) && + Objects.equals(scopes, that.scopes) && + Objects.equals(authorities, that.authorities) && + Objects.equals(grantTypes, that.grantTypes) && + Objects.equals(identityZoneSubdomain, that.identityZoneSubdomain) && + Objects.equals(identityZoneId, that.identityZoneId); + } + + @Override + public int hashCode() { + return Objects.hash(clientId, clientSecret, clientName, scopes, authorities, + grantTypes, identityZoneSubdomain, identityZoneId); + } + + @Override + public String toString() { + return "CreateOAuth2ClientRequest{" + + "clientId='" + clientId + '\'' + + ", clientSecret='" + clientSecret + '\'' + + ", clientName='" + clientName + '\'' + + ", scopes=" + scopes + + ", authorities=" + authorities + + ", grantTypes=" + grantTypes + + ", identityZoneSubdomain='" + identityZoneSubdomain + '\'' + + ", identityZoneId='" + identityZoneId + '\'' + + '}'; + } + + public static class CreateOAuth2ClientRequestBuilder { + + private String clientId; + private String clientSecret; + private String clientName; + private List scopes; + private List authorities; + private List grantTypes; + private String identityZoneSubdomain; + private String identityZoneId; + + CreateOAuth2ClientRequestBuilder() { + } + + public CreateOAuth2ClientRequestBuilder clientId(String clientId) { + this.clientId = clientId; + return this; + } + + public CreateOAuth2ClientRequestBuilder clientSecret(String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + public CreateOAuth2ClientRequestBuilder clientName(String clientName) { + this.clientName = clientName; + return this; + } + + public CreateOAuth2ClientRequestBuilder scopes(String... scopes) { + this.scopes = Arrays.asList(scopes); + return this; + } + + public CreateOAuth2ClientRequestBuilder authorities(String... authorities) { + this.authorities = Arrays.asList(authorities); + return this; + } + + public CreateOAuth2ClientRequestBuilder grantTypes(String... grantTypes) { + this.grantTypes = Arrays.asList(grantTypes); + return this; + } + + public CreateOAuth2ClientRequestBuilder identityZoneSubdomain(String identityZoneSubdomain) { + this.identityZoneSubdomain = identityZoneSubdomain; + return this; + } + + public CreateOAuth2ClientRequestBuilder identityZoneId(String identityZoneId) { + this.identityZoneId = identityZoneId; + return this; + } + + public CreateOAuth2ClientRequest build() { + return new CreateOAuth2ClientRequest(clientId, clientSecret, clientName, + scopes, authorities, grantTypes, + identityZoneSubdomain, identityZoneId); + } + } +} diff --git a/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/CreateOAuth2ClientResponse.java b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/CreateOAuth2ClientResponse.java new file mode 100644 index 0000000..ccd635c --- /dev/null +++ b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/CreateOAuth2ClientResponse.java @@ -0,0 +1,136 @@ +/* + * 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.oauth2; + +import java.util.List; +import java.util.Objects; + +public class CreateOAuth2ClientResponse { + + private final String clientId; + private final String clientName; + private final List scopes; + private final List authorities; + private final List grantTypes; + + CreateOAuth2ClientResponse(String clientId, String clientName, + List scopes, List authorities, + List grantTypes) { + + this.clientId = clientId; + this.clientName = clientName; + this.scopes = scopes; + this.authorities = authorities; + this.grantTypes = grantTypes; + } + + public String getClientId() { + return clientId; + } + + public String getClientName() { + return clientName; + } + + public List getScopes() { + return scopes; + } + + public List getAuthorities() { + return authorities; + } + + public List getGrantTypes() { + return grantTypes; + } + + public static CreateOAuth2ClientResponseBuilder builder() { + return new CreateOAuth2ClientResponseBuilder(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CreateOAuth2ClientResponse)) { + return false; + } + CreateOAuth2ClientResponse that = (CreateOAuth2ClientResponse) o; + return Objects.equals(clientId, that.clientId) && + Objects.equals(clientName, that.clientName) && + Objects.equals(scopes, that.scopes) && + Objects.equals(authorities, that.authorities) && + Objects.equals(grantTypes, that.grantTypes); + } + + @Override + public int hashCode() { + return Objects.hash(clientId, clientName, scopes, authorities, grantTypes); + } + + @Override + public String toString() { + return "CreateOAuth2ClientResponse{" + + "clientId='" + clientId + '\'' + + ", clientName='" + clientName + '\'' + + ", scopes=" + scopes + + ", authorities=" + authorities + + ", grantTypes=" + grantTypes + + '}'; + } + + public static class CreateOAuth2ClientResponseBuilder { + private String clientId; + private String clientName; + private List scopes; + private List authorities; + private List grantTypes; + + CreateOAuth2ClientResponseBuilder() { + } + + public CreateOAuth2ClientResponseBuilder clientId(String clientId) { + this.clientId = clientId; + return this; + } + + public CreateOAuth2ClientResponseBuilder clientName(String name) { + this.clientName = name; + return this; + } + + public CreateOAuth2ClientResponseBuilder scopes(List scopes) { + this.scopes = scopes; + return this; + } + + public CreateOAuth2ClientResponseBuilder authorities(List authorities) { + this.authorities = authorities; + return this; + } + + public CreateOAuth2ClientResponseBuilder grantTypes(List grantTypes) { + this.grantTypes = grantTypes; + return this; + } + + public CreateOAuth2ClientResponse build() { + return new CreateOAuth2ClientResponse(clientId, clientName, scopes, authorities, grantTypes); + } + } +} diff --git a/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/DeleteOAuth2ClientRequest.java b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/DeleteOAuth2ClientRequest.java new file mode 100644 index 0000000..290e4ad --- /dev/null +++ b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/DeleteOAuth2ClientRequest.java @@ -0,0 +1,102 @@ +/* + * 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.oauth2; + +import java.util.Objects; + +public class DeleteOAuth2ClientRequest { + + private final String clientId; + private final String identityZoneSubdomain; + private final String identityZoneId; + + DeleteOAuth2ClientRequest(String clientId, String identityZoneSubdomain, String identityZoneId) { + this.clientId = clientId; + this.identityZoneSubdomain = identityZoneSubdomain; + this.identityZoneId = identityZoneId; + } + + public String getClientId() { + return clientId; + } + + public String getIdentityZoneSubdomain() { + return identityZoneSubdomain; + } + + public String getIdentityZoneId() { + return identityZoneId; + } + + public static DeleteOAuth2ClientRequestBuilder builder() { + return new DeleteOAuth2ClientRequestBuilder(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DeleteOAuth2ClientRequest)) { + return false; + } + DeleteOAuth2ClientRequest that = (DeleteOAuth2ClientRequest) o; + return Objects.equals(clientId, that.clientId) && + Objects.equals(identityZoneSubdomain, that.identityZoneSubdomain) && + Objects.equals(identityZoneId, that.identityZoneId); + } + + @Override + public int hashCode() { + return Objects.hash(clientId, identityZoneSubdomain, identityZoneId); + } + + @Override + public String toString() { + return "DeleteOAuth2ClientRequest{" + + "clientId='" + clientId + '\'' + + ", identityZoneSubdomain='" + identityZoneSubdomain + '\'' + + ", identityZoneId='" + identityZoneId + '\'' + + '}'; + } + + public static class DeleteOAuth2ClientRequestBuilder { + + private String clientId; + private String identityZoneSubdomain; + private String identityZoneId; + + public DeleteOAuth2ClientRequestBuilder clientId(String clientId) { + this.clientId = clientId; + return this; + } + + public DeleteOAuth2ClientRequestBuilder identityZoneSubdomain(String identityZoneSubdomain) { + this.identityZoneSubdomain = identityZoneSubdomain; + return this; + } + + public DeleteOAuth2ClientRequestBuilder identityZoneId(String identityZoneId) { + this.identityZoneId = identityZoneId; + return this; + } + + public DeleteOAuth2ClientRequest build() { + return new DeleteOAuth2ClientRequest(clientId, identityZoneSubdomain, identityZoneId); + } + } +} diff --git a/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/DeleteOAuth2ClientResponse.java b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/DeleteOAuth2ClientResponse.java new file mode 100644 index 0000000..11e8280 --- /dev/null +++ b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/DeleteOAuth2ClientResponse.java @@ -0,0 +1,134 @@ +/* + * 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.oauth2; + +import java.util.List; +import java.util.Objects; + +public class DeleteOAuth2ClientResponse { + private final String clientId; + private final String clientName; + private final List scopes; + private final List authorities; + private final List grantTypes; + + DeleteOAuth2ClientResponse(String clientId, String clientName, + List scopes, List authorities, + List grantTypes) { + this.clientId = clientId; + this.clientName = clientName; + this.scopes = scopes; + this.authorities = authorities; + this.grantTypes = grantTypes; + } + + public String getClientId() { + return clientId; + } + + public String getClientName() { + return clientName; + } + + public List getScopes() { + return scopes; + } + + public List getAuthorities() { + return authorities; + } + + public List getGrantTypes() { + return grantTypes; + } + + public static DeleteOAuth2ClientResponseBuilder builder() { + return new DeleteOAuth2ClientResponseBuilder(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DeleteOAuth2ClientResponse)) { + return false; + } + DeleteOAuth2ClientResponse that = (DeleteOAuth2ClientResponse) o; + return Objects.equals(clientId, that.clientId) && + Objects.equals(clientName, that.clientName) && + Objects.equals(scopes, that.scopes) && + Objects.equals(authorities, that.authorities) && + Objects.equals(grantTypes, that.grantTypes); + } + + @Override + public int hashCode() { + return Objects.hash(clientId, clientName, scopes, authorities, grantTypes); + } + + @Override + public String toString() { + return "DeleteOAuth2ClientResponse{" + + "clientId='" + clientId + '\'' + + ", clientName='" + clientName + '\'' + + ", scopes=" + scopes + + ", authorities=" + authorities + + ", grantTypes=" + grantTypes + + '}'; + } + + public static class DeleteOAuth2ClientResponseBuilder { + private String clientId; + private String clientName; + private List scopes; + private List authorities; + private List grantTypes; + + DeleteOAuth2ClientResponseBuilder() { + } + + public DeleteOAuth2ClientResponseBuilder clientId(String clientId) { + this.clientId = clientId; + return this; + } + + public DeleteOAuth2ClientResponseBuilder clientName(String name) { + this.clientName = name; + return this; + } + + public DeleteOAuth2ClientResponseBuilder scopes(List scopes) { + this.scopes = scopes; + return this; + } + + public DeleteOAuth2ClientResponseBuilder authorities(List authorities) { + this.authorities = authorities; + return this; + } + + public DeleteOAuth2ClientResponseBuilder grantTypes(List grantTypes) { + this.grantTypes = grantTypes; + return this; + } + + public DeleteOAuth2ClientResponse build() { + return new DeleteOAuth2ClientResponse(clientId, clientName, scopes, authorities, grantTypes); + } + } +} diff --git a/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/OAuth2Client.java b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/OAuth2Client.java new file mode 100644 index 0000000..9ce589a --- /dev/null +++ b/spring-cloud-app-broker-deployer/src/main/java/org/springframework/cloud/appbroker/oauth2/OAuth2Client.java @@ -0,0 +1,29 @@ +/* + * 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.oauth2; + +import reactor.core.publisher.Mono; + +public interface OAuth2Client { + default Mono createClient(CreateOAuth2ClientRequest request) { + return Mono.empty(); + } + + default Mono deleteClient(DeleteOAuth2ClientRequest request) { + return Mono.empty(); + } +} \ No newline at end of file diff --git a/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithCredentialsComponentTest.java b/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithBasicAuthCredentialsComponentTest.java similarity index 95% rename from spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithCredentialsComponentTest.java rename to spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithBasicAuthCredentialsComponentTest.java index dc61263..ca83347 100644 --- a/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithCredentialsComponentTest.java +++ b/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithBasicAuthCredentialsComponentTest.java @@ -29,7 +29,7 @@ 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.sample.CreateInstanceWithCredentialsComponentTest.APP_NAME; +import static org.springframework.cloud.appbroker.sample.CreateInstanceWithBasicAuthCredentialsComponentTest.APP_NAME; @TestPropertySource(properties = { "spring.cloud.appbroker.services[0].service-name=example", @@ -43,7 +43,7 @@ import static org.springframework.cloud.appbroker.sample.CreateInstanceWithCrede "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" }) -class CreateInstanceWithCredentialsComponentTest extends WiremockComponentTest { +class CreateInstanceWithBasicAuthCredentialsComponentTest extends WiremockComponentTest { static final String APP_NAME = "app-with-credentials"; @@ -54,7 +54,7 @@ class CreateInstanceWithCredentialsComponentTest extends WiremockComponentTest { private CloudControllerStubFixture cloudControllerFixture; @Test - void pushAppWithEnvironmentVariables() { + void pushAppWithCredentials() { cloudControllerFixture.stubAppDoesNotExist(APP_NAME); cloudControllerFixture.stubPushApp(APP_NAME, matchingJsonPath("$.environment_json[?(@.SPRING_APPLICATION_JSON =~ /.*security.user.name.*:.*[a-zA-Z]{14}.*/)]"), diff --git a/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithOAuth2CredentialsComponentTest.java b/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithOAuth2CredentialsComponentTest.java new file mode 100644 index 0000000..dbab5e2 --- /dev/null +++ b/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/CreateInstanceWithOAuth2CredentialsComponentTest.java @@ -0,0 +1,120 @@ +/* + * 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.sample; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.appbroker.sample.fixtures.CloudControllerStubFixture; +import org.springframework.cloud.appbroker.sample.fixtures.OpenServiceBrokerApiFixture; +import org.springframework.cloud.appbroker.sample.fixtures.UaaStubFixture; +import org.springframework.cloud.servicebroker.model.instance.OperationState; +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.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.springframework.cloud.appbroker.sample.CreateInstanceWithOAuth2CredentialsComponentTest.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=SpringSecurityOAuth2", + "spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.registration=example-app-client", + "spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.client-id=test-client", + "spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.grant-types=[\"client_credentials\"]", + "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" +}) +class CreateInstanceWithOAuth2CredentialsComponentTest extends WiremockComponentTest { + + static final String APP_NAME = "app-with-outh2-credentials"; + + @Autowired + private OpenServiceBrokerApiFixture brokerFixture; + + @Autowired + private CloudControllerStubFixture cloudControllerFixture; + + @Autowired + private UaaStubFixture uaaStubFixture; + + @Test + void pushAppWithOAuth2Credentials() { + cloudControllerFixture.stubAppDoesNotExist(APP_NAME); + cloudControllerFixture.stubPushApp(APP_NAME, + matchingJsonPath("$.environment_json[?(@.SPRING_APPLICATION_JSON =~ " + + "/.*spring.security.oauth2.client.registration.example-app-client.client-id.*:.*test-client.*/)]"), + matchingJsonPath("$.environment_json[?(@.SPRING_APPLICATION_JSON =~ " + + "/.*spring.security.oauth2.client.registration.example-app-client.client-secret.*:.*[a-zA-Z]{14}.*/)]")); + + uaaStubFixture.stubCreateClient(); + + // when a service instance is created + given(brokerFixture.serviceInstanceRequest()) + .when() + .put(brokerFixture.createServiceInstanceUrl(), "instance-id") + .then() + .statusCode(HttpStatus.ACCEPTED.value()); + + // when the "last_operation" API is polled + given(brokerFixture.serviceInstanceRequest()) + .when() + .get(brokerFixture.getLastInstanceOperationUrl(), "instance-id") + .then() + .statusCode(HttpStatus.OK.value()) + .body("state", is(equalTo(OperationState.IN_PROGRESS.toString()))); + + String state = brokerFixture.waitForAsyncOperationComplete("instance-id"); + assertThat(state).isEqualTo(OperationState.SUCCEEDED.toString()); + } + + @Test + void deleteAppWithOAuth2Credentials() { + cloudControllerFixture.stubAppExists(APP_NAME); + cloudControllerFixture.stubServiceBindingDoesNotExist(APP_NAME); + cloudControllerFixture.stubDeleteApp(APP_NAME); + + uaaStubFixture.stubDeleteClient("test-client"); + + // when the service instance is deleted + given(brokerFixture.serviceInstanceRequest()) + .when() + .delete(brokerFixture.deleteServiceInstanceUrl(), "instance-id") + .then() + .statusCode(HttpStatus.ACCEPTED.value()); + + // when the "last_operation" API is polled + given(brokerFixture.serviceInstanceRequest()) + .when() + .get(brokerFixture.getLastInstanceOperationUrl(), "instance-id") + .then() + .statusCode(HttpStatus.OK.value()) + .body("state", is(equalTo(OperationState.IN_PROGRESS.toString()))); + + String state = brokerFixture.waitForAsyncOperationComplete("instance-id"); + assertThat(state).isEqualTo(OperationState.SUCCEEDED.toString()); + } + +} \ No newline at end of file diff --git a/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/fixtures/UaaStubFixture.java b/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/fixtures/UaaStubFixture.java index 022e65d..5f758a2 100644 --- a/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/fixtures/UaaStubFixture.java +++ b/spring-cloud-app-broker-sample/src/test/java/org.springframework.cloud.appbroker/sample/fixtures/UaaStubFixture.java @@ -18,6 +18,7 @@ package org.springframework.cloud.appbroker.sample.fixtures; import org.springframework.boot.test.context.TestComponent; +import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.ok; import static com.github.tomakehurst.wiremock.client.WireMock.post; @@ -88,6 +89,18 @@ public class UaaStubFixture extends WiremockStubFixture { .withBody(uaa("get-token-keys")))); } + public void stubCreateClient() { + stubFor(post(urlPathEqualTo("/oauth/clients")) + .willReturn(ok() + .withBody(uaa("post-oauth-clients")))); + } + + public void stubDeleteClient(String clientId) { + stubFor(delete(urlPathEqualTo("/oauth/clients/" + clientId)) + .willReturn(ok() + .withBody(uaa("delete-oauth-clients")))); + } + private String uaa(String fileRoot) { return readResponseFromFile(fileRoot, "uaa"); } diff --git a/spring-cloud-app-broker-sample/src/test/resources/responses/uaa/delete-oauth-clients.json b/spring-cloud-app-broker-sample/src/test/resources/responses/uaa/delete-oauth-clients.json new file mode 100644 index 0000000..319a4af --- /dev/null +++ b/spring-cloud-app-broker-sample/src/test/resources/responses/uaa/delete-oauth-clients.json @@ -0,0 +1,9 @@ +{ + "client_id" : "foo", + "name" : "Foo Client Name", + "client_secret" : "fooclientsecret", + "scope" : ["uaa.none"], + "authorities" : ["cloud_controller.read","cloud_controller.write","openid"], + "authorized_grant_types" : ["client_credentials"], + "access_token_validity": 43200 +} \ No newline at end of file diff --git a/spring-cloud-app-broker-sample/src/test/resources/responses/uaa/post-oauth-clients.json b/spring-cloud-app-broker-sample/src/test/resources/responses/uaa/post-oauth-clients.json new file mode 100644 index 0000000..319a4af --- /dev/null +++ b/spring-cloud-app-broker-sample/src/test/resources/responses/uaa/post-oauth-clients.json @@ -0,0 +1,9 @@ +{ + "client_id" : "foo", + "name" : "Foo Client Name", + "client_secret" : "fooclientsecret", + "scope" : ["uaa.none"], + "authorities" : ["cloud_controller.read","cloud_controller.write","openid"], + "authorized_grant_types" : ["client_credentials"], + "access_token_validity": 43200 +} \ No newline at end of file