001 /** 002 * Copyright 2005-2014 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 */ 016 package org.kuali.rice.devtools.pdle; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.apache.commons.logging.Log; 020 import org.apache.commons.logging.LogFactory; 021 import org.kuali.rice.krad.bo.PersistableBusinessObject; 022 import org.kuali.rice.krad.service.BusinessObjectService; 023 import org.kuali.rice.krad.service.KRADServiceLocator; 024 import org.kuali.rice.krad.service.KRADServiceLocatorInternal; 025 import org.springframework.core.io.FileSystemResource; 026 027 import javax.servlet.ServletException; 028 import javax.servlet.ServletRequest; 029 import javax.servlet.ServletResponse; 030 import javax.servlet.http.HttpServlet; 031 import java.io.IOException; 032 import java.util.Arrays; 033 import java.util.Collection; 034 import java.util.HashSet; 035 import java.util.Map; 036 import java.util.Properties; 037 import java.util.Set; 038 039 /** 040 * This is a 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.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 * 076 */ 077 public class PostDataLoadEncryptionServlet extends HttpServlet { 078 079 private static final Log LOG = LogFactory.getLog(PostDataLoadEncryptionServlet.class); 080 081 private static final String ATTRIBUTES_TO_ENCRYPT_PROPERTIES = "attributesToEncryptProperties"; 082 private static final String CHECK_OJB_ENCRYPT_CONFIG = "checkOjbEncryptConfig"; 083 084 @Override 085 public void service(ServletRequest request, ServletResponse response) 086 throws ServletException, IOException { 087 String attributesToEncryptPropertyFileName = request.getParameter(ATTRIBUTES_TO_ENCRYPT_PROPERTIES); 088 if (StringUtils.isBlank(attributesToEncryptPropertyFileName)) { 089 throw new IllegalArgumentException("No valid " + ATTRIBUTES_TO_ENCRYPT_PROPERTIES + " parameter was passed to this Servlet."); 090 } 091 boolean checkOjbEncryptConfig = true; 092 String checkOjbEncryptConfigValue = request.getParameter(CHECK_OJB_ENCRYPT_CONFIG); 093 if (!StringUtils.isBlank(checkOjbEncryptConfigValue)) { 094 checkOjbEncryptConfig = Boolean.valueOf(checkOjbEncryptConfigValue).booleanValue(); 095 } 096 execute(attributesToEncryptPropertyFileName, checkOjbEncryptConfig); 097 response.getOutputStream().write(("<html><body><p>Successfully encrypted attributes as defined in: " + attributesToEncryptPropertyFileName + "</p></body></html>").getBytes()); 098 } 099 100 public void execute(String attributesToEncryptPropertyFileName, boolean checkOjbEncryptConfig) { 101 PostDataLoadEncryptionService postDataLoadEncryptionService = KRADServiceLocatorInternal.getService(PostDataLoadEncryptionService.POST_DATA_LOAD_ENCRYPTION_SERVICE); 102 Properties attributesToEncryptProperties = new Properties(); 103 try { 104 attributesToEncryptProperties.load(new FileSystemResource(attributesToEncryptPropertyFileName).getInputStream()); 105 } 106 catch (Exception e) { 107 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); 108 } 109 for (Map.Entry<Object, Object> entry : attributesToEncryptProperties.entrySet()) { 110 Class<? extends PersistableBusinessObject> businessObjectClass; 111 try { 112 businessObjectClass = (Class<? extends PersistableBusinessObject>) Class.forName((String) entry.getKey()); 113 } 114 catch (Exception e) { 115 throw new IllegalArgumentException(new StringBuffer("Unable to load Class ").append((String) entry.getKey()).append(" specified by name in attributesToEncryptProperties file ").append(attributesToEncryptProperties).toString(), e); 116 } 117 final Set<String> attributeNames; 118 try { 119 attributeNames = new HashSet<String>(Arrays.asList(StringUtils.split((String) entry.getValue(), ","))); 120 } 121 catch (Exception e) { 122 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); 123 } 124 postDataLoadEncryptionService.checkArguments(businessObjectClass, attributeNames, checkOjbEncryptConfig); 125 postDataLoadEncryptionService.createBackupTable(businessObjectClass); 126 BusinessObjectService businessObjectService = KRADServiceLocator.getBusinessObjectService(); 127 try { 128 postDataLoadEncryptionService.prepClassDescriptor(businessObjectClass, attributeNames); 129 Collection<? extends PersistableBusinessObject> objectsToEncrypt = businessObjectService.findAll(businessObjectClass); 130 for (Object businessObject : objectsToEncrypt) { 131 postDataLoadEncryptionService.encrypt((PersistableBusinessObject) businessObject, attributeNames); 132 } 133 postDataLoadEncryptionService.restoreClassDescriptor(businessObjectClass, attributeNames); 134 LOG.info(new StringBuffer("Encrypted ").append(entry.getValue()).append(" attributes of Class ").append(entry.getKey())); 135 } 136 catch (Exception e) { 137 postDataLoadEncryptionService.restoreTableFromBackup(businessObjectClass); 138 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); 139 } 140 postDataLoadEncryptionService.dropBackupTable(businessObjectClass); 141 } 142 } 143 144 }