Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) Mirth Corporation. All rights reserved.
*
* http://www.mirthcorp.com
*
* The software in this package is published under the terms of the MPL license a copy of which has
* been included with this distribution in the LICENSE.txt file.
*/

package com.mirth.connect.client.core.api.servlets;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.mirth.connect.client.core.ClientException;
import com.mirth.connect.client.core.api.BaseServletInterface;
import com.mirth.connect.client.core.api.MirthOperation;
import com.mirth.connect.client.core.api.Param;

/**
* Serializes a message through a data type's own serializer — the engine's exact toXML()/toJSON()
* output — so a browser client can build message trees that match the runtime {@code msg}/{@code tmp}
* without shipping the engine's datatype libraries. Uses the installed datatype plugins, so every
* data type (and strict/non-strict via serialization-property overrides) is covered. Session-authed,
* not audited.
*/
@Path("/datatypes")
@Tag(name = "Data Types")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public interface DataTypeServletInterface extends BaseServletInterface {

@POST
@Path("/_serialize")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Serializes a message via the given data type's serializer. Returns { format, data, meta: { root, descriptions } }.")
@MirthOperation(name = "serializeMessage", display = "Serialize message for data type", auditable = false)
public Response serializeMessage(// @formatter:off
// dataType is a query param (not a path segment) so values containing a slash — e.g. "EDI/X12" — pass cleanly.
@Param("dataType") @Parameter(description = "The data type name (HL7V2, XML, JSON, EDI/X12, NCPDP, DELIMITED, RAW, DICOM, HL7V3).", required = true) @QueryParam("dataType") String dataType,
@Param("props") @Parameter(description = "Optional serialization-property overrides as newline-separated key=value pairs (e.g. useStrictParser=true).", required = false) @QueryParam("props") String props,
@Param("message") String message) throws ClientException;
// @formatter:on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) Mirth Corporation. All rights reserved.
*
* http://www.mirthcorp.com
*
* The software in this package is published under the terms of the MPL license a copy of which has
* been included with this distribution in the LICENSE.txt file.
*/

package com.mirth.connect.client.core.api.servlets;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.mirth.connect.client.core.ClientException;
import com.mirth.connect.client.core.api.BaseServletInterface;
import com.mirth.connect.client.core.api.MirthOperation;
import com.mirth.connect.client.core.api.Param;

/**
* JavaScript utilities the script editors need — script validation — exposed over REST so a
* browser client can use the engine's own Rhino compiler (the same one the Swing client uses
* in-process) instead of shipping its own. Takes a raw script body, requires only a valid
* session, and is not audited (editor-support calls, hit frequently). (Formatting is done
* client-side by the web admin with js-beautify, so there is no pretty-print endpoint.)
*/
@Path("/javascript")
@Tag(name = "JavaScript")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public interface JavaScriptServletInterface extends BaseServletInterface {

@POST
@Path("/_validate")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Validates a JavaScript script with the engine's Rhino compiler. Returns { \"error\": <message|null> }.")
@MirthOperation(name = "validateScript", display = "Validate JavaScript", auditable = false)
public Response validateScript(@Param("script") String script) throws ClientException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) Mirth Corporation. All rights reserved.
*
* http://www.mirthcorp.com
*
* The software in this package is published under the terms of the MPL license a copy of which has
* been included with this distribution in the LICENSE.txt file.
*/

package com.mirth.connect.client.core.api.servlets;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;

import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.mirth.connect.client.core.ClientException;
import com.mirth.connect.client.core.api.BaseServletInterface;
import com.mirth.connect.client.core.api.MirthOperation;
import com.mirth.connect.client.core.api.Param;

/**
* Serves the browser (web administrator) half of installed extensions.
*
* An extension may ship a web UI alongside its engine code as a {@code webadmin/} folder
* (containing a {@code plugin.json} manifest and its compiled ES-module assets). This servlet
* lets the web administrator discover and fetch those web halves directly from the engine that
* has them installed — so a plugin's UI follows the engine, not the web-admin install. Both
* endpoints require only a valid session (any authenticated user) and are not audited: they
* serve non-sensitive static UI code and are hit once per page load.
*/
@Path("/webplugins")
@Tag(name = "Web Plugins")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public interface WebPluginServletInterface extends BaseServletInterface {

@GET
@Path("/")
@Operation(summary = "Returns the install-directory paths of all enabled extensions that ship a web administrator UI (i.e. contain webadmin/plugin.json).")
@MirthOperation(name = "getWebPluginPaths", display = "Get web plugin paths", auditable = false)
public List<String> getWebPluginPaths() throws ClientException;

@GET
@Path("/{extensionPath}/{resourcePath:.*}")
@Produces(MediaType.WILDCARD)
@Operation(summary = "Serves a static file from an extension's webadmin/ folder (the browser half of the plugin).")
@MirthOperation(name = "getWebPluginResource", display = "Get web plugin resource", auditable = false)
public Response getWebPluginResource(// @formatter:off
@Param("extensionPath") @Parameter(description = "The extension's install-directory name.", required = true) @PathParam("extensionPath") String extensionPath,
@Param("resourcePath") @Parameter(description = "The file path within the extension's webadmin/ folder.", required = true) @PathParam("resourcePath") String resourcePath) throws ClientException;
// @formatter:on
}
11 changes: 11 additions & 0 deletions server/src/com/mirth/connect/plugins/DataTypeServerPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.mirth.connect.model.datatype.SerializationProperties;
import com.mirth.connect.model.datatype.SerializerProperties;
import com.mirth.connect.model.transmission.TransmissionModeProperties;
import com.mirth.connect.model.util.MessageVocabulary;
import com.mirth.connect.server.message.DefaultAutoResponder;
import com.mirth.connect.server.message.DefaultResponseValidator;

Expand Down Expand Up @@ -90,4 +91,14 @@ public AutoResponder getAutoResponder(SerializationProperties serializationPrope
public ResponseValidator getResponseValidator(SerializationProperties serializationProperties, ResponseValidationProperties responseValidationProperties) {
return new DefaultResponseValidator();
}

/**
* Get the message vocabulary for this data type — element descriptions used to annotate a
* message tree (the same text the Swing client shows). {@code version} and {@code type} come
* from the serializer's message metadata (mirth_version / mirth_type). Returns null for data
* types with no vocabulary (the default); types that have one override this.
*/
public MessageVocabulary getVocabulary(String version, String type) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package com.mirth.connect.plugins.datatypes.dicom;

import com.mirth.connect.model.datatype.DataTypeDelegate;
import com.mirth.connect.model.util.MessageVocabulary;
import com.mirth.connect.plugins.DataTypeServerPlugin;

public class DICOMDataTypeServerPlugin extends DataTypeServerPlugin {
Expand All @@ -31,4 +32,9 @@ protected DataTypeDelegate getDataTypeDelegate() {
return dataTypeDelegate;
}

@Override
public MessageVocabulary getVocabulary(String version, String type) {
return new DICOMVocabulary(version, type);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.mirth.connect.donkey.server.message.batch.BatchAdaptorFactory;
import com.mirth.connect.model.datatype.DataTypeDelegate;
import com.mirth.connect.model.datatype.SerializerProperties;
import com.mirth.connect.model.util.MessageVocabulary;
import com.mirth.connect.plugins.DataTypeServerPlugin;

public class EDIDataTypeServerPlugin extends DataTypeServerPlugin {
Expand All @@ -38,4 +39,9 @@ public BatchAdaptorFactory getBatchAdaptorFactory(SourceConnector sourceConnecto
protected DataTypeDelegate getDataTypeDelegate() {
return dataTypeDelegate;
}

@Override
public MessageVocabulary getVocabulary(String version, String type) {
return new X12Vocabulary(version, type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.mirth.connect.model.datatype.SerializerProperties;
import com.mirth.connect.model.transmission.TransmissionModeProperties;
import com.mirth.connect.model.transmission.framemode.FrameModeProperties;
import com.mirth.connect.model.util.MessageVocabulary;
import com.mirth.connect.plugins.DataTypeServerPlugin;
import com.mirth.connect.util.TcpUtil;

Expand Down Expand Up @@ -71,4 +72,9 @@ protected DataTypeDelegate getDataTypeDelegate() {
return dataTypeDelegate;
}

@Override
public MessageVocabulary getVocabulary(String version, String type) {
return new HL7v2Vocabulary(version, type);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.mirth.connect.donkey.server.message.batch.BatchAdaptorFactory;
import com.mirth.connect.model.datatype.DataTypeDelegate;
import com.mirth.connect.model.datatype.SerializerProperties;
import com.mirth.connect.model.util.MessageVocabulary;
import com.mirth.connect.plugins.DataTypeServerPlugin;

public class NCPDPDataTypeServerPlugin extends DataTypeServerPlugin {
Expand All @@ -38,4 +39,9 @@ public BatchAdaptorFactory getBatchAdaptorFactory(SourceConnector sourceConnecto
protected DataTypeDelegate getDataTypeDelegate() {
return dataTypeDelegate;
}

@Override
public MessageVocabulary getVocabulary(String version, String type) {
return new NCPDPVocabulary(version, type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,34 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha

HttpServletRequest servletRequest = (HttpServletRequest)request;
String requestedWithHeader = (String) servletRequest.getHeader("X-Requested-With");

//if header is required and not present, send an error
if(isRequestedWithHeaderRequired && StringUtils.isBlank(requestedWithHeader)) {
if(isRequestedWithHeaderRequired && StringUtils.isBlank(requestedWithHeader) && !isWebPluginAssetRequest(servletRequest)) {
res.sendError(400, "All requests must have 'X-Requested-With' header");
}
else {
chain.doFilter(request, response);
}


}

/**
* A web administrator loads a plugin's browser assets from /api/webplugins/... using
* &lt;script&gt;/import(), which cannot set request headers. Those GETs serve only static
* UI code (no state change, nothing sensitive), so they are exempt from the CSRF header
* requirement. State-changing requests and all other endpoints still require the header.
*/
private static boolean isWebPluginAssetRequest(HttpServletRequest request) {
if (!"GET".equalsIgnoreCase(request.getMethod())) {
return false;
}
// getPathInfo() is context-relative (e.g. "/webplugins/..."); fall back to the full
// URI so a configured http.contextpath doesn't defeat the check.
String path = request.getPathInfo();
if (StringUtils.isBlank(path)) {
path = request.getRequestURI();
}
return path != null && (path.startsWith("/webplugins/") || path.contains("/api/webplugins/"));
}

public boolean isRequestedWithHeaderRequired() {
Expand Down
Loading
Loading