001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.devtools.pdle; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.commons.logging.Log; 020import org.apache.commons.logging.LogFactory; 021import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 022import org.kuali.rice.krad.bo.PersistableBusinessObject; 023import org.kuali.rice.krad.service.BusinessObjectService; 024import org.kuali.rice.krad.service.KRADServiceLocator; 025import org.springframework.core.io.FileSystemResource; 026 027import javax.servlet.ServletException; 028import javax.servlet.ServletRequest; 029import javax.servlet.ServletResponse; 030import javax.servlet.http.HttpServlet; 031import java.io.IOException; 032import java.util.Arrays; 033import java.util.Collection; 034import java.util.HashSet; 035import java.util.Map; 036import java.util.Properties; 037import java.util.Set; 038 039/** 040 * Servlet that can be used to invoke the PostDataLoadEncryptionService 041 * 042 * It is not recommended to leave this Servlet running at all times. It is really only intended 043 * to be made available during initial data load and then removed (from the web.xml of the 044 * application) after data load and encryption is complete. 045 * 046 * This was done as a Servlet for now because Rice does not have a batch runner yet similar 047 * to what KFS has (which is where a lot of the code below was borrowed from). 048 * 049 * 050 * <code> 051 052 <!-- 053 Add the following to your web.xml these if you want to run the servlet in order to encrypt 054 some data in the Rice database. 055 (this would most likely be used for external identifiers in KIM that require encryption) 056 057 It is not recommended to enable this service for a production instance except during initial 058 data load. It is not secured in any way and would allow for easy corruption of data if mishandled. 059 --> 060 061 <servlet> 062 <servlet-name>postDataLoadEncryption</servlet-name> 063 <servlet-class>org.kuali.rice.krad.devtools.pdle.PostDataLoadEncryptionServlet</servlet-class> 064 </servlet> 065 066 <servlet-mapping> 067 <servlet-name>postDataLoadEncryption</servlet-name> 068 <url-pattern>/postDataLoadEncryption</url-pattern> 069 </servlet-mapping> 070 071 </code> 072 * 073 * 074 * @author Kuali Rice Team (rice.collab@kuali.org) 075 */ 076public class PostDataLoadEncryptionServlet extends HttpServlet { 077 private static final Log LOG = LogFactory.getLog(PostDataLoadEncryptionServlet.class); 078 079 private static final String ATTRIBUTES_TO_ENCRYPT_PROPERTIES = "attributesToEncryptProperties"; 080 private static final String CHECK_OJB_ENCRYPT_CONFIG = "checkOjbEncryptConfig"; 081 082 @Override 083 public void service(ServletRequest request, ServletResponse response) 084 throws ServletException, IOException { 085 String attributesToEncryptPropertyFileName = request.getParameter(ATTRIBUTES_TO_ENCRYPT_PROPERTIES); 086 if (StringUtils.isBlank(attributesToEncryptPropertyFileName)) { 087 throw new IllegalArgumentException("No valid " + ATTRIBUTES_TO_ENCRYPT_PROPERTIES + " parameter was passed to this Servlet."); 088 } 089 boolean checkOjbEncryptConfig = true; 090 String checkOjbEncryptConfigValue = request.getParameter(CHECK_OJB_ENCRYPT_CONFIG); 091 if (!StringUtils.isBlank(checkOjbEncryptConfigValue)) { 092 checkOjbEncryptConfig = Boolean.valueOf(checkOjbEncryptConfigValue).booleanValue(); 093 } 094 execute(attributesToEncryptPropertyFileName, checkOjbEncryptConfig); 095 response.getOutputStream().write(("<html><body><p>Successfully encrypted attributes as defined in: " + attributesToEncryptPropertyFileName + "</p></body></html>").getBytes()); 096 } 097 098 public void execute(String attributesToEncryptPropertyFileName, boolean checkOjbEncryptConfig) { 099 PostDataLoadEncryptionService postDataLoadEncryptionService = GlobalResourceLoader.getService( 100 PostDataLoadEncryptionService.POST_DATA_LOAD_ENCRYPTION_SERVICE); 101 Properties attributesToEncryptProperties = new Properties(); 102 try { 103 attributesToEncryptProperties.load(new FileSystemResource(attributesToEncryptPropertyFileName).getInputStream()); 104 } 105 catch (Exception e) { 106 throw new IllegalArgumentException("PostDataLoadEncrypter requires the full, absolute path to a properties file where the keys are the names of the BusinessObject classes that should be processed and the values are the list of attributes on each that require encryption", e); 107 } 108 for (Map.Entry<Object, Object> entry : attributesToEncryptProperties.entrySet()) { 109 Class<? extends PersistableBusinessObject> businessObjectClass; 110 try { 111 businessObjectClass = (Class<? extends PersistableBusinessObject>) Class.forName((String) entry.getKey()); 112 } 113 catch (Exception e) { 114 throw new IllegalArgumentException(new StringBuffer("Unable to load Class ").append((String) entry.getKey()).append(" specified by name in attributesToEncryptProperties file ").append(attributesToEncryptProperties).toString(), e); 115 } 116 final Set<String> attributeNames; 117 try { 118 attributeNames = new HashSet<String>(Arrays.asList(StringUtils.split((String) entry.getValue(), ","))); 119 } 120 catch (Exception e) { 121 throw new IllegalArgumentException(new StringBuffer("Unable to load attributeNames Set from comma-delimited list of attribute names specified as value for property with Class name ").append(entry.getKey()).append(" key in attributesToEncryptProperties file ").append(attributesToEncryptProperties).toString(), e); 122 } 123 postDataLoadEncryptionService.checkArguments(businessObjectClass, attributeNames, checkOjbEncryptConfig); 124 postDataLoadEncryptionService.createBackupTable(businessObjectClass); 125 BusinessObjectService businessObjectService = KRADServiceLocator.getBusinessObjectService(); 126 try { 127 postDataLoadEncryptionService.prepClassDescriptor(businessObjectClass, attributeNames); 128 Collection<? extends PersistableBusinessObject> objectsToEncrypt = businessObjectService.findAll(businessObjectClass); 129 for (Object businessObject : objectsToEncrypt) { 130 postDataLoadEncryptionService.encrypt((PersistableBusinessObject) businessObject, attributeNames); 131 } 132 postDataLoadEncryptionService.restoreClassDescriptor(businessObjectClass, attributeNames); 133 LOG.info(new StringBuffer("Encrypted ").append(entry.getValue()).append(" attributes of Class ").append(entry.getKey())); 134 } 135 catch (Exception e) { 136 postDataLoadEncryptionService.restoreTableFromBackup(businessObjectClass); 137 LOG.error(new StringBuffer("Caught exception, while encrypting ").append(entry.getValue()).append(" attributes of Class ").append(entry.getKey()).append(" and restored table from backup"), e); 138 } 139 postDataLoadEncryptionService.dropBackupTable(businessObjectClass); 140 } 141 } 142 143}