From 6dac7cad7032aa9787ad4dafc8eb239ff4348b15 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 17 Jan 2024 13:11:20 -0600 Subject: [PATCH] feat(edit-ema): Update ema next dotcms app #27302 (#27309) * #27302 adding an endpoint to expose the ema 2 app config to be users * #27302 minor fix and postman test * #27302 adding more postman test * #26227 fixing the postman test * #27302 trying to fix the postman test * #27302 upd postman * #27302 upd postman * #26227 fixing the postman test * #26227 fixing the postman test * #27302 fixing an issue with the yml --- .../src/curl-test/EMA.postman_collection.json | 231 ++++++++++++++++++ .../dotcms/rest/api/v1/ema/EMAResource.java | 106 ++++++++ .../rest/config/DotRestApplication.java | 2 + .../main/resources/apps/dotema-config-v2.yml | 42 ++-- 4 files changed, 361 insertions(+), 20 deletions(-) create mode 100644 dotCMS/src/curl-test/EMA.postman_collection.json create mode 100644 dotCMS/src/main/java/com/dotcms/rest/api/v1/ema/EMAResource.java diff --git a/dotCMS/src/curl-test/EMA.postman_collection.json b/dotCMS/src/curl-test/EMA.postman_collection.json new file mode 100644 index 000000000000..6d1944be9eac --- /dev/null +++ b/dotCMS/src/curl-test/EMA.postman_collection.json @@ -0,0 +1,231 @@ +{ + "info": { + "_postman_id": "029879f9-ad5c-48a1-94b8-12f0369d3605", + "name": "EMA", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "781456" + }, + "item": [ + { + "name": "GetCurrentSite", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"currentSiteId\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/site/currentSite", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "site", + "currentSite" + ] + } + }, + "response": [] + }, + { + "name": "NoConfig", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"User FE has not access to EMA\", function () {", + " pm.response.to.have.status(404);", + "", + " ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/ema", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "ema" + ] + } + }, + "response": [] + }, + { + "name": "app-save1", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotCMS.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{ \n\t \"configuration\": {\n\t\t \"value\": \"{\\r\\n \\\"config\\\":[\\r\\n {\\r\\n \\\"pattern\\\":\\\"\\/blogs\\/(.*)\\\",\\r\\n \\\"url\\\":\\\"https:\\/\\/myspa.blogs.com:3000\\\",\\r\\n \\\"options\\\":{\\r\\n \\\"authenticationToken\\\":\\\"123\\\",\\r\\n \\\"depth\\\":3,\\r\\n \\\"X-CONTENT-APP\\\":\\\"dotCMS\\\"\\r\\n }\\r\\n },\\r\\n {\\r\\n \\\"pattern\\\":\\\".*\\\",\\r\\n \\\"url\\\":\\\"https:\\/\\/myspa.com:3000\\\",\\r\\n \\\"options\\\":{\\r\\n \\\"authenticationToken\\\":\\\"456\\\",\\r\\n \\\"depth\\\":1,\\r\\n \\\"X-CONTENT-APP\\\":\\\"dotCMS\\\"\\r\\n }\\r\\n }\\r\\n ]\\r\\n }\"\n }\n}\n" + }, + "url": { + "raw": "{{serverURL}}/api/v1/apps/dotema-config-v2/{{currentSiteId}}", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "apps", + "dotema-config-v2", + "{{currentSiteId}}" + ] + }, + "description": "This tests the endpoint that brings back one specific App/integration given the App-key followed by the site-id" + }, + "response": [] + }, + { + "name": "TestConfig", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Now available the EMA\", function () {", + " pm.response.to.have.status(200);", + "", + " ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/ema", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "ema" + ] + } + }, + "response": [] + } + ], + "variable": [ + { + "key": "currentSiteId", + "value": "" + } + ] +} \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/ema/EMAResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/ema/EMAResource.java new file mode 100644 index 000000000000..2b73da0e519a --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/ema/EMAResource.java @@ -0,0 +1,106 @@ +package com.dotcms.rest.api.v1.ema; + +import com.dotcms.rest.InitDataObject; +import com.dotcms.rest.ResponseEntityView; +import com.dotcms.rest.WebResource; +import com.dotcms.rest.annotation.NoCache; +import com.dotcms.security.apps.AppDescriptor; +import com.dotcms.security.apps.AppSecrets; +import com.dotcms.security.apps.AppsAPI; +import com.dotcms.security.apps.Secret; +import com.dotmarketing.beans.Host; +import com.dotmarketing.business.APILocator; +import com.dotmarketing.business.web.WebAPILocator; +import com.dotmarketing.exception.DoesNotExistException; +import com.dotmarketing.exception.DotDataException; +import com.dotmarketing.exception.DotSecurityException; +import com.dotmarketing.util.Logger; +import com.dotmarketing.util.json.JSONObject; +import com.fasterxml.jackson.jaxrs.json.annotation.JSONP; +import com.google.common.annotations.VisibleForTesting; +import com.liferay.portal.model.User; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.Optional; + + +/** + * Resource API that deals with secrets and their usage on third-party apps integrations. + * @author jsanca + */ +@Path("/v1/ema") +public class EMAResource { + + private static final String EMA_APP_KEY = "dotema-config-v2"; + + private final WebResource webResource; + private AppsAPI appsAPI; + + @VisibleForTesting + public EMAResource(final WebResource webResource, + final AppsAPI appsAPI) { + this.webResource = webResource; + this.appsAPI = appsAPI; + } + + public EMAResource() { + this(new WebResource(), APILocator.getAppsAPI()); + } + + + /** + * Returns the ema config for the current site + * @param request + * @param response + * @return Response + * @throws DotDataException + * @throws DotSecurityException + */ + @GET + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response getDetails( + @Context final HttpServletRequest request, + @Context final HttpServletResponse response + ) throws DotDataException, DotSecurityException { + + final Host site = WebAPILocator.getHostWebAPI().getCurrentHostNoThrow(request); + final InitDataObject initData = + new WebResource.InitBuilder(webResource) + .requiredBackendUser(true) + .requiredFrontendUser(false) + .requestAndResponse(request, response) + .rejectWhenNoUser(true) + .init(); + + Logger.debug(this, ()-> "Getting EMA config for site: " + site.getHostname()); + + final Optional appDescriptorOptional = appsAPI + .getAppDescriptor(EMA_APP_KEY, APILocator.systemUser()); // we use the system b/c we don't want to check permissions, but only have access to this app and should be backend + if (appDescriptorOptional.isPresent()) { + + final Optional optionalAppSecrets = appsAPI + .getSecrets(EMA_APP_KEY, false, site, APILocator.systemUser()); + + if (optionalAppSecrets.isPresent()) { + + final AppSecrets appSecrets = optionalAppSecrets.get(); + final Secret configSecret = appSecrets.getSecrets().get("configuration"); + final String configJson = configSecret.getString(); + + return Response.ok(new ResponseEntityView<>(new JSONObject(configJson))).build(); + } + } + + throw new DoesNotExistException(String.format( + "No configuration was found for EMA on the current site `%s`. ", site.getHostname())); + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java b/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java index be0cc24a7f75..7edd7a2738cb 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java +++ b/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java @@ -42,6 +42,7 @@ import com.dotcms.rest.api.v1.contenttype.ContentTypeResource; import com.dotcms.rest.api.v1.contenttype.FieldResource; import com.dotcms.rest.api.v1.contenttype.FieldVariableResource; +import com.dotcms.rest.api.v1.ema.EMAResource; import com.dotcms.rest.api.v1.event.EventsResource; import com.dotcms.rest.api.v1.experiments.ExperimentsResource; import com.dotcms.rest.api.v1.fileasset.FileAssetsResource; @@ -234,6 +235,7 @@ public class DotRestApplication extends Application { .add(TempFileResource.class) .add(UpgradeTaskResource.class) .add(AppsResource.class) + .add(EMAResource.class) .add(BrowserResource.class) .add(ResourceLinkResource.class) .add(PushPublishFilterResource.class) diff --git a/dotCMS/src/main/resources/apps/dotema-config-v2.yml b/dotCMS/src/main/resources/apps/dotema-config-v2.yml index 53a88fb0bcdc..7e8d553c7c4f 100644 --- a/dotCMS/src/main/resources/apps/dotema-config-v2.yml +++ b/dotCMS/src/main/resources/apps/dotema-config-v2.yml @@ -10,26 +10,28 @@ params: label: "Configuration" hint: "Allows you to enter a JSON object that specifies how to match multiple URL patterns via RegExes with different 3rd party servers and rendering options, if required. For instance, in the following JSON object:

``` - [ - { - \"pattern\":\"/blogs/(.*)\", - \"url\":\"https://myspa.blogs.com:3000\", - \"options\": { - \"authenticationToken\": \"123\", - \"depth\": 3, - \"X-CONTENT-APP\": \"dotCMS\" - } - }, - { - \"pattern\":\".*\", - \"url\":\"https://myspa.com:3000\", - \"options\": { - \"authenticationToken\": \"456\", - \"depth\": 1, - \"X-CONTENT-APP\": \"dotCMS\" - } - } - ] + { + \"config\":[ + { + \"pattern\":\"/blogs/(.*)\", + \"url\":\"https://myspa.blogs.com:3000\", + \"options\": { + \"authenticationToken\":\"123\", + \"depth\":3, + \"X-CONTENT-APP\":\"dotCMS\" + } + }, + { + \"pattern\":\".*\", + \"url\":\"https://myspa.com:3000\", + \"options\":{ + \"authenticationToken\":\"456\", + \"depth\":1, + \"X-CONTENT-APP\":\"dotCMS\" + } + } + ] + } ```

You have a specific configuration for pages that start with '/blogs/' and a generic configuration for all other pages, i.e., '.*'. The 'options' object allows you to specify additional headers to be sent to the EMA Service. The 'authenticationToken' is an optional header in case you need a security layer for EMA requests. So, if the token sent by EMA does not equal the token in your app, the request will fail. The 'depth' attribute indicates dotCMS how many levels of related content must be returned by the API. The 'X-CONTENT-APP' is just an example of a custom header you can send to your app, so you can add your own." required: true