001    /**
002     * Copyright 2005-2013 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.kns.document.authorization;
017    
018    import org.apache.commons.logging.Log;
019    import org.apache.commons.logging.LogFactory;
020    import org.kuali.rice.kew.api.KewApiConstants;
021    import org.kuali.rice.kew.api.KewApiServiceLocator;
022    import org.kuali.rice.kew.api.WorkflowDocument;
023    import org.kuali.rice.kew.api.action.ActionType;
024    import org.kuali.rice.kew.api.doctype.ProcessDefinition;
025    import org.kuali.rice.kew.api.doctype.RoutePath;
026    import org.kuali.rice.kim.api.KimConstants;
027    import org.kuali.rice.kim.api.identity.Person;
028    import org.kuali.rice.kns.bo.authorization.BusinessObjectAuthorizerBase;
029    import org.kuali.rice.krad.document.Document;
030    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
031    import org.kuali.rice.krad.util.KRADConstants;
032    
033    import java.util.Collections;
034    import java.util.HashMap;
035    import java.util.Map;
036    import java.util.Set;
037    
038    /**
039     * DocumentAuthorizer containing common, reusable document-level authorization
040     * code.
041     */
042    public class DocumentAuthorizerBase extends BusinessObjectAuthorizerBase implements DocumentAuthorizer {
043        protected static Log LOG = LogFactory.getLog(DocumentAuthorizerBase.class);
044    
045        public static final String PRE_ROUTING_ROUTE_NAME = "PreRoute";
046        public static final String EDIT_MODE_DEFAULT_TRUE_VALUE = "TRUE";
047        public static final String USER_SESSION_METHOD_TO_CALL_OBJECT_KEY = "METHOD_TO_CALL_KEYS_METHOD_OBJECT_KEY";
048        public static final String USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY =
049                "METHOD_TO_CALL_KEYS_COMPLETE_OBJECT_KEY";
050        public static final String USER_SESSION_METHOD_TO_CALL_COMPLETE_MARKER = "_EXITING";
051    
052        /**
053         * Individual document families will need to reimplement this according to
054         * their own needs; this version should be good enough to be usable during
055         * initial development.
056         */
057        public Set<String> getDocumentActions(Document document, Person user, Set<String> documentActions) {
058            if (LOG.isDebugEnabled()) {
059                LOG.debug("calling DocumentAuthorizerBase.getDocumentActionFlags for document '"
060                        + document.getDocumentNumber()
061                        + "'. user '"
062                        + user.getPrincipalName()
063                        + "'");
064            }
065            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_EDIT) && !canEdit(document, user)) {
066                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_EDIT);
067            }
068    
069            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_COPY) && !canCopy(document, user)) {
070                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_COPY);
071            }
072    
073            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_CLOSE) && !canClose(document, user)) {
074                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_CLOSE);
075            }
076    
077            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_RELOAD) && !canReload(document, user)) {
078                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_RELOAD);
079            }
080    
081            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE) && !canBlanketApprove(document, user)) {
082                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE);
083            }
084    
085            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_CANCEL) && !canCancel(document, user)) {
086                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_CANCEL);
087            }
088    
089            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_RECALL) && !canRecall(document, user)) {
090                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_RECALL);
091            }
092    
093            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_SAVE) && !canSave(document, user)) {
094                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_SAVE);
095            }
096    
097            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_ROUTE) && !canRoute(document, user)) {
098                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_ROUTE);
099            }
100    
101            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_ACKNOWLEDGE) && !canAcknowledge(document, user)) {
102                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_ACKNOWLEDGE);
103            }
104    
105            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_FYI) && !canFyi(document, user)) {
106                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_FYI);
107            }
108    
109            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_APPROVE) && !canApprove(document, user)) {
110                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_APPROVE);
111            }
112    
113            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_DISAPPROVE) && !canDisapprove(document, user)) {
114                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_DISAPPROVE);
115            }
116    
117            if (!canSendAnyTypeAdHocRequests(document, user)) {
118                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_ADD_ADHOC_REQUESTS);
119                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_SEND_ADHOC_REQUESTS);
120                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_SEND_NOTE_FYI);
121            }
122    
123            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_SEND_NOTE_FYI) && !canSendNoteFyi(document, user)) {
124                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_SEND_NOTE_FYI);
125            }
126    
127            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_ANNOTATE) && !canAnnotate(document, user)) {
128                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_ANNOTATE);
129            }
130    
131            if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_EDIT_DOCUMENT_OVERVIEW) && !canEditDocumentOverview(
132                    document, user)) {
133                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_EDIT_DOCUMENT_OVERVIEW);
134            }
135    
136            if (documentActions.contains(KRADConstants.KUALI_ACTION_PERFORM_ROUTE_REPORT) && !canPerformRouteReport(document,
137                    user)) {
138                documentActions.remove(KRADConstants.KUALI_ACTION_PERFORM_ROUTE_REPORT);
139            }
140    
141            return documentActions;
142        }
143    
144        public boolean canInitiate(String documentTypeName, Person user) {
145            String nameSpaceCode = KRADConstants.KUALI_RICE_SYSTEM_NAMESPACE;
146            Map<String, String> permissionDetails = new HashMap<String, String>();
147            permissionDetails.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, documentTypeName);
148            return getPermissionService().isAuthorizedByTemplate(user.getPrincipalId(), nameSpaceCode,
149                    KimConstants.PermissionTemplateNames.INITIATE_DOCUMENT, permissionDetails,
150                    Collections.<String, String>emptyMap());
151        }
152    
153        public boolean canEdit(Document document, Person user) {
154            // KULRICE-7864: document can be editable on adhoc route for completion 
155            return document.getDocumentHeader().getWorkflowDocument().isCompletionRequested()
156                    || isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.EDIT_DOCUMENT, user.getPrincipalId());
157        }
158    
159        public boolean canAnnotate(Document document, Person user) {
160            return canEdit(document, user);
161        }
162    
163        public boolean canReload(Document document, Person user) {
164            return true;
165        }
166    
167        public boolean canClose(Document document, Person user) {
168            return true;
169        }
170    
171        public boolean canSave(Document document, Person user) {
172            return isAuthorizedByTemplate(document, KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
173                    KimConstants.PermissionTemplateNames.SAVE_DOCUMENT, user.getPrincipalId());
174        }
175    
176        public boolean canRoute(Document document, Person user) {
177            return isAuthorizedByTemplate(document, KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
178                    KimConstants.PermissionTemplateNames.ROUTE_DOCUMENT, user.getPrincipalId());
179        }
180    
181        public boolean canCancel(Document document, Person user) {
182            // KULRICE-8762: CANCEL button should be enabled for a person who is doing COMPLETE action 
183            boolean isCompletionRequested = document.getDocumentHeader().getWorkflowDocument().isCompletionRequested();
184            return isCompletionRequested || isAuthorizedByTemplate(document, KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
185                    KimConstants.PermissionTemplateNames.CANCEL_DOCUMENT, user.getPrincipalId());
186        }
187    
188        public boolean canRecall(Document document, Person user) {
189            return KewApiServiceLocator.getWorkflowDocumentActionsService().determineValidActions(document.getDocumentNumber(), user.getPrincipalId()).getValidActions().contains(ActionType.RECALL);
190        }
191    
192        public boolean canCopy(Document document, Person user) {
193            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
194                    KimConstants.PermissionTemplateNames.COPY_DOCUMENT, user.getPrincipalId());
195        }
196    
197        public boolean canPerformRouteReport(Document document, Person user) {
198            return true;
199        }
200    
201        public boolean canBlanketApprove(Document document, Person user) {
202            return isAuthorizedByTemplate(document, KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
203                    KimConstants.PermissionTemplateNames.BLANKET_APPROVE_DOCUMENT, user.getPrincipalId());
204        }
205    
206        public boolean canApprove(Document document, Person user) {
207            return canTakeRequestedAction(document, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, user);
208        }
209    
210        public boolean canDisapprove(Document document, Person user) {
211            return canApprove(document, user);
212        }
213    
214        public boolean canSendNoteFyi(Document document, Person user) {
215            return canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_FYI_REQ, user);
216        }
217    
218        public boolean canFyi(Document document, Person user) {
219            return canTakeRequestedAction(document, KewApiConstants.ACTION_REQUEST_FYI_REQ, user);
220        }
221    
222        public boolean canAcknowledge(Document document, Person user) {
223            return canTakeRequestedAction(document, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, user);
224        }
225    
226        public boolean canReceiveAdHoc(Document document, Person user, String actionRequestCode) {
227            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
228            additionalPermissionDetails.put(KimConstants.AttributeConstants.ACTION_REQUEST_CD, actionRequestCode);
229            return isAuthorizedByTemplate(document, KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
230                    KimConstants.PermissionTemplateNames.AD_HOC_REVIEW_DOCUMENT, user.getPrincipalId(),
231                    additionalPermissionDetails, Collections.<String, String>emptyMap());
232        }
233    
234        public boolean canOpen(Document document, Person user) {
235            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
236                    KimConstants.PermissionTemplateNames.OPEN_DOCUMENT, user.getPrincipalId());
237        }
238    
239        public boolean canAddNoteAttachment(Document document, String attachmentTypeCode, Person user) {
240            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
241            if (attachmentTypeCode != null) {
242                additionalPermissionDetails.put(KimConstants.AttributeConstants.ATTACHMENT_TYPE_CODE, attachmentTypeCode);
243            }
244            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
245                    KimConstants.PermissionTemplateNames.ADD_NOTE_ATTACHMENT, user.getPrincipalId(),
246                    additionalPermissionDetails, Collections.<String, String>emptyMap());
247        }
248    
249        public boolean canDeleteNoteAttachment(Document document, String attachmentTypeCode, String createdBySelfOnly,
250                Person user) {
251            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
252            if (attachmentTypeCode != null) {
253                additionalPermissionDetails.put(KimConstants.AttributeConstants.ATTACHMENT_TYPE_CODE, attachmentTypeCode);
254            }
255            additionalPermissionDetails.put(KimConstants.AttributeConstants.CREATED_BY_SELF, createdBySelfOnly);
256            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
257                    KimConstants.PermissionTemplateNames.DELETE_NOTE_ATTACHMENT, user.getPrincipalId(),
258                    additionalPermissionDetails, Collections.<String, String>emptyMap());
259        }
260    
261        public boolean canViewNoteAttachment(Document document, String attachmentTypeCode, Person user) {
262            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
263            if (attachmentTypeCode != null) {
264                additionalPermissionDetails.put(KimConstants.AttributeConstants.ATTACHMENT_TYPE_CODE, attachmentTypeCode);
265            }
266            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
267                    KimConstants.PermissionTemplateNames.VIEW_NOTE_ATTACHMENT, user.getPrincipalId(),
268                    additionalPermissionDetails, Collections.<String, String>emptyMap());
269        }
270    
271        public boolean canViewNoteAttachment(Document document, String attachmentTypeCode, String authorUniversalIdentifier,
272                Person user) {
273            return canViewNoteAttachment(document, attachmentTypeCode, user);
274        }
275    
276        public boolean canSendAdHocRequests(Document document, String actionRequestCd, Person user) {
277            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
278            if (actionRequestCd != null) {
279                additionalPermissionDetails.put(KimConstants.AttributeConstants.ACTION_REQUEST_CD, actionRequestCd);
280            }
281            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
282                    KimConstants.PermissionTemplateNames.SEND_AD_HOC_REQUEST, user.getPrincipalId(),
283                    additionalPermissionDetails, Collections.<String, String>emptyMap());
284        }
285    
286        public boolean canEditDocumentOverview(Document document, Person user) {
287            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
288                    KimConstants.PermissionTemplateNames.EDIT_DOCUMENT, user.getPrincipalId()) && this.isDocumentInitiator(
289                    document, user);
290        }
291    
292        public boolean canSendAnyTypeAdHocRequests(Document document, Person user) {
293            if (canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_FYI_REQ, user)) {
294                RoutePath routePath = KewApiServiceLocator.getDocumentTypeService().getRoutePathForDocumentTypeName(
295                        document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
296                ProcessDefinition processDefinition = routePath.getPrimaryProcess();
297                if (processDefinition != null) {
298                    if (processDefinition.getInitialRouteNode() == null) {
299                        return false;
300                    }
301                } else {
302                    return false;
303                }
304                return true;
305            } else if (canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, user)) {
306                return true;
307            }
308            return canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, user);
309        }
310    
311        public boolean canTakeRequestedAction(Document document, String actionRequestCode, Person user) {
312            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
313            additionalPermissionDetails.put(KimConstants.AttributeConstants.ACTION_REQUEST_CD, actionRequestCode);
314            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
315                    KimConstants.PermissionTemplateNames.TAKE_REQUESTED_ACTION, user.getPrincipalId(),
316                    additionalPermissionDetails, Collections.<String, String>emptyMap());
317        }
318    
319        @Override
320        protected void addPermissionDetails(Object dataObject, Map<String, String> attributes) {
321            super.addPermissionDetails(dataObject, attributes);
322            if (dataObject instanceof Document) {
323                addStandardAttributes((Document) dataObject, attributes);
324            }
325        }
326    
327        @Override
328        protected void addRoleQualification(Object dataObject, Map<String, String> attributes) {
329            super.addRoleQualification(dataObject, attributes);
330            if (dataObject instanceof Document) {
331                addStandardAttributes((Document) dataObject, attributes);
332            }
333        }
334    
335        protected void addStandardAttributes(Document document, Map<String, String> attributes) {
336            WorkflowDocument wd = document.getDocumentHeader().getWorkflowDocument();
337            attributes.put(KimConstants.AttributeConstants.DOCUMENT_NUMBER, document.getDocumentNumber());
338            attributes.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, wd.getDocumentTypeName());
339            if (wd.isInitiated() || wd.isSaved()) {
340                attributes.put(KimConstants.AttributeConstants.ROUTE_NODE_NAME, PRE_ROUTING_ROUTE_NAME);
341            } else {
342                attributes.put(KimConstants.AttributeConstants.ROUTE_NODE_NAME,
343                        KRADServiceLocatorWeb.getWorkflowDocumentService().getCurrentRouteNodeNames(wd));
344            }
345            attributes.put(KimConstants.AttributeConstants.ROUTE_STATUS_CODE, wd.getStatus().getCode());
346        }
347    
348        protected boolean isDocumentInitiator(Document document, Person user) {
349            WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
350            return workflowDocument.getInitiatorPrincipalId().equalsIgnoreCase(user.getPrincipalId());
351        }
352    }