/*******************************************************************************
* 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) {
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() {
}
}