Add SpringSecurityOAuth2 credential provider.

This commit is contained in:
Scott Frederick
2018-11-14 17:09:58 -06:00
parent d78e6cf73c
commit 0685897dac
21 changed files with 1518 additions and 62 deletions

View File

@@ -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<CredentialProviderFactory<?>> providers) {
return new CredentialProviderService(providers);

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ import org.springframework.cloud.appbroker.deployer.BackingApplication;
import reactor.core.publisher.Mono;
public class SpringSecurityBasicAuthCredentialProviderFactory extends
CredentialProviderFactory<SpringSecurityBasicAuthCredentialProviderFactory.Config> {
CredentialProviderFactory<CredentialGenerationConfig> {
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<BackingApplication> 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<String, String> 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;
}
}
}

View File

@@ -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<SpringSecurityOAuth2CredentialProviderFactory.Config> {
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<BackingApplication> addCredentials(BackingApplication backingApplication,
String serviceInstanceGuid) {
return generateCredentials(config, backingApplication, serviceInstanceGuid)
.flatMap(client -> createOAuth2Client(config, client))
.flatMap(response -> Mono.just(backingApplication));
}
@Override
public Mono<BackingApplication> 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<Pair<String, String>> generateCredentials(Config config,
BackingApplication backingApplication,
String serviceInstanceGuid) {
String id = generateClientId(config, backingApplication, serviceInstanceGuid);
String secret = generateClientSecret(config, backingApplication, serviceInstanceGuid);
Pair<String, String> 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<CreateOAuth2ClientResponse> createOAuth2Client(Config config, Pair<String, String> 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<DeleteOAuth2ClientResponse> 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;
}
}
}

View File

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

View File

@@ -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<CreateOAuth2ClientResponse> createClient(CreateOAuth2ClientRequest request) {
return uaaClient.clients()
.create(mapCreateRequest(request))
.map(this::mapCreateResponse);
}
@Override
public Mono<DeleteOAuth2ClientResponse> 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<GrantType> mapStringToGrantType(List<String> grantTypes) {
if (CollectionUtils.isEmpty(grantTypes)) {
return null;
}
return grantTypes.stream()
.map(GrantType::from)
.collect(Collectors.toList());
}
private List<String> mapGrantTypeToString(List<GrantType> grantTypes) {
if (CollectionUtils.isEmpty(grantTypes)) {
return null;
}
return grantTypes.stream()
.map(GrantType::getValue)
.collect(Collectors.toList());
}
}

View File

@@ -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<String> scopes, List<String> authorities,
List<String> 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");
}
}

View File

@@ -39,8 +39,6 @@ public class CreateServiceInstanceRequest {
this.properties = properties;
}
public static CreateServiceInstanceRequestBuilder builder() {
return new CreateServiceInstanceRequestBuilder();
}

View File

@@ -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<String> scopes;
private final List<String> authorities;
private final List<String> grantTypes;
private final String identityZoneSubdomain;
private final String identityZoneId;
CreateOAuth2ClientRequest(String clientId, String clientSecret, String clientName,
List<String> scopes, List<String> authorities, List<String> 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<String> getScopes() {
return scopes;
}
public List<String> getAuthorities() {
return authorities;
}
public List<String> 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<String> scopes;
private List<String> authorities;
private List<String> 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);
}
}
}

View File

@@ -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<String> scopes;
private final List<String> authorities;
private final List<String> grantTypes;
CreateOAuth2ClientResponse(String clientId, String clientName,
List<String> scopes, List<String> authorities,
List<String> 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<String> getScopes() {
return scopes;
}
public List<String> getAuthorities() {
return authorities;
}
public List<String> 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<String> scopes;
private List<String> authorities;
private List<String> 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<String> scopes) {
this.scopes = scopes;
return this;
}
public CreateOAuth2ClientResponseBuilder authorities(List<String> authorities) {
this.authorities = authorities;
return this;
}
public CreateOAuth2ClientResponseBuilder grantTypes(List<String> grantTypes) {
this.grantTypes = grantTypes;
return this;
}
public CreateOAuth2ClientResponse build() {
return new CreateOAuth2ClientResponse(clientId, clientName, scopes, authorities, grantTypes);
}
}
}

View File

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

View File

@@ -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<String> scopes;
private final List<String> authorities;
private final List<String> grantTypes;
DeleteOAuth2ClientResponse(String clientId, String clientName,
List<String> scopes, List<String> authorities,
List<String> 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<String> getScopes() {
return scopes;
}
public List<String> getAuthorities() {
return authorities;
}
public List<String> 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<String> scopes;
private List<String> authorities;
private List<String> 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<String> scopes) {
this.scopes = scopes;
return this;
}
public DeleteOAuth2ClientResponseBuilder authorities(List<String> authorities) {
this.authorities = authorities;
return this;
}
public DeleteOAuth2ClientResponseBuilder grantTypes(List<String> grantTypes) {
this.grantTypes = grantTypes;
return this;
}
public DeleteOAuth2ClientResponse build() {
return new DeleteOAuth2ClientResponse(clientId, clientName, scopes, authorities, grantTypes);
}
}
}

View File

@@ -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<CreateOAuth2ClientResponse> createClient(CreateOAuth2ClientRequest request) {
return Mono.empty();
}
default Mono<DeleteOAuth2ClientResponse> deleteClient(DeleteOAuth2ClientRequest request) {
return Mono.empty();
}
}

View File

@@ -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}.*/)]"),

View File

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

View File

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

View File

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

View File

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