Add support to optionally generate secrets from CredHub
- Add `CredHubCredentialsGenerator` - Update `CredHubAutoconfiguration` to conditionally create beans - Refactor the `CredentialGenerator` interface to be reactive - Replace use of Pair in favor of Tuple2 Resolves #152
This commit is contained in:
committed by
Scott Frederick
parent
c33bb39953
commit
c504576793
@@ -16,27 +16,32 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.autoconfigure;
|
||||
|
||||
import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingCreateServiceInstanceAppBindingWorkflow;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.cloud.appbroker.extensions.credentials.CredHubCredentialsGenerator;
|
||||
import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindingWorkflow;
|
||||
import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingCreateServiceInstanceAppBindingWorkflow;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.credhub.core.ReactiveCredHubOperations;
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(AppBrokerAutoConfiguration.class)
|
||||
@ConditionalOnBean(ReactiveCredHubOperations.class)
|
||||
public class CredHubAutoConfiguration {
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String appName;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ReactiveCredHubOperations.class)
|
||||
public CreateServiceInstanceAppBindingWorkflow credhubPersistingCreateServiceInstanceAppBindingWorkflow(ReactiveCredHubOperations credHubOperations) {
|
||||
return new CredHubPersistingCreateServiceInstanceAppBindingWorkflow(credHubOperations, appName);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CredHubCredentialsGenerator credHubCredentialsGenerator(ReactiveCredHubOperations credHubOperations) {
|
||||
return new CredHubCredentialsGenerator(credHubOperations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
package org.springframework.cloud.appbroker.autoconfigure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.appbroker.extensions.credentials.CredHubCredentialsGenerator;
|
||||
import org.springframework.cloud.appbroker.extensions.credentials.CredentialGenerator;
|
||||
import org.springframework.cloud.appbroker.extensions.credentials.SimpleCredentialGenerator;
|
||||
import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingCreateServiceInstanceAppBindingWorkflow;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
@@ -60,7 +64,14 @@ class AppBrokerAutoConfigurationTest {
|
||||
@Test
|
||||
void servicesAreCreatedWithCloudFoundryConfigured() {
|
||||
configuredContext()
|
||||
.run(this::assertContext);
|
||||
.run(context -> {
|
||||
assertContext(context);
|
||||
assertThat(context).doesNotHaveBean(CreateServiceInstanceAppBindingWorkflow.class);
|
||||
assertThat(context)
|
||||
.hasSingleBean(CredentialGenerator.class)
|
||||
.getBean(CredentialGenerator.class)
|
||||
.isExactlyInstanceOf(SimpleCredentialGenerator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,6 +84,10 @@ class AppBrokerAutoConfigurationTest {
|
||||
.hasSingleBean(CreateServiceInstanceAppBindingWorkflow.class)
|
||||
.getBean(CreateServiceInstanceAppBindingWorkflow.class)
|
||||
.isExactlyInstanceOf(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class);
|
||||
assertThat(context)
|
||||
.hasSingleBean(CredentialGenerator.class)
|
||||
.getBean(CredentialGenerator.class)
|
||||
.isExactlyInstanceOf(CredHubCredentialsGenerator.class);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,23 +16,26 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.extensions.credentials;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
public interface CredentialGenerator {
|
||||
|
||||
Pair<String, String> generateUser(String applicationId, String serviceInstanceId,
|
||||
int length, boolean includeUppercaseAlpha,
|
||||
boolean includeLowercaseAlpha, boolean includeNumeric,
|
||||
boolean includeSpecial);
|
||||
Mono<Tuple2<String, String>> generateUser(String applicationId, String serviceInstanceId, String descriptor,
|
||||
int length, boolean includeUppercaseAlpha,
|
||||
boolean includeLowercaseAlpha, boolean includeNumeric,
|
||||
boolean includeSpecial);
|
||||
|
||||
String generateString(String applicationId, String serviceInstanceId,
|
||||
int length, boolean includeUppercaseAlpha,
|
||||
boolean includeLowercaseAlpha, boolean includeNumeric,
|
||||
boolean includeSpecial);
|
||||
Mono<String> generateString(String applicationId, String serviceInstanceId, String descriptor,
|
||||
int length, boolean includeUppercaseAlpha,
|
||||
boolean includeLowercaseAlpha, boolean includeNumeric,
|
||||
boolean includeSpecial);
|
||||
|
||||
default void deleteUser(String applicationId, String serviceInstanceId) {
|
||||
default Mono<Void> deleteUser(String applicationId, String serviceInstanceId, String descriptor) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
default void deleteString(String applicationId, String serviceInstanceId) {
|
||||
default Mono<Void> deleteString(String applicationId, String serviceInstanceId, String descriptor) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.extensions.credentials;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
public class SimpleCredentialGenerator implements CredentialGenerator {
|
||||
|
||||
private static final String UPPERCASE_ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
@@ -29,22 +31,22 @@ public class SimpleCredentialGenerator implements CredentialGenerator {
|
||||
private static final String SPECIAL_CHARACTERS = "~`!@#$%^&*()-_=+[{]}\\|;:\'\",<.>/?";
|
||||
|
||||
private final SecureRandom secureRandom = new SecureRandom();
|
||||
|
||||
|
||||
@Override
|
||||
public Pair<String, String> generateUser(String applicationId, String serviceInstanceId, int length,
|
||||
boolean includeUppercaseAlpha, boolean includeLowercaseAlpha,
|
||||
boolean includeNumeric, boolean includeSpecial) {
|
||||
return Pair.of(
|
||||
generateString(applicationId, serviceInstanceId, length,
|
||||
includeUppercaseAlpha, includeLowercaseAlpha, includeNumeric, includeSpecial),
|
||||
generateString(applicationId, serviceInstanceId, length,
|
||||
includeUppercaseAlpha, includeLowercaseAlpha, includeNumeric, includeSpecial));
|
||||
public Mono<Tuple2<String, String>> generateUser(String applicationId, String serviceInstanceId, String descriptor, int length,
|
||||
boolean includeUppercaseAlpha, boolean includeLowercaseAlpha,
|
||||
boolean includeNumeric, boolean includeSpecial) {
|
||||
return generateString(applicationId, serviceInstanceId, descriptor, length,
|
||||
includeUppercaseAlpha, includeLowercaseAlpha, includeNumeric, includeSpecial)
|
||||
.flatMap(username -> generateString(applicationId, serviceInstanceId, descriptor, length,
|
||||
includeUppercaseAlpha, includeLowercaseAlpha, includeNumeric, includeSpecial)
|
||||
.map(password -> Tuples.of(username, password)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateString(String applicationId, String serviceInstanceId, int length,
|
||||
boolean includeUppercaseAlpha, boolean includeLowercaseAlpha,
|
||||
boolean includeNumeric, boolean includeSpecial) {
|
||||
public Mono<String> generateString(String applicationId, String serviceInstanceId, String descriptor, int length,
|
||||
boolean includeUppercaseAlpha, boolean includeLowercaseAlpha,
|
||||
boolean includeNumeric, boolean includeSpecial) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (includeUppercaseAlpha) {
|
||||
@@ -65,13 +67,13 @@ public class SimpleCredentialGenerator implements CredentialGenerator {
|
||||
|
||||
if (builder.length() == 0) {
|
||||
builder.append(UPPERCASE_ALPHA)
|
||||
.append(LOWERCASE_ALPHA)
|
||||
.append(DIGITS)
|
||||
.append(SPECIAL_CHARACTERS);
|
||||
.append(LOWERCASE_ALPHA)
|
||||
.append(DIGITS)
|
||||
.append(SPECIAL_CHARACTERS);
|
||||
}
|
||||
|
||||
char[] chars = builder.toString().toCharArray();
|
||||
|
||||
return RandomStringUtils.random(length, 0, chars.length - 1, false, false, chars, secureRandom);
|
||||
return Mono.just(RandomStringUtils.random(length, 0, chars.length - 1, false, false, chars, secureRandom));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,19 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.extensions.credentials;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.cloud.appbroker.deployer.BackingApplication;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import org.springframework.cloud.appbroker.deployer.BackingApplication;
|
||||
|
||||
public class SpringSecurityBasicAuthCredentialProviderFactory extends
|
||||
CredentialProviderFactory<CredentialGenerationConfig> {
|
||||
|
||||
private static final String CREDENTIAL_DESCRIPTOR = "basic";
|
||||
|
||||
static final String SPRING_KEY = "spring";
|
||||
static final String SPRING_SECURITY_KEY = "security";
|
||||
static final String SPRING_SECURITY_USER_KEY = "user";
|
||||
@@ -46,35 +48,37 @@ public class SpringSecurityBasicAuthCredentialProviderFactory extends
|
||||
@Override
|
||||
public Mono<BackingApplication> addCredentials(BackingApplication backingApplication,
|
||||
String serviceInstanceGuid) {
|
||||
Pair<String, String> user = generateCredentials(config, backingApplication, serviceInstanceGuid);
|
||||
addUserToEnvironment(backingApplication, user);
|
||||
return Mono.just(backingApplication);
|
||||
return generateCredentials(config, backingApplication, serviceInstanceGuid)
|
||||
.flatMap(user -> addUserToEnvironment(backingApplication, user))
|
||||
.thenReturn(backingApplication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<BackingApplication> deleteCredentials(BackingApplication backingApplication,
|
||||
String serviceInstanceGuid) {
|
||||
credentialGenerator.deleteUser(backingApplication.getName(), serviceInstanceGuid);
|
||||
return Mono.just(backingApplication);
|
||||
return credentialGenerator.deleteUser(backingApplication.getName(), serviceInstanceGuid, CREDENTIAL_DESCRIPTOR)
|
||||
.thenReturn(backingApplication);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Pair<String, String> generateCredentials(CredentialGenerationConfig config,
|
||||
BackingApplication backingApplication,
|
||||
String serviceInstanceGuid) {
|
||||
return credentialGenerator.generateUser(backingApplication.getName(), serviceInstanceGuid,
|
||||
config.getLength(), config.isIncludeUppercaseAlpha(), config.isIncludeLowercaseAlpha(),
|
||||
config.isIncludeNumeric(), config.isIncludeSpecial());
|
||||
private Mono<Tuple2<String, String>> generateCredentials(CredentialGenerationConfig config,
|
||||
BackingApplication backingApplication,
|
||||
String serviceInstanceGuid) {
|
||||
return credentialGenerator.generateUser(backingApplication.getName(), serviceInstanceGuid, CREDENTIAL_DESCRIPTOR,
|
||||
config.getLength(), config.isIncludeUppercaseAlpha(), config.isIncludeLowercaseAlpha(),
|
||||
config.isIncludeNumeric(), config.isIncludeSpecial());
|
||||
}
|
||||
|
||||
private void addUserToEnvironment(BackingApplication backingApplication, Pair<String, String> user) {
|
||||
Map<String, String> userProperties = new HashMap<>(2);
|
||||
userProperties.put(SPRING_SECURITY_USER_NAME_KEY, user.getLeft());
|
||||
userProperties.put(SPRING_SECURITY_USER_PASSWORD_KEY, user.getRight());
|
||||
|
||||
backingApplication.addEnvironment(SPRING_KEY,
|
||||
Collections.singletonMap(SPRING_SECURITY_KEY,
|
||||
Collections.singletonMap(SPRING_SECURITY_USER_KEY, userProperties)));
|
||||
private Mono<Void> addUserToEnvironment(BackingApplication backingApplication, Tuple2<String, String> user) {
|
||||
return Mono.just(new HashMap<>(2))
|
||||
.flatMap(userProperties -> {
|
||||
userProperties.put(SPRING_SECURITY_USER_NAME_KEY, user.getT1());
|
||||
userProperties.put(SPRING_SECURITY_USER_PASSWORD_KEY, user.getT2());
|
||||
backingApplication.addEnvironment(SPRING_KEY,
|
||||
Collections.singletonMap(SPRING_SECURITY_KEY,
|
||||
Collections.singletonMap(SPRING_SECURITY_USER_KEY, userProperties)));
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,26 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.extensions.credentials;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
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 java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SpringSecurityOAuth2CredentialProviderFactory extends
|
||||
CredentialProviderFactory<SpringSecurityOAuth2CredentialProviderFactory.Config> {
|
||||
|
||||
private static final String CREDENTIAL_DESCRIPTOR = "oauth2";
|
||||
|
||||
static final String SPRING_KEY = "spring";
|
||||
static final String SPRING_SECURITY_KEY = "security";
|
||||
static final String SPRING_SECURITY_OAUTH2_KEY = "oauth2";
|
||||
@@ -65,33 +69,30 @@ public class SpringSecurityOAuth2CredentialProviderFactory extends
|
||||
@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));
|
||||
return credentialGenerator.deleteString(backingApplication.getName(), serviceInstanceGuid, CREDENTIAL_DESCRIPTOR)
|
||||
.then(generateClientId(config, backingApplication, serviceInstanceGuid))
|
||||
.flatMap(clientId -> 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);
|
||||
|
||||
return Mono.just(client);
|
||||
private Mono<Tuple2<String, String>> generateCredentials(Config config,
|
||||
BackingApplication backingApplication,
|
||||
String serviceInstanceGuid) {
|
||||
return generateClientId(config, backingApplication, serviceInstanceGuid)
|
||||
.flatMap(id -> generateClientSecret(config, backingApplication, serviceInstanceGuid)
|
||||
.map(secret -> Tuples.of(id, secret)));
|
||||
}
|
||||
|
||||
private Mono<Pair<String, String>> addClientToEnvironment(Config config,
|
||||
private Mono<Tuple2<String, String>> addClientToEnvironment(Config config,
|
||||
BackingApplication backingApplication,
|
||||
Pair<String, String> client) {
|
||||
Tuple2<String, String> client) {
|
||||
|
||||
|
||||
Map<String, String> clientProperties = new HashMap<>(2);
|
||||
clientProperties.put(SPRING_SECURITY_CLIENT_ID_KEY, client.getLeft());
|
||||
clientProperties.put(SPRING_SECURITY_CLIENT_SECRET_KEY, client.getRight());
|
||||
clientProperties.put(SPRING_SECURITY_CLIENT_ID_KEY, client.getT1());
|
||||
clientProperties.put(SPRING_SECURITY_CLIENT_SECRET_KEY, client.getT2());
|
||||
|
||||
backingApplication.addEnvironment(SPRING_KEY,
|
||||
Collections.singletonMap(SPRING_SECURITY_KEY,
|
||||
@@ -103,25 +104,27 @@ public class SpringSecurityOAuth2CredentialProviderFactory extends
|
||||
return Mono.just(client);
|
||||
}
|
||||
|
||||
private String generateClientId(Config config, BackingApplication backingApplication,
|
||||
private Mono<String> generateClientId(Config config, BackingApplication backingApplication,
|
||||
String serviceInstanceGuid) {
|
||||
if (config.clientId == null) {
|
||||
return backingApplication.getName() + "-" + serviceInstanceGuid;
|
||||
}
|
||||
return config.getClientId();
|
||||
return Mono.defer(() -> {
|
||||
if (config.clientId == null) {
|
||||
return Mono.just(backingApplication.getName() + "-" + serviceInstanceGuid);
|
||||
}
|
||||
return Mono.just(config.getClientId());
|
||||
});
|
||||
}
|
||||
|
||||
private String generateClientSecret(Config config, BackingApplication backingApplication,
|
||||
private Mono<String> generateClientSecret(Config config, BackingApplication backingApplication,
|
||||
String serviceInstanceGuid) {
|
||||
return credentialGenerator.generateString(backingApplication.getName(), serviceInstanceGuid,
|
||||
config.getLength(), config.isIncludeUppercaseAlpha(), config.isIncludeLowercaseAlpha(),
|
||||
CREDENTIAL_DESCRIPTOR, config.getLength(), config.isIncludeUppercaseAlpha(), config.isIncludeLowercaseAlpha(),
|
||||
config.isIncludeNumeric(), config.isIncludeSpecial());
|
||||
}
|
||||
|
||||
private Mono<CreateOAuth2ClientResponse> createOAuth2Client(Config config, Pair<String, String> client) {
|
||||
private Mono<CreateOAuth2ClientResponse> createOAuth2Client(Config config, Tuple2<String, String> client) {
|
||||
CreateOAuth2ClientRequest.CreateOAuth2ClientRequestBuilder builder = CreateOAuth2ClientRequest.builder()
|
||||
.clientId(client.getLeft())
|
||||
.clientSecret(client.getRight())
|
||||
.clientId(client.getT1())
|
||||
.clientSecret(client.getT2())
|
||||
.clientName(config.getClientName())
|
||||
.identityZoneSubdomain(config.getIdentityZoneSubdomain())
|
||||
.identityZoneId(config.getIdentityZoneId());
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.extensions.credentials;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -27,28 +27,40 @@ class SimpleCredentialGeneratorTest {
|
||||
void generateString() {
|
||||
SimpleCredentialGenerator generator = new SimpleCredentialGenerator();
|
||||
|
||||
assertThat(generator.generateString(null, null, 12, true, false, false, false))
|
||||
.matches("^[A-Z]{12}$");
|
||||
assertThat(generator.generateString(null, null, 12, false, true, false, false))
|
||||
.matches("^[a-z]{12}$");
|
||||
assertThat(generator.generateString(null, null, 12, false, false, true, false))
|
||||
.matches("^[0-9]{12}$");
|
||||
assertThat(generator.generateString(null, null, 12, false, false, false, true))
|
||||
.matches("^[\\p{Punct}]{12}$");
|
||||
StepVerifier.create(generator.generateString(null, null, null, 12, true, false, false, false))
|
||||
.assertNext(s -> assertThat(s).matches("^[A-Z]{12}$"))
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(generator.generateString(null, null, 12, true, true, true, true))
|
||||
.matches("^[a-zA-Z0-9\\p{Punct}]{12}$");
|
||||
assertThat(generator.generateString(null, null, 12, false, false, false, false))
|
||||
.matches("^[a-zA-Z0-9\\p{Punct}]{12}$");
|
||||
StepVerifier.create(generator.generateString(null, null, null, 12, false, true, false, false))
|
||||
.assertNext(s -> assertThat(s).matches("^[a-z]{12}$"))
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(generator.generateString(null, null, null, 12, false, false, true, false))
|
||||
.assertNext(s -> assertThat(s).matches("^[0-9]{12}$"))
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(generator.generateString(null, null, null, 12, false, false, false, true))
|
||||
.assertNext(s -> assertThat(s).matches("^[\\p{Punct}]{12}$"))
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(generator.generateString(null, null, null, 12, true, true, true, true))
|
||||
.assertNext(s -> assertThat(s).matches("^[a-zA-Z0-9\\p{Punct}]{12}$"))
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(generator.generateString(null, null, null, 12, false, false, false, false))
|
||||
.assertNext(s -> assertThat(s).matches("^[a-zA-Z0-9\\p{Punct}]{12}$"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUser() {
|
||||
SimpleCredentialGenerator generator = new SimpleCredentialGenerator();
|
||||
|
||||
Pair<String, String> user = generator.generateUser(null, null, 10, true, true, true, true);
|
||||
|
||||
assertThat(user.getLeft().length()).isEqualTo(10);
|
||||
assertThat(user.getRight().length()).isEqualTo(10);
|
||||
StepVerifier.create(generator.generateUser(null, null, null, 10, true, true, true, true))
|
||||
.assertNext(user -> {
|
||||
assertThat(user.getT1().length()).isEqualTo(10);
|
||||
assertThat(user.getT2().length()).isEqualTo(10);
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,21 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.extensions.credentials;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.cloud.appbroker.deployer.BackingApplication;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.cloud.appbroker.deployer.BackingApplication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -63,9 +66,9 @@ class SpringSecurityBasicAuthCredentialProviderFactoryTest {
|
||||
BackingApplication backingApplication = BackingApplication.builder()
|
||||
.build();
|
||||
|
||||
when(credentialGenerator.generateUser(backingApplication.getName(), "service-instance-id",
|
||||
when(credentialGenerator.generateUser(backingApplication.getName(), "service-instance-id", "basic",
|
||||
8, true, false, true, false))
|
||||
.thenReturn(Pair.of("username", "password"));
|
||||
.thenReturn(Mono.just(Tuples.of("username", "password")));
|
||||
|
||||
StepVerifier
|
||||
.create(provider.addCredentials(backingApplication, "service-instance-id"))
|
||||
@@ -83,15 +86,19 @@ class SpringSecurityBasicAuthCredentialProviderFactoryTest {
|
||||
|
||||
@Test
|
||||
void deleteCredentials() {
|
||||
BackingApplication backingApplication = BackingApplication.builder()
|
||||
BackingApplication backingApplication = BackingApplication
|
||||
.builder()
|
||||
.build();
|
||||
|
||||
given(credentialGenerator.deleteUser(backingApplication.getName(), "service-instance-id", "basic"))
|
||||
.willReturn(Mono.empty());
|
||||
|
||||
StepVerifier
|
||||
.create(provider.deleteCredentials(backingApplication, "service-instance-id"))
|
||||
.expectNext(backingApplication)
|
||||
.verifyComplete();
|
||||
|
||||
verify(credentialGenerator).deleteUser(backingApplication.getName(), "service-instance-id");
|
||||
verify(credentialGenerator).deleteUser(backingApplication.getName(), "service-instance-id", "basic");
|
||||
verifyNoMoreInteractions(credentialGenerator);
|
||||
}
|
||||
}
|
||||
@@ -16,23 +16,25 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.extensions.credentials;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
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 java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -69,9 +71,9 @@ class SpringSecurityOAuth2CredentialProviderFactoryTest {
|
||||
BackingApplication backingApplication = BackingApplication.builder()
|
||||
.build();
|
||||
|
||||
when(credentialGenerator.generateString(backingApplication.getName(), "service-instance-id",
|
||||
when(credentialGenerator.generateString(backingApplication.getName(), "service-instance-id", "oauth2",
|
||||
8, true, false, true, false))
|
||||
.thenReturn("test-secret");
|
||||
.thenReturn(Mono.just("test-secret"));
|
||||
|
||||
when(oAuth2Client.createClient(buildCreateOAuth2Request("test-id")))
|
||||
.thenReturn(Mono.just(CreateOAuth2ClientResponse.builder().build()));
|
||||
@@ -93,9 +95,9 @@ class SpringSecurityOAuth2CredentialProviderFactoryTest {
|
||||
.build();
|
||||
String clientId = backingApplication.getName() + "-" + "service-instance-id";
|
||||
|
||||
when(credentialGenerator.generateString(backingApplication.getName(), "service-instance-id",
|
||||
when(credentialGenerator.generateString(backingApplication.getName(), "service-instance-id", "oauth2",
|
||||
8, true, false, true, false))
|
||||
.thenReturn("test-secret");
|
||||
.thenReturn(Mono.just("test-secret"));
|
||||
|
||||
when(oAuth2Client.createClient(buildCreateOAuth2Request(clientId)))
|
||||
.thenReturn(Mono.just(CreateOAuth2ClientResponse.builder().build()));
|
||||
@@ -130,12 +132,15 @@ class SpringSecurityOAuth2CredentialProviderFactoryTest {
|
||||
when(oAuth2Client.deleteClient(buildDeleteOAuth2Request("test-id")))
|
||||
.thenReturn(Mono.just(DeleteOAuth2ClientResponse.builder().build()));
|
||||
|
||||
given(credentialGenerator.deleteString(backingApplication.getName(), "service-instance-id", "oauth2"))
|
||||
.willReturn(Mono.empty());
|
||||
|
||||
StepVerifier
|
||||
.create(provider.deleteCredentials(backingApplication, "service-instance-id"))
|
||||
.expectNext(backingApplication)
|
||||
.verifyComplete();
|
||||
|
||||
verify(credentialGenerator).deleteString(backingApplication.getName(), "service-instance-id");
|
||||
verify(credentialGenerator).deleteString(backingApplication.getName(), "service-instance-id", "oauth2");
|
||||
verifyNoMoreInteractions(credentialGenerator);
|
||||
}
|
||||
|
||||
@@ -151,12 +156,15 @@ class SpringSecurityOAuth2CredentialProviderFactoryTest {
|
||||
when(oAuth2Client.deleteClient(buildDeleteOAuth2Request(clientId)))
|
||||
.thenReturn(Mono.just(DeleteOAuth2ClientResponse.builder().build()));
|
||||
|
||||
given(credentialGenerator.deleteString(backingApplication.getName(), "service-instance-id", "oauth2"))
|
||||
.willReturn(Mono.empty());
|
||||
|
||||
StepVerifier
|
||||
.create(provider.deleteCredentials(backingApplication, "service-instance-id"))
|
||||
.expectNext(backingApplication)
|
||||
.verifyComplete();
|
||||
|
||||
verify(credentialGenerator).deleteString(backingApplication.getName(), "service-instance-id");
|
||||
verify(credentialGenerator).deleteString(backingApplication.getName(), "service-instance-id", "oauth2");
|
||||
verifyNoMoreInteractions(credentialGenerator);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
import org.springframework.credhub.core.ReactiveCredHubOperations;
|
||||
import org.springframework.credhub.support.CredentialDetails;
|
||||
import org.springframework.credhub.support.SimpleCredentialName;
|
||||
import org.springframework.credhub.support.password.PasswordCredential;
|
||||
import org.springframework.credhub.support.password.PasswordParameters;
|
||||
import org.springframework.credhub.support.password.PasswordParametersRequest;
|
||||
import org.springframework.credhub.support.user.UserCredential;
|
||||
import org.springframework.credhub.support.user.UserParametersRequest;
|
||||
|
||||
public class CredHubCredentialsGenerator implements CredentialGenerator {
|
||||
|
||||
private final ReactiveCredHubOperations credHubOperations;
|
||||
|
||||
public CredHubCredentialsGenerator(ReactiveCredHubOperations credHubOperations) {
|
||||
this.credHubOperations = credHubOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Tuple2<String, String>> generateUser(String applicationId, String serviceInstanceId, String descriptor,
|
||||
int length, boolean includeUppercaseAlpha, boolean includeLowercaseAlpha,
|
||||
boolean includeNumeric, boolean includeSpecial) {
|
||||
return this.credHubOperations.credentials().generate(UserParametersRequest
|
||||
.builder()
|
||||
.name(new SimpleCredentialName(applicationId, serviceInstanceId, descriptor))
|
||||
.parameters(passwordParameters(length, includeUppercaseAlpha, includeLowercaseAlpha, includeNumeric, includeSpecial))
|
||||
.build(), UserCredential.class)
|
||||
.map(CredentialDetails::getValue)
|
||||
.map(userCredential -> Tuples.of(userCredential.getUsername(), userCredential.getPassword()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> generateString(String applicationId, String serviceInstanceId, String descriptor, int length,
|
||||
boolean includeUppercaseAlpha, boolean includeLowercaseAlpha, boolean includeNumeric,
|
||||
boolean includeSpecial) {
|
||||
return credHubOperations.credentials().generate(PasswordParametersRequest
|
||||
.builder()
|
||||
.name(new SimpleCredentialName(applicationId, serviceInstanceId, descriptor))
|
||||
.parameters(passwordParameters(length, includeUppercaseAlpha, includeLowercaseAlpha, includeNumeric, includeSpecial))
|
||||
.build(), PasswordCredential.class)
|
||||
.map(CredentialDetails::getValue)
|
||||
.map(PasswordCredential::getPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteUser(String applicationId, String serviceInstanceId, String descriptor) {
|
||||
return credHubOperations.credentials().deleteByName(new SimpleCredentialName(applicationId, serviceInstanceId, descriptor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteString(String applicationId, String serviceInstanceId, String descriptor) {
|
||||
return credHubOperations.credentials().deleteByName(new SimpleCredentialName(applicationId, serviceInstanceId, descriptor));
|
||||
}
|
||||
|
||||
private PasswordParameters passwordParameters(int length, boolean includeUppercaseAlpha, boolean includeLowercaseAlpha,
|
||||
boolean includeNumeric, boolean includeSpecial) {
|
||||
return PasswordParameters
|
||||
.builder()
|
||||
.length(length)
|
||||
.excludeUpper(!includeUppercaseAlpha)
|
||||
.excludeLower(!includeLowercaseAlpha)
|
||||
.excludeNumber(!includeNumeric)
|
||||
.includeSpecial(includeSpecial)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstan
|
||||
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.credhub.core.ReactiveCredHubOperations;
|
||||
import org.springframework.credhub.support.ServiceInstanceCredentialName;
|
||||
import org.springframework.credhub.support.json.JsonCredentialRequest;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -37,7 +38,7 @@ public class CredHubPersistingCreateServiceInstanceAppBindingWorkflow implements
|
||||
|
||||
private static final String CREDENTIALS_KEY = "credhub-ref";
|
||||
|
||||
private static final String CREDENTIALS_VALUE_TEMPLATE = "/c/%s/%s/%s/credentials-json";
|
||||
private static final String CREDENTIALS_NAME = "credentials-json";
|
||||
|
||||
private final String appName;
|
||||
|
||||
@@ -80,14 +81,17 @@ public class CredHubPersistingCreateServiceInstanceAppBindingWorkflow implements
|
||||
.builder()
|
||||
.async(response.isAsync())
|
||||
.bindingExisted(response.isBindingExisted())
|
||||
.credentials(CREDENTIALS_KEY, formatCredentials(request))
|
||||
.credentials(CREDENTIALS_KEY, ServiceInstanceCredentialName
|
||||
.builder()
|
||||
.serviceBrokerName(this.appName)
|
||||
.serviceOfferingName(request.getServiceDefinitionId())
|
||||
.serviceBindingId(request.getBindingId())
|
||||
.credentialName(CREDENTIALS_NAME)
|
||||
.build()
|
||||
.getName())
|
||||
.operation(response.getOperation())
|
||||
.syslogDrainUrl(response.getSyslogDrainUrl())
|
||||
.volumeMounts(response.getVolumeMounts()));
|
||||
}
|
||||
|
||||
private String formatCredentials(CreateServiceInstanceBindingRequest request) {
|
||||
return String.format(CREDENTIALS_VALUE_TEMPLATE, this.appName, request.getServiceDefinitionId(), request.getBindingId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.credhub.core.ReactiveCredHubOperations;
|
||||
import org.springframework.credhub.core.credential.ReactiveCredHubCredentialOperations;
|
||||
import org.springframework.credhub.support.CredentialDetails;
|
||||
import org.springframework.credhub.support.CredentialType;
|
||||
import org.springframework.credhub.support.SimpleCredentialName;
|
||||
import org.springframework.credhub.support.password.PasswordCredential;
|
||||
import org.springframework.credhub.support.password.PasswordParameters;
|
||||
import org.springframework.credhub.support.user.UserCredential;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CredHubCredentialsGeneratorTest {
|
||||
|
||||
@Mock
|
||||
private ReactiveCredHubOperations credHubOperations;
|
||||
|
||||
@Mock
|
||||
private ReactiveCredHubCredentialOperations credHubCredentialOperations;
|
||||
|
||||
private CredHubCredentialsGenerator generator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.generator = new CredHubCredentialsGenerator(credHubOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void passwordParameters() {
|
||||
PasswordParameters params = ReflectionTestUtils.invokeMethod(generator, "passwordParameters", 42, false, false, false, false);
|
||||
assertThat(params.getLength()).isEqualTo(42);
|
||||
assertThat(params.getExcludeUpper()).isTrue();
|
||||
assertThat(params.getExcludeLower()).isTrue();
|
||||
assertThat(params.getExcludeNumber()).isTrue();
|
||||
assertThat(params.getIncludeSpecial()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUser() {
|
||||
CredentialDetails<UserCredential> credentialDetails = new CredentialDetails<UserCredential>("id",
|
||||
new SimpleCredentialName("app-service"), CredentialType.PASSWORD,
|
||||
new UserCredential("username", "password"));
|
||||
|
||||
given(this.credHubOperations.credentials())
|
||||
.willReturn(credHubCredentialOperations);
|
||||
|
||||
given(this.credHubCredentialOperations.generate(any(), eq(UserCredential.class)))
|
||||
.willReturn(Mono.just(credentialDetails));
|
||||
|
||||
StepVerifier.create(generator.generateUser("foo", "bar", "hello", 12, false, false, false, false))
|
||||
.assertNext(tuple2 -> {
|
||||
assertThat(tuple2.getT1()).isEqualTo("username");
|
||||
assertThat(tuple2.getT2()).isEqualTo("password");
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateString() {
|
||||
CredentialDetails<PasswordCredential> credentialDetails = new CredentialDetails<PasswordCredential>("id",
|
||||
new SimpleCredentialName("app-service"), CredentialType.PASSWORD,
|
||||
new PasswordCredential("password"));
|
||||
|
||||
given(this.credHubOperations.credentials())
|
||||
.willReturn(credHubCredentialOperations);
|
||||
|
||||
given(this.credHubCredentialOperations.generate(any(), eq(PasswordCredential.class)))
|
||||
.willReturn(Mono.just(credentialDetails));
|
||||
|
||||
StepVerifier.create(generator.generateString("foo", "bar", "hello", 12, false, false, false, false))
|
||||
.assertNext(password -> assertThat(password).isEqualTo("password"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteUser() {
|
||||
given(this.credHubOperations.credentials())
|
||||
.willReturn(credHubCredentialOperations);
|
||||
|
||||
given(this.credHubCredentialOperations.deleteByName(new SimpleCredentialName("foo", "bar", "hello")))
|
||||
.willReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(generator.deleteUser("foo", "bar", "hello"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(credHubCredentialOperations).deleteByName(new SimpleCredentialName("foo", "bar", "hello"));
|
||||
verifyNoMoreInteractions(credHubOperations);
|
||||
verifyNoMoreInteractions(credHubCredentialOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteString() {
|
||||
given(this.credHubOperations.credentials())
|
||||
.willReturn(credHubCredentialOperations);
|
||||
|
||||
given(this.credHubCredentialOperations.deleteByName(new SimpleCredentialName("foo", "bar", "hello")))
|
||||
.willReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(generator.deleteString("foo", "bar", "hello"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(credHubCredentialOperations).deleteByName(new SimpleCredentialName("foo", "bar", "hello"));
|
||||
verifyNoMoreInteractions(credHubOperations);
|
||||
verifyNoMoreInteractions(credHubCredentialOperations);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user