|
1 | | -import groovy.json.JsonOutput |
| 1 | +import static org.forgerock.http.protocol.Response.newResponsePromise |
| 2 | +import static org.forgerock.http.protocol.Status.OK |
| 3 | +import static org.forgerock.json.JsonValue.field |
| 4 | +import static org.forgerock.json.JsonValue.json |
| 5 | +import static org.forgerock.json.JsonValue.object |
| 6 | +import static org.forgerock.util.Closeables.closeSilently |
2 | 7 |
|
| 8 | +import org.forgerock.util.promise.NeverThrowsException |
| 9 | +import org.forgerock.json.JsonValue |
| 10 | + |
| 11 | +/** |
| 12 | + * Scripted Handler implementation to fetch an API client from the repo. |
| 13 | + */ |
| 14 | + |
| 15 | +// FAPI logging |
3 | 16 | def fapiInteractionId = request.getHeaders().getFirst("x-fapi-interaction-id"); |
4 | 17 | if(fapiInteractionId == null) fapiInteractionId = "No x-fapi-interaction-id"; |
5 | 18 | SCRIPT_NAME = "[RepoApiClient] (" + fapiInteractionId + ") - "; |
6 | 19 | logger.debug(SCRIPT_NAME + "Running...") |
7 | 20 |
|
8 | | -// Fetch the API Client from IDM |
9 | | -Request apiClientRequest = new Request(); |
10 | | -apiClientRequest.setMethod('GET'); |
11 | | - |
12 | | -// response object |
13 | | -response = new Response(Status.OK) |
14 | | -response.headers['Content-Type'] = "application/json" |
| 21 | +return handle(context, request) |
| 22 | + |
| 23 | +/** |
| 24 | + * Fetch the API client from the repo. |
| 25 | + * @param unusedContext Context is unused |
| 26 | + * @param request Request to obtain API client |
| 27 | + * @return Promise of a Response containing the API client |
| 28 | + */ |
| 29 | +Promise<Response, NeverThrowsException> handle(final Context unusedContext, final Request request) { |
| 30 | + def apiClientId = extractApiClientId(request) |
| 31 | + if (apiClientId == null) { |
| 32 | + message = "Can't parse api client ID from inbound request" |
| 33 | + logger.error(SCRIPT_NAME + message) |
| 34 | + return newResponsePromise(fail(BAD_REQUEST, message)) |
| 35 | + } |
15 | 36 |
|
16 | | -def splitUri = request.uri.path.split("/") |
| 37 | + // Fetch the API Client from the repo (IDM) |
| 38 | + Request apiClientRequest = new Request(); |
| 39 | + def apiClientUri = routeArgIdmBaseUri + "/openidm/managed/" + routeArgObjApiClient + "/" + apiClientId |
| 40 | + apiClientRequest.setMethod('GET'); |
| 41 | + apiClientRequest.setUri(apiClientUri) |
17 | 42 |
|
18 | | -if (splitUri.length == 0) { |
19 | | - message = "Can't parse api client ID from inbound request" |
20 | | - logger.error(SCRIPT_NAME + message) |
21 | | - response.status = Status.BAD_REQUEST |
22 | | - response.entity = "{ \"error\":\"" + message + "\"}" |
23 | | - return response |
| 43 | + logger.debug(SCRIPT_NAME + "Obtaining API client data from repo {}", apiClientUri) |
| 44 | + return http.send(apiClientRequest) |
| 45 | + .thenAlways(() -> closeSilently(apiClientRequest)) |
| 46 | + .thenAsync(apiClientResponse -> handleApiClientResponse(apiClientResponse)) |
24 | 47 | } |
25 | 48 |
|
26 | | -def apiClientId = splitUri[splitUri.length - 1]; |
27 | | - |
28 | | -logger.debug(SCRIPT_NAME + "Looking up API Client {}",apiClientId) |
29 | | - |
30 | | -apiClientRequest.setUri(routeArgIdmBaseUri + "/openidm/managed/" + routeArgObjApiClient + "/" + apiClientId) |
31 | | - |
32 | | -http.send(apiClientRequest).then(apiClientResponse -> { |
33 | | - apiClientRequest.close() |
34 | | - logger.debug(SCRIPT_NAME + "Back from IDM") |
| 49 | +private String extractApiClientId(Request request) { |
| 50 | + // Extract the API client ID from the REST request |
| 51 | + def splitUri = request.uri.path.split("/") |
| 52 | + if (splitUri.length == 0) { |
| 53 | + return null |
| 54 | + } |
| 55 | + def apiClientId = splitUri[splitUri.length - 1] |
| 56 | + logger.debug(SCRIPT_NAME + "Looking up API Client {}", apiClientId) |
| 57 | + return apiClientId |
| 58 | +} |
35 | 59 |
|
36 | | - def apiClientResponseStatus = apiClientResponse.getStatus(); |
| 60 | +private Promise<Response, NeverThrowsException> handleApiClientResponse(Response apiClientResponse) { |
| 61 | + logger.debug(SCRIPT_NAME + "Handling API client response") |
| 62 | + return processResponseContent(apiClientResponse) |
| 63 | + .thenAlways(() -> closeSilently(apiClientResponse)) |
| 64 | + .then(apiClientResponseJson -> transformApiClientResponse(apiClientResponseJson), |
| 65 | + exception -> { |
| 66 | + fail(apiClientResponseStatus, exception.getMessage()) |
| 67 | + }) |
| 68 | +} |
37 | 69 |
|
38 | | - if (apiClientResponseStatus != Status.OK) { |
39 | | - message = "Failed to get API Client details" |
40 | | - logger.error(message) |
41 | | - response.status = apiClientResponseStatus |
42 | | - response.entity = "{ \"error\":\"" + message + "\"}" |
43 | | - return response |
| 70 | +private Promise<JsonValue, Exception> processResponseContent(final Response apiClientResponse) { |
| 71 | + if (!(OK.equals(apiClientResponse.getStatus()))) { |
| 72 | + logger.error("Unable to communicate with API client endpoint - status code {}", |
| 73 | + apiClientResponse.getStatus().getCode()) |
| 74 | + return newExceptionPromise( |
| 75 | + new IOException("Failed to get API Client details - problem communicating with repo")) |
44 | 76 | } |
| 77 | + ContentTypeHeader contentTypeHeader = ContentTypeHeader.valueOf(apiClientResponse) |
| 78 | + String contentType = contentTypeHeader != null ? contentTypeHeader.getType() : null |
| 79 | + if (contentType == null || !contentType.toLowerCase(Locale.ROOT).startsWith("application/json")) { |
| 80 | + logger.error("API client endpoint response has unexpected content-type {}", contentType) |
| 81 | + return newExceptionPromise( |
| 82 | + new IOException("Failed to get API Client details - unexpected content " + contentType)) |
| 83 | + } |
| 84 | + return getJsonContentAsync(apiClientResponse) |
| 85 | +} |
45 | 86 |
|
46 | | - def apiClientResponseContent = apiClientResponse.getEntity(); |
47 | | - def apiClientResponseObject = apiClientResponseContent.getJson(); |
48 | | - |
49 | | - def responseObj = [ |
50 | | - "id": apiClientResponseObject.id, |
51 | | - "name": apiClientResponseObject.name, |
52 | | - "officialName": apiClientResponseObject.name, |
53 | | - "oauth2ClientId": apiClientResponseObject.oauth2ClientId, |
54 | | - "logoUri": apiClientResponseObject.logoUri |
55 | | - ] |
| 87 | +private static Promise<JsonValue, Exception> getJsonContentAsync(final Response response) { |
| 88 | + return response.getEntity() |
| 89 | + .getJsonAsync() |
| 90 | + .then(jsonContent -> new JsonValue(jsonContent).expect(Map.class)) |
| 91 | + .thenCatch(exception -> { |
| 92 | + throw new IOException("Evaluation response has malformed response JSON"); |
| 93 | + }) |
| 94 | +} |
56 | 95 |
|
57 | | - def responseJson = JsonOutput.toJson(responseObj); |
58 | | - logger.debug(SCRIPT_NAME + "Final JSON " + responseJson) |
| 96 | +private Response transformApiClientResponse(JsonValue apiClientResponseJson) { |
| 97 | + JsonValue transformedResponseJson = json(object( |
| 98 | + field("id", apiClientResponseJson.get("id")), |
| 99 | + field("name", apiClientResponseJson.get("name")), |
| 100 | + field("officialName", apiClientResponseJson.get("name")), |
| 101 | + field("oauth2ClientId", apiClientResponseJson.get("oauth2ClientId")), |
| 102 | + field("logoUri", apiClientResponseJson.get("logoUri")))) |
| 103 | + logger.debug(SCRIPT_NAME + "Transformed JSON {}", transformedResponseJson) |
| 104 | + Response transformedResponse = new Response(Status.OK) |
| 105 | + transformedResponse.getEntity().setJson(transformedResponseJson); |
| 106 | + return transformedResponse |
| 107 | +} |
59 | 108 |
|
60 | | - response.entity = responseJson; |
| 109 | +private Response fail(Status errorStatus, String errorMessage) { |
| 110 | + Response response = new Response(OK) |
| 111 | + response.headers['Content-Type'] = "application/json" |
| 112 | + response.status = errorStatus |
| 113 | + response.entity = json(object(field("error", errorMessage))) |
61 | 114 | return response |
| 115 | +} |
62 | 116 |
|
63 | | -}).then(response -> { return response }) |
|
0 commit comments