Coverage Report - org.kuali.rice.kew.doctype.service.impl.DocumentSecurityServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
DocumentSecurityServiceImpl
0%
0/194
0%
0/106
4.591
DocumentSecurityServiceImpl$PartitionKey
0%
0/18
0%
0/4
4.591
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kew.doctype.service.impl;
 17  
 
 18  
 import org.apache.commons.collections.CollectionUtils;
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 import org.apache.commons.lang.builder.EqualsBuilder;
 21  
 import org.apache.commons.lang.builder.HashCodeBuilder;
 22  
 import org.kuali.rice.core.api.CoreConstants;
 23  
 import org.kuali.rice.core.api.datetime.DateTimeService;
 24  
 import org.kuali.rice.core.api.reflect.ObjectDefinition;
 25  
 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
 26  
 import org.kuali.rice.core.api.util.KeyValue;
 27  
 import org.kuali.rice.kew.api.KewApiServiceLocator;
 28  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 29  
 import org.kuali.rice.kew.api.document.Document;
 30  
 import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
 31  
 import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
 32  
 import org.kuali.rice.kew.api.extension.ExtensionDefinition;
 33  
 import org.kuali.rice.kew.api.extension.ExtensionRepositoryService;
 34  
 import org.kuali.rice.kew.doctype.DocumentTypeSecurity;
 35  
 import org.kuali.rice.kew.framework.KewFrameworkServiceLocator;
 36  
 import org.kuali.rice.kew.framework.document.security.DocumentSecurityDirective;
 37  
 import org.kuali.rice.kew.framework.document.security.DocumentSecurityHandlerService;
 38  
 import org.kuali.rice.kew.framework.document.security.DocumentSecurityAttribute;
 39  
 import org.kuali.rice.kew.doctype.SecurityPermissionInfo;
 40  
 import org.kuali.rice.kew.doctype.SecuritySession;
 41  
 import org.kuali.rice.kew.doctype.bo.DocumentType;
 42  
 import org.kuali.rice.kew.doctype.service.DocumentSecurityService;
 43  
 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
 44  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 45  
 import org.kuali.rice.kew.user.UserUtils;
 46  
 import org.kuali.rice.kew.api.KewApiConstants;
 47  
 import org.kuali.rice.kim.api.group.Group;
 48  
 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
 49  
 import org.springframework.util.LinkedMultiValueMap;
 50  
 import org.springframework.util.MultiValueMap;
 51  
 
 52  
 import java.lang.reflect.Field;
 53  
 import java.util.ArrayList;
 54  
 import java.util.Calendar;
 55  
 import java.util.Collection;
 56  
 import java.util.Collections;
 57  
 import java.util.HashMap;
 58  
 import java.util.HashSet;
 59  
 import java.util.Iterator;
 60  
 import java.util.List;
 61  
 import java.util.Map;
 62  
 import java.util.Set;
 63  
 
 64  0
 public class DocumentSecurityServiceImpl implements DocumentSecurityService {
 65  
 
 66  0
     public static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(
 67  
             DocumentSecurityServiceImpl.class);
 68  
 
 69  
     private ExtensionRepositoryService extensionRepositoryService;
 70  
 
 71  
     @Override
 72  
     public boolean routeLogAuthorized(String principalId, DocumentRouteHeaderValue routeHeader,
 73  
             SecuritySession securitySession) {
 74  0
         List<Document> documents = Collections.singletonList(DocumentRouteHeaderValue.to(routeHeader));
 75  0
         Set<String> authorizationResults = checkAuthorizations(principalId, securitySession, documents);
 76  0
         return authorizationResults.contains(routeHeader.getDocumentId());
 77  
     }
 78  
 
 79  
     @Override
 80  
     public Set<String> documentSearchResultAuthorized(String principalId, DocumentSearchResults results,
 81  
             SecuritySession securitySession) {
 82  0
         List<Document> documents = new ArrayList<Document>();
 83  0
         for (DocumentSearchResult result : results.getSearchResults()) {
 84  0
             documents.add(result.getDocument());
 85  
         }
 86  0
         return checkAuthorizations(principalId, securitySession, documents);
 87  
     }
 88  
 
 89  
     protected Set<String> checkAuthorizations(String principalId, SecuritySession securitySession,
 90  
             List<Document> documents) {
 91  0
         Set<String> authorizations = new HashSet<String>();
 92  
         // a list of documents which need to be processed with security extension attributes after the standard set of
 93  
         // security has been attempted
 94  0
         List<Document> documentsRequiringExtensionProcessing = new ArrayList<Document>();
 95  0
         boolean admin = isAdmin(securitySession);
 96  0
         for (Document document : documents) {
 97  0
             if (admin) {
 98  0
                 authorizations.add(document.getDocumentId());
 99  0
                 continue;
 100  
             }
 101  0
             DocumentTypeSecurity security = null;
 102  
             try {
 103  0
                 security = getDocumentTypeSecurity(document.getDocumentTypeName(), securitySession);
 104  0
                 if (security == null || !security.isActive() || checkStandardAuthorization(security, principalId,
 105  
                         document, securitySession)) {
 106  0
                     authorizations.add(document.getDocumentId());
 107  
                 } else {
 108  
                     // if we get to this point, it means we aren't authorized yet, last chance for authorization will be
 109  
                     // security extension attributes, so prepare for execution of those after the main loop is complete
 110  0
                     if (CollectionUtils.isNotEmpty(security.getSecurityAttributeExtensionNames())) {
 111  0
                         documentsRequiringExtensionProcessing.add(document);
 112  
                     }
 113  
                 }
 114  0
             } catch (Exception e) {
 115  0
                 LOG.warn(
 116  
                         "Not able to retrieve DocumentTypeSecurity from remote system for documentTypeName: " + document
 117  
                                 .getDocumentTypeName(), e);
 118  0
                 continue;
 119  0
             }
 120  0
         }
 121  0
         processDocumentRequiringExtensionProcessing(documentsRequiringExtensionProcessing, securitySession,
 122  
                 authorizations);
 123  0
         return authorizations;
 124  
     }
 125  
 
 126  
     protected void processDocumentRequiringExtensionProcessing(List<Document> documentsRequiringExtensionProcessing,
 127  
             SecuritySession securitySession, Set<String> authorizations) {
 128  0
         if (CollectionUtils.isNotEmpty(documentsRequiringExtensionProcessing)) {
 129  0
             LOG.info("Beginning processing of documents requiring extension processing (total: "
 130  
                     + documentsRequiringExtensionProcessing.size()
 131  
                     + " documents)");
 132  0
             long start = System.currentTimeMillis();
 133  0
             MultiValueMap<PartitionKey, Document> partitions = partitionDocumentsForSecurity(
 134  
                     documentsRequiringExtensionProcessing, securitySession);
 135  0
             MultiValueMap<String, DocumentSecurityDirective> applicationSecurityDirectives =
 136  
                     new LinkedMultiValueMap<String, DocumentSecurityDirective>();
 137  0
             for (PartitionKey partitionKey : partitions.keySet()) {
 138  0
                 DocumentSecurityDirective directive = DocumentSecurityDirective.create(
 139  
                         partitionKey.getDocumentSecurityAttributeNameList(), partitions.get(partitionKey));
 140  0
                 applicationSecurityDirectives.add(partitionKey.applicationId, directive);
 141  0
             }
 142  0
             for (String applicationId : applicationSecurityDirectives.keySet()) {
 143  0
                 List<DocumentSecurityDirective> documentSecurityDirectives = applicationSecurityDirectives.get(
 144  
                         applicationId);
 145  0
                 DocumentSecurityHandlerService securityHandler = loadSecurityHandler(applicationId);
 146  0
                 List<String> authorizedDocumentIds = securityHandler.getAuthorizedDocumentIds(
 147  
                         securitySession.getPrincipalId(), documentSecurityDirectives);
 148  0
                 if (CollectionUtils.isNotEmpty(authorizedDocumentIds)) {
 149  0
                     authorizations.addAll(authorizedDocumentIds);
 150  
                 }
 151  0
             }
 152  0
             long end = System.currentTimeMillis();
 153  0
             LOG.info("Finished processing of documents requiring extension processing (total time: "
 154  
                     + (start - end)
 155  
                     + ")");
 156  
         }
 157  0
     }
 158  
 
 159  
     protected MultiValueMap<PartitionKey, Document> partitionDocumentsForSecurity(List<Document> documents,
 160  
             SecuritySession securitySession) {
 161  0
         MultiValueMap<PartitionKey, Document> partitions = new LinkedMultiValueMap<PartitionKey, Document>();
 162  0
         for (Document document : documents) {
 163  0
             DocumentTypeSecurity security = getDocumentTypeSecurity(document.getDocumentTypeName(), securitySession);
 164  0
             MultiValueMap<String, ExtensionDefinition> securityAttributeExtensionDefinitions = loadExtensionDefinitions(
 165  
                     security, securitySession);
 166  0
             for (String applicationId : securityAttributeExtensionDefinitions.keySet()) {
 167  0
                 List<ExtensionDefinition> extensionDefinitions = securityAttributeExtensionDefinitions.get(
 168  
                         applicationId);
 169  0
                 PartitionKey key = new PartitionKey(applicationId, extensionDefinitions);
 170  0
                 partitions.add(key, document);
 171  0
             }
 172  0
         }
 173  0
         return partitions;
 174  
     }
 175  
 
 176  
     protected MultiValueMap<String, ExtensionDefinition> loadExtensionDefinitions(DocumentTypeSecurity security,
 177  
             SecuritySession securitySession) {
 178  0
         MultiValueMap<String, ExtensionDefinition> securityAttributeExtensionDefinitions =
 179  
                 new LinkedMultiValueMap<String, ExtensionDefinition>();
 180  0
         List<String> securityAttributeExtensionNames = security.getSecurityAttributeExtensionNames();
 181  0
         for (String securityAttributeExtensionName : securityAttributeExtensionNames) {
 182  0
             ExtensionDefinition extensionDefinition = extensionRepositoryService.getExtensionByName(
 183  
                     securityAttributeExtensionName);
 184  0
             securityAttributeExtensionDefinitions.add(extensionDefinition.getApplicationId(), extensionDefinition);
 185  0
         }
 186  0
         return securityAttributeExtensionDefinitions;
 187  
     }
 188  
 
 189  
     protected DocumentSecurityHandlerService loadSecurityHandler(String applicationId) {
 190  0
         DocumentSecurityHandlerService service = KewFrameworkServiceLocator.getDocumentSecurityHandlerService(
 191  
                 applicationId);
 192  0
         if (service == null) {
 193  0
             throw new WorkflowRuntimeException(
 194  
                     "Failed to locate DocumentSecurityHandlerService for applicationId: " + applicationId);
 195  
         }
 196  0
         return service;
 197  
     }
 198  
 
 199  
     protected boolean isAdmin(SecuritySession session) {
 200  0
         if (session.getPrincipalId() == null) {
 201  0
             return false;
 202  
         }
 203  0
         return KimApiServiceLocator.getPermissionService().isAuthorized(session.getPrincipalId(),
 204  
                 KewApiConstants.KEW_NAMESPACE, KewApiConstants.PermissionNames.UNRESTRICTED_DOCUMENT_SEARCH,
 205  
                 new HashMap<String, String>(), new HashMap<String, String>());
 206  
     }
 207  
 
 208  
     protected boolean checkStandardAuthorization(DocumentTypeSecurity security, String principalId, Document document,
 209  
             SecuritySession securitySession) {
 210  0
         String documentId = document.getDocumentId();
 211  0
         String initiatorPrincipalId = document.getInitiatorPrincipalId();
 212  
 
 213  0
         LOG.debug("auth check user=" + principalId + " docId=" + documentId);
 214  
 
 215  
         // Doc Initiator Authorization
 216  0
         if (security.getInitiatorOk() != null && security.getInitiatorOk()) {
 217  0
             boolean isInitiator = StringUtils.equals(initiatorPrincipalId, principalId);
 218  0
             if (isInitiator) {
 219  0
                 return true;
 220  
             }
 221  
         }
 222  
 
 223  
         // Permission Authorization
 224  0
         List<SecurityPermissionInfo> securityPermissions = security.getPermissions();
 225  0
         if (securityPermissions != null) {
 226  0
             for (SecurityPermissionInfo securityPermission : securityPermissions) {
 227  0
                 if (isAuthenticatedByPermission(documentId, securityPermission.getPermissionNamespaceCode(),
 228  
                         securityPermission.getPermissionName(), securityPermission.getPermissionDetails(),
 229  
                         securityPermission.getQualifications(), securitySession)) {
 230  0
                     return true;
 231  
                 }
 232  
             }
 233  
         }
 234  
 
 235  
         //  Group Authorization
 236  0
         List<Group> securityWorkgroups = security.getWorkgroups();
 237  0
         if (securityWorkgroups != null) {
 238  0
             for (Group securityWorkgroup : securityWorkgroups) {
 239  0
                 if (isGroupAuthenticated(securityWorkgroup.getNamespaceCode(), securityWorkgroup.getName(),
 240  
                         securitySession)) {
 241  0
                     return true;
 242  
                 }
 243  
             }
 244  
         }
 245  
 
 246  
         // Searchable Attribute Authorization
 247  0
         Collection searchableAttributes = security.getSearchableAttributes();
 248  0
         if (searchableAttributes != null) {
 249  0
             for (Iterator iterator = searchableAttributes.iterator(); iterator.hasNext(); ) {
 250  0
                 KeyValue searchableAttr = (KeyValue) iterator.next();
 251  0
                 String attrName = searchableAttr.getKey();
 252  0
                 String idType = searchableAttr.getValue();
 253  0
                 String idValue = UserUtils.getIdValue(idType, principalId);
 254  0
                 if (!StringUtils.isEmpty(idValue)) {
 255  0
                     if (KEWServiceLocator.getRouteHeaderService().hasSearchableAttributeValue(documentId, attrName,
 256  
                             idValue)) {
 257  0
                         return true;
 258  
                     }
 259  
                 }
 260  0
             }
 261  
         }
 262  
 
 263  
         // Route Log Authorization
 264  0
         if (security.getRouteLogAuthenticatedOk() != null && security.getRouteLogAuthenticatedOk()) {
 265  0
             boolean isInitiator = StringUtils.equals(initiatorPrincipalId, principalId);
 266  0
             if (isInitiator) {
 267  0
                 return true;
 268  
             }
 269  0
             boolean hasTakenAction = KEWServiceLocator.getActionTakenService().hasUserTakenAction(principalId,
 270  
                     documentId);
 271  0
             if (hasTakenAction) {
 272  0
                 return true;
 273  
             }
 274  0
             boolean hasRequest = KEWServiceLocator.getActionRequestService().doesPrincipalHaveRequest(principalId,
 275  
                     documentId);
 276  0
             if (hasRequest) {
 277  0
                 return true;
 278  
             }
 279  
         }
 280  
 
 281  
         // local security attribute authorization
 282  0
         List<DocumentSecurityAttribute> immediateSecurityAttributes = getImmediateSecurityAttributes(document, security,
 283  
                 securitySession);
 284  0
         if (immediateSecurityAttributes != null) {
 285  0
             for (DocumentSecurityAttribute immediateSecurityAttribute : immediateSecurityAttributes) {
 286  0
                 boolean isAuthorized = immediateSecurityAttribute.isAuthorizedForDocument(principalId, document);
 287  0
                 if (isAuthorized) {
 288  0
                     return true;
 289  
                 }
 290  0
             }
 291  
         }
 292  
 
 293  0
         LOG.debug("user not authorized");
 294  0
         return false;
 295  
     }
 296  
 
 297  
     protected List<DocumentSecurityAttribute> getImmediateSecurityAttributes(Document document, DocumentTypeSecurity security,
 298  
             SecuritySession securitySession) {
 299  0
         List<DocumentSecurityAttribute> securityAttributes = new ArrayList<DocumentSecurityAttribute>();
 300  0
         for (String securityAttributeClassName : security.getSecurityAttributeClassNames()) {
 301  0
             DocumentSecurityAttribute securityAttribute = securitySession.getSecurityAttributeForClass(
 302  
                     securityAttributeClassName);
 303  0
             if (securityAttribute == null) {
 304  0
                 securityAttribute = GlobalResourceLoader.getObject(new ObjectDefinition(securityAttributeClassName));
 305  0
                 securitySession.setSecurityAttributeForClass(securityAttributeClassName, securityAttribute);
 306  
             }
 307  0
             securityAttributes.add(securityAttribute);
 308  0
         }
 309  0
         return securityAttributes;
 310  
     }
 311  
 
 312  
     protected DocumentTypeSecurity getDocumentTypeSecurity(String documentTypeName, SecuritySession session) {
 313  0
         DocumentTypeSecurity security = session.getDocumentTypeSecurity().get(documentTypeName);
 314  0
         if (security == null) {
 315  0
             DocumentType docType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName);
 316  0
             security = docType.getDocumentTypeSecurity();
 317  0
             session.getDocumentTypeSecurity().put(documentTypeName, security);
 318  
         }
 319  0
         return security;
 320  
     }
 321  
 
 322  
     protected boolean isGroupAuthenticated(String namespace, String groupName, SecuritySession session) {
 323  0
         String key = namespace.trim() + KewApiConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + groupName.trim();
 324  0
         Boolean existingAuth = session.getAuthenticatedWorkgroups().get(key);
 325  0
         if (existingAuth != null) {
 326  0
             return existingAuth;
 327  
         }
 328  0
         boolean memberOfGroup = isMemberOfGroupWithName(namespace, groupName, session.getPrincipalId());
 329  0
         session.getAuthenticatedWorkgroups().put(key, memberOfGroup);
 330  0
         return memberOfGroup;
 331  
     }
 332  
 
 333  
     private boolean isMemberOfGroupWithName(String namespace, String groupName, String principalId) {
 334  0
         for (Group group : KimApiServiceLocator.getGroupService().getGroupsByPrincipalId(principalId)) {
 335  0
             if (StringUtils.equals(namespace, group.getNamespaceCode()) && StringUtils.equals(groupName,
 336  
                     group.getName())) {
 337  0
                 return true;
 338  
             }
 339  
         }
 340  0
         return false;
 341  
     }
 342  
 
 343  
     protected boolean isAuthenticatedByPermission(String documentId, String permissionNamespaceCode,
 344  
             String permissionName, Map<String, String> permissionDetails, Map<String, String> qualification,
 345  
             SecuritySession session) {
 346  
 
 347  
         Document document;
 348  
         try {
 349  0
             document = KewApiServiceLocator.getWorkflowDocumentService().getDocument(documentId);
 350  
 
 351  0
             for (String qualificationKey : qualification.keySet()) {
 352  0
                 String qualificationValue = qualification.get(qualificationKey);
 353  0
                 String replacementValue = getReplacementString(document, qualificationValue);
 354  0
                 qualification.put(qualificationKey, replacementValue);
 355  0
             }
 356  
 
 357  0
             for (String permissionDetailKey : permissionDetails.keySet()) {
 358  0
                 String detailValue = qualification.get(permissionDetailKey);
 359  0
                 String replacementValue = getReplacementString(document, detailValue);
 360  0
                 qualification.put(permissionDetailKey, replacementValue);
 361  0
             }
 362  0
         } catch (Exception e) {
 363  0
             LOG.error(e.getMessage(), e);
 364  0
             return false;
 365  0
         }
 366  0
         return KimApiServiceLocator.getPermissionService().isAuthorized(session.getPrincipalId(),
 367  
                 permissionNamespaceCode, permissionName, permissionDetails, qualification);
 368  
     }
 369  
 
 370  
     private String getReplacementString(Document document, String value) throws Exception {
 371  0
         String startsWith = "${document.";
 372  0
         String endsWith = "}";
 373  0
         if (value.startsWith(startsWith)) {
 374  0
             int tokenStart = value.indexOf(startsWith);
 375  0
             int tokenEnd = value.indexOf(endsWith, tokenStart + startsWith.length());
 376  0
             if (tokenEnd == -1) {
 377  0
                 throw new RuntimeException("No ending bracket on token in value " + value);
 378  
             }
 379  0
             String token = value.substring(tokenStart + startsWith.length(), tokenEnd);
 380  
 
 381  0
             return getRouteHeaderVariableValue(document, token);
 382  
         }
 383  0
         return value;
 384  
 
 385  
     }
 386  
 
 387  
     private String getRouteHeaderVariableValue(Document document, String variableName) throws Exception {
 388  
         Field field;
 389  
         try {
 390  0
             field = document.getClass().getDeclaredField(variableName);
 391  0
         } catch (NoSuchFieldException nsfe) {
 392  0
             LOG.error("Field '" + variableName + "' not found on Document object.");
 393  
             // instead of raising an exception, return null as a value
 394  
             // this leaves it up to proper permission configuration to fail the check if a field value
 395  
             // is required
 396  0
             return null;
 397  0
         }
 398  0
         field.setAccessible(true);
 399  0
         Object fieldValue = field.get(document);
 400  0
         Class<?> clazzType = field.getType();
 401  0
         if (clazzType.equals(String.class)) {
 402  0
             return (String) fieldValue;
 403  0
         } else if (clazzType.getName().equals("boolean") || clazzType.getName().equals("java.lang.Boolean")) {
 404  0
             if ((Boolean) fieldValue) {
 405  0
                 return "Y";
 406  
             }
 407  0
             return "N";
 408  0
         } else if (clazzType.getName().equals("java.util.Calendar")) {
 409  
 
 410  0
             DateTimeService dateTimeService = GlobalResourceLoader.getService(CoreConstants.Services.DATETIME_SERVICE);
 411  0
             return dateTimeService.toDateString(((Calendar) fieldValue).getTime());
 412  
         }
 413  0
         return String.valueOf(fieldValue);
 414  
     }
 415  
 
 416  
     public ExtensionRepositoryService getExtensionRepositoryService() {
 417  0
         return extensionRepositoryService;
 418  
     }
 419  
 
 420  
     public void setExtensionRepositoryService(ExtensionRepositoryService extensionRepositoryService) {
 421  0
         this.extensionRepositoryService = extensionRepositoryService;
 422  0
     }
 423  
 
 424  
     /**
 425  
      * Simple class which defines the key of a partition of security attributes associated with an application id.
 426  
      *
 427  
      * <p>This class allows direct field access since it is intended for internal use only.</p>
 428  
      */
 429  0
     private static final class PartitionKey {
 430  
         String applicationId;
 431  
         Set<String> documentSecurityAttributeNames;
 432  
 
 433  0
         PartitionKey(String applicationId, Collection<ExtensionDefinition> extensionDefinitions) {
 434  0
             this.applicationId = applicationId;
 435  0
             this.documentSecurityAttributeNames = new HashSet<String>();
 436  0
             for (ExtensionDefinition extensionDefinition : extensionDefinitions) {
 437  0
                 this.documentSecurityAttributeNames.add(extensionDefinition.getName());
 438  
             }
 439  0
         }
 440  
 
 441  
         List<String> getDocumentSecurityAttributeNameList() {
 442  0
             return new ArrayList<String>(documentSecurityAttributeNames);
 443  
         }
 444  
 
 445  
         @Override
 446  
         public boolean equals(Object o) {
 447  0
             if (!(o instanceof PartitionKey)) {
 448  0
                 return false;
 449  
             }
 450  0
             PartitionKey key = (PartitionKey) o;
 451  0
             EqualsBuilder builder = new EqualsBuilder();
 452  0
             builder.append(applicationId, key.applicationId);
 453  0
             builder.append(documentSecurityAttributeNames, key.documentSecurityAttributeNames);
 454  0
             return builder.isEquals();
 455  
         }
 456  
 
 457  
         @Override
 458  
         public int hashCode() {
 459  0
             HashCodeBuilder builder = new HashCodeBuilder();
 460  0
             builder.append(applicationId);
 461  0
             builder.append(documentSecurityAttributeNames);
 462  0
             return builder.hashCode();
 463  
         }
 464  
     }
 465  
 
 466  
 }