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.krad.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.core.api.resourceloader.GlobalResourceLoader;
22  import org.kuali.rice.krad.bo.PersistableBusinessObject;
23  import org.kuali.rice.krad.service.BusinessObjectService;
24  import org.kuali.rice.krad.service.KRADServiceLocator;
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   * 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.krad.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  public class PostDataLoadEncryptionServlet extends HttpServlet {
77  	private static final Log LOG = LogFactory.getLog(PostDataLoadEncryptionServlet.class);
78  	
79  	private static final String ATTRIBUTES_TO_ENCRYPT_PROPERTIES = "attributesToEncryptProperties";
80  	private static final String CHECK_OJB_ENCRYPT_CONFIG = "checkOjbEncryptConfig";
81  	
82  	@Override
83  	public void service(ServletRequest request, ServletResponse response)
84  			throws ServletException, IOException {
85  		String attributesToEncryptPropertyFileName = request.getParameter(ATTRIBUTES_TO_ENCRYPT_PROPERTIES);
86  		if (StringUtils.isBlank(attributesToEncryptPropertyFileName)) {
87  			throw new IllegalArgumentException("No valid " + ATTRIBUTES_TO_ENCRYPT_PROPERTIES + " parameter was passed to this Servlet.");
88  		}
89  		boolean checkOjbEncryptConfig = true;
90  		String checkOjbEncryptConfigValue = request.getParameter(CHECK_OJB_ENCRYPT_CONFIG);
91  		if (!StringUtils.isBlank(checkOjbEncryptConfigValue)) {
92  			checkOjbEncryptConfig = Boolean.valueOf(checkOjbEncryptConfigValue).booleanValue();
93  		}
94  		execute(attributesToEncryptPropertyFileName, checkOjbEncryptConfig);
95  		response.getOutputStream().write(("<html><body><p>Successfully encrypted attributes as defined in: " + attributesToEncryptPropertyFileName + "</p></body></html>").getBytes());
96  	}
97  
98  	public void execute(String attributesToEncryptPropertyFileName, boolean checkOjbEncryptConfig) {
99  		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 }