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.kew.doctype.service.impl;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.log4j.Logger;
020import org.kuali.rice.kew.api.WorkflowRuntimeException;
021import org.kuali.rice.kew.api.action.ActionType;
022import org.kuali.rice.kew.api.document.Document;
023import org.kuali.rice.kew.api.extension.ExtensionDefinition;
024import org.kuali.rice.kew.api.extension.ExtensionUtils;
025import org.kuali.rice.kew.doctype.bo.DocumentType;
026import org.kuali.rice.kew.doctype.service.DocumentTypePermissionService;
027import org.kuali.rice.kew.engine.node.RouteNodeInstance;
028import org.kuali.rice.kew.framework.document.security.AuthorizableAction;
029import org.kuali.rice.kew.framework.document.security.DocumentTypeAuthorizer;
030import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
031
032import java.util.*;
033
034/**
035 * Implementation of {@link DocumentTypePermissionService} that delegates all calls (based on the
036 * {@link DocumentType} or {@link DocumentRouteHeaderValue} parameter to the method being called)
037 * <ul>
038 *     <li>to the DocumentTypeAuthorizer configured on the {@link org.kuali.rice.kew.doctype.bo.DocumentType} if there is one</li>
039 *     <li>otherwise, to the default {@link DocumentTypeAuthorizer} implementation</li>
040 * </ul>
041 */
042public class DocumentTypePermissionServiceAuthorizerImpl extends DocumentTypePermissionServiceImpl {
043    private static final Logger LOG = Logger.getLogger(DocumentTypePermissionServiceAuthorizerImpl.class);
044
045    /**
046     * Arbitrary placeholder strings for Extension lookup.  We are simply using the Extension loading convention
047     * to instantiate the DocumentTypeAuthorizor, so we do not need meaningful name or type parameters
048     */
049    private static final String PLACEHOLDER_EXTENSION_NAME = "DocumenTypeAuthorizer";
050    private static final String PLACEHOLDER_EXTENSION_TYPE = "DocumenTypeAuthorizer";
051
052    /**
053     * The default DocumentTypeAuthorizer implementation.  Kept as a singleton for performance.
054     */
055    protected DocumentTypeAuthorizer defaultDocumentTypeAuthorizer = new KimDocumentTypeAuthorizer();
056
057    /**
058     * Load the KimDocumentTypeAuthorizer for the specified document, or default impl if custom impl is not specified
059     * @param documentType the document type whose DocumentTypeAuthorizer to load
060     * @return a DocumentTypeAuthorizer impl
061     */
062    protected DocumentTypeAuthorizer getDocumentTypeAuthorizer(DocumentType documentType) {
063        DocumentTypeAuthorizer delegate = defaultDocumentTypeAuthorizer;
064
065        if (documentType == null) {
066            LOG.warn("DocumentType is null, using default DocumentTypeAuthorizer impl: " + delegate.getClass());
067        } else {
068            String documentTypeAuthorizer = documentType.getAuthorizer();
069
070            if (StringUtils.isNotBlank(documentTypeAuthorizer)) {
071                ExtensionDefinition extensionDef = ExtensionDefinition.Builder.create(PLACEHOLDER_EXTENSION_NAME, PLACEHOLDER_EXTENSION_TYPE, documentTypeAuthorizer).build();
072                Object extension = ExtensionUtils.loadExtension(extensionDef);
073
074                if (extension == null) {
075                    throw new WorkflowRuntimeException("Could not load DocumentTypeAuthorizer: " + documentTypeAuthorizer);
076                }
077
078                if (!DocumentTypeAuthorizer.class.isAssignableFrom(extension.getClass())) {
079                    throw new WorkflowRuntimeException("DocumentType Authorizer '" + documentTypeAuthorizer + "' configured for document type '" + documentType.getName() + " does not implement " + DocumentTypeAuthorizer.class.getName());
080                }
081
082                delegate = (DocumentTypeAuthorizer) extension;
083            }
084        }
085
086        return delegate;
087    }
088
089    @Override
090    public boolean canInitiate(String principalId, DocumentType documentType) {
091        return getDocumentTypeAuthorizer(documentType).isActionAuthorized(new AuthorizableAction(AuthorizableAction.CheckType.INITIATION), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(documentType).build(), null, Collections.EMPTY_MAP).isAuthorized();
092    }
093
094    @Override
095    public boolean canBlanketApprove(String principalId, DocumentRouteHeaderValue document) {
096        validateDocument(document);
097        return getDocumentTypeAuthorizer(document.getDocumentType()).isActionAuthorized(new AuthorizableAction(ActionType.BLANKET_APPROVE), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(document.getDocumentType()).build(), Document.Builder.create(document).build(), Collections.EMPTY_MAP).isAuthorized();
098    }
099
100    @Override
101    public boolean canCancel(String principalId, DocumentRouteHeaderValue document) {
102        validateDocument(document);
103        return getDocumentTypeAuthorizer(document.getDocumentType()).isActionAuthorized(new AuthorizableAction(ActionType.CANCEL), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(document.getDocumentType()).build(), Document.Builder.create(document).build(), Collections.EMPTY_MAP).isAuthorized();
104    }
105
106    @Override
107    public boolean canRecall(String principalId, DocumentRouteHeaderValue document) {
108        validateDocument(document);
109        return getDocumentTypeAuthorizer(document.getDocumentType()).isActionAuthorized(new AuthorizableAction(ActionType.RECALL), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(document.getDocumentType()).build(), Document.Builder.create(document).build(), Collections.EMPTY_MAP).isAuthorized();
110    }
111
112    @Override
113    public boolean canSave(String principalId, DocumentRouteHeaderValue document) {
114        validateDocument(document);
115        return getDocumentTypeAuthorizer(document.getDocumentType()).isActionAuthorized(new AuthorizableAction(ActionType.SAVE), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(document.getDocumentType()).build(), Document.Builder.create(document).build(), Collections.EMPTY_MAP).isAuthorized();
116    }
117
118    @Override
119    public boolean canRoute(String principalId, DocumentRouteHeaderValue document) {
120        validateDocument(document);
121        return getDocumentTypeAuthorizer(document.getDocumentType()).isActionAuthorized(new AuthorizableAction(ActionType.ROUTE), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(document.getDocumentType()).build(), Document.Builder.create(document).build(), Collections.EMPTY_MAP).isAuthorized();
122    }
123
124    @Override
125    public boolean canSuperUserApproveDocument(String principalId, DocumentType documentType, Collection<String> routeNodeNames, String routeStatusCode) {
126        Map<DocumentTypeAuthorizer.ActionArgument, Object> actionParams = new HashMap<DocumentTypeAuthorizer.ActionArgument, Object>();
127        actionParams.put(DocumentTypeAuthorizer.ActionArgument.ROUTENODE_NAMES, routeNodeNames);
128        actionParams.put(DocumentTypeAuthorizer.ActionArgument.DOCSTATUS, routeStatusCode);
129        return getDocumentTypeAuthorizer(documentType).isActionAuthorized(new AuthorizableAction(ActionType.SU_APPROVE), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(documentType).build(), null, actionParams).isAuthorized();
130    }
131
132    @Override
133    public boolean canSuperUserDisapproveDocument(String principalId, DocumentType documentType, Collection<String> routeNodeNames, String routeStatusCode) {
134        Map<DocumentTypeAuthorizer.ActionArgument, Object> actionParams = new HashMap<DocumentTypeAuthorizer.ActionArgument, Object>();
135        actionParams.put(DocumentTypeAuthorizer.ActionArgument.ROUTENODE_NAMES, routeNodeNames);
136        actionParams.put(DocumentTypeAuthorizer.ActionArgument.DOCSTATUS, routeStatusCode);
137        return getDocumentTypeAuthorizer(documentType).isActionAuthorized(new AuthorizableAction(ActionType.SU_DISAPPROVE), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(documentType).build(), null, actionParams).isAuthorized();
138    }
139
140    @Override
141    protected boolean canSuperUserApproveSingleActionRequest(String principalId, DocumentType documentType, Collection<String> routeNodeNames, String routeStatusCode) {
142        Map<DocumentTypeAuthorizer.ActionArgument, Object> actionParams = new HashMap<DocumentTypeAuthorizer.ActionArgument, Object>();
143        actionParams.put(DocumentTypeAuthorizer.ActionArgument.ROUTENODE_NAMES, routeNodeNames);
144        actionParams.put(DocumentTypeAuthorizer.ActionArgument.DOCSTATUS, routeStatusCode);
145        return getDocumentTypeAuthorizer(documentType).isActionAuthorized(new AuthorizableAction(AuthorizableAction.CheckType.SU_APPROVE_ACTION_REQUEST), principalId, org.kuali.rice.kew.api.doctype.DocumentType.Builder.create(documentType).build(), null, actionParams).isAuthorized();
146    }
147}