1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * 19 */ 20 package org.apache.directory.server.core.api.subtree; 21 22 23 import org.apache.directory.api.ldap.model.entry.Entry; 24 import org.apache.directory.api.ldap.model.exception.LdapException; 25 import org.apache.directory.api.ldap.model.name.Dn; 26 import org.apache.directory.api.ldap.model.schema.SchemaManager; 27 import org.apache.directory.api.ldap.model.subtree.SubtreeSpecification; 28 import org.apache.directory.server.core.api.event.Evaluator; 29 import org.apache.directory.server.core.api.event.ExpressionEvaluator; 30 31 32 /** 33 * An evaluator used to determine if an entry is included in the collection 34 * represented by a subtree specification. 35 * 36 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 37 */ 38 public class SubtreeEvaluator 39 { 40 /** A refinement filter evaluator */ 41 private final Evaluator evaluator; 42 43 44 /** 45 * Creates a subtreeSpecification evaluatior which can be used to determine 46 * if an entry is included within the collection of a subtree. 47 * 48 * @param schemaManager The server schemaManager 49 */ 50 public SubtreeEvaluator( SchemaManager schemaManager ) 51 { 52 evaluator = new ExpressionEvaluator( schemaManager ); 53 } 54 55 56 /** 57 * Determines if an entry is selected by a subtree specification. 58 * 59 * @param subtree the subtree specification 60 * @param apDn the distinguished name of the administrative point containing the subentry 61 * @param entryDn the distinguished name of the candidate entry 62 * @param entry The entry to evaluate 63 * @return true if the entry is selected by the specification, false if it is not 64 * @throws LdapException if errors are encountered while evaluating selection 65 */ 66 public boolean evaluate( SubtreeSpecification subtree, Dn apDn, Dn entryDn, Entry entry ) 67 throws LdapException 68 { 69 /* ===================================================================== 70 * NOTE: Regarding the overall approach, we try to narrow down the 71 * possibilities by slowly pruning relative names off of the entryDn. 72 * For example we check first if the entry is a descendant of the AP. 73 * If so we use the relative name thereafter to calculate if it is 74 * a descendant of the base. This means shorter names to compare and 75 * less work to do while we continue to deduce inclusion by the subtree 76 * specification. 77 * ===================================================================== 78 */ 79 // First construct the subtree base, which is the concatenation of the 80 // AP Dn and the subentry base 81 Dn subentryBaseDn = apDn; 82 subentryBaseDn = subentryBaseDn.add( subtree.getBase() ); 83 84 if ( !entryDn.isDescendantOf( subentryBaseDn ) ) 85 { 86 // The entry Dn is not part of the subtree specification, get out 87 return false; 88 } 89 90 /* 91 * Evaluate based on minimum and maximum chop values. Here we simply 92 * need to compare the distances respectively with the size of the 93 * baseRelativeRdn. For the max distance entries with a baseRelativeRdn 94 * size greater than the max distance are rejected. For the min distance 95 * entries with a baseRelativeRdn size less than the minimum distance 96 * are rejected. 97 */ 98 int entryRelativeDnSize = entryDn.size() - subentryBaseDn.size(); 99 100 if ( ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX ) 101 && ( entryRelativeDnSize > subtree.getMaxBaseDistance() ) ) 102 { 103 return false; 104 } 105 106 if ( ( subtree.getMinBaseDistance() > 0 ) && ( entryRelativeDnSize < subtree.getMinBaseDistance() ) ) 107 { 108 return false; 109 } 110 111 /* 112 * For specific exclusions we must iterate through the set and check 113 * if the baseRelativeRdn is a descendant of the exclusion. The 114 * isDescendant() function will return true if the compared names 115 * are equal so for chopAfter exclusions we must check for equality 116 * as well and reject if the relative names are equal. 117 */ 118 // Now, get the entry's relative part 119 120 if ( !subtree.getChopBeforeExclusions().isEmpty() || !subtree.getChopAfterExclusions().isEmpty() ) 121 { 122 Dn entryRelativeDn = entryDn.getDescendantOf( apDn ).getDescendantOf( subtree.getBase() ); 123 124 for ( Dn chopBeforeDn : subtree.getChopBeforeExclusions() ) 125 { 126 if ( entryRelativeDn.isDescendantOf( chopBeforeDn ) ) 127 { 128 return false; 129 } 130 } 131 132 for ( Dn chopAfterDn : subtree.getChopAfterExclusions() ) 133 { 134 if ( entryRelativeDn.isDescendantOf( chopAfterDn ) && !chopAfterDn.equals( entryRelativeDn ) ) 135 { 136 return false; 137 } 138 } 139 } 140 141 /* 142 * The last remaining step is to check and see if the refinement filter 143 * selects the entry candidate based on objectClass attribute values. 144 * To do this we invoke the refinement evaluator members evaluate() method. 145 */ 146 if ( subtree.getRefinement() != null ) 147 { 148 return evaluator.evaluate( subtree.getRefinement(), entryDn, entry ); 149 } 150 151 /* 152 * If nothing has rejected the candidate entry and there is no refinement 153 * filter then the entry is included in the collection represented by the 154 * subtree specification so we return true. 155 */ 156 return true; 157 } 158 }