Skip to content

Commit b033974

Browse files
committed
Got rid of custom token fetch; implemented check for SAP-provisioned Solace Message Broker.
1 parent d07c5c6 commit b033974

File tree

11 files changed

+210
-35
lines changed

11 files changed

+210
-35
lines changed

cds-feature-advanced-event-mesh/src/main/java/com/sap/cds/feature/messaging/aem/client/AemManagementClient.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313

1414
import com.fasterxml.jackson.databind.JsonNode;
1515
import com.fasterxml.jackson.databind.node.ObjectNode;
16+
import com.sap.cds.feature.messaging.aem.client.binding.AemAuthorizationServiceView;
17+
import com.sap.cds.feature.messaging.aem.client.binding.AemEndpointView;
1618
import com.sap.cds.integration.cloudsdk.rest.client.JsonRestClient;
1719
import com.sap.cds.integration.cloudsdk.rest.client.JsonRestClientResponseException;
20+
import com.sap.cds.services.ServiceException;
1821
import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
1922
import com.sap.cloud.sdk.cloudplatform.connectivity.ServiceBindingDestinationOptions;
2023

@@ -33,17 +36,23 @@ public class AemManagementClient extends JsonRestClient {
3336
public static final String ATTR_QUEUE_NAME = "queueName";
3437
public static final String ATTR_SUBSCRIPTION_TOPIC = "subscriptionTopic";
3538

36-
private final ServiceBinding binding;
39+
private final AemAuthorizationServiceView authorizationServiceView;
40+
private final AemEndpointView endpointView;
3741
private final String vpn;
3842
private final String owner;
3943

4044
public AemManagementClient(ServiceBinding binding) {
4145
super(ServiceBindingDestinationOptions.forService(binding).build());
42-
this.binding = binding;
46+
this.authorizationServiceView = new AemAuthorizationServiceView(binding);
47+
this.endpointView = new AemEndpointView(binding);
4348
this.vpn = getVpn();
4449
this.owner = getOwner();
4550
}
4651

52+
public String getEndpoint() {
53+
return this.endpointView.getUri().orElseThrow(() -> new ServiceException("Management endpoint not available in binding"));
54+
}
55+
4756
public void removeQueue(String queue) throws IOException {
4857
logger.debug("Removing queue {}", queue);
4958

@@ -139,7 +148,7 @@ public boolean isTopicSubscribed(JsonNode jsonNode, String queueName, String top
139148
}
140149

141150
private String getVpn() {
142-
return (String) this.binding.getCredentials().get("vpn");
151+
return this.endpointView.getVpn().get();
143152
}
144153

145154
private String getOwner() {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.sap.cds.feature.messaging.aem.client;
2+
3+
import java.io.IOException;
4+
import java.net.URI;
5+
import java.net.URISyntaxException;
6+
import java.util.Map;
7+
8+
import com.sap.cds.integration.cloudsdk.rest.client.JsonRestClient;
9+
import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
10+
import com.sap.cloud.sdk.cloudplatform.connectivity.ServiceBindingDestinationOptions;
11+
12+
public class AemValidationClient extends JsonRestClient {
13+
14+
public AemValidationClient(ServiceBinding binding) {
15+
super(ServiceBindingDestinationOptions.forService(binding).build());
16+
}
17+
18+
public void validate(String managementUri) throws IOException, URISyntaxException {
19+
URI uri = new URI(managementUri);
20+
String payload = this.mapper.writeValueAsString(Map.of("hostName", uri.getHost()));
21+
22+
// The response is not used, only the status code is relevant. If there is a status code not equal to 200,
23+
// an exception is thrown which means that the validation failed.
24+
postRequest("", payload, Map.of());
25+
}
26+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.sap.cds.feature.messaging.aem.client.binding;
2+
3+
record AemClientIdentity(String clientId, String clientSecret)
4+
implements com.sap.cloud.security.config.ClientIdentity {
5+
6+
@Override
7+
public String getId() {
8+
return this.clientId;
9+
}
10+
11+
@Override
12+
public String getSecret() {
13+
return this.clientSecret;
14+
}
15+
}
Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import com.sap.cloud.sdk.cloudplatform.connectivity.OAuth2ServiceBindingDestinationLoader;
1515
import com.sap.cloud.sdk.cloudplatform.connectivity.ServiceBindingDestinationOptions;
1616

17-
public class AemOauth2PropertySupplier implements OAuth2PropertySupplier {
17+
public class AemManagementOauth2PropertySupplier implements OAuth2PropertySupplier {
1818

1919
private static boolean initialized = false;
2020

@@ -27,12 +27,12 @@ public static synchronized void initialize() {
2727
OAuth2ServiceBindingDestinationLoader.registerPropertySupplier(
2828
options -> ServiceBindingUtils.matches(options.getServiceBinding(),
2929
AemMessagingServiceConfiguration.BINDING_AEM_LABEL),
30-
AemOauth2PropertySupplier::new);
30+
AemManagementOauth2PropertySupplier::new);
3131
initialized = true;
3232
}
3333
}
3434

35-
public AemOauth2PropertySupplier(@Nonnull ServiceBindingDestinationOptions options) {
35+
public AemManagementOauth2PropertySupplier(@Nonnull ServiceBindingDestinationOptions options) {
3636
this.binding = options.getServiceBinding();
3737
this.authorizationServiceView = new AemAuthorizationServiceView(binding);
3838
this.endpointView = new AemEndpointView(binding);
@@ -69,7 +69,7 @@ public URI getTokenUri() {
6969
@Nonnull
7070
@Override
7171
public com.sap.cloud.security.config.ClientIdentity getClientIdentity() {
72-
return new ClientIdentity(this.authorizationServiceView.getClientId().get(),
72+
return new AemClientIdentity(this.authorizationServiceView.getClientId().get(),
7373
this.authorizationServiceView.getClientSecret().get());
7474
}
7575

@@ -85,17 +85,4 @@ private boolean areOAuth2ParametersPresent(ServiceBinding binding) {
8585
&& this.authorizationServiceView.getClientSecret().isPresent();
8686
}
8787

88-
private record ClientIdentity(String clientId, String clientSecret)
89-
implements com.sap.cloud.security.config.ClientIdentity {
90-
91-
@Override
92-
public String getId() {
93-
return this.clientId;
94-
}
95-
96-
@Override
97-
public String getSecret() {
98-
return this.clientSecret;
99-
}
100-
}
10188
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package com.sap.cds.feature.messaging.aem.client.binding;
2+
3+
import java.net.URI;
4+
import java.net.URISyntaxException;
5+
import java.util.Map;
6+
import java.util.Optional;
7+
8+
import com.sap.cds.feature.messaging.aem.service.AemMessagingServiceConfiguration;
9+
import com.sap.cds.services.ServiceException;
10+
import com.sap.cds.services.utils.environment.ServiceBindingUtils;
11+
import com.sap.cloud.sdk.cloudplatform.connectivity.OAuth2PropertySupplier;
12+
import com.sap.cloud.sdk.cloudplatform.connectivity.OAuth2ServiceBindingDestinationLoader;
13+
import com.sap.cloud.sdk.cloudplatform.connectivity.ServiceBindingDestinationOptions;
14+
import com.sap.cloud.security.config.ClientIdentity;
15+
16+
public class AemValidationOAuth2PropertySupplier implements OAuth2PropertySupplier {
17+
18+
private static boolean initialized = false;
19+
20+
private final CredentialsView credentialsView;
21+
22+
public static synchronized void initialize() {
23+
if (!initialized) {
24+
OAuth2ServiceBindingDestinationLoader.registerPropertySupplier(
25+
options -> ServiceBindingUtils.matches(options.getServiceBinding(),
26+
AemMessagingServiceConfiguration.BINDING_AEM_VALIDATION_LABEL),
27+
AemValidationOAuth2PropertySupplier::new);
28+
initialized = true;
29+
}
30+
}
31+
32+
protected AemValidationOAuth2PropertySupplier(ServiceBindingDestinationOptions options) {
33+
this.credentialsView = new CredentialsView(options.getServiceBinding().getCredentials());
34+
}
35+
36+
@Override
37+
public boolean isOAuth2Binding() {
38+
return this.credentialsView.getServiceUri().isPresent()
39+
&& this.credentialsView.getTokenUri().isPresent()
40+
&& this.credentialsView.getClientId().isPresent()
41+
&& this.credentialsView.getClientSecret().isPresent();
42+
}
43+
44+
@Override
45+
public URI getServiceUri() {
46+
try {
47+
return new URI(this.credentialsView.getServiceUri().get());
48+
} catch (URISyntaxException e) {
49+
throw new ServiceException("Invalid AEM Validation Service URI.", e);
50+
}
51+
}
52+
53+
@Override
54+
public URI getTokenUri() {
55+
String uri = this.credentialsView.getTokenUri().get();
56+
57+
try {
58+
return new URI(uri);
59+
} catch (URISyntaxException e) {
60+
throw new ServiceException("Invalid AEM Validation Service token endpoint URI.", e);
61+
}
62+
}
63+
64+
@Override
65+
public ClientIdentity getClientIdentity() {
66+
return new AemClientIdentity(this.credentialsView.getClientId().get(), this.credentialsView.getClientSecret().get());
67+
}
68+
69+
private record CredentialsView(Map<String, Object> credentials) {
70+
71+
public Optional<String> getServiceUri() {
72+
return getHandshake().map(handshake -> (String) handshake.get("uri"));
73+
}
74+
75+
public Optional<String> getTokenUri() {
76+
return getOAuth2().map(oa2 -> (String) oa2.get("tokenendpoint"));
77+
}
78+
79+
public Optional<String> getClientId() {
80+
return getOAuth2().map(oa2 -> (String) oa2.get("clientid"));
81+
}
82+
83+
public Optional<String> getClientSecret() {
84+
return getOAuth2().map(oa2 -> (String) oa2.get("clientsecret"));
85+
}
86+
87+
@SuppressWarnings("unchecked")
88+
private Optional<Map<String, Object>> getHandshake() {
89+
return Optional.ofNullable((Map<String, Object>) credentials.get("handshake"));
90+
}
91+
92+
@SuppressWarnings("unchecked")
93+
private Optional<Map<String, Object>> getOAuth2() {
94+
return getHandshake().map(handshake -> (Map<String, Object>) handshake.get("oa2"));
95+
}
96+
97+
}
98+
}

cds-feature-advanced-event-mesh/src/main/java/com/sap/cds/feature/messaging/aem/jms/AemMessagingConnectionProvider.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
import org.apache.qpid.jms.JmsConnectionFactory;
1010
import org.slf4j.Logger;
1111
import org.slf4j.LoggerFactory;
12+
1213
import com.sap.cds.feature.messaging.aem.client.binding.AemEndpointView;
13-
import com.sap.cds.feature.messaging.aem.client.binding.AemOauth2PropertySupplier;
14+
import com.sap.cds.feature.messaging.aem.client.binding.AemManagementOauth2PropertySupplier;
1415
import com.sap.cds.services.ServiceException;
1516
import com.sap.cds.services.messaging.jms.BrokerConnection;
1617
import com.sap.cds.services.messaging.jms.BrokerConnectionProvider;
@@ -39,7 +40,7 @@ public AemMessagingConnectionProvider(ServiceBinding binding) {
3940
super(binding.getName().get());
4041
this.binding = binding;
4142
this.endpointView = new AemEndpointView(binding);
42-
OAuth2PropertySupplier supplier = new AemOauth2PropertySupplier(ServiceBindingDestinationOptions.forService(binding).build());
43+
OAuth2PropertySupplier supplier = new AemManagementOauth2PropertySupplier(ServiceBindingDestinationOptions.forService(binding).build());
4344
this.destination = OAuth2DestinationBuilder
4445
.forTargetUrl(this.endpointView.getAmqpUri().get())
4546
.withTokenEndpoint(supplier.getTokenUri().toString())

cds-feature-advanced-event-mesh/src/main/java/com/sap/cds/feature/messaging/aem/service/AemMessagingService.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import static com.sap.cds.feature.messaging.aem.client.AemManagementClient.ATTR_DEAD_MSG_QUEUE;
44

55
import java.io.IOException;
6+
import java.net.URISyntaxException;
67
import java.util.Collections;
78
import java.util.Map;
9+
import java.util.Optional;
810
import java.util.function.Consumer;
911

1012
import org.apache.qpid.jms.message.JmsBytesMessage;
@@ -15,7 +17,9 @@
1517
import org.slf4j.LoggerFactory;
1618

1719
import com.sap.cds.feature.messaging.aem.client.AemManagementClient;
20+
import com.sap.cds.feature.messaging.aem.client.AemValidationClient;
1821
import com.sap.cds.feature.messaging.aem.jms.AemMessagingConnectionProvider;
22+
import com.sap.cds.services.ServiceException;
1923
import com.sap.cds.services.environment.CdsProperties.Messaging.MessagingServiceConfig;
2024
import com.sap.cds.services.messaging.TopicMessageEventContext;
2125
import com.sap.cds.services.messaging.jms.BrokerConnection;
@@ -32,14 +36,20 @@ public class AemMessagingService extends AbstractMessagingService {
3236

3337
private final AemMessagingConnectionProvider connectionProvider;
3438
private final AemManagementClient managementClient;
39+
private final Optional<ServiceBinding> validationBinding;
3540

3641
private volatile BrokerConnection connection;
42+
private volatile Boolean aemBrokerValidated = false;
43+
44+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
45+
protected AemMessagingService(ServiceBinding binding, Optional<ServiceBinding> validationBinding,
46+
MessagingServiceConfig serviceConfig, AemMessagingConnectionProvider connectionProvider, CdsRuntime runtime) {
3747

38-
protected AemMessagingService(ServiceBinding binding, MessagingServiceConfig serviceConfig, AemMessagingConnectionProvider connectionProvider, CdsRuntime runtime) {
3948
super(serviceConfig, runtime);
4049

4150
this.connectionProvider = connectionProvider;
4251
this.managementClient = new AemManagementClient(binding);
52+
this.validationBinding = validationBinding;
4353
}
4454

4555
@Override
@@ -98,6 +108,7 @@ protected void registerQueueListener(String queue, MessagingBrokerQueueListener
98108

99109
@Override
100110
protected void emitTopicMessage(String topic, TopicMessageEventContext messageEventContext) {
111+
this.validate(this.managementClient.getEndpoint());
101112
this.connection.emitTopicMessage("topic://" + topic, messageEventContext);
102113
}
103114

@@ -116,4 +127,23 @@ private String getMessageTopic(Message message) {
116127
}
117128
return null;
118129
}
130+
131+
private void validate(String endpoint) {
132+
synchronized (this.aemBrokerValidated) {
133+
if (!this.aemBrokerValidated) {
134+
ServiceBinding binding = this.validationBinding.orElseThrow(() -> new ServiceException("No binding for AEM Validation Service found."));
135+
AemValidationClient validationClient = new AemValidationClient(binding);
136+
137+
try {
138+
validationClient.validate(endpoint);
139+
synchronized (this.aemBrokerValidated) {
140+
this.aemBrokerValidated = true;
141+
}
142+
} catch (IOException | URISyntaxException e) {
143+
throw new ServiceException("Failed to validate the AEM endpoint.", e);
144+
}
145+
}
146+
}
147+
}
148+
119149
}

0 commit comments

Comments
 (0)