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.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.krad.bo.PersistableBusinessObject;
022import org.kuali.rice.krad.service.BusinessObjectService;
023import org.kuali.rice.krad.service.KRADServiceLocator;
024import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
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 * 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 */
077public 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}