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