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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.extensions; 028 029 030 031 import java.util.Collection; 032 import java.util.LinkedHashSet; 033 import java.util.List; 034 035 import org.opends.server.admin.std.server.IsMemberOfVirtualAttributeCfg; 036 import org.opends.server.api.Group; 037 import org.opends.server.api.VirtualAttributeProvider; 038 import org.opends.server.config.ConfigException; 039 import org.opends.server.core.DirectoryServer; 040 import org.opends.server.core.SearchOperation; 041 import org.opends.server.loggers.debug.DebugTracer; 042 import org.opends.server.types.AttributeType; 043 import org.opends.server.types.AttributeValue; 044 import org.opends.server.types.ByteString; 045 import org.opends.server.types.ConditionResult; 046 import org.opends.server.types.DebugLogLevel; 047 import org.opends.server.types.DirectoryException; 048 import org.opends.server.types.DN; 049 import org.opends.server.types.Entry; 050 import org.opends.server.types.InitializationException; 051 import org.opends.server.types.MemberList; 052 import org.opends.server.types.SearchFilter; 053 import org.opends.server.types.SearchScope; 054 import org.opends.server.types.VirtualAttributeRule; 055 056 import static org.opends.server.loggers.debug.DebugLogger.*; 057 import static org.opends.server.util.ServerConstants.*; 058 059 060 061 /** 062 * This class implements a virtual attribute provider that is meant to serve the 063 * isMemberOf operational attribute. This attribute will be used to provide a 064 * list of all groups in which the specified user is a member. 065 */ 066 public class IsMemberOfVirtualAttributeProvider 067 extends VirtualAttributeProvider<IsMemberOfVirtualAttributeCfg> 068 { 069 /** 070 * The tracer object for the debug logger. 071 */ 072 private static final DebugTracer TRACER = getTracer(); 073 074 /** 075 * Creates a new instance of this entryDN virtual attribute provider. 076 */ 077 public IsMemberOfVirtualAttributeProvider() 078 { 079 super(); 080 081 // All initialization should be performed in the 082 // initializeVirtualAttributeProvider method. 083 } 084 085 086 087 /** 088 * {@inheritDoc} 089 */ 090 @Override() 091 public void initializeVirtualAttributeProvider( 092 IsMemberOfVirtualAttributeCfg configuration) 093 throws ConfigException, InitializationException 094 { 095 // No initialization is required. 096 } 097 098 099 100 /** 101 * {@inheritDoc} 102 */ 103 @Override() 104 public boolean isMultiValued() 105 { 106 return true; 107 } 108 109 110 111 /** 112 * {@inheritDoc} 113 */ 114 @Override() 115 public LinkedHashSet<AttributeValue> getValues(Entry entry, 116 VirtualAttributeRule rule) 117 { 118 // FIXME -- This probably isn't the most efficient implementation. 119 LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(); 120 for (Group g : DirectoryServer.getGroupManager().getGroupInstances()) 121 { 122 try 123 { 124 if (g.isMember(entry)) 125 { 126 values.add(new AttributeValue(rule.getAttributeType(), 127 g.getGroupDN().toString())); 128 } 129 } 130 catch (Exception e) 131 { 132 if (debugEnabled()) 133 { 134 TRACER.debugCaught(DebugLogLevel.ERROR, e); 135 } 136 } 137 } 138 139 return values; 140 } 141 142 143 144 /** 145 * {@inheritDoc} 146 */ 147 @Override() 148 public boolean hasValue(Entry entry, VirtualAttributeRule rule) 149 { 150 // FIXME -- This probably isn't the most efficient implementation. 151 for (Group g : DirectoryServer.getGroupManager().getGroupInstances()) 152 { 153 try 154 { 155 if (g.isMember(entry)) 156 { 157 return true; 158 } 159 } 160 catch (Exception e) 161 { 162 if (debugEnabled()) 163 { 164 TRACER.debugCaught(DebugLogLevel.ERROR, e); 165 } 166 } 167 } 168 169 return false; 170 } 171 172 173 174 /** 175 * {@inheritDoc} 176 */ 177 @Override() 178 public boolean hasValue(Entry entry, VirtualAttributeRule rule, 179 AttributeValue value) 180 { 181 try 182 { 183 DN groupDN = DN.decode(value.getValue()); 184 Group g = DirectoryServer.getGroupManager().getGroupInstance(groupDN); 185 if (g == null) 186 { 187 return false; 188 } 189 else 190 { 191 return g.isMember(entry); 192 } 193 } 194 catch (Exception e) 195 { 196 if (debugEnabled()) 197 { 198 TRACER.debugCaught(DebugLogLevel.ERROR, e); 199 } 200 201 return false; 202 } 203 } 204 205 206 207 /** 208 * {@inheritDoc} 209 */ 210 @Override() 211 public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule, 212 Collection<AttributeValue> values) 213 { 214 for (AttributeValue value : values) 215 { 216 if (hasValue(entry, rule, value)) 217 { 218 return true; 219 } 220 } 221 222 return false; 223 } 224 225 226 227 /** 228 * {@inheritDoc} 229 */ 230 @Override() 231 public ConditionResult matchesSubstring(Entry entry, 232 VirtualAttributeRule rule, 233 ByteString subInitial, 234 List<ByteString> subAny, 235 ByteString subFinal) 236 { 237 // DNs cannot be used in substring matching. 238 return ConditionResult.UNDEFINED; 239 } 240 241 242 243 /** 244 * {@inheritDoc} 245 */ 246 @Override() 247 public ConditionResult greaterThanOrEqualTo(Entry entry, 248 VirtualAttributeRule rule, 249 AttributeValue value) 250 { 251 // DNs cannot be used in ordering matching. 252 return ConditionResult.UNDEFINED; 253 } 254 255 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override() 261 public ConditionResult lessThanOrEqualTo(Entry entry, 262 VirtualAttributeRule rule, 263 AttributeValue value) 264 { 265 // DNs cannot be used in ordering matching. 266 return ConditionResult.UNDEFINED; 267 } 268 269 270 271 /** 272 * {@inheritDoc} 273 */ 274 @Override() 275 public ConditionResult approximatelyEqualTo(Entry entry, 276 VirtualAttributeRule rule, 277 AttributeValue value) 278 { 279 // DNs cannot be used in approximate matching. 280 return ConditionResult.UNDEFINED; 281 } 282 283 284 285 /** 286 * {@inheritDoc}. This virtual attribute will support search operations only 287 * if one of the following is true about the search filter: 288 * <UL> 289 * <LI>It is an equality filter targeting the associated attribute 290 * type.</LI> 291 * <LI>It is an AND filter in which at least one of the components is an 292 * equality filter targeting the associated attribute type.</LI> 293 * </UL> 294 */ 295 @Override() 296 public boolean isSearchable(VirtualAttributeRule rule, 297 SearchOperation searchOperation) 298 { 299 return isSearchable(rule.getAttributeType(), searchOperation.getFilter(), 300 0); 301 } 302 303 304 305 306 /** 307 * Indicates whether the provided search filter is one that may be used with 308 * this virtual attribute provider, optionally operating in a recursive manner 309 * to make the determination. 310 * 311 * @param attributeType The attribute type used to hold the entryDN value. 312 * @param searchFilter The search filter for which to make the 313 * determination. 314 * @param depth The current recursion depth for this processing. 315 * 316 * @return {@code true} if the provided filter may be used with this virtual 317 * attribute provider, or {@code false} if not. 318 */ 319 private boolean isSearchable(AttributeType attributeType, SearchFilter filter, 320 int depth) 321 { 322 switch (filter.getFilterType()) 323 { 324 case AND: 325 if (depth >= MAX_NESTED_FILTER_DEPTH) 326 { 327 return false; 328 } 329 330 for (SearchFilter f : filter.getFilterComponents()) 331 { 332 if (isSearchable(attributeType, f, depth+1)) 333 { 334 return true; 335 } 336 } 337 return false; 338 339 case EQUALITY: 340 return filter.getAttributeType().equals(attributeType); 341 342 default: 343 return false; 344 } 345 } 346 347 348 349 /** 350 * {@inheritDoc} 351 */ 352 @Override() 353 public void processSearch(VirtualAttributeRule rule, 354 SearchOperation searchOperation) 355 { 356 SearchFilter filter = searchOperation.getFilter(); 357 Group group = extractGroup(rule.getAttributeType(), filter); 358 if (group == null) 359 { 360 return; 361 } 362 363 DN baseDN = searchOperation.getBaseDN(); 364 SearchScope scope = searchOperation.getScope(); 365 try 366 { 367 MemberList memberList = group.getMembers(); 368 while (memberList.hasMoreMembers()) 369 { 370 try 371 { 372 Entry e = memberList.nextMemberEntry(); 373 if (e.matchesBaseAndScope(baseDN, scope) && 374 filter.matchesEntry(e)) 375 { 376 searchOperation.returnEntry(e, null); 377 } 378 } 379 catch (Exception e) 380 { 381 if (debugEnabled()) 382 { 383 TRACER.debugCaught(DebugLogLevel.ERROR, e); 384 } 385 } 386 } 387 } 388 catch (DirectoryException de) 389 { 390 searchOperation.setResponseData(de); 391 } 392 } 393 394 395 396 /** 397 * Extracts the first group DN encountered in the provided filter, operating 398 * recursively as necessary. 399 * 400 * @param attributeType The attribute type holding the entryDN value. 401 * @param filter The search filter to be processed. 402 * 403 * @return The first group encountered in the provided filter, or 404 * {@code null} if there is no match. 405 */ 406 private Group extractGroup(AttributeType attributeType, SearchFilter filter) 407 { 408 switch (filter.getFilterType()) 409 { 410 case AND: 411 for (SearchFilter f : filter.getFilterComponents()) 412 { 413 Group g = extractGroup(attributeType, f); 414 if (g != null) 415 { 416 return g; 417 } 418 } 419 break; 420 421 case EQUALITY: 422 if (filter.getAttributeType().equals(attributeType)) 423 { 424 try 425 { 426 DN dn = DN.decode(filter.getAssertionValue().getValue()); 427 return DirectoryServer.getGroupManager().getGroupInstance(dn); 428 } 429 catch (Exception e) 430 { 431 if (debugEnabled()) 432 { 433 TRACER.debugCaught(DebugLogLevel.ERROR, e); 434 } 435 } 436 } 437 break; 438 } 439 440 return null; 441 } 442 } 443