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.Collections; 032 import java.util.Iterator; 033 import java.util.LinkedHashSet; 034 import java.util.List; 035 import java.util.Set; 036 037 import org.opends.messages.Message; 038 import org.opends.server.admin.std.server.DynamicGroupImplementationCfg; 039 import org.opends.server.api.Group; 040 import org.opends.server.config.ConfigException; 041 import org.opends.server.loggers.ErrorLogger; 042 import org.opends.server.loggers.debug.DebugTracer; 043 import org.opends.server.types.Attribute; 044 import org.opends.server.types.AttributeType; 045 import org.opends.server.types.AttributeValue; 046 import org.opends.server.types.DebugLogLevel; 047 import org.opends.server.types.DirectoryConfig; 048 import org.opends.server.types.DirectoryException; 049 import org.opends.server.types.DN; 050 import org.opends.server.types.Entry; 051 import org.opends.server.types.InitializationException; 052 import org.opends.server.types.LDAPURL; 053 import org.opends.server.types.MemberList; 054 import org.opends.server.types.ObjectClass; 055 import org.opends.server.types.SearchFilter; 056 import org.opends.server.types.SearchScope; 057 058 import static org.opends.messages.ExtensionMessages.*; 059 import static org.opends.server.config.ConfigConstants.*; 060 import static org.opends.server.loggers.debug.DebugLogger.*; 061 import static org.opends.server.util.ServerConstants.*; 062 import static org.opends.server.util.Validator.*; 063 064 065 066 /** 067 * This class provides a dynamic group implementation, in which 068 * membership is determined dynamically based on criteria provided 069 * in the form of one or more LDAP URLs. All dynamic groups should 070 * contain the groupOfURLs object class, with the memberURL attribute 071 * specifying the membership criteria. 072 */ 073 public class DynamicGroup 074 extends Group<DynamicGroupImplementationCfg> 075 { 076 /** 077 * The tracer object for the debug logger. 078 */ 079 private static final DebugTracer TRACER = getTracer(); 080 081 // The DN of the entry that holds the definition for this group. 082 private DN groupEntryDN; 083 084 // The set of the LDAP URLs that define the membership criteria. 085 private LinkedHashSet<LDAPURL> memberURLs; 086 087 088 089 /** 090 * Creates a new, uninitialized dynamic group instance. This is intended for 091 * internal use only. 092 */ 093 public DynamicGroup() 094 { 095 super(); 096 097 // No initialization is required here. 098 } 099 100 101 102 /** 103 * Creates a new dynamic group instance with the provided information. 104 * 105 * @param groupEntryDN The DN of the entry that holds the definition for 106 * this group. It must not be {@code null}. 107 * @param memberURLs The set of LDAP URLs that define the membership 108 * criteria for this group. It must not be 109 * {@code null}. 110 */ 111 public DynamicGroup(DN groupEntryDN, LinkedHashSet<LDAPURL> memberURLs) 112 { 113 super(); 114 115 ensureNotNull(groupEntryDN, memberURLs); 116 117 this.groupEntryDN = groupEntryDN; 118 this.memberURLs = memberURLs; 119 } 120 121 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override() 127 public void initializeGroupImplementation( 128 DynamicGroupImplementationCfg configuration) 129 throws ConfigException, InitializationException 130 { 131 // No additional initialization is required. 132 } 133 134 135 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override() 141 public DynamicGroup newInstance(Entry groupEntry) 142 throws DirectoryException 143 { 144 ensureNotNull(groupEntry); 145 146 147 // Get the memberURL attribute from the entry, if there is one, and parse 148 // out the LDAP URLs that it contains. 149 LinkedHashSet<LDAPURL> memberURLs = new LinkedHashSet<LDAPURL>(); 150 AttributeType memberURLType = 151 DirectoryConfig.getAttributeType(ATTR_MEMBER_URL_LC, true); 152 List<Attribute> attrList = groupEntry.getAttribute(memberURLType); 153 if (attrList != null) 154 { 155 for (Attribute a : attrList) 156 { 157 for (AttributeValue v : a.getValues()) 158 { 159 try 160 { 161 memberURLs.add(LDAPURL.decode(v.getStringValue(), true)); 162 } 163 catch (DirectoryException de) 164 { 165 if (debugEnabled()) 166 { 167 TRACER.debugCaught(DebugLogLevel.ERROR, de); 168 } 169 170 Message message = ERR_DYNAMICGROUP_CANNOT_DECODE_MEMBERURL. 171 get(v.getStringValue(), String.valueOf(groupEntry.getDN()), 172 de.getMessageObject()); 173 ErrorLogger.logError(message); 174 } 175 } 176 } 177 } 178 179 return new DynamicGroup(groupEntry.getDN(), memberURLs); 180 } 181 182 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override() 188 public SearchFilter getGroupDefinitionFilter() 189 throws DirectoryException 190 { 191 // FIXME -- This needs to exclude enhanced groups once we have support for 192 // them. 193 return SearchFilter.createFilterFromString("(" + ATTR_OBJECTCLASS + "=" + 194 OC_GROUP_OF_URLS + ")"); 195 } 196 197 198 199 /** 200 * {@inheritDoc} 201 */ 202 @Override() 203 public boolean isGroupDefinition(Entry entry) 204 { 205 ensureNotNull(entry); 206 207 // FIXME -- This needs to exclude enhanced groups once we have support for 208 //them. 209 ObjectClass groupOfURLsClass = 210 DirectoryConfig.getObjectClass(OC_GROUP_OF_URLS_LC, true); 211 return entry.hasObjectClass(groupOfURLsClass); 212 } 213 214 215 216 /** 217 * {@inheritDoc} 218 */ 219 @Override() 220 public DN getGroupDN() 221 { 222 return groupEntryDN; 223 } 224 225 226 227 /** 228 * Retrieves the set of member URLs for this dynamic group. The returned set 229 * must not be altered by the caller. 230 * 231 * @return The set of member URLs for this dynamic group. 232 */ 233 public Set<LDAPURL> getMemberURLs() 234 { 235 return memberURLs; 236 } 237 238 239 240 /** 241 * {@inheritDoc} 242 */ 243 @Override() 244 public boolean supportsNestedGroups() 245 { 246 // Dynamic groups don't support nesting. 247 return false; 248 } 249 250 251 252 /** 253 * {@inheritDoc} 254 */ 255 @Override() 256 public List<DN> getNestedGroupDNs() 257 { 258 // Dynamic groups don't support nesting. 259 return Collections.<DN>emptyList(); 260 } 261 262 263 264 /** 265 * {@inheritDoc} 266 */ 267 @Override() 268 public void addNestedGroup(DN nestedGroupDN) 269 throws UnsupportedOperationException, DirectoryException 270 { 271 // Dynamic groups don't support nesting. 272 Message message = ERR_DYNAMICGROUP_NESTING_NOT_SUPPORTED.get(); 273 throw new UnsupportedOperationException(message.toString()); 274 } 275 276 277 278 /** 279 * {@inheritDoc} 280 */ 281 @Override() 282 public void removeNestedGroup(DN nestedGroupDN) 283 throws UnsupportedOperationException, DirectoryException 284 { 285 // Dynamic groups don't support nesting. 286 Message message = ERR_DYNAMICGROUP_NESTING_NOT_SUPPORTED.get(); 287 throw new UnsupportedOperationException(message.toString()); 288 } 289 290 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override() 296 public boolean isMember(DN userDN, Set<DN> examinedGroups) 297 throws DirectoryException 298 { 299 if (! examinedGroups.add(getGroupDN())) 300 { 301 return false; 302 } 303 304 Entry entry = DirectoryConfig.getEntry(userDN); 305 if (entry == null) 306 { 307 return false; 308 } 309 else 310 { 311 return isMember(entry); 312 } 313 } 314 315 316 317 /** 318 * {@inheritDoc} 319 */ 320 @Override() 321 public boolean isMember(Entry userEntry, Set<DN> examinedGroups) 322 throws DirectoryException 323 { 324 if (! examinedGroups.add(getGroupDN())) 325 { 326 return false; 327 } 328 329 for (LDAPURL memberURL : memberURLs) 330 { 331 if (memberURL.matchesEntry(userEntry)) 332 { 333 return true; 334 } 335 } 336 337 return false; 338 } 339 340 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override() 346 public MemberList getMembers() 347 throws DirectoryException 348 { 349 return new DynamicGroupMemberList(groupEntryDN, memberURLs); 350 } 351 352 353 354 /** 355 * {@inheritDoc} 356 */ 357 @Override() 358 public MemberList getMembers(DN baseDN, SearchScope scope, 359 SearchFilter filter) 360 throws DirectoryException 361 { 362 if ((baseDN == null) && (filter == null)) 363 { 364 return new DynamicGroupMemberList(groupEntryDN, memberURLs); 365 } 366 else 367 { 368 return new DynamicGroupMemberList(groupEntryDN, memberURLs, baseDN, scope, 369 filter); 370 } 371 } 372 373 374 375 /** 376 * {@inheritDoc} 377 */ 378 @Override() 379 public boolean mayAlterMemberList() 380 { 381 return false; 382 } 383 384 385 386 /** 387 * {@inheritDoc} 388 */ 389 @Override() 390 public void addMember(Entry userEntry) 391 throws UnsupportedOperationException, DirectoryException 392 { 393 // Dynamic groups don't support altering the member list. 394 Message message = ERR_DYNAMICGROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get(); 395 throw new UnsupportedOperationException(message.toString()); 396 } 397 398 399 400 /** 401 * {@inheritDoc} 402 */ 403 @Override() 404 public void removeMember(DN userDN) 405 throws UnsupportedOperationException, DirectoryException 406 { 407 // Dynamic groups don't support altering the member list. 408 Message message = ERR_DYNAMICGROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get(); 409 throw new UnsupportedOperationException(message.toString()); 410 } 411 412 413 414 /** 415 * {@inheritDoc} 416 */ 417 @Override() 418 public void toString(StringBuilder buffer) 419 { 420 buffer.append("DynamicGroup(dn="); 421 buffer.append(groupEntryDN); 422 buffer.append(",urls={"); 423 424 if (! memberURLs.isEmpty()) 425 { 426 Iterator<LDAPURL> iterator = memberURLs.iterator(); 427 buffer.append("\""); 428 iterator.next().toString(buffer, false); 429 430 while (iterator.hasNext()) 431 { 432 buffer.append("\", "); 433 iterator.next().toString(buffer, false); 434 } 435 436 buffer.append("\""); 437 } 438 439 buffer.append("})"); 440 } 441 } 442