001    /**
002     * Copyright 2005-2012 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.ksb.messaging.web;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.apache.struts.action.ActionErrors;
020    import org.apache.struts.action.ActionForm;
021    import org.apache.struts.action.ActionForward;
022    import org.apache.struts.action.ActionMapping;
023    import org.apache.struts.action.ActionMessages;
024    import org.apache.struts.actions.DispatchAction;
025    import org.kuali.rice.core.api.util.RiceConstants;
026    import org.kuali.rice.coreservice.framework.parameter.ParameterService;
027    import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
028    import org.kuali.rice.kim.api.KimConstants;
029    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
030    import org.kuali.rice.kns.util.WebUtils;
031    import org.kuali.rice.kns.web.struts.form.KualiForm;
032    import org.kuali.rice.krad.exception.AuthorizationException;
033    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
034    import org.kuali.rice.krad.service.KualiModuleService;
035    import org.kuali.rice.krad.service.ModuleService;
036    import org.kuali.rice.krad.util.GlobalVariables;
037    import org.kuali.rice.krad.util.KRADConstants;
038    import org.kuali.rice.krad.util.KRADUtils;
039    
040    import javax.servlet.http.HttpServletRequest;
041    import javax.servlet.http.HttpServletResponse;
042    import java.util.HashMap;
043    import java.util.Iterator;
044    import 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     */
052    public 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    }