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.ksb.messaging.web;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.struts.action.ActionErrors;
020import org.apache.struts.action.ActionForm;
021import org.apache.struts.action.ActionForward;
022import org.apache.struts.action.ActionMapping;
023import org.apache.struts.action.ActionMessages;
024import org.apache.struts.actions.DispatchAction;
025import org.kuali.rice.core.api.util.RiceConstants;
026import org.kuali.rice.coreservice.framework.parameter.ParameterService;
027import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
028import org.kuali.rice.kim.api.KimConstants;
029import org.kuali.rice.kim.api.services.KimApiServiceLocator;
030import org.kuali.rice.kns.util.WebUtils;
031import org.kuali.rice.kns.web.struts.form.KualiForm;
032import org.kuali.rice.krad.exception.AuthorizationException;
033import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
034import org.kuali.rice.krad.service.KualiModuleService;
035import org.kuali.rice.krad.service.ModuleService;
036import org.kuali.rice.krad.util.GlobalVariables;
037import org.kuali.rice.krad.util.KRADConstants;
038import org.kuali.rice.krad.util.KRADUtils;
039
040import javax.servlet.http.HttpServletRequest;
041import javax.servlet.http.HttpServletResponse;
042import java.util.HashMap;
043import java.util.Iterator;
044import java.util.Map;
045
046/**
047 * An abstract super class for all Struts Actions in KEW.  Adds some custom
048 * dispatch behavior by extending the Struts DispatchAction.
049 *
050 * @author Kuali Rice Team (rice.collab@kuali.org)
051 */
052public abstract class KSBAction extends DispatchAction {
053
054        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KSBAction.class);
055
056        @Override
057        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
058
059                checkAuthorization(form, "");
060                
061                if(isModuleLocked(form, findMethodToCall(form, request), request)) {
062                    return mapping.findForward(RiceConstants.MODULE_LOCKED_MAPPING);
063                }
064                
065                try {
066
067                        
068                        ActionMessages messages = null;
069                        messages = establishRequiredState(request, form);
070                        if (messages != null && !messages.isEmpty()) {
071                                // XXX: HACK: FIXME:
072                                // obviously this implies that we can't return both ActionErrors
073                                // and ActionMessages... :(
074                                // probably establishRequiredState should be refactored to have
075                                // a generic 'should-we-continue'
076                                // boolean return, so that control flow can be more explicitly
077                                // specified by the subclass
078                                if (messages instanceof ActionErrors) {
079                                        saveErrors(request, messages);
080                                } else {
081                                        saveMessages(request, messages);
082                                }
083                                return mapping.findForward("requiredStateError");
084                        }
085                        LOG.info(request.getQueryString());
086                        ActionForward returnForward = null;
087
088                        if (request.getParameterMap() != null) {
089                                for (Iterator iter = request.getParameterMap().entrySet().iterator(); iter.hasNext();) {
090                                        String parameterName = (String) ((Map.Entry) iter.next()).getKey();
091                                        if (parameterName.startsWith("methodToCall.") && parameterName.endsWith(".x")) {
092                                                String methodToCall = parameterName.substring(parameterName.indexOf("methodToCall.") + 13, parameterName.lastIndexOf(".x"));
093                                                if (methodToCall != null && methodToCall.length() > 0) {
094                                                        returnForward = this.dispatchMethod(mapping, form, request, response, methodToCall);
095                                                }
096                                        }
097                                }
098                        }
099                        if (returnForward == null) {
100                                if (request.getParameter("methodToCall") != null && !"".equals(request.getParameter("methodToCall")) && !"execute".equals(request.getParameter("methodToCall"))) {
101                                        LOG.info("dispatch to methodToCall " + request.getParameter("methodToCall") + " called");
102                                        returnForward = super.execute(mapping, form, request, response);
103                                } else {
104                                        LOG.info("dispatch to default start methodToCall");
105                                        returnForward = start(mapping, form, request, response);
106                                }
107                        }
108
109                        
110                        
111                        messages = establishFinalState(request, form);
112                        if (messages != null && !messages.isEmpty()) {
113                                saveMessages(request, messages);
114                                return mapping.findForward("finalStateError");
115                        }
116                        return returnForward;
117                } catch (Exception e) {
118                        LOG.error("Error processing action " + mapping.getPath(), e);
119                        throw new RuntimeException(e);
120                }
121        }
122        
123        protected void checkAuthorization( ActionForm form, String methodToCall) throws AuthorizationException 
124    {
125        String principalId = GlobalVariables.getUserSession().getPrincipalId();
126        Map<String, String> roleQualifier = new HashMap<String, String>(getRoleQualification(form, methodToCall));
127        Map<String, String> permissionDetails = KRADUtils.getNamespaceAndActionClass(this.getClass());
128        
129        if (!KimApiServiceLocator.getPermissionService().isAuthorizedByTemplate(principalId,
130                KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.USE_SCREEN, permissionDetails,
131                roleQualifier))
132        {
133                throw new AuthorizationException(GlobalVariables.getUserSession().getPrincipalName(), 
134                        methodToCall,
135                        this.getClass().getSimpleName());
136        }
137    }
138    
139    /** 
140     * override this method to add data from the form for role qualification in the authorization check
141     */
142    protected Map<String,String> getRoleQualification(ActionForm form, String methodToCall) {
143        return new HashMap<String,String>();
144    }
145
146        public abstract ActionForward start(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception;
147
148        public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
149                return start(mapping, form, request, response);
150        }
151
152        public abstract ActionMessages establishRequiredState(HttpServletRequest request, ActionForm form) throws Exception;
153
154        public ActionMessages establishFinalState(HttpServletRequest request, ActionForm form) throws Exception {
155                return null;
156        }
157
158        public ActionForward noOp(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
159                return mapping.findForward("basic");
160        }
161        
162        protected static KualiModuleService getKualiModuleService() {
163        return KRADServiceLocatorWeb.getKualiModuleService();
164    }
165        
166        protected String findMethodToCall(ActionForm form, HttpServletRequest request) throws Exception {
167            String methodToCall;
168            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getMethodToCall())) {
169                methodToCall = ((KualiForm) form).getMethodToCall();
170            }
171            else {
172                // call utility method to parse the methodToCall from the request.
173                methodToCall = WebUtils.parseMethodToCall(form, request);
174            }
175            return methodToCall;
176        }
177
178        protected boolean isModuleLocked(ActionForm form, String methodToCall, HttpServletRequest request) {
179            String boClass = request.getParameter(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE);
180            ModuleService moduleService = null;
181            if(StringUtils.isNotBlank(boClass)) {
182                try {
183                    moduleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(Class.forName(boClass));
184                } catch (ClassNotFoundException e) {
185                    LOG.warn("Module locking mechanism experienced a class not found exception while trying to load " + boClass, e);
186                }
187            } else {
188                moduleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(this.getClass());
189            }
190            if(moduleService != null && moduleService.isLocked()) {
191                String principalId = GlobalVariables.getUserSession().getPrincipalId();
192                String namespaceCode = KRADConstants.KUALI_RICE_SYSTEM_NAMESPACE;
193            String permissionName = KimConstants.PermissionNames.ACCESS_LOCKED_MODULE;
194                Map<String, String> permissionDetails = new HashMap<String, String>();
195                Map<String, String> qualification = new HashMap<String, String>(getRoleQualification(form, methodToCall));
196                if(!KimApiServiceLocator.getPermissionService().isAuthorized(principalId, namespaceCode, permissionName, qualification)) {
197                ParameterService parameterSerivce = CoreFrameworkServiceLocator.getParameterService();
198                String messageParamNamespaceCode = moduleService.getModuleConfiguration().getNamespaceCode();
199                String messageParamComponentCode = KRADConstants.DetailTypes.ALL_DETAIL_TYPE;
200                String messageParamName = KRADConstants.SystemGroupParameterNames.OLTP_LOCKOUT_MESSAGE_PARM;
201                String lockoutMessage = parameterSerivce.getParameterValueAsString(messageParamNamespaceCode, messageParamComponentCode, messageParamName);
202                
203                if(StringUtils.isBlank(lockoutMessage)) {
204                    String defaultMessageParamName = KRADConstants.SystemGroupParameterNames.OLTP_LOCKOUT_DEFAULT_MESSAGE;
205                    lockoutMessage = parameterSerivce.getParameterValueAsString(KRADConstants.KNS_NAMESPACE, messageParamComponentCode, defaultMessageParamName);
206                }
207                request.setAttribute(KRADConstants.MODULE_LOCKED_MESSAGE_REQUEST_PARAMETER, lockoutMessage);
208                return true;
209            }
210            }
211            return false;
212        }
213}