Skip to content

Commit

Permalink
Merge pull request #249 from nsadaphal/PI-21895
Browse files Browse the repository at this point in the history
PI-21895 SDK OAuth2.0 implementations for Event APIs
  • Loading branch information
rajbhupendra588 authored Feb 9, 2021
2 parents 5bf60e1 + 514252a commit 13389ac
Show file tree
Hide file tree
Showing 20 changed files with 524 additions and 132 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent>

<dependencies>
<!-- Spring -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.appdirect.sdk.appmarket;

import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;

/**
* Implementations of this interface provide a way for the service-integration-sdk
* to retrieve the separate credentials ie OAuth2. Each SDK client application must contain a bean
* of this type in its application context in order for the communication with AppMarket to work.
*/
@FunctionalInterface
public interface OAuth2CredentialsSupplier {

/**
* Returns the OAuth2ProtectedResourceDetails that applies to the given applicationUuid key
*
* @param applicationUuid the key used to make a request
* @return the full OAuth2ProtectedResourceDetails object associated with this key
*/
OAuth2ProtectedResourceDetails getOAuth2ResourceDetails(String applicationUuid);

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.appdirect.sdk.meteredusage.service.MeteredUsageApiClientServiceImpl;
import com.appdirect.sdk.web.exception.AppmarketEventClientExceptionHandler;
import com.appdirect.sdk.web.oauth.DefaultRestTemplateFactoryImpl;
import com.appdirect.sdk.web.oauth.OAuth2ClientDetailsService;
import com.appdirect.sdk.web.oauth.OAuthKeyExtractor;
import com.appdirect.sdk.web.oauth.ReportUsageRestTemplateFactoryImpl;
import com.appdirect.sdk.web.oauth.RestTemplateFactory;
Expand Down Expand Up @@ -75,9 +76,10 @@ public AppmarketEventClient appmarketEventFetcher(DeveloperSpecificAppmarketCred

@Bean
public AppmarketEventService appmarketEventService(DeveloperSpecificAppmarketCredentialsSupplier credentialsSupplier,
OAuth2ClientDetailsService oAuth2ClientDetailsService,
AppmarketEventDispatcher eventDispatcher,
AppmarketEventClient appmarketEventClient) {
return new AppmarketEventService(appmarketEventClient, credentialsSupplier, eventDispatcher);
return new AppmarketEventService(appmarketEventClient, credentialsSupplier, oAuth2ClientDetailsService, eventDispatcher);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.web.client.RestTemplate;

import com.appdirect.sdk.appmarket.Credentials;
Expand All @@ -38,74 +39,128 @@
*/
@Slf4j
public class AppmarketEventClient {
private final RestTemplateFactory restTemplateFactory;
private final DeveloperSpecificAppmarketCredentialsSupplier credentialsSupplier;
private final ObjectMapper jsonMapper;

AppmarketEventClient(RestTemplateFactory restTemplateFactory, DeveloperSpecificAppmarketCredentialsSupplier credentialsSupplier, ObjectMapper jsonMapper) {
this.restTemplateFactory = restTemplateFactory;
this.credentialsSupplier = credentialsSupplier;
this.jsonMapper = jsonMapper;
}

/**
* Perform "signed fetch" in order to retrieve the payload of an event sent to the connector from the AppMarket
*
* @param url from which we can fetch the event payload
* @param credentials the credentials used to sign the request
* @return an {@link EventInfo} instance representing the retrieved payload
*/
EventInfo fetchEvent(String url, Credentials credentials) {
log.debug("Consuming event from url={}", url);
final RestTemplate restTemplate = restTemplateFactory
.getOAuthRestTemplate(credentials.developerKey, credentials.developerSecret);

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(singletonList(APPLICATION_JSON));
final HttpEntity<String> requestEntity = new HttpEntity<>("", requestHeaders);

EventInfo fetchedEvent = restTemplate
.exchange(url, GET, requestEntity, EventInfo.class)
.getBody();

fetchedEvent.setId(extractId(url));
return fetchedEvent;
}

/**
* Send an "event resolved" notification for an asynchronous event. It serves to notify the
* AppMarket that the processing of a given event by the connector has been completed
*
* @param baseAppmarketUrl host on which the marketplace is running
* @param eventToken the id of the event we would like to resolve
* @param result represents the event processing result sent to the AppMarket. It would indicate if the event
* processing has been successful or not.
* @param key the client key used to sign the resolve request
*/
@SneakyThrows
public void resolve(String baseAppmarketUrl, String eventToken, APIResult result, String key) {
String url = eventResolutionEndpoint(baseAppmarketUrl, eventToken);
String secret = credentialsSupplier.getConsumerCredentials(key).developerSecret;

final RestTemplate restTemplate = restTemplateFactory.getOAuthRestTemplate(key, secret);

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(APPLICATION_JSON);

final HttpEntity<String> requestEntity = new HttpEntity<>(jsonMapper.writeValueAsString(result), requestHeaders);

restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

log.info("Resolved event with eventToken={} with apiResult={}", eventToken, result);
}

public ServiceProviderInformation resolveSamlIdp(String url, String key) {
String secret = credentialsSupplier.getConsumerCredentials(key).developerSecret;

return restTemplateFactory.getOAuthRestTemplate(key, secret).getForObject(url, ServiceProviderInformation.class);
}

private String eventResolutionEndpoint(String baseAppmarketUrl, String eventToken) {
return format("%s/api/integration/v1/events/%s/result", baseAppmarketUrl, eventToken);
}
private final RestTemplateFactory restTemplateFactory;
private final DeveloperSpecificAppmarketCredentialsSupplier credentialsSupplier;
private final ObjectMapper jsonMapper;

AppmarketEventClient(RestTemplateFactory restTemplateFactory, DeveloperSpecificAppmarketCredentialsSupplier credentialsSupplier, ObjectMapper jsonMapper) {
this.restTemplateFactory = restTemplateFactory;
this.credentialsSupplier = credentialsSupplier;
this.jsonMapper = jsonMapper;
}

/**
* Perform "signed fetch" in order to retrieve the payload of an event sent to the connector from the AppMarket
*
* @param url from which we can fetch the event payload
* @param credentials the credentials used to sign the request
* @return an {@link EventInfo} instance representing the retrieved payload
*/
EventInfo fetchEvent(String url, Credentials credentials) {
log.debug("Consuming event from url={}", url);
final RestTemplate restTemplate = restTemplateFactory
.getOAuthRestTemplate(credentials.developerKey, credentials.developerSecret);
return execute(url, restTemplate);
}

/**
* Perform "signed fetch" in order to retrieve the payload of an event sent to the connector from the AppMarket
* using OAuth2 authentication
*
* @param url from which we can fetch the event payload
* @param oAuth2ProtectedResourceDetails the oAuth2ProtectedResourceDetails used to sign the request
* @return an {@link EventInfo} instance representing the retrieved payload
*/
EventInfo fetchEvent(String url, OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails) {
log.debug("Consuming event from url={}", url);
final RestTemplate restTemplate = restTemplateFactory.getOAuth2RestTemplate(oAuth2ProtectedResourceDetails);
return execute(url, restTemplate);
}

private EventInfo execute(String url, RestTemplate restTemplate) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(singletonList(APPLICATION_JSON));
final HttpEntity<String> requestEntity = new HttpEntity<>("", requestHeaders);
EventInfo fetchedEvent = restTemplate
.exchange(url, GET, requestEntity, EventInfo.class)
.getBody();

fetchedEvent.setId(extractId(url));
return fetchedEvent;
}

/**
* Send an "event resolved" notification for an asynchronous event. It serves to notify the
* AppMarket that the processing of a given event by the connector has been completed
*
* @param baseAppmarketUrl host on which the marketplace is running
* @param eventToken the id of the event we would like to resolve
* @param result represents the event processing result sent to the AppMarket. It would indicate if the event
* processing has been successful or not.
* @param key the client key used to sign the resolve request
* @Deprecated Use resolve with the type OAuth2ProtectedResourceDetails instead.
*/

@Deprecated
@SneakyThrows
public void resolve(String baseAppmarketUrl, String eventToken, APIResult result, String key) {
String url = eventResolutionEndpoint(baseAppmarketUrl, eventToken);
String secret = credentialsSupplier.getConsumerCredentials(key).developerSecret;

final RestTemplate restTemplate = restTemplateFactory.getOAuthRestTemplate(key, secret);

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(APPLICATION_JSON);

final HttpEntity<String> requestEntity = new HttpEntity<>(jsonMapper.writeValueAsString(result), requestHeaders);

restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

log.info("Resolved event with eventToken={} with apiResult={}", eventToken, result);
}


/**
* Send an "event resolved" notification for an asynchronous event. It serves to notify the
* AppMarket that the processing of a given event by the connector has been completed
* This is done using OAuth2 authentication
*
* @param baseAppmarketUrl host on which the marketplace is running
* @param eventToken the id of the event we would like to resolve
* @param result represents the event processing result sent to the AppMarket. It would indicate if the event
* processing has been successful or not.
* @param oAuth2ResourceDetails the oAuth2ProtectedResourceDetails used to sign the request
*/
@SneakyThrows
public void resolve(String baseAppmarketUrl, String eventToken, APIResult result, OAuth2ProtectedResourceDetails oAuth2ResourceDetails) {
String url = eventResolutionEndpoint(baseAppmarketUrl, eventToken);

final RestTemplate restTemplate = restTemplateFactory.getOAuth2RestTemplate(oAuth2ResourceDetails);

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(APPLICATION_JSON);

final HttpEntity<String> requestEntity = new HttpEntity<>(jsonMapper.writeValueAsString(result), requestHeaders);

restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

log.info("Resolved event with eventToken={} with apiResult={}", eventToken, result);
}

/**
* @Deprecated Use resolveSamlIdp with the type OAuth2ProtectedResourceDetails instead.
*/
@Deprecated
public ServiceProviderInformation resolveSamlIdp(String url, String key) {
String secret = credentialsSupplier.getConsumerCredentials(key).developerSecret;
return restTemplateFactory.getOAuthRestTemplate(key, secret).getForObject(url, ServiceProviderInformation.class);
}

public ServiceProviderInformation resolveSamlIdp(String url, OAuth2ProtectedResourceDetails oAuth2ResourceDetails) {
return restTemplateFactory.getOAuth2RestTemplate(oAuth2ResourceDetails).getForObject(url, ServiceProviderInformation.class);
}

private String eventResolutionEndpoint(String baseAppmarketUrl, String eventToken) {
return format("%s/api/integration/v1/events/%s/result", baseAppmarketUrl, eventToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class AppmarketEventController {
* @param eventUrl the url from which the payload of the incoming event can be retrieved.
* @return the HTTP response to return to the AppMarket.
*/
@RequestMapping(method = GET, value = {"/api/v1/integration/processEvent", "/api/v2/integration/processEvent"}, produces = APPLICATION_JSON_VALUE)
@RequestMapping(method = GET, value = "/api/v1/integration/processEvent", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<APIResult> processEvent(HttpServletRequest request, @RequestParam("eventUrl") String eventUrl) {

String keyUsedToSignRequest = keyExtractor.extractFrom(request);
Expand All @@ -60,6 +60,17 @@ public ResponseEntity<APIResult> processEvent(HttpServletRequest request, @Reque
return new ResponseEntity<>(result, httpStatusOf(result));
}


@RequestMapping(method = GET, value = "/api/v2/integration/processEvent", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<APIResult> processEvent(HttpServletRequest request, @RequestParam("eventUrl") String eventUrl, @RequestParam("applicationUuid") String applicationUuid) {
log.info("Received Event with applicationUuid ={} and eventUrl={}", applicationUuid, eventUrl);

APIResult result = appmarketEventService.processEvent(eventUrl, eventExecutionContext(request, applicationUuid),applicationUuid);

log.info("apiResult={} for applicationUuid={}", result, applicationUuid);
return new ResponseEntity<>(result, httpStatusOf(result));
}

private EventHandlingContext eventExecutionContext(HttpServletRequest request, String keyUsedToSignRequest) {
HashMap<String, String[]> queryParamsNotTiedToRequestReference = new HashMap<>(request.getParameterMap());
return new EventHandlingContext(keyUsedToSignRequest, queryParamsNotTiedToRequestReference);
Expand Down
Loading

0 comments on commit 13389ac

Please sign in to comment.