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.server.config.server.EntryDNVirtualAttributeCfg; 032import org.forgerock.opendj.ldap.schema.MatchingRule; 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 /** Creates a new instance of this entryDN virtual attribute provider. */ 054 public EntryDNVirtualAttributeProvider() 055 { 056 super(); 057 058 // All initialization should be performed in the 059 // initializeVirtualAttributeProvider method. 060 } 061 062 @Override 063 public boolean isMultiValued() 064 { 065 return false; 066 } 067 068 @Override 069 public Attribute getValues(Entry entry, VirtualAttributeRule rule) 070 { 071 String dnString = entry.getName().toString(); 072 return Attributes.create(rule.getAttributeType(), dnString); 073 } 074 075 @Override 076 public boolean hasValue(Entry entry, VirtualAttributeRule rule) 077 { 078 // This virtual attribute provider will always generate a value. 079 return true; 080 } 081 082 @Override 083 public boolean hasValue(Entry entry, VirtualAttributeRule rule, ByteString value) 084 { 085 try 086 { 087 MatchingRule eqRule = rule.getAttributeType().getEqualityMatchingRule(); 088 ByteString dn = ByteString.valueOfUtf8(entry.getName().toString()); 089 ByteString normalizedDN = eqRule.normalizeAttributeValue(dn); 090 ByteString normalizedValue = eqRule.normalizeAttributeValue(value); 091 return normalizedDN.equals(normalizedValue); 092 } 093 catch (DecodeException e) 094 { 095 logger.traceException(e); 096 return false; 097 } 098 } 099 100 @Override 101 public ConditionResult matchesSubstring(Entry entry, 102 VirtualAttributeRule rule, 103 ByteString subInitial, 104 List<ByteString> subAny, 105 ByteString subFinal) 106 { 107 // DNs cannot be used in substring matching. 108 return ConditionResult.UNDEFINED; 109 } 110 111 @Override 112 public ConditionResult greaterThanOrEqualTo(Entry entry, 113 VirtualAttributeRule rule, 114 ByteString value) 115 { 116 // DNs cannot be used in ordering matching. 117 return ConditionResult.UNDEFINED; 118 } 119 120 @Override 121 public ConditionResult lessThanOrEqualTo(Entry entry, 122 VirtualAttributeRule rule, 123 ByteString value) 124 { 125 // DNs cannot be used in ordering matching. 126 return ConditionResult.UNDEFINED; 127 } 128 129 @Override 130 public ConditionResult approximatelyEqualTo(Entry entry, 131 VirtualAttributeRule rule, 132 ByteString value) 133 { 134 // DNs cannot be used in approximate matching. 135 return ConditionResult.UNDEFINED; 136 } 137 138 /** 139 * {@inheritDoc}. This virtual attribute will support search operations only 140 * if one of the following is true about the search filter: 141 * <UL> 142 * <LI>It is an equality filter targeting the associated attribute 143 * type.</LI> 144 * <LI>It is an AND filter in which at least one of the components is an 145 * equality filter targeting the associated attribute type.</LI> 146 * <LI>It is an OR filter in which all of the components are equality 147 * filters targeting the associated attribute type.</LI> 148 * </UL> 149 * This virtual attribute also can be optimized as pre-indexed. 150 */ 151 @Override 152 public boolean isSearchable(VirtualAttributeRule rule, 153 SearchOperation searchOperation, 154 boolean isPreIndexed) 155 { 156 return isSearchable(rule.getAttributeType(), searchOperation.getFilter(), 0); 157 } 158 159 /** 160 * Indicates whether the provided search filter is one that may be used with 161 * this virtual attribute provider, optionally operating in a recursive manner 162 * to make the determination. 163 * 164 * @param attributeType The attribute type used to hold the entryDN value. 165 * @param searchFilter The search filter for which to make the 166 * determination. 167 * @param depth The current recursion depth for this processing. 168 * 169 * @return {@code true} if the provided filter may be used with this virtual 170 * attribute provider, or {@code false} if not. 171 */ 172 private boolean isSearchable(AttributeType attributeType, SearchFilter filter, 173 int depth) 174 { 175 switch (filter.getFilterType()) 176 { 177 case AND: 178 if (depth >= MAX_NESTED_FILTER_DEPTH) 179 { 180 return false; 181 } 182 183 for (SearchFilter f : filter.getFilterComponents()) 184 { 185 if (isSearchable(attributeType, f, depth+1)) 186 { 187 return true; 188 } 189 } 190 return false; 191 192 case OR: 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 false; 203 } 204 } 205 return true; 206 207 case EQUALITY: 208 return filter.getAttributeType().equals(attributeType); 209 210 default: 211 return false; 212 } 213 } 214 215 @Override 216 public void processSearch(VirtualAttributeRule rule, 217 SearchOperation searchOperation) 218 { 219 SearchFilter filter = searchOperation.getFilter(); 220 Set<DN> dnSet = new LinkedHashSet<>(); 221 extractDNs(rule.getAttributeType(), filter, dnSet); 222 223 if (dnSet.isEmpty()) 224 { 225 return; 226 } 227 228 DN baseDN = searchOperation.getBaseDN(); 229 SearchScope scope = searchOperation.getScope(); 230 for (DN dn : dnSet) 231 { 232 if (! dn.isInScopeOf(baseDN, scope)) 233 { 234 continue; 235 } 236 237 try 238 { 239 Entry entry = DirectoryServer.getEntry(dn); 240 if (entry != null && filter.matchesEntry(entry)) 241 { 242 searchOperation.returnEntry(entry, null); 243 } 244 } 245 catch (Exception e) 246 { 247 logger.traceException(e); 248 } 249 } 250 } 251 252 /** 253 * Extracts the user DNs from the provided filter, operating recursively as 254 * necessary, and adds them to the provided set. 255 * 256 * @param attributeType The attribute type holding the entryDN value. 257 * @param filter The search filter to be processed. 258 * @param dnSet The set into which the identified DNs should be 259 * placed. 260 */ 261 private void extractDNs(AttributeType attributeType, SearchFilter filter, Set<DN> dnSet) 262 { 263 switch (filter.getFilterType()) 264 { 265 case AND: 266 case OR: 267 for (SearchFilter f : filter.getFilterComponents()) 268 { 269 extractDNs(attributeType, f, dnSet); 270 } 271 break; 272 273 case EQUALITY: 274 if (filter.getAttributeType().equals(attributeType)) 275 { 276 try 277 { 278 dnSet.add(DN.valueOf(filter.getAssertionValue())); 279 } 280 catch (LocalizedIllegalArgumentException e) 281 { 282 logger.traceException(e); 283 } 284 } 285 break; 286 } 287 } 288}