View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.devtools.pdle;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.kuali.rice.krad.bo.PersistableBusinessObject;
22  import org.kuali.rice.krad.service.BusinessObjectService;
23  import org.kuali.rice.krad.service.KRADServiceLocator;
24  import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
25  import org.springframework.core.io.FileSystemResource;
26  
27  import javax.servlet.ServletException;
28  import javax.servlet.ServletRequest;
29  import javax.servlet.ServletResponse;
30  import javax.servlet.http.HttpServlet;
31  import java.io.IOException;
32  import java.util.Arrays;
33  import java.util.Collection;
34  import java.util.HashSet;
35  import java.util.Map;
36  import java.util.Properties;
37  import java.util.Set;
38  
39  /**
40   * This is a servlet that can be used to invoke the PostDataLoadEncryptionService.
41   * 
42   * It is not recommended to leave this Servlet running at all times.  It is really only intended
43   * to be made available during initial data load and then removed (from the web.xml of the
44   * application) after data load and encryption is complete.
45   * 
46   * This was done as a Servlet for now because Rice does not have a batch runner yet similar
47   * to what KFS has (which is where a lot of the code below was borrowed from).
48   *
49   *
50   *  <code>
51  
52     <!--
53  		Add the following to your web.xml these if you want to run the servlet in order to encrypt
54          some data in the Rice database.
55  		(this would most likely be used for external identifiers in KIM that require encryption)
56  
57  		It is not recommended to enable this service for a production instance except during initial
58  		data load.  It is not secured in any way and would allow for easy corruption of data if mishandled.
59  	-->
60  
61  	<servlet>
62  	    <servlet-name>postDataLoadEncryption</servlet-name>
63  	    <servlet-class>org.kuali.rice.devtools.pdle.PostDataLoadEncryptionServlet</servlet-class>
64  	</servlet>
65  
66  	<servlet-mapping>
67  		<servlet-name>postDataLoadEncryption</servlet-name>
68  		<url-pattern>/postDataLoadEncryption</url-pattern>
69  	</servlet-mapping>
70  
71      </code>
72   *
73   *
74   * @author Kuali Rice Team (rice.collab@kuali.org)
75   *
76   */
77  public class PostDataLoadEncryptionServlet extends HttpServlet {
78  
79  	private static final Log LOG = LogFactory.getLog(PostDataLoadEncryptionServlet.class);
80  	
81  	private static final String ATTRIBUTES_TO_ENCRYPT_PROPERTIES = "attributesToEncryptProperties";
82  	private static final String CHECK_OJB_ENCRYPT_CONFIG = "checkOjbEncryptConfig";
83  	
84  	@Override
85  	public void service(ServletRequest request, ServletResponse response)
86  			throws ServletException, IOException {
87  		String attributesToEncryptPropertyFileName = request.getParameter(ATTRIBUTES_TO_ENCRYPT_PROPERTIES);
88  		if (StringUtils.isBlank(attributesToEncryptPropertyFileName)) {
89  			throw new IllegalArgumentException("No valid " + ATTRIBUTES_TO_ENCRYPT_PROPERTIES + " parameter was passed to this Servlet.");
90  		}
91  		boolean checkOjbEncryptConfig = true;
92  		String checkOjbEncryptConfigValue = request.getParameter(CHECK_OJB_ENCRYPT_CONFIG);
93  		if (!StringUtils.isBlank(checkOjbEncryptConfigValue)) {
94  			checkOjbEncryptConfig = Boolean.valueOf(checkOjbEncryptConfigValue).booleanValue();
95  		}
96  		execute(attributesToEncryptPropertyFileName, checkOjbEncryptConfig);
97  		response.getOutputStream().write(("<html><body><p>Successfully encrypted attributes as defined in: " + attributesToEncryptPropertyFileName + "</p></body></html>").getBytes());
98  	}
99  
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 }