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