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}