/*******************************************************************************
* Open Behavioral Health Information Technology Architecture (OBHITA.org)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package gov.samhsa.consent2share.web.controller;
import gov.samhsa.acs.common.validation.XmlValidation;
import gov.samhsa.consent2share.common.AuthenticatedUser;
import gov.samhsa.consent2share.common.UserContext;
import gov.samhsa.consent2share.infrastructure.eventlistener.EventService;
import gov.samhsa.consent2share.infrastructure.security.AccessReferenceMapper;
import gov.samhsa.consent2share.infrastructure.security.ClamAVClientNotAvailableException;
import gov.samhsa.consent2share.infrastructure.security.ClamAVService;
import gov.samhsa.consent2share.infrastructure.security.RecaptchaService;
import gov.samhsa.consent2share.infrastructure.securityevent.FileDownloadedEvent;
import gov.samhsa.consent2share.infrastructure.securityevent.FileUploadedEvent;
import gov.samhsa.consent2share.infrastructure.securityevent.MaliciousFileDetectedEvent;
import gov.samhsa.consent2share.service.clinicaldata.ClinicalDocumentService;
import gov.samhsa.consent2share.service.dto.ClinicalDocumentDto;
import gov.samhsa.consent2share.service.dto.LookupDto;
import gov.samhsa.consent2share.service.dto.PatientProfileDto;
import gov.samhsa.consent2share.service.patient.PatientService;
import gov.samhsa.consent2share.service.reference.ClinicalDocumentTypeCodeService;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
/**
* The Class ClinicalDocumentController.
*/
@Controller
@RequestMapping("/patients")
public class ClinicalDocumentController implements InitializingBean {
/** The clinical document service. */
@Autowired
ClinicalDocumentService clinicalDocumentService;
/** The patient service. */
@Autowired
PatientService patientService;
/** The clinical document type code service. */
@Autowired
ClinicalDocumentTypeCodeService clinicalDocumentTypeCodeService;
/** The user context. */
@Autowired
UserContext userContext;
/** The access reference mapper. */
@Autowired
AccessReferenceMapper accessReferenceMapper;
@Autowired
ClamAVService clamAVUtil;
@Autowired
RecaptchaService recaptchaUtil;
@Autowired
EventService eventService;
/** The xml validator. */
private XmlValidation xmlValidator;
/** The Constant C32_CDA_XSD_PATH. */
public static final String C32_CDA_XSD_PATH = "schema/cdar2c32/infrastructure/cda/";
/** The Constant C32_CDA_XSD_NAME. */
public static final String C32_CDA_XSD_NAME = "C32_CDA.xsd";
/** The logger. */
final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* Document home.
*
* @param model
* the model
* @return the string
*/
@RequestMapping("/medicalinfo.html")
public String documentHome(Model model) {
return "views/clinicaldocuments/mymedicalinfo";
}
/**
* Show clinical documents.
*
* @param model
* the model
* @return the string
*/
@RequestMapping("/clinicaldocuments.html")
public String showSecureClinicalDocuments(
Model model,
@RequestParam(value = "notify", required = false) String notification) {
AuthenticatedUser currentUser = userContext.getCurrentUser();
String username = currentUser.getUsername();
PatientProfileDto patientDto = patientService
.findPatientProfileByUsername(username);
String captchaString = recaptchaUtil.createSecureRecaptchaHtml();
List<ClinicalDocumentDto> clinicaldocumentDtos = clinicalDocumentService
.findDtoByPatientDto(patientDto);
accessReferenceMapper.setupAccessReferenceMap(clinicaldocumentDtos);
List<LookupDto> allDocumentTypeCodes = clinicalDocumentTypeCodeService
.findAllClinicalDocumentTypeCodes();
model.addAttribute("clinicaldocumentDtos", clinicaldocumentDtos);
model.addAttribute("allDocumentTypeCodes", allDocumentTypeCodes);
model.addAttribute("notification", notification);
model.addAttribute("captcha", captchaString);
return "views/clinicaldocuments/secureClinicalDocuments";
}
/**
* Upload clinical documents.
*
* @param request
* the request
* @param clinicalDocumentDto
* the clinical document dto
* @param file
* the file
* @param documentName
* the document name
* @param description
* the description
* @param documentTypeCode
* the document type code
* @return the string
*/
@RequestMapping(value = "/clinicaldocuments.html", method = RequestMethod.POST)
public String uploadClinicalDocumentsSecurely(
HttpServletRequest request,
@ModelAttribute("document") ClinicalDocumentDto clinicalDocumentDto,
@RequestParam("file") MultipartFile file,
@RequestParam("name") String documentName,
@RequestParam("description") String description,
@RequestParam("documentType") String documentTypeCode) {
/*Commented out temporally for prod server
if(scanMultipartFile(file)=="error") {
return "redirect:/patients/clinicaldocuments.html?notify=error";
}else if (scanMultipartFile(file)=="false") {
eventService.raiseSecurityEvent(new MaliciousFileDetectedEvent(request.getRemoteAddr(),
userContext.getCurrentUser().getUsername(),documentName));
return "redirect:/patients/clinicaldocuments.html?notify=virus_detected";
}*/
if (clinicalDocumentService.isDocumentOversized(file))
return "redirect:/patients/clinicaldocuments.html?notify=size_over_limits";
if (clinicalDocumentService.isDocumentExtensionPermitted(file) == false)
return "redirect:/patients/clinicaldocuments.html?notify=extension_not_permitted";
if (request.getParameter("recaptcha_challenge_field") == null)
return "redirect:/patients/clinicaldocuments.html?notify=wrong_captcha";
if (recaptchaUtil.checkAnswer(request.getRemoteAddr(),
request.getParameter("recaptcha_challenge_field"),
request.getParameter("recaptcha_response_field")) == false)
return "redirect:/patients/clinicaldocuments.html?notify=wrong_captcha";
try {
xmlValidator.validate(file.getInputStream());
} catch (Exception e) {
return "redirect:/patients/clinicaldocuments.html?notify=invalid_c32";
}
try {
AuthenticatedUser currentUser = userContext.getCurrentUser();
String username = currentUser.getUsername();
clinicalDocumentDto.setName(documentName);
clinicalDocumentDto.setDescription(description);
clinicalDocumentDto.setContent(file.getBytes());
clinicalDocumentDto.setFilename(file.getOriginalFilename());
clinicalDocumentDto.setContentType(file.getContentType());
clinicalDocumentDto.setDocumentSize(file.getSize());
clinicalDocumentDto.setPatientId(patientService
.findIdByUsername(username));
LookupDto clinicalDocumentTypeCode = new LookupDto();
clinicalDocumentTypeCode.setCode(documentTypeCode);
clinicalDocumentDto
.setClinicalDocumentTypeCode(clinicalDocumentTypeCode);
clinicalDocumentService.saveClinicalDocument(clinicalDocumentDto);
eventService.raiseSecurityEvent(new FileUploadedEvent(request
.getRemoteAddr(), username, documentName));
} catch (IOException e) {
e.printStackTrace();
}
return "redirect:/patients/clinicaldocuments.html";
}
/**
* Download.
*
* @param request
* the request
* @param response
* the response
* @param documentId
* the document id
* @return the string
*/
@RequestMapping(value = "/downloaddoc.html", method = RequestMethod.POST)
public String download(HttpServletRequest request,
HttpServletResponse response,
@RequestParam("download_id") String documentId) {
Long directDocumentId = accessReferenceMapper
.getDirectReference(documentId);
ClinicalDocumentDto clinicalDocumentDto = clinicalDocumentService
.findClinicalDocumentDto(directDocumentId);
if (clinicalDocumentService
.isDocumentBelongsToThisUser(clinicalDocumentDto)) {
try {
response.setHeader(
"Content-Disposition",
"attachment;filename=\""
+ clinicalDocumentDto.getFilename() + "\"");
response.setHeader("Content-Type", "application/"
+ clinicalDocumentDto.getContentType());
OutputStream out = response.getOutputStream();
response.setContentType(clinicalDocumentDto.getContentType());
IOUtils.copy(
new ByteArrayInputStream(clinicalDocumentDto
.getContent()), out);
out.flush();
out.close();
eventService
.raiseSecurityEvent(new FileDownloadedEvent(request
.getRemoteAddr(), userContext.getCurrentUser()
.getUsername(), "Clinical_Document_"
+ directDocumentId));
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Removes the.
*
* @param documentId
* the document id
* @return the string
*/
@RequestMapping(value = "/deletedoc.html", method = RequestMethod.POST)
public String remove(@RequestParam("delete_id") String documentId) {
Long directDocumentId = accessReferenceMapper
.getDirectReference(documentId);
ClinicalDocumentDto clinicalDocumentDto = clinicalDocumentService
.findClinicalDocumentDto(directDocumentId);
if (clinicalDocumentService
.isDocumentBelongsToThisUser(clinicalDocumentDto)) {
clinicalDocumentService.deleteClinicalDocument(clinicalDocumentDto);
}
return "redirect:/patients/clinicaldocuments.html";
}
String scanMultipartFile(MultipartFile file){
Boolean isItClean=null;
try (InputStream inputStream = file.getInputStream()){
isItClean = clamAVUtil.fileScanner(inputStream);
} catch (ClamAVClientNotAvailableException e){
logger.error(e.getMessage());
return "error";
} catch (Exception e) {
logger.error(e.getMessage());
return "error";
}
return isItClean.toString();
}
@Override
public void afterPropertiesSet() throws Exception {
this.xmlValidator = new XmlValidation(this.getClass().getClassLoader()
.getResourceAsStream(C32_CDA_XSD_PATH + C32_CDA_XSD_NAME),
C32_CDA_XSD_PATH);
}
public ClinicalDocumentController(
ClinicalDocumentService clinicalDocumentService,
PatientService patientService,
ClinicalDocumentTypeCodeService clinicalDocumentTypeCodeService,
UserContext userContext,
AccessReferenceMapper accessReferenceMapper,
ClamAVService clamAVUtil, RecaptchaService recaptchaUtil,
EventService eventService, XmlValidation xmlValidator) {
super();
this.clinicalDocumentService = clinicalDocumentService;
this.patientService = patientService;
this.clinicalDocumentTypeCodeService = clinicalDocumentTypeCodeService;
this.userContext = userContext;
this.accessReferenceMapper = accessReferenceMapper;
this.clamAVUtil = clamAVUtil;
this.recaptchaUtil = recaptchaUtil;
this.eventService = eventService;
this.xmlValidator = xmlValidator;
}
@SuppressWarnings("unused")
private ClinicalDocumentController() {
}
}