diff --git a/omod-2.4/src/main/java/org/openmrs/module/webservices/rest/web/v1_0/controller/openmrs2_4/FrontendJsonConfigController2_4.java b/omod-2.4/src/main/java/org/openmrs/module/webservices/rest/web/v1_0/controller/openmrs2_4/FrontendJsonConfigController2_4.java new file mode 100644 index 000000000..56105ff77 --- /dev/null +++ b/omod-2.4/src/main/java/org/openmrs/module/webservices/rest/web/v1_0/controller/openmrs2_4/FrontendJsonConfigController2_4.java @@ -0,0 +1,155 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.webservices.rest.web.v1_0.controller.openmrs2_4; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openmrs.User; +import org.openmrs.api.AdministrationService; +import org.openmrs.api.context.Context; +import org.openmrs.api.context.UsernamePasswordCredentials; +import org.openmrs.module.webservices.rest.web.RestConstants; +import org.openmrs.module.webservices.rest.web.v1_0.controller.BaseRestController; +import org.openmrs.util.OpenmrsUtil; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codehaus.jackson.JsonProcessingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.openmrs.module.ModuleException; + +@Controller +@RequestMapping(value = "/rest/" + RestConstants.VERSION_1 + "/frontend/config.json") +public class FrontendJsonConfigController2_4 extends BaseRestController { + + public static final String DEFAULT_FRONTEND_DIRECTORY = "frontend"; + public static final String GP_LOCAL_DIRECTORY = "spa.local.directory"; + private static final String JSON_CONFIG_FILE_NAME = "config.json"; + + private Log log = LogFactory.getLog(this.getClass()); + + @RequestMapping(method = RequestMethod.POST) + @ResponseStatus(HttpStatus.OK) + public void saveFrontendConfigFile(HttpServletRequest request, HttpServletResponse response) throws IOException { + String basicAuth = request.getHeader("Authorization"); + boolean isAuthenticated = Context.isAuthenticated(); + if (!isAuthenticated && basicAuth == null) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "No Authorisation Provided"); + return; + } + if (!isAuthenticated && isValidAuthFormat(response, basicAuth)) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid credentials provided"); + return; + } + User user = Context.getAuthenticatedUser(); + if (user == null || !user.isSuperUser()) { + log.error("Authorization error while creating a config.json file"); + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + return; + } + saveJsonConfigFile(request, 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: '{" + jsonConfigFile.getAbsolutePath() + "}' written successfully"); + 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)) { + return true; + } + + String decoded = new String(Base64.getDecoder().decode(basicAuth), StandardCharsets.UTF_8); + if (StringUtils.isBlank(decoded) || !decoded.contains(":")) { + return true; + } + + String[] userAndPass = decoded.split(":"); + Context.authenticate(new UsernamePasswordCredentials(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; + } + + private File getJsonConfigFile() { + File folder = getSpaStaticFilesDir(); + if (!folder.isDirectory()) { + log.warn("SPA frontend repository is not a directory hence creating it at: " + folder.getAbsolutePath()); + if (!folder.mkdirs()) { + throw new ModuleException("Failed to create the SPA frontend repository folder at: " + folder.getAbsolutePath()); + } + } + return new File(folder.getAbsolutePath(), JSON_CONFIG_FILE_NAME); + } + + private File getSpaStaticFilesDir() { + AdministrationService as = Context.getAdministrationService(); + String folderName = as.getGlobalProperty(GP_LOCAL_DIRECTORY, DEFAULT_FRONTEND_DIRECTORY); + + // try to load the repository folder straight away. + File folder = new File(folderName); + + // if the property wasn't a full path already, assume it was intended to be a + // folder in the + // application directory + if (!folder.exists()) { + folder = new File(OpenmrsUtil.getApplicationDataDirectory(), folderName); + } + return folder; + } + +}