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.krad.kim;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.criteria.CriteriaLookupService;
020import org.kuali.rice.core.api.criteria.QueryByCriteria;
021import org.kuali.rice.core.api.uif.RemotableAttributeError;
022import org.kuali.rice.coreservice.impl.namespace.NamespaceBo;
023import org.kuali.rice.kim.api.KimConstants;
024import org.kuali.rice.kim.api.permission.Permission;
025import org.kuali.rice.kim.api.type.KimType;
026import org.kuali.rice.kim.impl.permission.PermissionBo;
027import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
028
029import java.util.ArrayList;
030import java.util.Collections;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034
035import static org.kuali.rice.core.api.criteria.PredicateFactory.like;
036
037/**
038 * @author Kuali Rice Team (rice.collab@kuali.org)
039 */
040public class NamespaceWildcardAllowedAndOrStringExactMatchPermissionTypeServiceImpl
041                extends NamespacePermissionTypeServiceImpl {
042        protected static final String NAMESPACE_CODE = KimConstants.UniqueKeyConstants.NAMESPACE_CODE;
043        
044        protected String exactMatchStringAttributeName;
045        protected boolean namespaceRequiredOnStoredMap;
046    private List<String> requiredAttributes = new ArrayList<String>();
047
048    private CriteriaLookupService criteriaLookupService;
049
050    @Override
051    protected List<String> getRequiredAttributes() {
052        return Collections.unmodifiableList(requiredAttributes);
053    }
054
055        @Override
056        protected List<Permission> performPermissionMatches(Map<String, String> requestedDetails, List<Permission> permissionsList) {
057            List<Permission> matchingPermissions = new ArrayList<Permission>();
058        List<Permission> matchingBlankPermissions = new ArrayList<Permission>();
059            String requestedAttributeValue = requestedDetails.get(exactMatchStringAttributeName);
060            for ( Permission kpi : permissionsList ) {
061            PermissionBo bo = PermissionBo.from(kpi);
062                String permissionAttributeValue = bo.getDetails().get(exactMatchStringAttributeName);
063                if ( StringUtils.equals(requestedAttributeValue, permissionAttributeValue) ) {
064                    matchingPermissions.add(kpi);
065                } else if ( StringUtils.isBlank(permissionAttributeValue) ) {
066                    matchingBlankPermissions.add(kpi);
067                }
068            }
069            // if the exact match worked, use those when checking the namespace
070            // otherwise, use those with a blank additional property value
071            if ( !matchingPermissions.isEmpty() ) {
072            List<Permission> matchingWithNamespace = super.performPermissionMatches(requestedDetails, matchingPermissions);
073                if ( !namespaceRequiredOnStoredMap ) {
074                    // if the namespace is not required and the namespace match would have excluded
075                    // the results, return the original set of matches
076                    if ( matchingWithNamespace.isEmpty() ) {
077                        return matchingPermissions;
078                    }
079                }
080            return matchingWithNamespace;
081            } else if ( !matchingBlankPermissions.isEmpty() ) {
082            List<Permission> matchingWithNamespace = super.performPermissionMatches(requestedDetails, matchingBlankPermissions);
083            if ( !namespaceRequiredOnStoredMap ) {
084                // if the namespace is not required and the namespace match would have excluded
085                // the results, return the original set of matches
086                if ( matchingWithNamespace.isEmpty() ) {
087                    return matchingBlankPermissions;
088                }
089            }
090            return matchingWithNamespace;
091            }
092            return matchingPermissions; // will be empty if drops to here
093        }
094        
095        public void setExactMatchStringAttributeName(
096                        String exactMatchStringAttributeName) {
097                this.exactMatchStringAttributeName = exactMatchStringAttributeName;
098                requiredAttributes.add(exactMatchStringAttributeName);
099        }
100
101        public void setNamespaceRequiredOnStoredMap(
102                        boolean namespaceRequiredOnStoredMap) {
103                this.namespaceRequiredOnStoredMap = namespaceRequiredOnStoredMap;
104        }
105
106        /**
107         * Overrides the superclass's version of this method in order to account for "namespaceCode" permission detail values containing wildcards.
108         */
109        @Override
110        protected List<RemotableAttributeError> validateReferencesExistAndActive(KimType kimType, Map<String, String> attributes, List<RemotableAttributeError> previousValidationErrors) {
111                List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
112                Map<String, String> nonNamespaceCodeAttributes = new HashMap<String, String>(attributes);
113
114                // Check if "namespaceCode" is one of the permission detail values.
115                if (attributes.containsKey(NAMESPACE_CODE)) {
116                        nonNamespaceCodeAttributes.remove(NAMESPACE_CODE);
117
118            String namespaceCode = attributes.get(NAMESPACE_CODE);
119
120            QueryByCriteria criteria = QueryByCriteria.Builder.fromPredicates(like("code", namespaceCode));
121            List<NamespaceBo> namespaces = getCriteriaLookupService().lookup(NamespaceBo.class, criteria).getResults();
122
123            if (!namespaces.isEmpty()) {
124                // If namespaces were found, let the superclass generate any appropriate errors
125                for (NamespaceBo namespace : namespaces) {
126                    errors.addAll(super.validateReferencesExistAndActive(kimType,
127                        Collections.singletonMap(NAMESPACE_CODE, namespace.getCode()), previousValidationErrors));
128                }
129                        } else {
130                                // If no namespaces were found, let the superclass generate an appropriate error.
131                                errors.addAll(super.validateReferencesExistAndActive(kimType,
132                    Collections.singletonMap(NAMESPACE_CODE, namespaceCode), previousValidationErrors));
133                        }
134                }
135
136                // Validate all non-namespaceCode attributes.
137                errors.addAll(super.validateReferencesExistAndActive(kimType, nonNamespaceCodeAttributes, previousValidationErrors));
138
139                return errors;
140        }
141
142    public CriteriaLookupService getCriteriaLookupService() {
143       if (criteriaLookupService == null) {
144           criteriaLookupService = KRADServiceLocatorWeb.getService("criteriaLookupService");
145       }
146
147       return criteriaLookupService;
148    }
149
150    public void setCriteriaLookupService(CriteriaLookupService criteriaLookupService) {
151        this.criteriaLookupService = criteriaLookupService;
152    }
153}