/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.sys.service.impl;
import java.util.Collections;
import java.util.HashMap;
import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.permission.PermissionService;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.kns.service.KNSServiceLocator;
import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
import org.kuali.rice.krad.bo.BusinessObject;
import org.kuali.rice.krad.bo.DataObjectAuthorizer;
import org.kuali.rice.krad.datadictionary.AttributeDefinition;
import org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.document.DocumentAuthorizer;
import org.kuali.rice.krad.service.KRADServiceLocator;
import org.kuali.rice.krad.util.KRADConstants;
/**
* Override of BusinessObjectAuthorizationServiceImpl to allow document authorizers to build qualifiers for checks in {@link #canFullyUnmaskField(org.kuali.rice.kim.api.identity.Person, Class, String, org.kuali.rice.krad.document.Document)}
* The developer of this class apologizes for its tortuous complexity - it was all to get TemProfile to work...
* and {@link #canPartiallyUnmaskField(org.kuali.rice.kim.api.identity.Person, Class, String, org.kuali.rice.krad.document.Document)}
*/
public class BusinessObjectAuthorizationServiceImpl extends org.kuali.rice.kns.service.impl.BusinessObjectAuthorizationServiceImpl {
protected volatile PermissionService permissionServiceForUs;
protected volatile ConfigurationService kualiConfigurationServiceForUs;
protected volatile MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
protected volatile DataDictionaryService dataDictionaryService;
/**
* Overridden to defer to canFullyUnmaskFieldForBusinessObject and canPartiallyUnmaskFieldForBusinessObject
* @see org.kuali.rice.kns.service.impl.BusinessObjectAuthorizationServiceImpl#considerBusinessObjectFieldUnmaskAuthorization(java.lang.Object, org.kuali.rice.kim.api.identity.Person, org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions, java.lang.String, org.kuali.rice.krad.document.Document)
*/
@Override
protected void considerBusinessObjectFieldUnmaskAuthorization(Object dataObject, Person user, BusinessObjectRestrictions businessObjectRestrictions, String propertyPrefix, Document document) {
final DataDictionaryEntryBase objectEntry = (dataObject instanceof org.kuali.rice.krad.document.Document) ?
getDataDictionaryService().getDataDictionary().getDocumentEntry(getDataDictionaryService().getDocumentTypeNameByClass(dataObject.getClass())) :
getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(dataObject.getClass().getName());
BusinessObject permissionTarget = (dataObject instanceof BusinessObject) ? (BusinessObject)dataObject : document;
for (String attributeName : objectEntry.getAttributeNames()) {
AttributeDefinition attributeDefinition = objectEntry.getAttributeDefinition(attributeName);
if (attributeDefinition.getAttributeSecurity() != null) {
if (attributeDefinition.getAttributeSecurity().isMask() &&
!canFullyUnmaskFieldForBusinessObject(user, dataObject.getClass(), attributeName, permissionTarget, document)) {
businessObjectRestrictions.addFullyMaskedField(propertyPrefix + attributeName, attributeDefinition.getAttributeSecurity().getMaskFormatter());
}
if (attributeDefinition.getAttributeSecurity().isPartialMask() &&
!canPartiallyUnmaskFieldForBusinessObject(user, dataObject.getClass(), attributeName, permissionTarget, document)) {
businessObjectRestrictions.addPartiallyMaskedField(propertyPrefix + attributeName, attributeDefinition.getAttributeSecurity().getPartialMaskFormatter());
}
}
}
}
/**
* Defers to canFullyUnmaskFieldForBusinessObject
* @see org.kuali.rice.kns.service.impl.BusinessObjectAuthorizationServiceImpl#canFullyUnmaskField(org.kuali.rice.kim.api.identity.Person, java.lang.Class, java.lang.String, org.kuali.rice.krad.document.Document)
*/
@Override
public boolean canFullyUnmaskField(Person user, Class<?> dataObjectClass, String fieldName, Document document) {
return canFullyUnmaskFieldForBusinessObject(user, dataObjectClass, fieldName, document, null);
}
/**
* Defers to canPartiallyUnmaskFieldForBusinessObject
* @see org.kuali.rice.kns.service.impl.BusinessObjectAuthorizationServiceImpl#canPartiallyUnmaskField(org.kuali.rice.kim.api.identity.Person, java.lang.Class, java.lang.String, org.kuali.rice.krad.document.Document)
*/
@Override
public boolean canPartiallyUnmaskField(Person user, Class<?> dataObjectClass, String fieldName, Document document) {
return canPartiallyUnmaskFieldForBusinessObject(user, dataObjectClass, fieldName, document, null);
}
/**
* Determines if the given field on the given business object can be unmasked, using the business object to build role qualifiers if possible
* @param user the person to check if there is full unmask field permission for
* @param dataObjectClass the class of the data object being checked
* @param fieldName the name of the field to potentially unmask
* @param businessObject the business object containing sensitive information
* @param document the document we are acting on, or null if no document is known
* @return true if the field on the business object can be fully unmasked, false otherwise
*/
protected boolean canFullyUnmaskFieldForBusinessObject(Person user, Class<?> dataObjectClass, String fieldName, BusinessObject businessObject, Document document) {
if(isNonProductionEnvAndUnmaskingTurnedOffForUs()) {
return false;
}
if(user==null || StringUtils.isEmpty(user.getPrincipalId())) {
return false;
}
DataObjectAuthorizer authorizer = null;
BusinessObject boForAuthorization = null;
if (document != null) {
authorizer = findDocumentAuthorizerForBusinessObject(document);
boForAuthorization = document;
}
if (authorizer == null) {
authorizer = findDocumentAuthorizerForBusinessObject(businessObject);
if (authorizer == null) {
authorizer = findInquiryAuthorizerForBusinessObject(businessObject);
}
boForAuthorization = businessObject;
}
if (authorizer == null) {
return getPermissionServiceForUs().isAuthorizedByTemplate(user.getPrincipalId(), KRADConstants.KNS_NAMESPACE,
KimConstants.PermissionTemplateNames.FULL_UNMASK_FIELD, new HashMap<String, String>(
getFieldPermissionDetails(dataObjectClass, fieldName)), Collections.<String, String>emptyMap());
}
return authorizer
.isAuthorizedByTemplate( boForAuthorization,
KRADConstants.KNS_NAMESPACE,
KimConstants.PermissionTemplateNames.FULL_UNMASK_FIELD,
user.getPrincipalId(), getFieldPermissionDetails(dataObjectClass, fieldName), Collections.<String, String>emptyMap());
}
/**
* Determines if the given field on the given business object can be unmasked, using the business object to build role qualifiers if possible
* @param user the person to check if there is partial unmask field permission for
* @param dataObjectClass the class of the data object being checked
* @param fieldName the name of the field to potentially unmask
* @param businessObject the business object containing sensitive information
* @param document the document we are acting on, or null if no document is known
* @return true if the field on the business object can be partially unmasked, false otherwise
*/
protected boolean canPartiallyUnmaskFieldForBusinessObject(Person user, Class<?> dataObjectClass, String fieldName, BusinessObject businessObject, Document document) {
if(isNonProductionEnvAndUnmaskingTurnedOffForUs()) {
return false;
}
if(user==null || StringUtils.isEmpty(user.getPrincipalId())) {
return false;
}
DataObjectAuthorizer authorizer = null;
BusinessObject boForAuthorization = null;
if (document != null) {
authorizer = findDocumentAuthorizerForBusinessObject(document);
boForAuthorization = document;
}
if (authorizer == null) {
authorizer = findDocumentAuthorizerForBusinessObject(businessObject);
if (authorizer == null) {
authorizer = findInquiryAuthorizerForBusinessObject(businessObject);
}
boForAuthorization = businessObject;
}
if ( authorizer == null ) {
return getPermissionServiceForUs().isAuthorizedByTemplate(user.getPrincipalId(), KRADConstants.KNS_NAMESPACE,
KimConstants.PermissionTemplateNames.PARTIAL_UNMASK_FIELD, new HashMap<String, String>(
getFieldPermissionDetails(dataObjectClass, fieldName)), Collections.<String, String>emptyMap());
} else { // if a document was passed, evaluate the permission in the context of a document
return authorizer
.isAuthorizedByTemplate( boForAuthorization,
KRADConstants.KNS_NAMESPACE,
KimConstants.PermissionTemplateNames.PARTIAL_UNMASK_FIELD,
user.getPrincipalId(), getFieldPermissionDetails(dataObjectClass, fieldName), Collections.<String, String>emptyMap() );
}
}
/**
* Attempts to find a DocumentAuthorizer for the given business object. If the business object is a document, simply looks up
* its associated authorizer. Otherwise, it checks to see if there's a maintenance document associated with the business object and uses
* the document authorizer associated with that
* @param businessObject the business object to attempt to find a DocumentAuthorizer for
* @return an instantiated DocumentAuthorizer associated with the business object, or null if none could be found
*/
protected DocumentAuthorizer findDocumentAuthorizerForBusinessObject(BusinessObject businessObject) {
if (businessObject == null) {
return null;
}
if (businessObject instanceof Document) {
return getDocumentHelperService().getDocumentAuthorizer( (Document)businessObject );
}
final String maintDocType = getMaintenanceDocumentDictionaryService().getDocumentTypeName(businessObject.getClass());
if (StringUtils.isBlank(maintDocType)) {
return null;
}
return getDocumentHelperService().getDocumentAuthorizer(maintDocType);
}
/**
* Attempts to find an InquiryAuthorizer for the given business object, by looking at the inquiry definition
* @param businessObject the business object to attempt to find a InquiryAuthorizer for
* @return an instantiated InquiryAuthorizer associated with the business object, or null if none could be found
*/
protected DataObjectAuthorizer findInquiryAuthorizerForBusinessObject(BusinessObject businessObject) {
if (businessObject == null) {
return null;
}
final BusinessObjectEntry boEntry = (BusinessObjectEntry)getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(businessObject.getClass().getName());
if (boEntry != null && boEntry.getInquiryDefinition() != null && boEntry.getInquiryDefinition().getAuthorizerClass() != null) {
try {
return (DataObjectAuthorizer)boEntry.getInquiryDefinition().getAuthorizerClass().newInstance();
}
catch (InstantiationException ie) {
throw new RuntimeException("Could not instantiate authorizer for inquiry of "+businessObject.getClass().getName(), ie);
}
catch (IllegalAccessException iae) {
throw new RuntimeException("Could not instantiate authorizer for inquiry of "+businessObject.getClass().getName(), iae);
}
}
return null;
}
/**
* Renamed to avoid shadowing
*/
protected boolean isNonProductionEnvAndUnmaskingTurnedOffForUs(){
return !getKualiConfigurationServiceForUs().getPropertyValueAsString(KRADConstants.PROD_ENVIRONMENT_CODE_KEY)
.equalsIgnoreCase(
getKualiConfigurationServiceForUs().getPropertyValueAsString(KRADConstants.ENVIRONMENT_KEY)) &&
!getKualiConfigurationServiceForUs().getPropertyValueAsBoolean(KRADConstants.ENABLE_NONPRODUCTION_UNMASKING);
}
/**
* Renamed to avoid shadowing
*/
protected PermissionService getPermissionServiceForUs() {
if (permissionServiceForUs == null) {
permissionServiceForUs = KimApiServiceLocator
.getPermissionService();
}
return permissionServiceForUs;
}
/**
* Renamed to avoid shadowing
*/
protected ConfigurationService getKualiConfigurationServiceForUs() {
if (kualiConfigurationServiceForUs == null) {
kualiConfigurationServiceForUs = KRADServiceLocator.getKualiConfigurationService();
}
return kualiConfigurationServiceForUs;
}
protected MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
if (maintenanceDocumentDictionaryService == null) {
maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
}
return maintenanceDocumentDictionaryService;
}
@Override
protected DataDictionaryService getDataDictionaryService() {
if (dataDictionaryService == null) {
dataDictionaryService = KNSServiceLocator.getDataDictionaryService();
}
return dataDictionaryService;
}
}