diff --git a/README.md b/README.md index d11e5cc..d0c4fc4 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,4 @@ cd openmrs-module-spa && mvn clean install | `spa.local.directory` | The directory containing the Frontend 3.0 application's `index.html`. Can be an absolute path, or relative to the application data directory. Only used if `spa.remote.enabled` is false. | frontend | | `spa.remote.enabled` | If enabled, serves from `spa.remote.url` instead of `spa.local.directory` | false | | `spa.remote.url` | The URL of the Frontend 3.0 application files. Only used if `spa.remote.enabled` is true. | https://spa-modules.nyc3.digitaloceanspaces.com/@openmrs/esm-app-shell/latest/ | -| `spa.json.config.filename` | The name of the Frontend 3.0 application's json configuration file. | config.json | diff --git a/omod/pom.xml b/omod/pom.xml index e80b709..dd02cd3 100644 --- a/omod/pom.xml +++ b/omod/pom.xml @@ -44,11 +44,6 @@ 1.18.16 provided - - commons-codec - commons-codec - 1.15 - diff --git a/omod/src/main/java/org/openmrs/module/spa/SpaConstants.java b/omod/src/main/java/org/openmrs/module/spa/SpaConstants.java index 8f33706..b8c1cf6 100644 --- a/omod/src/main/java/org/openmrs/module/spa/SpaConstants.java +++ b/omod/src/main/java/org/openmrs/module/spa/SpaConstants.java @@ -29,10 +29,6 @@ private SpaConstants() {} public static final String GP_REMOTE_URL = "spa.remote.url"; - public static final String DEFAULT_CONFIG_DIRECTORY = "config"; - public static final String CONFIG_DIRECTORY = "spa.config.directory"; - public static final String DEFAULT_JSON_CONFIG_FILE_NAME = "config.json"; - public static final String GP_JSON_CONFIG_FILE_NAME = "spa.json.config.filename"; } diff --git a/omod/src/main/java/org/openmrs/module/spa/servlet/SpaServlet.java b/omod/src/main/java/org/openmrs/module/spa/servlet/SpaServlet.java index 3dd052b..3707188 100644 --- a/omod/src/main/java/org/openmrs/module/spa/servlet/SpaServlet.java +++ b/omod/src/main/java/org/openmrs/module/spa/servlet/SpaServlet.java @@ -11,11 +11,8 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; -import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonProcessingException; -import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; import org.openmrs.User; import org.openmrs.api.AdministrationService; @@ -33,17 +30,14 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.Base64; import static org.openmrs.module.spa.SpaConstants.DEFAULT_JSON_CONFIG_FILE_NAME; -import static org.openmrs.module.spa.SpaConstants.GP_JSON_CONFIG_FILE_NAME; @Slf4j public class SpaServlet extends HttpServlet { @@ -82,67 +76,20 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t } @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { String requestURI = request.getRequestURI(); if (requestURI.endsWith("/config.json")) { if (!Context.isAuthenticated()) { String basicAuth = request.getHeader("Authorization"); if (basicAuth != null) { // check that header is in format "Basic ${base64encode(username + ":" + password)}" - if (basicAuth.startsWith("Basic")) { - try { - // remove the leading "Basic " - basicAuth = basicAuth.substring(6); - if (StringUtils.isBlank(basicAuth)) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid credentials provided"); - return; - } - - String decoded = new String(Base64.decodeBase64(basicAuth), StandardCharsets.UTF_8); - if (StringUtils.isBlank(decoded) || !decoded.contains(":")) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid credentials provided"); - return; - } - - String[] userAndPass = decoded.split(":"); - Context.authenticate(userAndPass[0], userAndPass[1]); - log.debug("authenticated [{}]", userAndPass[0]); - } - catch (Exception ex) { - // This filter never stops execution. If the user failed to - // authenticate, that will be caught later. - log.debug("authentication exception ", ex); - } - } + if (isValidAuthFormat(response, basicAuth)) return; } } User user = Context.getAuthenticatedUser(); if (user != null && user.isSuperUser()) { - File jsonConfigFile = getJsonConfigFile(); - try { - BufferedReader reader = request.getReader(); - StringBuilder stringBuilder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - stringBuilder.append(line); - } - String requestBody = stringBuilder.toString(); - - new ObjectMapper().readTree(requestBody); // verify that is in a valid JSON format - - InputStream inputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8)); - OutputStream outStream = new FileOutputStream(jsonConfigFile); - OpenmrsUtil.copyFile(inputStream, outStream); - - if (jsonConfigFile.exists()) { - log.debug("file: '{}' written successfully", jsonConfigFile.getAbsolutePath()); - response.setStatus(HttpServletResponse.SC_OK); - } - } catch (JsonProcessingException e) { - log.error("Invalid JSON format", e); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - } + saveJsonConfigFile(request, response); } else { log.error("Authorisation error while creating a config.json file"); response.setStatus(HttpServletResponse.SC_FORBIDDEN); @@ -150,6 +97,62 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } } + private void saveJsonConfigFile(HttpServletRequest request, HttpServletResponse response) throws IOException { + File jsonConfigFile = getJsonConfigFile(); + try { + BufferedReader reader = request.getReader(); + StringBuilder stringBuilder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + } + String requestBody = stringBuilder.toString(); + + new ObjectMapper().readTree(requestBody); // verify that is in a valid JSON format + + InputStream inputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8)); + OutputStream outStream = Files.newOutputStream(jsonConfigFile.toPath()); + OpenmrsUtil.copyFile(inputStream, outStream); + + if (jsonConfigFile.exists()) { + log.debug("file: '{}' written successfully", jsonConfigFile.getAbsolutePath()); + response.setStatus(HttpServletResponse.SC_OK); + } + } catch (JsonProcessingException e) { + log.error("Invalid JSON format", e); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + } + } + + private boolean isValidAuthFormat(HttpServletResponse response, String basicAuth) { + if (basicAuth.startsWith("Basic")) { + try { + // remove the leading "Basic " + basicAuth = basicAuth.substring(6); + if (StringUtils.isBlank(basicAuth)) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid credentials provided"); + return true; + } + + String decoded = new String(Base64.getDecoder().decode(basicAuth), StandardCharsets.UTF_8); + if (StringUtils.isBlank(decoded) || !decoded.contains(":")) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid credentials provided"); + return true; + } + + String[] userAndPass = decoded.split(":"); + Context.authenticate(userAndPass[0], userAndPass[1]); + log.debug("authenticated [{}]", userAndPass[0]); + } + catch (Exception ex) { + // This filter never stops execution. If the user failed to + // authenticate, that will be caught later. + log.debug("authentication exception ", ex); + } + } + return false; + } + protected void handleLocalAssets(HttpServletRequest request, HttpServletResponse response) throws IOException { File file = getFile(request); @@ -167,7 +170,7 @@ protected void handleLocalAssets(HttpServletRequest request, HttpServletResponse String mimeType = getServletContext().getMimeType(file.getName()); response.setContentType(mimeType); - try (InputStream is = new FileInputStream(file)) { + try (InputStream is = Files.newInputStream(file.toPath())) { OpenmrsUtil.copyFile(is, response.getOutputStream()); } } @@ -180,7 +183,7 @@ protected void handleLocalAssets(HttpServletRequest request, HttpServletResponse * @param response {@link HttpServletResponse} * @throws IOException {@link IOException} F */ - protected void handleRemoteAssets(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + protected void handleRemoteAssets(HttpServletRequest request, HttpServletResponse response) throws IOException { Resource resource = getResource(request); if (!resource.exists()) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); @@ -259,7 +262,7 @@ protected File getFile(HttpServletRequest request) { File file = folder.toPath().resolve(extractedFile).toFile(); if (!file.exists()) { - log.warn("File with path '{}' doesn't exist", file.toString()); + log.warn("File with path '{}' doesn't exist", file); return null; } return file; @@ -280,7 +283,7 @@ private File getJsonConfigFile() { } AdministrationService as = Context.getAdministrationService(); - String jsonConfigFileName = as.getGlobalProperty(GP_JSON_CONFIG_FILE_NAME, DEFAULT_JSON_CONFIG_FILE_NAME); + String jsonConfigFileName = as.getGlobalProperty(DEFAULT_JSON_CONFIG_FILE_NAME); return new File(folder.getAbsolutePath(), jsonConfigFileName); } diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml index ce9b014..20b3fcd 100644 --- a/omod/src/main/resources/config.xml +++ b/omod/src/main/resources/config.xml @@ -38,13 +38,7 @@ https://spa-modules.nyc3.digitaloceanspaces.com/@openmrs/esm-app-shell/latest/ The URL of the Frontend 3.0 application files. Only used if `spa.remote.enabled` is true. - - - spa.json.config.filename - config.json - The name of the Frontend 3.0 application's json configuration file - - + spaServlet org.openmrs.module.spa.servlet.SpaServlet