Skip to content

Commit

Permalink
Restore support for multiple XSUAA Application Plan service bindings (#…
Browse files Browse the repository at this point in the history
…1249)

* Restore support in ServiceBindingEnvironment for multiple configurations with the same service plan.
To not break the existing API:
* Provide ServiceBindingEnvironment#getServiceConfigurationsAsList as an alternative that returns all configurations

---------

Co-authored-by: liga-oz <[email protected]>
  • Loading branch information
finkmanAtSap and liga-oz authored Aug 1, 2023
1 parent f85c599 commit 822e436
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
import com.sap.cloud.security.json.DefaultJsonObject;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
Expand All @@ -33,7 +31,7 @@
public class ServiceBindingEnvironment implements Environment {
private final ServiceBindingAccessor serviceBindingAccessor;
private UnaryOperator<String> environmentVariableReader = System::getenv;
private Map<Service, Map<ServiceConstants.Plan, OAuth2ServiceConfiguration>> serviceConfigurations;
private Map<Service, List<OAuth2ServiceConfiguration>> serviceConfigurations;

/**
* Uses the
Expand Down Expand Up @@ -80,17 +78,20 @@ public ServiceBindingEnvironment withEnvironmentVariableReader(UnaryOperator<Str
@Nullable
@Override
public OAuth2ServiceConfiguration getXsuaaConfiguration() {
return Stream
.of(ServiceConstants.Plan.APPLICATION, ServiceConstants.Plan.BROKER, ServiceConstants.Plan.SPACE,
ServiceConstants.Plan.DEFAULT)
.map(plan -> getServiceConfigurations().get(XSUAA).get(plan))
.filter(Objects::nonNull)
.findFirst().orElse(null);
List<ServiceConstants.Plan> orderedServicePlans = List.of(ServiceConstants.Plan.APPLICATION, ServiceConstants.Plan.BROKER,
ServiceConstants.Plan.SPACE, ServiceConstants.Plan.DEFAULT);
List<OAuth2ServiceConfiguration> xsuaaConfigurations = getServiceConfigurationsAsList().get(XSUAA);

return xsuaaConfigurations.stream()
.filter(config -> getServicePlan(config) != null)
.filter(config -> orderedServicePlans.contains(getServicePlan(config)))
.min(Comparator.comparingInt(config -> orderedServicePlans.indexOf(getServicePlan(config))))
.orElse(null);
}

@Override
public int getNumberOfXsuaaConfigurations() {
return getServiceConfigurations().get(XSUAA).size();
return getServiceConfigurationsAsList().get(XSUAA).size();
}

/**
Expand Down Expand Up @@ -119,16 +120,50 @@ public OAuth2ServiceConfiguration getIasConfiguration() {
* Gives access to all service configurations parsed from the environment. The
* service configurations are parsed on the first access, then cached.
*
* @return the service configurations grouped first by service, then by service
* plan.
* @return the service configurations grouped by service
*/
public Map<Service, List<OAuth2ServiceConfiguration>> getServiceConfigurationsAsList() {
if (serviceConfigurations == null) {
this.readServiceConfigurations();
}

return this.serviceConfigurations;
}

/**
* Gives access to all service configurations parsed from the environment. The
* service configurations are parsed on the first access, then cached.
*
* Note that the result contains only one service configuration per service plan and does not contain configurations
* with a service plan other than those from {@link ServiceConstants}#Plan.
* Use {@link ServiceBindingEnvironment#getServiceConfigurationsAsList()} to get a complete list of configurations.
*
* @return the service configurations grouped first by service, then by service plan.
*/
@Override
public Map<Service, Map<ServiceConstants.Plan, OAuth2ServiceConfiguration>> getServiceConfigurations() {
if (serviceConfigurations == null) {
this.readServiceConfigurations();
}

return serviceConfigurations;
Map<Service, Map<ServiceConstants.Plan, OAuth2ServiceConfiguration>> result = new EnumMap<>(Service.class);

for (Map.Entry<Service, List<OAuth2ServiceConfiguration>> entry : serviceConfigurations.entrySet()) {
Service service = entry.getKey();
List<OAuth2ServiceConfiguration> configurations = entry.getValue();

Map<ServiceConstants.Plan, OAuth2ServiceConfiguration> planConfigurations = configurations.stream()
.filter(config -> getServicePlan(config) != null)
.collect(Collectors.toMap(
config -> ServiceConstants.Plan.from(config.getProperty(SERVICE_PLAN)),
Function.identity(),
(a, b) -> a
));

result.put(service, planConfigurations);
}

return result;
}

/** Parses the service configurations from the environment. */
Expand All @@ -142,9 +177,7 @@ private void readServiceConfigurations() {
.filter(Objects::nonNull)
.map(builder -> builder.runInLegacyMode(runInLegacyMode()))
.map(OAuth2ServiceConfigurationBuilder::build)
.collect(
Collectors.toMap(config -> ServiceConstants.Plan.from(config.getProperty(SERVICE_PLAN)),
Function.identity()))));
.toList()));
}

/**
Expand All @@ -156,6 +189,15 @@ private void clearServiceConfigurations() {
this.serviceConfigurations = null;
}

@Nullable
private ServiceConstants.Plan getServicePlan(OAuth2ServiceConfiguration config) {
try {
return ServiceConstants.Plan.from(config.getProperty(SERVICE_PLAN));
} catch(IllegalArgumentException e) {
return null;
}
}

private boolean runInLegacyMode() {
String vcapApplicationJson = environmentVariableReader.apply(VCAP_APPLICATION);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import static java.nio.charset.StandardCharsets.UTF_8;
Expand All @@ -17,14 +18,17 @@ class ServiceBindingEnvironmentTest {
private static ServiceBindingEnvironment cutIas;
private static ServiceBindingEnvironment cutXsuaa;
private static ServiceBindingEnvironment cutMultipleXsuaa;

private static ServiceBindingEnvironment cutMultipleApplicationPlanXsuaa;
private static ServiceBindingEnvironment cutUnknownServicePlanXsuaa;
private static String vcapXsa;

@BeforeAll
static void setUp() throws IOException {
String singleXsuaaConfiguration = IOUtils.resourceToString("/vcapXsuaaServiceSingleBinding.json", UTF_8);
String multipleXsuaaConfigurations = IOUtils.resourceToString("/vcapXsuaaServiceMultipleBindings.json", UTF_8);
String multipleXsuaaApplicationPlanConfigurations = IOUtils.resourceToString("/vcapXsuaaServiceMultipleApplicationPlanBindings.json", UTF_8);
String singleIasConfiguration = IOUtils.resourceToString("/vcapIasServiceSingleBinding.json", UTF_8);
String unknownXsuaaPlanConfig = IOUtils.resourceToString("/vcapUnknownServicePlan.json", UTF_8);
vcapXsa = IOUtils.resourceToString("/vcapXsuaaXsaSingleBinding.json", UTF_8);

cutIas = new ServiceBindingEnvironment(
Expand All @@ -33,22 +37,33 @@ static void setUp() throws IOException {
new SapVcapServicesServiceBindingAccessor(any -> singleXsuaaConfiguration));
cutMultipleXsuaa = new ServiceBindingEnvironment(
new SapVcapServicesServiceBindingAccessor(any -> multipleXsuaaConfigurations));
cutMultipleApplicationPlanXsuaa = new ServiceBindingEnvironment(
new SapVcapServicesServiceBindingAccessor(any -> multipleXsuaaApplicationPlanConfigurations));
cutUnknownServicePlanXsuaa = new ServiceBindingEnvironment(
new SapVcapServicesServiceBindingAccessor(any -> unknownXsuaaPlanConfig));
}

@Test
void getNumberOfXsuaaConfigurations() {
assertEquals(0, cutIas.getNumberOfXsuaaConfigurations());
assertEquals(1, cutXsuaa.getNumberOfXsuaaConfigurations());
assertEquals(2, cutMultipleXsuaa.getNumberOfXsuaaConfigurations());
assertEquals(3, cutMultipleApplicationPlanXsuaa.getNumberOfXsuaaConfigurations());
assertEquals(1, cutUnknownServicePlanXsuaa.getNumberOfXsuaaConfigurations());
}

@Test
void getXsuaaConfiguration() {
assertNull(cutIas.getXsuaaConfiguration());
assertNotNull(cutXsuaa.getXsuaaConfiguration());
assertNull(cutUnknownServicePlanXsuaa.getXsuaaConfiguration());
assertEquals(Service.XSUAA, cutXsuaa.getXsuaaConfiguration().getService());
assertThat(cutMultipleXsuaa.getXsuaaConfiguration().getProperty(ServiceConstants.SERVICE_PLAN),
equalToIgnoringCase(ServiceConstants.Plan.APPLICATION.toString()));
assertThat(cutMultipleApplicationPlanXsuaa.getXsuaaConfiguration().getProperty(ServiceConstants.SERVICE_PLAN),
equalToIgnoringCase(ServiceConstants.Plan.APPLICATION.toString()));
assertThat(cutMultipleApplicationPlanXsuaa.getXsuaaConfiguration().getProperty(ServiceConstants.XSUAA.APP_ID),
equalTo("na-d6a3278d-5e07-40e9-92ae-546bbfd9cdde!t8066"));
}

@Test
Expand All @@ -58,6 +73,11 @@ void getXsuaaConfigurationForTokenExchange() {
assertNotSame(cutMultipleXsuaa.getXsuaaConfigurationForTokenExchange(),
cutMultipleXsuaa.getXsuaaConfiguration());

assertThat(cutMultipleApplicationPlanXsuaa.getXsuaaConfigurationForTokenExchange().getProperty(ServiceConstants.SERVICE_PLAN),
equalToIgnoringCase(ServiceConstants.Plan.BROKER.toString()));
assertNotSame(cutMultipleApplicationPlanXsuaa.getXsuaaConfigurationForTokenExchange(),
cutMultipleXsuaa.getXsuaaConfiguration());

assertThat(cutXsuaa.getXsuaaConfigurationForTokenExchange().getProperty(ServiceConstants.SERVICE_PLAN),
equalToIgnoringCase(ServiceConstants.Plan.APPLICATION.toString()));
assertSame(cutXsuaa.getXsuaaConfigurationForTokenExchange(), cutXsuaa.getXsuaaConfiguration());
Expand All @@ -76,6 +96,28 @@ void getIasConfiguration() {
assertFalse(cutIas.getIasConfiguration().isLegacyMode());
}

@Test
void getServiceConfigurationsAsList() {
Map<Service, List<OAuth2ServiceConfiguration>> configs = cutIas.getServiceConfigurationsAsList();
assertThat(configs.get(Service.XSUAA), hasSize(0));
assertThat(configs.get(Service.IAS), hasSize(1));

configs = cutXsuaa.getServiceConfigurationsAsList();
assertThat(configs.get(Service.XSUAA), hasSize(1));
assertThat(configs.get(Service.IAS), hasSize(0));

configs = cutMultipleXsuaa.getServiceConfigurationsAsList();
assertThat(configs.get(Service.XSUAA), hasSize(2));
assertThat(configs.get(Service.IAS), hasSize(0));

configs = cutMultipleApplicationPlanXsuaa.getServiceConfigurationsAsList();
assertThat(configs.get(Service.XSUAA), hasSize(3));
assertThat(configs.get(Service.IAS), hasSize(0));

configs = cutUnknownServicePlanXsuaa.getServiceConfigurationsAsList();
assertThat(configs.get(Service.XSUAA), hasSize(1));
}

@Test
void getServiceConfigurations() {
Map<Service, Map<ServiceConstants.Plan, OAuth2ServiceConfiguration>> configs = cutIas
Expand All @@ -90,6 +132,18 @@ void getServiceConfigurations() {
configs = cutMultipleXsuaa.getServiceConfigurations();
assertThat(configs.get(Service.XSUAA).entrySet(), hasSize(2));
assertThat(configs.get(Service.IAS).entrySet(), is(empty()));
assertNotNull(configs.get(Service.XSUAA).get(ServiceConstants.Plan.BROKER));
assertNotNull(configs.get(Service.XSUAA).get(ServiceConstants.Plan.APPLICATION));

configs = cutMultipleApplicationPlanXsuaa.getServiceConfigurations();
assertThat(configs.get(Service.XSUAA).entrySet(), hasSize(2));
assertThat(configs.get(Service.IAS).entrySet(), is(empty()));
assertThat(configs.get(Service.XSUAA).get(ServiceConstants.Plan.APPLICATION).getProperty(ServiceConstants.XSUAA.APP_ID), equalTo("na-d6a3278d-5e07-40e9-92ae-546bbfd9cdde!t8066"));
assertNotNull(configs.get(Service.XSUAA).get(ServiceConstants.Plan.BROKER));
assertNotNull(configs.get(Service.XSUAA).get(ServiceConstants.Plan.APPLICATION));

configs = cutUnknownServicePlanXsuaa.getServiceConfigurations();
assertThat(configs.get(Service.XSUAA).entrySet(), hasSize(0));
}

@Test
Expand Down
34 changes: 34 additions & 0 deletions env/src/test/resources/vcapUnknownServicePlan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"xsuaa": [
{
"binding_name": null,
"credentials": {
"apiurl": "https://api.uaadomain.org",
"certificate": "",
"key": "",
"clientid": "sb-xsuaa-unknown!b8066",
"clientsecret": "sppEXArNzp3tccATO99CZsRKESY=",
"identityzone": "id-test-zone",
"identityzoneid": "a179bf0f-5a57-2276-a3f0-94c6d79bd05b",
"sburl": "https://internal-xsuaa.uaadomain.org",
"tenantid": "a179bf0f-5a57-2276-a3f0-94c6d79bd05b",
"tenantmode": "dedicated",
"trustedclientidsuffix": "|xsuaa-unknown!b8066",
"uaadomain": "uaadomain.org",
"url": "https://id-test-zone.uaadomain.org",
"verificationkey": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAABBBQ8AMIIBCgKCAQEAx/jN5v1mp/TVn9nTQoYVIUfCsUDHa3Upr5tDZC7mzlTrN2PnwruzyS7w1Jd+StqwW4/vn87ua2YlZzU8Ob0jR4lbOPCKaHIi0kyNtJXQvQ7LZPG8epQLbx0IIP/WLVVVtB8bL5OWuHma3pUnibbmATtbOh5LksQ2zLMngEjUF52JQyzTpjoQkahp0BNe/drlAqO253keiY63FL6belKjJGmSqdnotSXxB2ym+HQ0ShaNvTFLEvi2+ObkyjGWgFpQaoCcGq0KX0y0mPzOvdFsNT+rBFdkHiK+Jl638Sbim1z9fItFbH9hiVwY37R9rLtH1YKi3PuATMjf/DJ7mUluDQIDAQAB-----END PUBLIC KEY-----",
"xsappname": "xsuaa-unknown!b8066"
},
"instance_name": "xsuaa-broker",
"label": "xsuaa",
"name": "xsuaa-unknown",
"plan": "unknown",
"provider": null,
"syslog_drain_url": null,
"tags": [
"xsuaa"
],
"volume_mounts": []
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"xsuaa": [
{
"binding_name": null,
"credentials": {
"apiurl": "https://api.uaadomain.org",
"certificate": "",
"key": "",
"clientid": "sb-xsuaa-broker!b8066",
"clientsecret": "sppEXArNzp3tccATO99CZsRKESY=",
"identityzone": "id-test-zone",
"identityzoneid": "a179bf0f-5a57-2276-a3f0-94c6d79bd05b",
"sburl": "https://internal-xsuaa.uaadomain.org",
"tenantid": "a179bf0f-5a57-2276-a3f0-94c6d79bd05b",
"tenantmode": "dedicated",
"trustedclientidsuffix": "|xsuaa-broker!b8066",
"uaadomain": "uaadomain.org",
"url": "https://id-test-zone.uaadomain.org",
"verificationkey": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAABBBQ8AMIIBCgKCAQEAx/jN5v1mp/TVn9nTQoYVIUfCsUDHa3Upr5tDZC7mzlTrN2PnwruzyS7w1Jd+StqwW4/vn87ua2YlZzU8Ob0jR4lbOPCKaHIi0kyNtJXQvQ7LZPG8epQLbx0IIP/WLVVVtB8bL5OWuHma3pUnibbmATtbOh5LksQ2zLMngEjUF52JQyzTpjoQkahp0BNe/drlAqO253keiY63FL6belKjJGmSqdnotSXxB2ym+HQ0ShaNvTFLEvi2+ObkyjGWgFpQaoCcGq0KX0y0mPzOvdFsNT+rBFdkHiK+Jl638Sbim1z9fItFbH9hiVwY37R9rLtH1YKi3PuATMjf/DJ7mUluDQIDAQAB-----END PUBLIC KEY-----",
"xsappname": "xsuaa-broker!b8066"
},
"instance_name": "xsuaa-broker",
"label": "xsuaa",
"name": "xsuaa-broker",
"plan": "broker",
"provider": null,
"syslog_drain_url": null,
"tags": [
"xsuaa"
],
"volume_mounts": []
},
{
"binding_name": null,
"credentials": {
"apiurl": "https://api.uaadomain.org",
"clientid": "sb-na-d6a3278d-5e07-40e9-92ae-546bbfd9cdde!t8066",
"clientsecret": "1idLRlbbb9oextvpmS6bfpQXLk=",
"identityzone": "id-test-zone",
"identityzoneid": "a179bf0f-5a57-2276-a3f0-94c6d79bd05b",
"sburl": "https://internal-xsuaa.uaadomain.org",
"tenantid": "a179bf0f-5a57-2276-a3f0-94c6d79bd05b",
"tenantmode": "shared",
"uaadomain": "uaadomain.org",
"url": "https://id-test-zone.uaadomain.org",
"verificationkey": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAABBBQ8AMIIBCgKCAQEAx/jN5v1mp/TVn9nTQoYVIUfCsUDHa3Upr5tDZC7mzlTrN2PnwruzyS7w1Jd+StqwW4/vn87ua2YlZzU8Ob0jR4lbOPCKaHIi0kyNtJXQvQ7LZPG8epQLbx0IIP/WLVVVtB8bL5OWuHma3pUnibbmATtbOh5LksQ2zLMngEjUF52JQyzTpjoQkahp0BNe/drlAqO253keiY63FL6belKjJGmSqdnotSXxB2ym+HQ0ShaNvTFLEvi2+ObkyjGWgFpQaoCcGq0KX0y0mPzOvdFsNT+rBFdkHiK+Jl638Sbim1z9fItFbH9hiVwY37R9rLtH1YKi3PuATMjf/DJ7mUluDQIDAQAB-----END PUBLIC KEY-----",
"xsappname": "na-d6a3278d-5e07-40e9-92ae-546bbfd9cdde!t8066"
},
"instance_name": "xsuaa-app",
"label": "xsuaa",
"name": "xsuaa-app",
"plan": "application",
"provider": null,
"syslog_drain_url": null,
"tags": [
"xsuaa"
],
"volume_mounts": []
},
{
"binding_name": null,
"credentials": {
"apiurl": "https://api2.uaadomain.org",
"clientid": "sb-na-d6a3278d-5e07-40e9-92ae-546bbfd9cdde!t8067",
"clientsecret": "1idLRlbbb9oextvpmS6bfpQXLk=",
"identityzone": "id-test-zone",
"identityzoneid": "a179bf0f-5a57-2276-a3f0-94c6d79bd05b",
"sburl": "https://internal-xsuaa.uaadomain.org",
"tenantid": "a179bf0f-5a57-2276-a3f0-94c6d79bd05b",
"tenantmode": "shared",
"uaadomain": "uaadomain.org",
"url": "https://id-test-zone.uaadomain.org",
"verificationkey": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAABBBQ8AMIIBCgKCAQEAx/jN5v1mp/TVn9nTQoYVIUfCsUDHa3Upr5tDZC7mzlTrN2PnwruzyS7w1Jd+StqwW4/vn87ua2YlZzU8Ob0jR4lbOPCKaHIi0kyNtJXQvQ7LZPG8epQLbx0IIP/WLVVVtB8bL5OWuHma3pUnibbmATtbOh5LksQ2zLMngEjUF52JQyzTpjoQkahp0BNe/drlAqO253keiY63FL6belKjJGmSqdnotSXxB2ym+HQ0ShaNvTFLEvi2+ObkyjGWgFpQaoCcGq0KX0y0mPzOvdFsNT+rBFdkHiK+Jl638Sbim1z9fItFbH9hiVwY37R9rLtH1YKi3PuATMjf/DJ7mUluDQIDAQAB-----END PUBLIC KEY-----",
"xsappname": "na-d6a3278d-5e07-40e9-92ae-546bbfd9cdde!t8067"
},
"instance_name": "xsuaa-app2",
"label": "xsuaa",
"name": "xsuaa-app2",
"plan": "application",
"provider": null,
"syslog_drain_url": null,
"tags": [
"xsuaa"
],
"volume_mounts": []
}
]
}

0 comments on commit 822e436

Please sign in to comment.