-
Notifications
You must be signed in to change notification settings - Fork 521
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
O3-310: allow Put operations on an application-writable config.json file
- Loading branch information
Showing
1 changed file
with
155 additions
and
0 deletions.
There are no files selected for viewing
155 changes: 155 additions & 0 deletions
155
...dule/webservices/rest/web/v1_0/controller/openmrs2_4/FrontendJsonConfigController2_4.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
|
||
} |