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    }