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            return isAuthorizedByTemplate(document, KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
183                    KimConstants.PermissionTemplateNames.CANCEL_DOCUMENT, user.getPrincipalId());
184        }
185    
186        public boolean canRecall(Document document, Person user) {
187            return KewApiServiceLocator.getWorkflowDocumentActionsService().determineValidActions(document.getDocumentNumber(), user.getPrincipalId()).getValidActions().contains(ActionType.RECALL);
188        }
189    
190        public boolean canCopy(Document document, Person user) {
191            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
192                    KimConstants.PermissionTemplateNames.COPY_DOCUMENT, user.getPrincipalId());
193        }
194    
195        public boolean canPerformRouteReport(Document document, Person user) {
196            return true;
197        }
198    
199        public boolean canBlanketApprove(Document document, Person user) {
200            return isAuthorizedByTemplate(document, KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
201                    KimConstants.PermissionTemplateNames.BLANKET_APPROVE_DOCUMENT, user.getPrincipalId());
202        }
203    
204        public boolean canApprove(Document document, Person user) {
205            return canTakeRequestedAction(document, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, user);
206        }
207    
208        public boolean canDisapprove(Document document, Person user) {
209            return canApprove(document, user);
210        }
211    
212        public boolean canSendNoteFyi(Document document, Person user) {
213            return canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_FYI_REQ, user);
214        }
215    
216        public boolean canFyi(Document document, Person user) {
217            return canTakeRequestedAction(document, KewApiConstants.ACTION_REQUEST_FYI_REQ, user);
218        }
219    
220        public boolean canAcknowledge(Document document, Person user) {
221            return canTakeRequestedAction(document, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, user);
222        }
223    
224        public boolean canReceiveAdHoc(Document document, Person user, String actionRequestCode) {
225            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
226            additionalPermissionDetails.put(KimConstants.AttributeConstants.ACTION_REQUEST_CD, actionRequestCode);
227            return isAuthorizedByTemplate(document, KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
228                    KimConstants.PermissionTemplateNames.AD_HOC_REVIEW_DOCUMENT, user.getPrincipalId(),
229                    additionalPermissionDetails, Collections.<String, String>emptyMap());
230        }
231    
232        public boolean canOpen(Document document, Person user) {
233            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
234                    KimConstants.PermissionTemplateNames.OPEN_DOCUMENT, user.getPrincipalId());
235        }
236    
237        public boolean canAddNoteAttachment(Document document, String attachmentTypeCode, Person user) {
238            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
239            if (attachmentTypeCode != null) {
240                additionalPermissionDetails.put(KimConstants.AttributeConstants.ATTACHMENT_TYPE_CODE, attachmentTypeCode);
241            }
242            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
243                    KimConstants.PermissionTemplateNames.ADD_NOTE_ATTACHMENT, user.getPrincipalId(),
244                    additionalPermissionDetails, Collections.<String, String>emptyMap());
245        }
246    
247        public boolean canDeleteNoteAttachment(Document document, String attachmentTypeCode, String createdBySelfOnly,
248                Person user) {
249            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
250            if (attachmentTypeCode != null) {
251                additionalPermissionDetails.put(KimConstants.AttributeConstants.ATTACHMENT_TYPE_CODE, attachmentTypeCode);
252            }
253            additionalPermissionDetails.put(KimConstants.AttributeConstants.CREATED_BY_SELF, createdBySelfOnly);
254            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
255                    KimConstants.PermissionTemplateNames.DELETE_NOTE_ATTACHMENT, user.getPrincipalId(),
256                    additionalPermissionDetails, Collections.<String, String>emptyMap());
257        }
258    
259        public boolean canViewNoteAttachment(Document document, String attachmentTypeCode, Person user) {
260            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
261            if (attachmentTypeCode != null) {
262                additionalPermissionDetails.put(KimConstants.AttributeConstants.ATTACHMENT_TYPE_CODE, attachmentTypeCode);
263            }
264            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
265                    KimConstants.PermissionTemplateNames.VIEW_NOTE_ATTACHMENT, user.getPrincipalId(),
266                    additionalPermissionDetails, Collections.<String, String>emptyMap());
267        }
268    
269        public boolean canViewNoteAttachment(Document document, String attachmentTypeCode, String authorUniversalIdentifier,
270                Person user) {
271            return canViewNoteAttachment(document, attachmentTypeCode, user);
272        }
273    
274        public boolean canSendAdHocRequests(Document document, String actionRequestCd, Person user) {
275            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
276            if (actionRequestCd != null) {
277                additionalPermissionDetails.put(KimConstants.AttributeConstants.ACTION_REQUEST_CD, actionRequestCd);
278            }
279            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
280                    KimConstants.PermissionTemplateNames.SEND_AD_HOC_REQUEST, user.getPrincipalId(),
281                    additionalPermissionDetails, Collections.<String, String>emptyMap());
282        }
283    
284        public boolean canEditDocumentOverview(Document document, Person user) {
285            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
286                    KimConstants.PermissionTemplateNames.EDIT_DOCUMENT, user.getPrincipalId()) && this.isDocumentInitiator(
287                    document, user);
288        }
289    
290        public boolean canSendAnyTypeAdHocRequests(Document document, Person user) {
291            if (canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_FYI_REQ, user)) {
292                RoutePath routePath = KewApiServiceLocator.getDocumentTypeService().getRoutePathForDocumentTypeName(
293                        document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
294                ProcessDefinition processDefinition = routePath.getPrimaryProcess();
295                if (processDefinition != null) {
296                    if (processDefinition.getInitialRouteNode() == null) {
297                        return false;
298                    }
299                } else {
300                    return false;
301                }
302                return true;
303            } else if (canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, user)) {
304                return true;
305            }
306            return canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, user);
307        }
308    
309        public boolean canTakeRequestedAction(Document document, String actionRequestCode, Person user) {
310            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
311            additionalPermissionDetails.put(KimConstants.AttributeConstants.ACTION_REQUEST_CD, actionRequestCode);
312            return isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE,
313                    KimConstants.PermissionTemplateNames.TAKE_REQUESTED_ACTION, user.getPrincipalId(),
314                    additionalPermissionDetails, Collections.<String, String>emptyMap());
315        }
316    
317        @Override
318        protected void addPermissionDetails(Object dataObject, Map<String, String> attributes) {
319            super.addPermissionDetails(dataObject, attributes);
320            if (dataObject instanceof Document) {
321                addStandardAttributes((Document) dataObject, attributes);
322            }
323        }
324    
325        @Override
326        protected void addRoleQualification(Object dataObject, Map<String, String> attributes) {
327            super.addRoleQualification(dataObject, attributes);
328            if (dataObject instanceof Document) {
329                addStandardAttributes((Document) dataObject, attributes);
330            }
331        }
332    
333        protected void addStandardAttributes(Document document, Map<String, String> attributes) {
334            WorkflowDocument wd = document.getDocumentHeader().getWorkflowDocument();
335            attributes.put(KimConstants.AttributeConstants.DOCUMENT_NUMBER, document.getDocumentNumber());
336            attributes.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, wd.getDocumentTypeName());
337            if (wd.isInitiated() || wd.isSaved()) {
338                attributes.put(KimConstants.AttributeConstants.ROUTE_NODE_NAME, PRE_ROUTING_ROUTE_NAME);
339            } else {
340                attributes.put(KimConstants.AttributeConstants.ROUTE_NODE_NAME,
341                        KRADServiceLocatorWeb.getWorkflowDocumentService().getCurrentRouteNodeNames(wd));
342            }
343            attributes.put(KimConstants.AttributeConstants.ROUTE_STATUS_CODE, wd.getStatus().getCode());
344        }
345    
346        protected boolean isDocumentInitiator(Document document, Person user) {
347            WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
348            return workflowDocument.getInitiatorPrincipalId().equalsIgnoreCase(user.getPrincipalId());
349        }
350    }