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.kns.document.authorization;
017
018import org.apache.commons.logging.Log;
019import org.apache.commons.logging.LogFactory;
020import org.kuali.rice.kew.api.KewApiConstants;
021import org.kuali.rice.kew.api.KewApiServiceLocator;
022import org.kuali.rice.kew.api.WorkflowDocument;
023import org.kuali.rice.kew.api.action.ActionType;
024import org.kuali.rice.kew.api.doctype.ProcessDefinition;
025import org.kuali.rice.kew.api.doctype.RoutePath;
026import org.kuali.rice.kim.api.KimConstants;
027import org.kuali.rice.kim.api.identity.Person;
028import org.kuali.rice.kns.bo.authorization.BusinessObjectAuthorizerBase;
029import org.kuali.rice.krad.document.Document;
030import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
031import org.kuali.rice.krad.util.KRADConstants;
032
033import java.util.Collections;
034import java.util.HashMap;
035import java.util.Map;
036import java.util.Set;
037
038/**
039 * DocumentAuthorizer containing common, reusable document-level authorization
040 * code.
041 */
042public 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}