001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2012-2016 ForgeRock AS. 016 */ 017package org.opends.server.extensions; 018 019import java.util.LinkedHashSet; 020import java.util.List; 021import java.util.Set; 022 023import org.forgerock.i18n.LocalizedIllegalArgumentException; 024import org.forgerock.i18n.slf4j.LocalizedLogger; 025import org.forgerock.opendj.ldap.ByteString; 026import org.forgerock.opendj.ldap.ConditionResult; 027import org.forgerock.opendj.ldap.DN; 028import org.forgerock.opendj.ldap.DecodeException; 029import org.forgerock.opendj.ldap.SearchScope; 030import org.forgerock.opendj.ldap.schema.AttributeType; 031import org.forgerock.opendj.ldap.schema.MatchingRule; 032import org.opends.server.admin.std.server.EntryDNVirtualAttributeCfg; 033import org.opends.server.api.VirtualAttributeProvider; 034import org.opends.server.core.DirectoryServer; 035import org.opends.server.core.SearchOperation; 036import org.opends.server.types.Attribute; 037import org.opends.server.types.Attributes; 038import org.opends.server.types.Entry; 039import org.opends.server.types.SearchFilter; 040import org.opends.server.types.VirtualAttributeRule; 041 042import static org.opends.server.util.ServerConstants.*; 043 044/** 045 * This class implements a virtual attribute provider that is meant to serve the 046 * entryDN operational attribute as described in draft-zeilenga-ldap-entrydn. 047 */ 048public class EntryDNVirtualAttributeProvider 049 extends VirtualAttributeProvider<EntryDNVirtualAttributeCfg> 050{ 051 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 052 053 /** 054 * Creates a new instance of this entryDN virtual attribute provider. 055 */ 056 public EntryDNVirtualAttributeProvider() 057 { 058 super(); 059 060 // All initialization should be performed in the 061 // initializeVirtualAttributeProvider method. 062 } 063 064 /** {@inheritDoc} */ 065 @Override 066 public boolean isMultiValued() 067 { 068 return false; 069 } 070 071 /** {@inheritDoc} */ 072 @Override 073 public Attribute getValues(Entry entry, VirtualAttributeRule rule) 074 { 075 String dnString = entry.getName().toString(); 076 return Attributes.create(rule.getAttributeType(), dnString); 077 } 078 079 /** {@inheritDoc} */ 080 @Override 081 public boolean hasValue(Entry entry, VirtualAttributeRule rule) 082 { 083 // This virtual attribute provider will always generate a value. 084 return true; 085 } 086 087 /** {@inheritDoc} */ 088 @Override 089 public boolean hasValue(Entry entry, VirtualAttributeRule rule, ByteString value) 090 { 091 try 092 { 093 MatchingRule eqRule = rule.getAttributeType().getEqualityMatchingRule(); 094 ByteString dn = ByteString.valueOfUtf8(entry.getName().toString()); 095 ByteString normalizedDN = eqRule.normalizeAttributeValue(dn); 096 ByteString normalizedValue = eqRule.normalizeAttributeValue(value); 097 return normalizedDN.equals(normalizedValue); 098 } 099 catch (DecodeException e) 100 { 101 logger.traceException(e); 102 return false; 103 } 104 } 105 106 /** {@inheritDoc} */ 107 @Override 108 public ConditionResult matchesSubstring(Entry entry, 109 VirtualAttributeRule rule, 110 ByteString subInitial, 111 List<ByteString> subAny, 112 ByteString subFinal) 113 { 114 // DNs cannot be used in substring matching. 115 return ConditionResult.UNDEFINED; 116 } 117 118 /** {@inheritDoc} */ 119 @Override 120 public ConditionResult greaterThanOrEqualTo(Entry entry, 121 VirtualAttributeRule rule, 122 ByteString value) 123 { 124 // DNs cannot be used in ordering matching. 125 return ConditionResult.UNDEFINED; 126 } 127 128 /** {@inheritDoc} */ 129 @Override 130 public ConditionResult lessThanOrEqualTo(Entry entry, 131 VirtualAttributeRule rule, 132 ByteString value) 133 { 134 // DNs cannot be used in ordering matching. 135 return ConditionResult.UNDEFINED; 136 } 137 138 /** {@inheritDoc} */ 139 @Override 140 public ConditionResult approximatelyEqualTo(Entry entry, 141 VirtualAttributeRule rule, 142 ByteString value) 143 { 144 // DNs cannot be used in approximate matching. 145 return ConditionResult.UNDEFINED; 146 } 147 148 149 150 /** 151 * {@inheritDoc}. This virtual attribute will support search operations only 152 * if one of the following is true about the search filter: 153 * <UL> 154 * <LI>It is an equality filter targeting the associated attribute 155 * type.</LI> 156 * <LI>It is an AND filter in which at least one of the components is an 157 * equality filter targeting the associated attribute type.</LI> 158 * <LI>It is an OR filter in which all of the components are equality 159 * filters targeting the associated attribute type.</LI> 160 * </UL> 161 * This virtual attribute also can be optimized as pre-indexed. 162 */ 163 @Override 164 public boolean isSearchable(VirtualAttributeRule rule, 165 SearchOperation searchOperation, 166 boolean isPreIndexed) 167 { 168 return isSearchable(rule.getAttributeType(), searchOperation.getFilter(), 0); 169 } 170 171 172 173 174 /** 175 * Indicates whether the provided search filter is one that may be used with 176 * this virtual attribute provider, optionally operating in a recursive manner 177 * to make the determination. 178 * 179 * @param attributeType The attribute type used to hold the entryDN value. 180 * @param searchFilter The search filter for which to make the 181 * determination. 182 * @param depth The current recursion depth for this processing. 183 * 184 * @return {@code true} if the provided filter may be used with this virtual 185 * attribute provider, or {@code false} if not. 186 */ 187 private boolean isSearchable(AttributeType attributeType, SearchFilter filter, 188 int depth) 189 { 190 switch (filter.getFilterType()) 191 { 192 case AND: 193 if (depth >= MAX_NESTED_FILTER_DEPTH) 194 { 195 return false; 196 } 197 198 for (SearchFilter f : filter.getFilterComponents()) 199 { 200 if (isSearchable(attributeType, f, depth+1)) 201 { 202 return true; 203 } 204 } 205 return false; 206 207 case OR: 208 if (depth >= MAX_NESTED_FILTER_DEPTH) 209 { 210 return false; 211 } 212 213 for (SearchFilter f : filter.getFilterComponents()) 214 { 215 if (! isSearchable(attributeType, f, depth+1)) 216 { 217 return false; 218 } 219 } 220 return true; 221 222 case EQUALITY: 223 return filter.getAttributeType().equals(attributeType); 224 225 default: 226 return false; 227 } 228 } 229 230 /** {@inheritDoc} */ 231 @Override 232 public void processSearch(VirtualAttributeRule rule, 233 SearchOperation searchOperation) 234 { 235 SearchFilter filter = searchOperation.getFilter(); 236 Set<DN> dnSet = new LinkedHashSet<>(); 237 extractDNs(rule.getAttributeType(), filter, dnSet); 238 239 if (dnSet.isEmpty()) 240 { 241 return; 242 } 243 244 DN baseDN = searchOperation.getBaseDN(); 245 SearchScope scope = searchOperation.getScope(); 246 for (DN dn : dnSet) 247 { 248 if (! dn.isInScopeOf(baseDN, scope)) 249 { 250 continue; 251 } 252 253 try 254 { 255 Entry entry = DirectoryServer.getEntry(dn); 256 if (entry != null && filter.matchesEntry(entry)) 257 { 258 searchOperation.returnEntry(entry, null); 259 } 260 } 261 catch (Exception e) 262 { 263 logger.traceException(e); 264 } 265 } 266 } 267 268 269 270 /** 271 * Extracts the user DNs from the provided filter, operating recursively as 272 * necessary, and adds them to the provided set. 273 * 274 * @param attributeType The attribute type holding the entryDN value. 275 * @param filter The search filter to be processed. 276 * @param dnSet The set into which the identified DNs should be 277 * placed. 278 */ 279 private void extractDNs(AttributeType attributeType, SearchFilter filter, Set<DN> dnSet) 280 { 281 switch (filter.getFilterType()) 282 { 283 case AND: 284 case OR: 285 for (SearchFilter f : filter.getFilterComponents()) 286 { 287 extractDNs(attributeType, f, dnSet); 288 } 289 break; 290 291 case EQUALITY: 292 if (filter.getAttributeType().equals(attributeType)) 293 { 294 try 295 { 296 dnSet.add(DN.valueOf(filter.getAssertionValue())); 297 } 298 catch (LocalizedIllegalArgumentException e) 299 { 300 logger.traceException(e); 301 } 302 } 303 break; 304 } 305 } 306}