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