001/*
002 * Copyright 2009 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 1.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/ecl1.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.ole.sec.service.impl;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025import org.apache.commons.lang.StringUtils;
026import org.kuali.ole.integration.cg.ContractsAndGrantsModuleService;
027import org.kuali.ole.sec.SecConstants;
028import org.kuali.ole.sec.SecConstants.SecurityTemplateNames;
029import org.kuali.ole.sec.businessobject.AccessSecurityRestrictionInfo;
030import org.kuali.ole.sec.datadictionary.AccessSecurityAttributeRestrictionEntry;
031import org.kuali.ole.sec.identity.SecKimAttributes;
032import org.kuali.ole.sec.service.AccessPermissionEvaluator;
033import org.kuali.ole.sec.service.AccessSecurityService;
034import org.kuali.ole.sys.OLEConstants;
035import org.kuali.ole.sys.businessobject.AccountingLine;
036import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry;
037import org.kuali.ole.sys.businessobject.ReportBusinessObject;
038import org.kuali.ole.sys.businessobject.SourceAccountingLine;
039import org.kuali.ole.sys.businessobject.TargetAccountingLine;
040import org.kuali.ole.sys.businessobject.datadictionary.FinancialSystemBusinessObjectEntry;
041import org.kuali.ole.sys.document.AccountingDocument;
042import org.kuali.rice.core.api.config.property.ConfigurationService;
043import org.kuali.rice.coreservice.framework.parameter.ParameterService;
044import org.kuali.rice.kew.api.WorkflowDocument;
045import org.kuali.rice.kim.api.KimConstants;
046import org.kuali.rice.kim.api.common.template.Template;
047import org.kuali.rice.kim.api.identity.Person;
048import org.kuali.rice.kim.api.permission.Permission;
049import org.kuali.rice.kim.api.permission.PermissionService;
050import org.kuali.rice.kim.api.role.RoleService;
051import org.kuali.rice.kim.api.services.KimApiServiceLocator;
052import org.kuali.rice.kns.service.DataDictionaryService;
053import org.kuali.rice.krad.bo.BusinessObject;
054import org.kuali.rice.krad.bo.PersistableBusinessObject;
055import org.kuali.rice.krad.datadictionary.AttributeDefinition;
056import org.kuali.rice.krad.document.Document;
057import org.kuali.rice.krad.util.GlobalVariables;
058import org.kuali.rice.krad.util.ObjectUtils;
059
060
061/**
062 * @see org.kuali.ole.sec.service.AccessSecurityService
063 */
064public class AccessSecurityServiceImpl implements AccessSecurityService {
065    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccessSecurityServiceImpl.class);
066
067    protected DataDictionaryService dataDictionaryService;
068    protected ParameterService parameterService;
069    private PermissionService permissionService;
070    private RoleService roleService;
071    protected ContractsAndGrantsModuleService contractsAndGrantsModuleService;
072    protected ConfigurationService configurationService;
073
074    /**
075     * @see org.kuali.ole.sec.service.AccessSecurityService#applySecurityRestrictionsForGLInquiry(java.util.List, org.kuali.rice.kim.api.identity.Person)
076     */
077    @Override
078    public void applySecurityRestrictionsForGLInquiry(List<? extends BusinessObject> results, Person person) {
079        applySecurityRestrictions(results, person, getInquiryWithFieldValueTemplate(), Collections.singletonMap(KimConstants.AttributeConstants.NAMESPACE_CODE, OLEConstants.CoreModuleNamespaces.GL));
080    }
081
082    /**
083     * @see org.kuali.ole.sec.service.AccessSecurityService#applySecurityRestrictionsForLookup(java.util.List, org.kuali.rice.kim.api.identity.Person)
084     */
085    @Override
086    public void applySecurityRestrictionsForLookup(List<? extends BusinessObject> results, Person person) {
087        applySecurityRestrictions(results, person, getLookupWithFieldValueTemplate(), null);
088    }
089
090    /**
091     * Retrieves any setup security permissions for the given person and evaluates against List of business objects. Any instances
092     * not passing validation are removed from given list.
093     *
094     * @param results List of business object instances with data to check
095     * @param person Person to apply security for
096     * @param templateId KIM template id for permissions to check
097     * @param additionalPermissionDetails Any additional details that should be matched on when retrieving permissions
098     */
099    @Override
100    public void applySecurityRestrictions(List<? extends BusinessObject> results, Person person, Template permissionTemplate, Map<String,String> additionalPermissionDetails) {
101        if (!isAccessSecurityEnabled()) {
102            return;
103        }
104
105        if (results == null || results.isEmpty()) {
106            return;
107        }
108
109        // evaluate permissions against business object instances
110        List<BusinessObject> restrictedRecords = new ArrayList<BusinessObject>();
111        for (BusinessObject businessObject : results) {
112            boolean accessAllowed = evaluateSecurityPermissionsByTemplate(businessObject, businessObject.getClass(), person, permissionTemplate, additionalPermissionDetails, null);
113            if (!accessAllowed) {
114                restrictedRecords.add(businessObject);
115            }
116        }
117
118        // remove restricted records from result list
119        for (BusinessObject businessObject : restrictedRecords) {
120            results.remove(businessObject);
121        }
122    }
123
124    /**
125     * @see org.kuali.ole.sec.service.AccessSecurityService#canEditDocumentAccountingLine(org.kuali.ole.sys.document.AccountingDocument,
126     *      org.kuali.ole.sys.businessobject.AccountingLine, org.kuali.rice.kim.api.identity.Person,
127     *      org.kuali.ole.sec.businessobject.AccessSecurityRestrictionInfo)
128     */
129    @Override
130    public boolean canEditDocumentAccountingLine(AccountingDocument document, AccountingLine accountingLine, Person person, AccessSecurityRestrictionInfo restrictionInfo) {
131        // check for edit line overrides
132        boolean meetsOverrideCondition = checkForEditLineOverrides(document, accountingLine, person);
133        if (meetsOverrideCondition) {
134            return true;
135        }
136
137        Class<?> entryClass = SourceAccountingLine.class;
138        if (TargetAccountingLine.class.isAssignableFrom(accountingLine.getClass())) {
139            entryClass = TargetAccountingLine.class;
140        }
141
142        if (restrictionInfo != null) {
143            restrictionInfo.setDocumentNumber(document.getDocumentNumber());
144        }
145
146        return evaluateSecurityPermissionsByTemplate(accountingLine, entryClass, person, getEditAccountingLineWithFieldValueTemplate(), getDocumentTypeDetail(document), restrictionInfo);
147    }
148
149    /**
150     * @see org.kuali.ole.sec.service.AccessSecurityService#canEditDocumentAccountingLine(org.kuali.ole.sys.document.AccountingDocument,
151     *      org.kuali.ole.sys.businessobject.AccountingLine, org.kuali.rice.kim.api.identity.Person)
152     */
153    @Override
154    public boolean canEditDocumentAccountingLine(AccountingDocument document, AccountingLine accountingLine, Person person) {
155        return canEditDocumentAccountingLine(document, accountingLine, person, new AccessSecurityRestrictionInfo());
156    }
157
158    /**
159     * @see org.kuali.ole.sec.service.AccessSecurityService#canViewDocument(org.kuali.ole.sys.document.AccountingDocument,
160     *      org.kuali.rice.kim.api.identity.Person, org.kuali.ole.sec.businessobject.AccessSecurityRestrictionInfo)
161     */
162    @Override
163    public boolean canViewDocument(AccountingDocument document, Person person, AccessSecurityRestrictionInfo restrictionInfo) {
164        // any workflow requests override view document access permissions
165        boolean hasWorkflowRequests = checkForWorkflowRoutingRequests(document, person);
166        if (hasWorkflowRequests) {
167            return true;
168        }
169
170        // check for parameter overrides
171        boolean meetsOverrideCondition = checkForViewDocumentOverrides(document, person);
172        if (meetsOverrideCondition) {
173            return true;
174        }
175
176        if (restrictionInfo != null) {
177            restrictionInfo.setDocumentNumber(document.getDocumentNumber());
178        }
179
180        return evaluateSecurityOnAccountingLinesByTemplate(document, person, getViewDocumentWithFieldValueTemplate(), restrictionInfo);
181    }
182
183    /**
184     * @see org.kuali.ole.sec.service.AccessSecurityService#canViewDocumentAccountingLine(org.kuali.ole.sys.document.AccountingDocument,
185     *      org.kuali.ole.sys.businessobject.AccountingLine, org.kuali.rice.kim.api.identity.Person)
186     */
187    @Override
188    public boolean canViewDocumentAccountingLine(AccountingDocument document, AccountingLine accountingLine, Person person) {
189        // check for view line overrides
190        boolean meetsOverrideCondition = checkForViewLineOverrides(document, accountingLine, person);
191        if (meetsOverrideCondition) {
192            return true;
193        }
194
195        Class entryClass = SourceAccountingLine.class;
196        if (TargetAccountingLine.class.isAssignableFrom(accountingLine.getClass())) {
197            entryClass = TargetAccountingLine.class;
198        }
199
200        return evaluateSecurityPermissionsByTemplate(accountingLine, entryClass, person, getViewAccountingLineWithFieldValueTemplate(), getDocumentTypeDetail(document), null);
201    }
202
203    /**
204     * @see org.kuali.ole.sec.service.AccessSecurityService#canViewDocumentNotesAttachments(org.kuali.ole.sys.document.AccountingDocument,
205     *      org.kuali.rice.kim.api.identity.Person)
206     */
207    @Override
208    public boolean canViewDocumentNotesAttachments(AccountingDocument document, Person person) {
209        // check for parameter overrides
210        boolean meetsOverrideCondition = checkForViewDocumentOverrides(document, person);
211        if (meetsOverrideCondition) {
212            return true;
213        }
214
215        return evaluateSecurityOnAccountingLinesByTemplate(document, person, getViewNotesAttachmentsWithFieldValueTemplate(), null);
216    }
217
218    /**
219     * Iterates through source and target accounting lines for the given document and evaluates any permissions with the given
220     * template id against the accounting line values
221     *
222     * @param document AccountingDocument instance with accounting lines to check, doc type of instance is used for retrieving
223     *        permissions
224     * @param person the user who we are checking access for
225     * @param templateId KIM template id for the permissions to check
226     * @param restrictionInfo Object providing information on a restriction if one is found
227     * @return boolean true if all accounting lines pass permissions, false if at least one accounting line does not pass
228     */
229    protected boolean evaluateSecurityOnAccountingLinesByTemplate(AccountingDocument document, Person person, Template permissionTemplate, AccessSecurityRestrictionInfo restrictionInfo) {
230        boolean success = true;
231
232        if (!isAccessSecurityEnabled()) {
233            return success;
234        }
235
236        Map<String,String> permissionDetails = getDocumentTypeDetail(document);
237
238        // check source lines
239        for ( AccountingLine accountingLine : (List<AccountingLine>)document.getSourceAccountingLines() ) {
240
241            // check for overrides
242            boolean meetsOverrideCondition = false;
243            if (permissionTemplate.getId().equals(getViewDocumentWithFieldValueTemplate().getId())) {
244                meetsOverrideCondition = checkForViewLineOverrides(document, accountingLine, person);
245            }
246            else {
247                meetsOverrideCondition = checkForEditLineOverrides(document, accountingLine, person);
248            }
249
250            if (meetsOverrideCondition) {
251                continue;
252            }
253
254            success = evaluateSecurityPermissionsByTemplate(accountingLine, SourceAccountingLine.class, person, permissionTemplate, permissionDetails, restrictionInfo);
255            if (!success) {
256                break;
257            }
258        }
259
260        // if source lines are ok, check target lines
261        if (success) {
262            for ( AccountingLine accountingLine : (List<AccountingLine>)document.getTargetAccountingLines() ) {
263                // check for overrides
264                boolean meetsOverrideCondition = false;
265                if (permissionTemplate.equals(getViewDocumentWithFieldValueTemplate())) {
266                    meetsOverrideCondition = checkForViewLineOverrides(document, accountingLine, person);
267                }
268                else {
269                    meetsOverrideCondition = checkForEditLineOverrides(document, accountingLine, person);
270                }
271
272                if (meetsOverrideCondition) {
273                    continue;
274                }
275
276                success = evaluateSecurityPermissionsByTemplate(accountingLine, TargetAccountingLine.class, person, permissionTemplate, permissionDetails, restrictionInfo);
277                if (!success) {
278                    break;
279                }
280            }
281        }
282
283        return success;
284    }
285
286    /**
287     * Checks for any workflow requests (approve, acknowledge, fyi) for the document to the given person
288     *
289     * @param document Document to check for routing requests
290     * @param person Person to check for routing requests
291     * @return boolean true if there are workflow requests, false if not
292     */
293    protected boolean checkForWorkflowRoutingRequests(AccountingDocument document, Person person) {
294        WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
295
296        return workflowDocument.isApprovalRequested() || workflowDocument.isAcknowledgeRequested() || workflowDocument.isFYIRequested();
297    }
298
299    /**
300     * Checks parameter overrides for view document permission. Currently only have initiator override parameter
301     *
302     * @param document Document that we are checking permissions for
303     * @param person Person we are checking permissions for
304     * @return boolean true if overrides are turned on and the person meets the override conditions, false if overrides are not
305     *         turned on or the person does not meet condition
306     */
307    protected boolean checkForViewDocumentOverrides(AccountingDocument document, Person person) {
308        boolean alwaysAllowInitiatorAccess = parameterService.getParameterValueAsBoolean(SecConstants.ACCESS_SECURITY_NAMESPACE_CODE, SecConstants.ALL_PARAMETER_DETAIL_COMPONENT, SecConstants.SecurityParameterNames.ALWAYS_ALLOW_INITIATOR_DOCUMENT_ACCESS_IND);
309        if (alwaysAllowInitiatorAccess) {
310            WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
311            if (StringUtils.equals(workflowDocument.getInitiatorPrincipalId(), person.getPrincipalId())) {
312                return true;
313            }
314        }
315
316        return false;
317    }
318
319    /**
320     * Checks parameter overrides for view line permission. Currently only have initiator, fiscal officer, and principal
321     * investigator overrides
322     *
323     * @param document Document that we are checking permissions for
324     * @param person Person we are checking permissions for
325     * @param line AccountingLine we are checking permissions for
326     * @return boolean true if any override is turned on and the person meets the override conditions, false otherwise
327     */
328    protected boolean checkForViewLineOverrides(AccountingDocument document, AccountingLine line, Person person) {
329        // initiator override
330        boolean alwaysAllowInitiatorAccess = parameterService.getParameterValueAsBoolean(SecConstants.ACCESS_SECURITY_NAMESPACE_CODE, SecConstants.ALL_PARAMETER_DETAIL_COMPONENT, SecConstants.SecurityParameterNames.ALWAYS_ALLOW_INITIATOR_LINE_ACCESS_IND);
331        if (alwaysAllowInitiatorAccess) {
332            WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
333            if (StringUtils.equals(workflowDocument.getInitiatorPrincipalId(), person.getPrincipalId())) {
334                return true;
335            }
336        }
337
338        // account must be not be empty for further override checks
339        if (line.getAccount() == null) {
340            return false;
341        }
342
343        // fiscal officer override
344        boolean alwaysAllowFOAccess = parameterService.getParameterValueAsBoolean(SecConstants.ACCESS_SECURITY_NAMESPACE_CODE, SecConstants.ALL_PARAMETER_DETAIL_COMPONENT, SecConstants.SecurityParameterNames.ALWAYS_ALLOW_FISCAL_OFFICER_LINE_ACCESS_IND);
345        if (alwaysAllowFOAccess) {
346            if (StringUtils.equals(line.getAccount().getAccountFiscalOfficerSystemIdentifier(), person.getPrincipalId())) {
347                return true;
348            }
349        }
350
351        // pi override
352        boolean alwaysAllowPIAccess = parameterService.getParameterValueAsBoolean(SecConstants.ACCESS_SECURITY_NAMESPACE_CODE, SecConstants.ALL_PARAMETER_DETAIL_COMPONENT, SecConstants.SecurityParameterNames.ALWAYS_ALLOW_PRINCIPAL_INVESTIGATOR_LINE_ACCESS_IND);
353        if (alwaysAllowPIAccess) {
354            Person principalInvestigator = contractsAndGrantsModuleService.getProjectDirectorForAccount(line.getAccount());
355            if (principalInvestigator != null && StringUtils.equals(principalInvestigator.getPrincipalId(), person.getPrincipalId())) {
356                return true;
357            }
358        }
359
360        return false;
361    }
362
363    /**
364     * Checks parameter overrides for edit line permission. Currently only have fiscal officer and principal investigator overrides
365     *
366     * @param document Document that we are checking permissions for
367     * @param person Person we are checking permissions for
368     * @param line AccountingLine we are checking permissions for
369     * @return boolean true if any override is turned on and the person meets the override conditions, false otherwise
370     */
371    protected boolean checkForEditLineOverrides(AccountingDocument document, AccountingLine line, Person person) {
372        // account must not be empty for override checks
373        if (ObjectUtils.isNull(line.getAccount()) || line.getAccountNumber() == null) {
374            return false;
375        }
376
377        // fiscal officer override
378        boolean alwaysAllowFOAccess = parameterService.getParameterValueAsBoolean(SecConstants.ACCESS_SECURITY_NAMESPACE_CODE, SecConstants.ALL_PARAMETER_DETAIL_COMPONENT, SecConstants.SecurityParameterNames.ALWAYS_ALLOW_FISCAL_OFFICER_LINE_ACCESS_IND);
379        if (alwaysAllowFOAccess) {
380            if (StringUtils.equals(line.getAccount().getAccountFiscalOfficerSystemIdentifier(), person.getPrincipalId())) {
381                return true;
382            }
383        }
384
385        // pi override
386        boolean alwaysAllowPIAccess = parameterService.getParameterValueAsBoolean(SecConstants.ACCESS_SECURITY_NAMESPACE_CODE, SecConstants.ALL_PARAMETER_DETAIL_COMPONENT, SecConstants.SecurityParameterNames.ALWAYS_ALLOW_PRINCIPAL_INVESTIGATOR_LINE_ACCESS_IND);
387        if (alwaysAllowPIAccess) {
388            Person principalInvestigator = contractsAndGrantsModuleService.getProjectDirectorForAccount(line.getAccount());
389            if (principalInvestigator != null && StringUtils.equals(principalInvestigator.getPrincipalId(), person.getPrincipalId())) {
390                return true;
391            }
392        }
393
394        return false;
395    }
396
397    /**
398     * Validates any security permissions setup for the user and attributes of the class against the business object values
399     *
400     * @param businessObject instance with attribute values to validate
401     * @param entryClass Class of business object to pull attribute restrictions for
402     * @param person the user who we are checking access for
403     * @param templateId type of security permissions to check
404     * @param additionalPermissionDetails any additional details that should be used for retrieving permissions
405     * @param restrictionInfo Object providing information on a restriction if one is found
406     * @return boolean true if user has access given by template to the business object, false otherwise
407     */
408    protected boolean evaluateSecurityPermissionsByTemplate(BusinessObject businessObject, Class entryClass, Person person, Template permissionTemplate, Map<String,String> additionalPermissionDetails, AccessSecurityRestrictionInfo restrictionInfo) {
409        boolean success = true;
410
411        if (!isAccessSecurityEnabled()) {
412            return success;
413        }
414
415        // get all configured restricted attributes for this business object through it data dictionary entry
416        FinancialSystemBusinessObjectEntry businessObjectEntry = (FinancialSystemBusinessObjectEntry) dataDictionaryService.getDataDictionary().getBusinessObjectEntryForConcreteClass(entryClass.getName());
417
418        //if the business object is of class ReportBusinessObject interface, use refreshNonUpdateableForReport();
419        if (ReportBusinessObject.class.isAssignableFrom(businessObject.getClass())) {
420            ((ReportBusinessObject) businessObject).refreshNonUpdateableForReport();
421        }
422        else if (PersistableBusinessObject.class.isAssignableFrom(businessObject.getClass())) {
423            ((PersistableBusinessObject) businessObject).refreshNonUpdateableReferences();
424        } else {
425            businessObject.refresh();
426        }
427
428        for (AccessSecurityAttributeRestrictionEntry accessRestrictedAttribute : businessObjectEntry.getAccessRestrictedAttributes()) {
429            Map<String,String> permissionDetails = new HashMap<String,String>();
430            permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, accessRestrictedAttribute.getSecurityAttributeName());
431
432            if (additionalPermissionDetails != null) {
433                permissionDetails.putAll(additionalPermissionDetails);
434            }
435
436            List<Permission> permissions = getPermissionService().getAuthorizedPermissionsByTemplate(person.getPrincipalId(), permissionTemplate.getNamespaceCode(), permissionTemplate.getName(), permissionDetails, new HashMap<String, String>(0));
437            if (permissions == null || permissions.isEmpty()) {
438                continue;
439            }
440
441            // retrieve field value to check
442            Object propertyValue = ObjectUtils.getPropertyValue(businessObject, accessRestrictedAttribute.getAttribute().getName());
443            if (propertyValue != null && StringUtils.isNotEmpty(propertyValue.toString())) {
444                // retrieve other field values that might be necessary to validate main field
445                Map<String, Object> otherKeyValues = new HashMap<String, Object>();
446                for (String keyFieldName : accessRestrictedAttribute.getOtherKeyFields().keySet()) {
447                    AttributeDefinition fieldDefinition = accessRestrictedAttribute.getOtherKeyFields().get(keyFieldName);
448
449                    Object keyFieldValue = ObjectUtils.getPropertyValue(businessObject, fieldDefinition.getName());
450                    otherKeyValues.put(keyFieldName, keyFieldValue);
451                }
452
453                success = evaluateSecurityPermissions(accessRestrictedAttribute.getAccessPermissionEvaluatorClass(), permissions, propertyValue.toString(), person, otherKeyValues);
454                if (!success) {
455                    if (restrictionInfo != null) {
456                        restrictionInfo.setSecurityAttributeName(accessRestrictedAttribute.getSecurityAttributeName());
457                        restrictionInfo.setPropertyName(accessRestrictedAttribute.getAttribute().getName());
458                        restrictionInfo.setPropertyLabel(accessRestrictedAttribute.getAttribute().getLabel());
459                        restrictionInfo.setRetrictedValue((String) propertyValue);
460                    }
461
462                    break;
463                }
464            }
465        }
466
467        return success;
468    }
469
470    /**
471     * Constructs a new Map<String,String> and adds document type name detail with value from document instance
472     *
473     * @param document AccountingDocument instance which document type will be set from
474     * @return Map<String,String> containing document type name detail
475     */
476    protected Map<String,String> getDocumentTypeDetail(AccountingDocument document) {
477        Map<String,String> details = new HashMap<String,String>();
478        details.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, document.getFinancialDocumentTypeCode());
479
480        return details;
481    }
482
483    /**
484     * Checks whether the given value is allowed based on the given permissions and user
485     *
486     * @param accessPermissionEvaluatorClass Class of type AccessPermissionEvaluator that will be used to evaluate the security
487     *        restriction
488     * @param permissions List of permissions to evaluate
489     * @param value the value that will be checked
490     * @param person the user who we are checking access for
491     * @param otherKeyValues Map for other key field name/value pairs
492     * @return boolean true if access is allowed false if not
493     */
494    protected boolean evaluateSecurityPermissions(Class<? extends AccessPermissionEvaluator> accessPermissionEvaluatorClass, List<Permission> permissions, String value, Person person, Map<String, Object> otherKeyValues) {
495        boolean success = true;
496
497        List<Map<String,String>> qualficationsToEvaluate = new ArrayList<Map<String,String>>();
498        for (Permission permission : permissions) {
499            // find all roles that have been granted this permission
500            List<String> roleIds = getPermissionService().getRoleIdsForPermission(permission.getNamespaceCode(), permission.getName() );
501
502            // for all the roles that have this permission, find the users qualification in those roles (if any)
503            List<Map<String,String>> qualfications = getRoleService().getNestedRoleQualifiersForPrincipalByRoleIds(person.getPrincipalId(), roleIds, null);
504
505            if (qualfications != null) {
506                qualficationsToEvaluate.addAll(qualfications);
507            }
508        }
509
510        // cycle through the users qualifications and evaluate against the given value
511        boolean hasAllowQualification = false;
512        boolean allowQualificationSuccess = false;
513        boolean hasDenyFailure = false;
514        boolean hasAllowOverride = false;
515        for (Map<String,String> attributeSet : qualficationsToEvaluate) {
516            AccessPermissionEvaluator accessPermissionEvaluator = constructAccessPermissionEvaluator(accessPermissionEvaluatorClass, attributeSet, otherKeyValues, person);
517            boolean allowed = accessPermissionEvaluator.valueIsAllowed(value);
518
519            // all qualifications with constraint 'D' (deny) must pass
520            String constraintCode = attributeSet.get(SecKimAttributes.CONSTRAINT_CODE);
521            if (!allowed && StringUtils.contains(constraintCode, SecConstants.SecurityConstraintCodes.DENIED)) {
522                hasDenyFailure = true;
523            }
524
525            // if there are any 'A' (allow) qualifications, at least one must succeed
526            if (StringUtils.contains(constraintCode, SecConstants.SecurityConstraintCodes.ALLOWED)) {
527                hasAllowQualification = true;
528                if (allowed) {
529                    allowQualificationSuccess = true;
530
531                    // check for override of deny
532                    String overrideDeny = attributeSet.get(SecKimAttributes.OVERRIDE_DENY);
533                    if (Boolean.parseBoolean(overrideDeny)) {
534                        hasAllowOverride = true;
535                    }
536                }
537            }
538        }
539
540        if ((hasDenyFailure && !hasAllowOverride) || (hasAllowQualification && !allowQualificationSuccess)) {
541            success = false;
542        }
543
544        return success;
545    }
546
547    /**
548     * Constructs a new instance of the AccessPermissionEvaluator class and sets the constraint, operator, and value based on the
549     * given qualification
550     *
551     * @param accessPermissionEvaluatorClass Class to create instance of (must implement AccessPermissionEvaluator interface)
552     * @param attributeSet Map<String,String> containing the qualifier values
553     * @param otherKeyValues Map for other key field name/value pairs
554     * @param person Person who permission should be evaluated for
555     * @return new instance of type AccessPermissionEvaluator
556     * @see org.kuali.ole.sec.service.AccessPermissionEvaluator
557     */
558    protected AccessPermissionEvaluator constructAccessPermissionEvaluator(Class<? extends AccessPermissionEvaluator> accessPermissionEvaluatorClass, Map<String,String> attributeSet, Map<String, Object> otherKeyValues, Person person) {
559        AccessPermissionEvaluator accessPermissionEvaluator = null;
560        try {
561            accessPermissionEvaluator = accessPermissionEvaluatorClass.newInstance();
562        }
563        catch (Exception e) {
564            String msg = "Unable to create new instance of AccessPermissionEvaluator class: " + accessPermissionEvaluatorClass.getName();
565            LOG.error(msg, e);
566            throw new RuntimeException(msg, e);
567        }
568
569        accessPermissionEvaluator.setConstraintCode(attributeSet.get(SecKimAttributes.CONSTRAINT_CODE));
570        accessPermissionEvaluator.setOperatorCode(attributeSet.get(SecKimAttributes.OPERATOR));
571        accessPermissionEvaluator.setPropertyValue(attributeSet.get(SecKimAttributes.PROPERTY_VALUE));
572        accessPermissionEvaluator.setOtherKeyFieldValueMap(otherKeyValues);
573        accessPermissionEvaluator.setPerson(person);
574
575        return accessPermissionEvaluator;
576    }
577
578    /**
579     * Helper method to check system parameter that turns access security on/off
580     *
581     * @return boolean indicating whether access security is turned on (true) or off (false)
582     */
583    protected boolean isAccessSecurityEnabled() {
584        return parameterService.getParameterValueAsBoolean(SecConstants.ACCESS_SECURITY_NAMESPACE_CODE, SecConstants.ALL_PARAMETER_DETAIL_COMPONENT, SecConstants.SecurityParameterNames.ENABLE_ACCESS_SECURITY);
585    }
586
587    /**
588     * Sets the dataDictionaryService attribute value.
589     *
590     * @param dataDictionaryService The dataDictionaryService to set.
591     */
592    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
593        this.dataDictionaryService = dataDictionaryService;
594    }
595
596    /**
597     * Sets the parameterService attribute value.
598     *
599     * @param parameterService The parameterService to set.
600     */
601    public void setParameterService(ParameterService parameterService) {
602        this.parameterService = parameterService;
603    }
604
605    public void setConfigurationService(ConfigurationService configurationService) {
606        this.configurationService = configurationService;
607    }
608
609    public PermissionService getPermissionService() {
610        if ( permissionService == null ) {
611            permissionService = KimApiServiceLocator.getPermissionService();
612        }
613        return permissionService;
614    }
615
616    public RoleService getRoleService() {
617        if ( roleService == null ) {
618            roleService = KimApiServiceLocator.getRoleService();
619        }
620        return roleService;
621    }
622
623    /**
624     * Sets the contractsAndGrantsModuleService attribute value.
625     *
626     * @param contractsAndGrantsModuleService The contractsAndGrantsModuleService to set.
627     */
628    public void setContractsAndGrantsModuleService(ContractsAndGrantsModuleService contractsAndGrantsModuleService) {
629        this.contractsAndGrantsModuleService = contractsAndGrantsModuleService;
630    }
631
632    /**
633     * @see org.kuali.ole.sec.service.AccessSecurityService#getEditAccountingLineWithFieldValueTemplateId()
634     */
635    @Override
636    public Template getEditAccountingLineWithFieldValueTemplate() {
637        Template templateInfo = getPermissionService().findPermTemplateByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, SecurityTemplateNames.EDIT_ACCOUNTING_LINE_FIELD_VALUE);
638        if ( templateInfo != null ) {
639            return templateInfo;
640        } else {
641            throw new RuntimeException(SecurityTemplateNames.EDIT_ACCOUNTING_LINE_FIELD_VALUE + " parameter does not exist");
642    }
643    }
644
645
646    /**
647     * @see org.kuali.ole.sec.service.AccessSecurityService#getEditDocumentWithFieldValueTemplateId()
648     */
649    @Override
650    public Template getEditDocumentWithFieldValueTemplate() {
651        Template templateInfo = getPermissionService().findPermTemplateByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, SecurityTemplateNames.EDIT_DOCUMENT_FIELD_VALUE);
652        if ( templateInfo != null ) {
653            return templateInfo;
654        } else {
655            throw new RuntimeException(SecurityTemplateNames.EDIT_DOCUMENT_FIELD_VALUE + " parameter does not exist");
656    }
657    }
658
659
660    /**
661     * @see org.kuali.ole.sec.service.AccessSecurityService#getInquiryWithFieldValueTemplateId()
662     */
663    @Override
664    public Template getInquiryWithFieldValueTemplate() {
665        Template templateInfo = getPermissionService().findPermTemplateByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, SecurityTemplateNames.INQUIRY_FIELD_VALUE);
666        if ( templateInfo != null ) {
667            return templateInfo;
668        } else {
669            throw new RuntimeException(SecurityTemplateNames.INQUIRY_FIELD_VALUE + " parameter does not exist");
670    }
671    }
672
673
674    /**
675     * @see org.kuali.ole.sec.service.AccessSecurityService#getLookupWithFieldValueTemplateId()
676     */
677    @Override
678    public Template getLookupWithFieldValueTemplate() {
679        Template templateInfo = getPermissionService().findPermTemplateByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, SecurityTemplateNames.LOOKUP_FIELD_VALUE);
680        if ( templateInfo != null ) {
681            return templateInfo;
682        } else {
683            throw new RuntimeException(SecurityTemplateNames.LOOKUP_FIELD_VALUE + " parameter does not exist");
684    }
685    }
686
687
688    /**
689     * @see org.kuali.ole.sec.service.AccessSecurityService#getViewAccountingLineWithFieldValueTemplateId()
690     */
691    @Override
692    public Template getViewAccountingLineWithFieldValueTemplate() {
693        Template templateInfo = getPermissionService().findPermTemplateByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, SecurityTemplateNames.VIEW_ACCOUNTING_LINE_FIELD_VALUE);
694        if ( templateInfo != null ) {
695            return templateInfo;
696        } else {
697            throw new RuntimeException(SecurityTemplateNames.VIEW_ACCOUNTING_LINE_FIELD_VALUE + " parameter does not exist");
698    }
699    }
700
701
702    /**
703     * @see org.kuali.ole.sec.service.AccessSecurityService#getViewDocumentWithFieldValueTemplateId()
704     */
705    @Override
706    public Template getViewDocumentWithFieldValueTemplate() {
707        Template templateInfo = getPermissionService().findPermTemplateByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, SecurityTemplateNames.VIEW_DOCUMENT_FIELD_VALUE);
708        if ( templateInfo != null ) {
709            return templateInfo;
710        } else {
711            throw new RuntimeException(SecurityTemplateNames.VIEW_DOCUMENT_FIELD_VALUE + " parameter does not exist");
712    }
713    }
714
715
716    /**
717     * @see org.kuali.ole.sec.service.AccessSecurityService#getViewNotesAttachmentsWithFieldValueTemplateId()
718     */
719    @Override
720    public Template getViewNotesAttachmentsWithFieldValueTemplate() {
721        Template templateInfo = getPermissionService().findPermTemplateByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, SecurityTemplateNames.VIEW_NOTES_ATTACHMENTS_FIELD_VALUE);
722        if ( templateInfo != null ) {
723            return templateInfo;
724        } else {
725            throw new RuntimeException(SecurityTemplateNames.VIEW_NOTES_ATTACHMENTS_FIELD_VALUE + " parameter does not exist");
726    }
727    }
728
729    /**
730     * Calls access security service to check view access on given GLPE for current user. Access to view the GLPE on the document should be related to the view permissions for an
731     * accounting line with the same account attributes. Called from generalLedgerPendingEntries.tag
732     *
733     * @param pendingEntry GeneralLedgerPendingEntry to check access for
734     * @return boolean true if given user has view permission, false otherwise
735     */
736    @Override
737    public boolean canViewGLPE(Document document, GeneralLedgerPendingEntry pendingEntry, Person person) {
738        boolean canView = true;
739
740        // If the module has not been loaded, then just skip any further checks as the services will not be defined
741        if ( configurationService.getPropertyValueAsBoolean(SecConstants.ACCESS_SECURITY_MODULE_ENABLED_PROPERTY_NAME) ) {
742            if (document instanceof AccountingDocument) {
743                AccountingLine line = new SourceAccountingLine();
744
745                line.setPostingYear(pendingEntry.getUniversityFiscalYear());
746                line.setChartOfAccountsCode(pendingEntry.getChartOfAccountsCode());
747                line.setAccountNumber(pendingEntry.getAccountNumber());
748                line.setSubAccountNumber(pendingEntry.getSubAccountNumber());
749                line.setFinancialObjectCode(pendingEntry.getFinancialObjectCode());
750                line.setFinancialSubObjectCode(pendingEntry.getFinancialSubObjectCode());
751                line.setProjectCode(pendingEntry.getProjectCode());
752
753                line.refreshNonUpdateableReferences();
754
755                canView = canViewDocumentAccountingLine((AccountingDocument) document, line, GlobalVariables.getUserSession().getPerson());
756            }
757        }
758
759        return canView;
760    }
761
762    /**
763     * Compares the size of the given list against the given previous size and if different adds an info message
764     *
765     * @param previousListSize int giving previous size of list to compare to
766     * @param results List to get size for and compare
767     * @param messageKey String key of message that should be added
768     */
769    @Override
770    public void compareListSizeAndAddMessageIfChanged(int previousListSize, List<?> results, String messageKey) {
771        int currentListSize = results.size();
772
773        if (previousListSize != currentListSize) {
774            GlobalVariables.getMessageMap().putInfo(OLEConstants.GLOBAL_MESSAGES, messageKey, Integer.toString(previousListSize - currentListSize));
775        }
776    }
777
778    @Override
779    public Collection<String> getAccessSecurityControlledDocumentTypeNames() {
780        return parameterService.getParameterValuesAsString(SecConstants.ACCESS_SECURITY_NAMESPACE_CODE, SecConstants.ALL_PARAMETER_DETAIL_COMPONENT, SecConstants.SecurityParameterNames.ACCESS_SECURITY_DOCUMENT_TYPES);
781    }
782
783    @Override
784    public boolean isAccessSecurityControlledDocumentType(String documentTypeName) {
785        return getAccessSecurityControlledDocumentTypeNames().contains(documentTypeName);
786    }
787}