Add support for dynamic groups, which use the groupOfURLs object class and the
memberURL attribute type to specify one or more LDAP URLs containing criteria
for membership.
Reviewed By: Andrew Coulbeck
OpenDS Issue Number: 424
3 files added
13 files modified
| | |
| | | objectClass: ds-cfg-branch |
| | | cn: Group Implementations |
| | | |
| | | dn: cn=Dynamic,cn=Group Implementations,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-group-implementation |
| | | cn: Dynamic |
| | | ds-cfg-group-implementation-class: org.opends.server.extensions.DynamicGroup |
| | | ds-cfg-group-implementation-enabled: true |
| | | |
| | | dn: cn=Static,cn=Group Implementations,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-group-implementation |
| | |
| | | STRUCTURAL MUST ( inheritable ) MAY ( blockInheritance ) |
| | | X-ORIGIN 'draft-ietf-ldup-subentry' ) |
| | | objectClasses: ( 2.16.840.1.113730.3.2.33 NAME 'groupOfURLs' |
| | | DESC 'Sun-defined objectclass' SUP top MUST ( cn ) |
| | | DESC 'Sun-defined objectclass' SUP top STRUCTURAL MUST ( cn ) |
| | | MAY ( memberURL $ businessCategory $ description $ o $ ou $ owner $ seeAlso ) |
| | | X-ORIGIN 'Sun Java System Directory Server' ) |
| | | |
| | |
| | | * entry is associated. |
| | | * @param searchEntry The search result entry to be sent to |
| | | * the client. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting |
| | | * to send the entry to the client and |
| | | * the search should be terminated. |
| | | */ |
| | | public abstract void sendSearchEntry( |
| | | SearchOperation searchOperation, |
| | | SearchResultEntry searchEntry); |
| | | SearchResultEntry searchEntry) |
| | | throws DirectoryException; |
| | | |
| | | |
| | | |
| | |
| | | * referrals, or <CODE>false</CODE> if the client cannot |
| | | * handle referrals and no more attempts should be made to |
| | | * send them for the associated search operation. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting |
| | | * to send the reference to the client |
| | | * and the search should be terminated. |
| | | */ |
| | | public abstract boolean sendSearchReference( |
| | | SearchOperation searchOperation, |
| | | SearchResultReference searchReference); |
| | | SearchResultReference searchReference) |
| | | throws DirectoryException; |
| | | |
| | | |
| | | |
| | |
| | | // Send the entry to the client. |
| | | if (pluginResult.sendEntry()) |
| | | { |
| | | clientConnection.sendSearchEntry(this, searchEntry); |
| | | try |
| | | { |
| | | clientConnection.sendSearchEntry(this, searchEntry); |
| | | |
| | | // Log the entry sent to the client. |
| | | logSearchResultEntry(this, searchEntry); |
| | | // Log the entry sent to the client. |
| | | logSearchResultEntry(this, searchEntry); |
| | | |
| | | entriesSent++; |
| | | entriesSent++; |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | return pluginResult.continueSearch(); |
| | | } |
| | | |
| | |
| | | // to send any more. |
| | | if (pluginResult.sendReference()) |
| | | { |
| | | if (clientConnection.sendSearchReference(this, reference)) |
| | | try |
| | | { |
| | | // Log the entry sent to the client. |
| | | logSearchResultReference(this, reference); |
| | | referencesSent++; |
| | | if (clientConnection.sendSearchReference(this, reference)) |
| | | { |
| | | // Log the entry sent to the client. |
| | | logSearchResultReference(this, reference); |
| | | referencesSent++; |
| | | |
| | | // FIXME -- Should the size limit apply here? |
| | | // FIXME -- Should the size limit apply here? |
| | | } |
| | | else |
| | | { |
| | | // We know that the client can't handle referrals, so we won't try to |
| | | // send it any more. |
| | | clientAcceptsReferrals = false; |
| | | } |
| | | } |
| | | else |
| | | catch (DirectoryException de) |
| | | { |
| | | // We know that the client can't handle referrals, so we won't try to |
| | | // send it any more. |
| | | clientAcceptsReferrals = false; |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | return false; |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, |
| | | * add the following below this CDDL HEADER, with the fields enclosed |
| | | * by brackets "[]" replaced with your own identifying information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | | |
| | | |
| | | import java.util.Collections; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import org.opends.server.api.Group; |
| | | import org.opends.server.config.ConfigEntry; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DirectoryConfig; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.LDAPURL; |
| | | import org.opends.server.types.MemberList; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.loggers.Error.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.messages.ExtensionsMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.Validator.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class provides a dynamic group implementation, in which |
| | | * membership is determined dynamically based on criteria provided |
| | | * in the form of one or more LDAP URLs. All dynamic groups should |
| | | * contain the groupOfURLs object class, with the memberURL attribute |
| | | * specifying the membership criteria. |
| | | */ |
| | | public class DynamicGroup |
| | | extends Group |
| | | { |
| | | // The DN of the entry that holds the definition for this group. |
| | | private DN groupEntryDN; |
| | | |
| | | // The set of the LDAP URLs that define the membership criteria. |
| | | private LinkedHashSet<LDAPURL> memberURLs; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new, uninitialized dynamic group instance. This is intended for |
| | | * internal use only. |
| | | */ |
| | | public DynamicGroup() |
| | | { |
| | | super(); |
| | | |
| | | // No initialization is required here. |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new dynamic group instance with the provided information. |
| | | * |
| | | * @param groupEntryDN The DN of the entry that holds the definition for |
| | | * this group. It must not be {@code null}. |
| | | * @param memberURLs The set of LDAP URLs that define the membership |
| | | * criteria for this group. It must not be |
| | | * {@code null}. |
| | | */ |
| | | public DynamicGroup(DN groupEntryDN, LinkedHashSet<LDAPURL> memberURLs) |
| | | { |
| | | super(); |
| | | |
| | | ensureNotNull(groupEntryDN, memberURLs); |
| | | |
| | | this.groupEntryDN = groupEntryDN; |
| | | this.memberURLs = memberURLs; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void initializeGroupImplementation(ConfigEntry configEntry) |
| | | throws ConfigException, InitializationException |
| | | { |
| | | // No additional initialization is required. |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public DynamicGroup newInstance(Entry groupEntry) |
| | | throws DirectoryException |
| | | { |
| | | ensureNotNull(groupEntry); |
| | | |
| | | |
| | | // Get the memberURL attribute from the entry, if there is one, and parse |
| | | // out the LDAP URLs that it contains. |
| | | LinkedHashSet<LDAPURL> memberURLs = new LinkedHashSet<LDAPURL>(); |
| | | AttributeType memberURLType = |
| | | DirectoryConfig.getAttributeType(ATTR_MEMBER_URL_LC, true); |
| | | List<Attribute> attrList = groupEntry.getAttribute(memberURLType); |
| | | if (attrList != null) |
| | | { |
| | | for (Attribute a : attrList) |
| | | { |
| | | for (AttributeValue v : a.getValues()) |
| | | { |
| | | try |
| | | { |
| | | memberURLs.add(LDAPURL.decode(v.getStringValue(), true)); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | int msgID = MSGID_DYNAMICGROUP_CANNOT_DECODE_MEMBERURL; |
| | | String message = getMessage(msgID, v.getStringValue(), |
| | | String.valueOf(groupEntry.getDN()), |
| | | de.getErrorMessage()); |
| | | logError(ErrorLogCategory.EXTENSIONS, ErrorLogSeverity.MILD_ERROR, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return new DynamicGroup(groupEntry.getDN(), memberURLs); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public SearchFilter getGroupDefinitionFilter() |
| | | throws DirectoryException |
| | | { |
| | | // FIXME -- This needs to exclude enhanced groups once we have support for |
| | | // them. |
| | | return SearchFilter.createFilterFromString("(" + ATTR_OBJECTCLASS + "=" + |
| | | OC_GROUP_OF_URLS + ")"); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean isGroupDefinition(Entry entry) |
| | | { |
| | | ensureNotNull(entry); |
| | | |
| | | // FIXME -- This needs to exclude enhanced groups once we have support for |
| | | //them. |
| | | ObjectClass groupOfURLsClass = |
| | | DirectoryConfig.getObjectClass(OC_GROUP_OF_URLS_LC, true); |
| | | return entry.hasObjectClass(groupOfURLsClass); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public DN getGroupDN() |
| | | { |
| | | return groupEntryDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of member URLs for this dynamic group. The returned set |
| | | * must not be altered by the caller. |
| | | * |
| | | * @return The set of member URLs for this dynamic group. |
| | | */ |
| | | public Set<LDAPURL> getMemberURLs() |
| | | { |
| | | return memberURLs; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean supportsNestedGroups() |
| | | { |
| | | // Dynamic groups don't support nesting. |
| | | return false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public List<DN> getNestedGroupDNs() |
| | | { |
| | | // Dynamic groups don't support nesting. |
| | | return Collections.<DN>emptyList(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void addNestedGroup(DN nestedGroupDN) |
| | | throws UnsupportedOperationException, DirectoryException |
| | | { |
| | | // Dynamic groups don't support nesting. |
| | | int msgID = MSGID_DYNAMICGROUP_NESTING_NOT_SUPPORTED; |
| | | String message = getMessage(msgID); |
| | | throw new UnsupportedOperationException(message); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void removeNestedGroup(DN nestedGroupDN) |
| | | throws UnsupportedOperationException, DirectoryException |
| | | { |
| | | // Dynamic groups don't support nesting. |
| | | int msgID = MSGID_DYNAMICGROUP_NESTING_NOT_SUPPORTED; |
| | | String message = getMessage(msgID); |
| | | throw new UnsupportedOperationException(message); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean isMember(DN userDN) |
| | | throws DirectoryException |
| | | { |
| | | Entry entry = DirectoryConfig.getEntry(userDN); |
| | | if (entry == null) |
| | | { |
| | | return false; |
| | | } |
| | | else |
| | | { |
| | | return isMember(entry); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean isMember(Entry userEntry) |
| | | throws DirectoryException |
| | | { |
| | | for (LDAPURL memberURL : memberURLs) |
| | | { |
| | | if (memberURL.matchesEntry(userEntry)) |
| | | { |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public MemberList getMembers() |
| | | throws DirectoryException |
| | | { |
| | | return new DynamicGroupMemberList(groupEntryDN, memberURLs); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public MemberList getMembers(DN baseDN, SearchScope scope, |
| | | SearchFilter filter) |
| | | throws DirectoryException |
| | | { |
| | | if ((baseDN == null) && (filter == null)) |
| | | { |
| | | return new DynamicGroupMemberList(groupEntryDN, memberURLs); |
| | | } |
| | | else |
| | | { |
| | | return new DynamicGroupMemberList(groupEntryDN, memberURLs, baseDN, scope, |
| | | filter); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean mayAlterMemberList() |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void addMember(Entry userEntry) |
| | | throws UnsupportedOperationException, DirectoryException |
| | | { |
| | | // Dynamic groups don't support altering the member list. |
| | | int msgID = MSGID_DYNAMICGROUP_ALTERING_MEMBERS_NOT_SUPPORTED; |
| | | String message = getMessage(msgID); |
| | | throw new UnsupportedOperationException(message); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void removeMember(DN userDN) |
| | | throws UnsupportedOperationException, DirectoryException |
| | | { |
| | | // Dynamic groups don't support altering the member list. |
| | | int msgID = MSGID_DYNAMICGROUP_ALTERING_MEMBERS_NOT_SUPPORTED; |
| | | String message = getMessage(msgID); |
| | | throw new UnsupportedOperationException(message); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("DynamicGroup(dn="); |
| | | buffer.append(groupEntryDN); |
| | | buffer.append(",urls={"); |
| | | |
| | | if (! memberURLs.isEmpty()) |
| | | { |
| | | Iterator<LDAPURL> iterator = memberURLs.iterator(); |
| | | buffer.append("\""); |
| | | iterator.next().toString(buffer, false); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append("\", "); |
| | | iterator.next().toString(buffer, false); |
| | | } |
| | | |
| | | buffer.append("\""); |
| | | } |
| | | |
| | | buffer.append("})"); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, |
| | | * add the following below this CDDL HEADER, with the fields enclosed |
| | | * by brackets "[]" replaced with your own identifying information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | | |
| | | |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.Set; |
| | | import java.util.concurrent.LinkedBlockingQueue; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.LDAPURL; |
| | | import org.opends.server.types.MemberList; |
| | | import org.opends.server.types.MembershipException; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines a mechanism that may be used to iterate over the |
| | | * members of a dynamic group, optionally using an additional set of |
| | | * criteria to further filter the results. |
| | | */ |
| | | public class DynamicGroupMemberList |
| | | extends MemberList |
| | | { |
| | | // Indicates whether the search thread has completed its processing. |
| | | private boolean searchesCompleted; |
| | | |
| | | // The base DN to use when filtering the set of group members. |
| | | private final DN baseDN; |
| | | |
| | | // The DN of the entry containing the group definition. |
| | | private final DN groupDN; |
| | | |
| | | // The queue into which results will be placed while they are waiting to be |
| | | // returned. The types of objects that may be placed in this queue are Entry |
| | | // objects to return or MembershipException objects to throw. |
| | | private final LinkedBlockingQueue<Object> resultQueue; |
| | | |
| | | // The search filter to use when filtering the set of group members. |
| | | private final SearchFilter filter; |
| | | |
| | | // The search scope to use when filtering the set of group members. |
| | | private final SearchScope scope; |
| | | |
| | | // The set of LDAP URLs that define the membership criteria. |
| | | private final Set<LDAPURL> memberURLs; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new dynamic group member list with the provided information. |
| | | * |
| | | * @param groupDN The DN of the entry containing the group definition. |
| | | * @param memberURLs The set of LDAP URLs that define the membership |
| | | * criteria for the associated group. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while creating the member |
| | | * list. |
| | | */ |
| | | public DynamicGroupMemberList(DN groupDN, Set<LDAPURL> memberURLs) |
| | | throws DirectoryException |
| | | { |
| | | this(groupDN, memberURLs, null, null, null); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new dynamic group member list with the provided information. |
| | | * |
| | | * @param groupDN The DN of the entry containing the group definition. |
| | | * @param memberURLs The set of LDAP URLs that define the membership |
| | | * criteria for the associated group. |
| | | * @param baseDN The base DN that should be enforced for all entries to |
| | | * return. |
| | | * @param scope The scope that should be enforced for all entries to |
| | | * return. |
| | | * @param filter The filter that should be enforced for all entries to |
| | | * return. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while creating the member |
| | | * list. |
| | | */ |
| | | public DynamicGroupMemberList(DN groupDN, Set<LDAPURL> memberURLs, |
| | | DN baseDN, SearchScope scope, |
| | | SearchFilter filter) |
| | | throws DirectoryException |
| | | { |
| | | this.groupDN = groupDN; |
| | | this.memberURLs = memberURLs; |
| | | this.baseDN = baseDN; |
| | | this.filter = filter; |
| | | |
| | | if (scope == null) |
| | | { |
| | | this.scope = SearchScope.WHOLE_SUBTREE; |
| | | } |
| | | else |
| | | { |
| | | this.scope = scope; |
| | | } |
| | | |
| | | searchesCompleted = false; |
| | | resultQueue = new LinkedBlockingQueue<Object>(10); |
| | | |
| | | |
| | | // We're going to have to perform one or more internal searches in order to |
| | | // get the results. We need to be careful about the way that we construct |
| | | // them in order to avoid the possibility of getting duplicate results, so |
| | | // searches with overlapping bases will need to be combined. |
| | | LinkedHashMap<DN,LinkedList<LDAPURL>> baseDNs = |
| | | new LinkedHashMap<DN,LinkedList<LDAPURL>>(); |
| | | for (LDAPURL memberURL : memberURLs) |
| | | { |
| | | // First, determine the base DN for the search. It needs to be evaluated |
| | | // as relative to both the overall base DN specified in the set of |
| | | // criteria, as well as any other existing base DNs in the same hierarchy. |
| | | DN urlBaseDN = memberURL.getBaseDN(); |
| | | if (baseDN != null) |
| | | { |
| | | if (baseDN.isDescendantOf(urlBaseDN)) |
| | | { |
| | | // The base DN requested by the user is below the base DN for this |
| | | // URL, so we'll use the base DN requested by the user. |
| | | urlBaseDN = baseDN; |
| | | } |
| | | else if (! urlBaseDN.isDescendantOf(baseDN)) |
| | | { |
| | | // The base DN from the URL is outside the base requested by the user, |
| | | // so we can skip this URL altogether. |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | // If this is the first URL, then we can just add it with the base DN. |
| | | // Otherwise, we need to see if it needs to be merged with other URLs in |
| | | // the same hierarchy. |
| | | if (baseDNs.isEmpty()) |
| | | { |
| | | LinkedList<LDAPURL> urlList = new LinkedList<LDAPURL>(); |
| | | urlList.add(memberURL); |
| | | baseDNs.put(urlBaseDN, urlList); |
| | | } |
| | | else |
| | | { |
| | | // See if the specified base DN is already in the map. If so, then |
| | | // just add the new URL to the existing list. |
| | | LinkedList<LDAPURL> urlList = baseDNs.get(urlBaseDN); |
| | | if (urlList == null) |
| | | { |
| | | // There's no existing list for the same base DN, but there might be |
| | | // DNs in an overlapping hierarchy. If so, then use the base DN that |
| | | // is closest to the naming context. If not, then add a new list with |
| | | // the current base DN. |
| | | boolean found = false; |
| | | Iterator<DN> iterator = baseDNs.keySet().iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | DN existingBaseDN = iterator.next(); |
| | | if (urlBaseDN.isDescendantOf(existingBaseDN)) |
| | | { |
| | | // The base DN for the current URL is below an existing base DN, |
| | | // so we can just add this URL to the existing list and be done. |
| | | urlList = baseDNs.get(existingBaseDN); |
| | | urlList.add(memberURL); |
| | | found = true; |
| | | break; |
| | | } |
| | | else if (existingBaseDN.isDescendantOf(urlBaseDN)) |
| | | { |
| | | // The base DN for the current URL is above the existing base DN, |
| | | // so we should use the base DN for the current URL instead of the |
| | | // existing one. |
| | | urlList = baseDNs.get(existingBaseDN); |
| | | urlList.add(memberURL); |
| | | iterator.remove(); |
| | | baseDNs.put(urlBaseDN, urlList); |
| | | found = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (! found) |
| | | { |
| | | urlList = new LinkedList<LDAPURL>(); |
| | | urlList.add(memberURL); |
| | | baseDNs.put(urlBaseDN, urlList); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // There was already a list with the same base DN, so just add the |
| | | // URL. |
| | | urlList.add(memberURL); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // At this point, we should know what base DN(s) we need to use, so we can |
| | | // create the filter to use with that base DN. There are some special-case |
| | | // optimizations that we can do here, but in general the filter will look |
| | | // like "(&(filter)(|(urlFilters)))". |
| | | LinkedHashMap<DN,SearchFilter> searchMap = |
| | | new LinkedHashMap<DN,SearchFilter>(); |
| | | for (DN urlBaseDN : baseDNs.keySet()) |
| | | { |
| | | LinkedList<LDAPURL> urlList = baseDNs.get(urlBaseDN); |
| | | LinkedHashSet<SearchFilter> urlFilters = |
| | | new LinkedHashSet<SearchFilter>(); |
| | | for (LDAPURL url : urlList) |
| | | { |
| | | urlFilters.add(url.getFilter()); |
| | | } |
| | | |
| | | SearchFilter combinedFilter; |
| | | if (filter == null) |
| | | { |
| | | if (urlFilters.size() == 1) |
| | | { |
| | | combinedFilter = urlFilters.iterator().next(); |
| | | } |
| | | else |
| | | { |
| | | combinedFilter = SearchFilter.createORFilter(urlFilters); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (urlFilters.size() == 1) |
| | | { |
| | | SearchFilter urlFilter = urlFilters.iterator().next(); |
| | | if (urlFilter.equals(filter)) |
| | | { |
| | | combinedFilter = filter; |
| | | } |
| | | else |
| | | { |
| | | LinkedHashSet<SearchFilter> filterSet = |
| | | new LinkedHashSet<SearchFilter>(); |
| | | filterSet.add(filter); |
| | | filterSet.add(urlFilter); |
| | | combinedFilter = SearchFilter.createANDFilter(filterSet); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (urlFilters.contains(filter)) |
| | | { |
| | | combinedFilter = filter; |
| | | } |
| | | else |
| | | { |
| | | LinkedHashSet<SearchFilter> filterSet = |
| | | new LinkedHashSet<SearchFilter>(); |
| | | filterSet.add(filter); |
| | | filterSet.add(SearchFilter.createORFilter(urlFilters)); |
| | | combinedFilter = SearchFilter.createANDFilter(filterSet); |
| | | } |
| | | } |
| | | } |
| | | |
| | | searchMap.put(urlBaseDN, combinedFilter); |
| | | } |
| | | |
| | | |
| | | // At this point, we should have all the information we need to perform the |
| | | // searches. Create arrays of the elements for each. |
| | | DN[] baseDNArray = new DN[baseDNs.size()]; |
| | | SearchFilter[] filterArray = new SearchFilter[baseDNArray.length]; |
| | | LDAPURL[][] urlArray = new LDAPURL[baseDNArray.length][]; |
| | | Iterator<DN> iterator = baseDNs.keySet().iterator(); |
| | | for (int i=0; i < baseDNArray.length; i++) |
| | | { |
| | | baseDNArray[i] = iterator.next(); |
| | | filterArray[i] = searchMap.get(baseDNArray[i]); |
| | | |
| | | LinkedList<LDAPURL> urlList = baseDNs.get(baseDNArray[i]); |
| | | urlArray[i] = new LDAPURL[urlList.size()]; |
| | | int j=0; |
| | | for (LDAPURL url : urlList) |
| | | { |
| | | urlArray[i][j++] = url; |
| | | } |
| | | } |
| | | |
| | | |
| | | DynamicGroupSearchThread searchThread = |
| | | new DynamicGroupSearchThread(this, baseDNArray, filterArray, urlArray); |
| | | searchThread.start(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the dynamic group with which this dynamic group member |
| | | * list is associated. |
| | | * |
| | | * @return The DN of the dynamic group with which this dynamic group member |
| | | * list is associated. |
| | | */ |
| | | public final DN getDynamicGroupDN() |
| | | { |
| | | return groupDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates that all of the searches needed to iterate across the member list |
| | | * have completed and there will not be any more results provided. |
| | | */ |
| | | final void setSearchesCompleted() |
| | | { |
| | | searchesCompleted = true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Adds the provided entry to the set of results that should be returned for |
| | | * this member list. |
| | | * |
| | | * @param entry The entry to add to the set of results that should be |
| | | * returned for this member list. |
| | | * |
| | | * @return {@code true} if the entry was added to the result set, or |
| | | * {@code false} if it was not (either because a timeout expired or |
| | | * the attempt was interrupted). If this method returns |
| | | * {@code false}, then the search thread should terminate |
| | | * immediately. |
| | | */ |
| | | final boolean addResult(Entry entry) |
| | | { |
| | | try |
| | | { |
| | | return resultQueue.offer(entry, 10, TimeUnit.SECONDS); |
| | | } |
| | | catch (InterruptedException ie) |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Adds the provided membership exception so that it will be thrown along with |
| | | * the set of results for this member list. |
| | | * |
| | | * @param membershipException The membership exception to be thrown. |
| | | * |
| | | * @return {@code true} if the exception was added to the result set, or |
| | | * {@code false} if it was not (either because a timeout expired or |
| | | * the attempt was interrupted). If this method returns |
| | | * {@code false}, then the search thread should terminate |
| | | * immediately. |
| | | */ |
| | | final boolean addResult(MembershipException membershipException) |
| | | { |
| | | try |
| | | { |
| | | return resultQueue.offer(membershipException, 10, TimeUnit.SECONDS); |
| | | } |
| | | catch (InterruptedException ie) |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean hasMoreMembers() |
| | | { |
| | | while (! searchesCompleted) |
| | | { |
| | | if (resultQueue.peek() != null) |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | try |
| | | { |
| | | Thread.sleep(0, 1000); |
| | | } catch (Exception e) {} |
| | | } |
| | | |
| | | return (resultQueue.peek() != null); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public Entry nextMemberEntry() |
| | | throws MembershipException |
| | | { |
| | | if (! hasMoreMembers()) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | Object result = resultQueue.poll(); |
| | | if (result == null) |
| | | { |
| | | close(); |
| | | return null; |
| | | } |
| | | else if (result instanceof Entry) |
| | | { |
| | | return (Entry) result; |
| | | } |
| | | else if (result instanceof MembershipException) |
| | | { |
| | | MembershipException me = (MembershipException) result; |
| | | if (! me.continueIterating()) |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | throw me; |
| | | } |
| | | |
| | | // We should never get here. |
| | | close(); |
| | | return null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void close() |
| | | { |
| | | searchesCompleted = true; |
| | | resultQueue.clear(); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, |
| | | * add the following below this CDDL HEADER, with the fields enclosed |
| | | * by brackets "[]" replaced with your own identifying information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | | |
| | | |
| | | import java.util.LinkedHashSet; |
| | | |
| | | import org.opends.server.api.DirectoryThread; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.internal.InternalSearchListener; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.types.DereferencePolicy; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.LDAPURL; |
| | | import org.opends.server.types.MembershipException; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchResultEntry; |
| | | import org.opends.server.types.SearchResultReference; |
| | | import org.opends.server.types.SearchScope; |
| | | |
| | | import static org.opends.server.loggers.Error.*; |
| | | import static org.opends.server.messages.ExtensionsMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class implements a Directory Server thread that will be used to perform |
| | | * a background search to retrieve all of the members of a dynamic group. |
| | | * <BR><BR> |
| | | */ |
| | | public class DynamicGroupSearchThread |
| | | // FIXME -- Would it be better to implement this class using an Executor |
| | | // rather than always creating a custom thread? |
| | | extends DirectoryThread |
| | | implements InternalSearchListener |
| | | { |
| | | // The set of base DNs for the search requests. |
| | | private final DN[] baseDNs; |
| | | |
| | | // The member list with which this search thread is associated. |
| | | private final DynamicGroupMemberList memberList; |
| | | |
| | | // A counter used to keep track of which search is currently in progress. |
| | | private int searchCounter; |
| | | |
| | | // The set of member URLs for determining whether entries match the criteria. |
| | | private final LDAPURL[][] memberURLs; |
| | | |
| | | // The set of search filters for the search requests. |
| | | private final SearchFilter[] searchFilters; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new dynamic group search thread that is associated with the |
| | | * provided member list and that will perform the search using the provided |
| | | * information. |
| | | * |
| | | * @param memberList The dynamic group member list with which this thread is |
| | | * associated. |
| | | * @param baseDNs The set of base DNs to use for the search requests. |
| | | * @param filters The set of search filters to use for the search |
| | | * requests. |
| | | * @param memberURLs The set of member URLs to use when determining if |
| | | * entries match the necessary group criteria. |
| | | */ |
| | | public DynamicGroupSearchThread(DynamicGroupMemberList memberList, |
| | | DN[] baseDNs, SearchFilter[] filters, |
| | | LDAPURL[][] memberURLs) |
| | | { |
| | | super("Dynamic Group Search Thread " + memberList.getDynamicGroupDN()); |
| | | |
| | | this.memberList = memberList; |
| | | this.baseDNs = baseDNs; |
| | | this.searchFilters = filters; |
| | | this.memberURLs = memberURLs; |
| | | |
| | | searchCounter = 0; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs the set of searches and provides the results to the associated |
| | | * member list. |
| | | */ |
| | | public void run() |
| | | { |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | LinkedHashSet<String> attributes = new LinkedHashSet<String>(0); |
| | | |
| | | for (searchCounter = 0; searchCounter < baseDNs.length; searchCounter++) |
| | | { |
| | | InternalSearchOperation searchOperation = |
| | | conn.processSearch(baseDNs[searchCounter], SearchScope.WHOLE_SUBTREE, |
| | | DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, |
| | | false, searchFilters[searchCounter], attributes, |
| | | this); |
| | | |
| | | ResultCode resultCode = searchOperation.getResultCode(); |
| | | if (resultCode != ResultCode.SUCCESS) |
| | | { |
| | | if (resultCode == ResultCode.NO_SUCH_OBJECT) |
| | | { |
| | | int msgID = MSGID_DYNAMICGROUP_NONEXISTENT_BASE_DN; |
| | | String message = |
| | | getMessage(msgID, String.valueOf(baseDNs[searchCounter]), |
| | | String.valueOf(memberList.getDynamicGroupDN())); |
| | | logError(ErrorLogCategory.EXTENSIONS, ErrorLogSeverity.MILD_WARNING, |
| | | message, msgID); |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_DYNAMICGROUP_INTERNAL_SEARCH_FAILED; |
| | | String message = |
| | | getMessage(msgID, String.valueOf(baseDNs[searchCounter]), |
| | | String.valueOf(searchFilters[searchCounter]), |
| | | String.valueOf(memberList.getDynamicGroupDN()), |
| | | String.valueOf(resultCode), |
| | | String.valueOf(searchOperation.getErrorMessage())); |
| | | if (! memberList.addResult( |
| | | new MembershipException(msgID, message, true))) |
| | | { |
| | | memberList.setSearchesCompleted(); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | memberList.setSearchesCompleted(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void handleInternalSearchEntry(InternalSearchOperation searchOperation, |
| | | SearchResultEntry searchEntry) |
| | | throws DirectoryException |
| | | { |
| | | for (LDAPURL url : memberURLs[searchCounter]) |
| | | { |
| | | if (url.matchesEntry(searchEntry)) |
| | | { |
| | | if (! memberList.addResult(searchEntry)) |
| | | { |
| | | int msgID = MSGID_DYNAMICGROUP_CANNOT_RETURN_ENTRY; |
| | | String message = getMessage(msgID, |
| | | String.valueOf(searchEntry.getDN()), |
| | | String.valueOf(memberList.getDynamicGroupDN())); |
| | | throw new DirectoryException( |
| | | DirectoryServer.getServerErrorResultCode(), message, |
| | | msgID); |
| | | } |
| | | |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void handleInternalSearchReference( |
| | | InternalSearchOperation searchOperation, |
| | | SearchResultReference searchReference) |
| | | { |
| | | // No implementation required. |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an error occurs while |
| | | * attempting to decode a memberURL value as an LDAP URL. This takes three |
| | | * arguments, which are the value that could not be decoded, the DN of the |
| | | * entry containing the value, and a message explaining the problem that |
| | | * occurred. |
| | | */ |
| | | public static final int MSGID_DYNAMICGROUP_CANNOT_DECODE_MEMBERURL = |
| | | CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 447; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an attempt is made to |
| | | * use nesting in conjunction with a dynamic group. This does not take any |
| | | * arguments. |
| | | */ |
| | | public static final int MSGID_DYNAMICGROUP_NESTING_NOT_SUPPORTED = |
| | | CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 448; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an attempt is made to |
| | | * alter the set of members in a dynamic group. This does not take any |
| | | * arguments. |
| | | */ |
| | | public static final int MSGID_DYNAMICGROUP_ALTERING_MEMBERS_NOT_SUPPORTED = |
| | | CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 449; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a dynamic group |
| | | * includes a member URL with a base DN that doesn't exist. This takes two |
| | | * arguments, which are the base DN and the DN of the dynamic group entry. |
| | | */ |
| | | public static final int MSGID_DYNAMICGROUP_NONEXISTENT_BASE_DN = |
| | | CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_WARNING | 450; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an error occurs while |
| | | * processing an internal search to determine dynamic group membership. This |
| | | * takes five arguments, which are the search base DN, the search filter, |
| | | * the DN of the dynamic group entry, the result code for the search, and the |
| | | * error message for the search. |
| | | */ |
| | | public static final int MSGID_DYNAMICGROUP_INTERNAL_SEARCH_FAILED = |
| | | CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 451; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an error occurs while |
| | | * trying to return an entry for a user that matches a set of dynamic group |
| | | * criteria. This takes two arguments, which are the DN of the entry that |
| | | * could not be returned and the DN of the dynamic group entry. |
| | | */ |
| | | public static final int MSGID_DYNAMICGROUP_CANNOT_RETURN_ENTRY = |
| | | CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 452; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Associates a set of generic messages with the message IDs defined in this |
| | | * class. |
| | | */ |
| | |
| | | registerMessage(MSGID_FCM_MULTIPLE_MATCHING_ENTRIES, |
| | | "The certificate with fingerprint %s could not be mapped " + |
| | | "to exactly one user. It maps to both %s and %s."); |
| | | |
| | | |
| | | registerMessage(MSGID_DYNAMICGROUP_CANNOT_DECODE_MEMBERURL, |
| | | "Unable to decode value \"%s\" in entry \"%s\" as an " + |
| | | "LDAP URL: %s."); |
| | | registerMessage(MSGID_DYNAMICGROUP_NESTING_NOT_SUPPORTED, |
| | | "Dynamic groups do not support nested groups."); |
| | | registerMessage(MSGID_DYNAMICGROUP_ALTERING_MEMBERS_NOT_SUPPORTED, |
| | | "Dynamic groups do not support explicitly altering their " + |
| | | "membership."); |
| | | registerMessage(MSGID_DYNAMICGROUP_NONEXISTENT_BASE_DN, |
| | | "Base DN %s specified in dynamic group %s does not exist " + |
| | | "in the server."); |
| | | registerMessage(MSGID_DYNAMICGROUP_INTERNAL_SEARCH_FAILED, |
| | | "An error occurred while attempting perform an internal " + |
| | | "search with base DN %s and filter %s to resolve the " + |
| | | "member list for dynamic group %s: result code %s, " + |
| | | "error message %s."); |
| | | registerMessage(MSGID_DYNAMICGROUP_CANNOT_RETURN_ENTRY, |
| | | "The server encountered a timeout while attempting to " + |
| | | "add user %s to the member list for dynamic group %s."); |
| | | } |
| | | } |
| | | |
| | |
| | | * entry is associated. |
| | | * @param searchEntry The search result entry to be sent to |
| | | * the client. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while processing |
| | | * the entry and the search should be |
| | | * terminated. |
| | | */ |
| | | public void sendSearchEntry(SearchOperation searchOperation, |
| | | SearchResultEntry searchEntry) |
| | | throws DirectoryException |
| | | { |
| | | ((InternalSearchOperation) searchOperation). |
| | | addSearchEntry(searchEntry); |
| | |
| | | * referrals, or <CODE>false</CODE> if the client cannot |
| | | * handle referrals and no more attempts should be made to |
| | | * send them for the associated search operation. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while processing |
| | | * the entry and the search should be |
| | | * terminated. |
| | | */ |
| | | public boolean sendSearchReference(SearchOperation searchOperation, |
| | | SearchResultReference searchReference) |
| | | throws DirectoryException |
| | | { |
| | | ((InternalSearchOperation) |
| | | searchOperation).addSearchReference(searchReference); |
| | |
| | | |
| | | |
| | | |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.SearchResultEntry; |
| | | import org.opends.server.types.SearchResultReference; |
| | | |
| | |
| | | * processed. |
| | | * @param searchEntry The matching search result entry to be |
| | | * processed. |
| | | * |
| | | * @throws DirectoryException If a problem occurred while handling |
| | | * the provided entry. Search |
| | | * processing will be terminated, and |
| | | * the search operation will result |
| | | * will be set based on this exception. |
| | | */ |
| | | public void handleInternalSearchEntry( |
| | | InternalSearchOperation searchOperation, |
| | | SearchResultEntry searchEntry); |
| | | SearchResultEntry searchEntry) |
| | | throws DirectoryException; |
| | | |
| | | |
| | | |
| | |
| | | * processed. |
| | | * @param searchReference The search result reference to be |
| | | * processed. |
| | | * |
| | | * @throws DirectoryException If a problem occurred while handling |
| | | * the provided entry. Search |
| | | * processing will be terminated, and |
| | | * the search operation will result |
| | | * will be set based on this exception. |
| | | */ |
| | | public void handleInternalSearchReference( |
| | | InternalSearchOperation searchOperation, |
| | | SearchResultReference searchReference); |
| | | SearchResultReference searchReference) |
| | | throws DirectoryException; |
| | | } |
| | | |
| | |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DereferencePolicy; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchResultEntry; |
| | | import org.opends.server.types.SearchResultReference; |
| | |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines a subclass of the core search operation that is |
| | | * to be used for internal searches. The primary difference between |
| | |
| | | public class InternalSearchOperation |
| | | extends SearchOperation |
| | | { |
| | | |
| | | |
| | | |
| | | // The internal search listener for this search, if one was |
| | | // provided. |
| | | private InternalSearchListener searchListener; |
| | |
| | | * |
| | | * @param searchEntry The search result entry returned for this |
| | | * search. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while processing |
| | | * the provided entry and the search |
| | | * should be terminated. |
| | | */ |
| | | public void addSearchEntry(SearchResultEntry searchEntry) |
| | | throws DirectoryException |
| | | { |
| | | if (searchListener == null) |
| | | { |
| | |
| | | * |
| | | * @param searchReference The search result reference returned for |
| | | * this search. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while processing |
| | | * the provided reference and the |
| | | * search should be terminated. |
| | | */ |
| | | public void addSearchReference( |
| | | SearchResultReference searchReference) |
| | | throws DirectoryException |
| | | { |
| | | if (searchListener == null) |
| | | { |
| | |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DereferencePolicy; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.IntermediateResponse; |
| | | import org.opends.server.types.SearchResultEntry; |
| | |
| | | * @param searchOperation The search operation with which the entry is |
| | | * associated. |
| | | * @param searchEntry The search result entry to be sent to the client. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to send |
| | | * the entry to the client and the search should |
| | | * be terminated. |
| | | */ |
| | | public void sendSearchEntry(SearchOperation searchOperation, |
| | | SearchResultEntry searchEntry) |
| | | throws DirectoryException |
| | | { |
| | | ((InternalSearchOperation) searchOperation).addSearchEntry(searchEntry); |
| | | } |
| | |
| | | * <CODE>false</CODE> if the client cannot handle referrals and no |
| | | * more attempts should be made to send them for the associated |
| | | * search operation. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to send |
| | | * the reference to the client and the search |
| | | * should be terminated. |
| | | */ |
| | | public boolean sendSearchReference(SearchOperation searchOperation, |
| | | SearchResultReference searchReference) |
| | | throws DirectoryException |
| | | { |
| | | ((InternalSearchOperation) |
| | | searchOperation).addSearchReference(searchReference); |
| | |
| | | |
| | | import org.opends.server.core.DirectoryServer; |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugCaught; |
| | | import static |
| | | org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.messages.UtilityMessages.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | |
| | | */ |
| | | public class LDAPURL |
| | | { |
| | | |
| | | |
| | | |
| | | /** |
| | | * The default scheme that will be used if none is provided. |
| | | */ |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the provided entry matches the criteria defined |
| | | * in this LDAP URL. |
| | | * |
| | | * @param entry The entry for which to make the determination. |
| | | * |
| | | * @return {@code true} if the provided entry does match the |
| | | * criteria specified in this LDAP URL, or {@code false} if |
| | | * it does not. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting |
| | | * to make the determination. |
| | | */ |
| | | public boolean matchesEntry(Entry entry) |
| | | throws DirectoryException |
| | | { |
| | | SearchScope scope = getScope(); |
| | | if (scope == null) |
| | | { |
| | | scope = SearchScope.BASE_OBJECT; |
| | | } |
| | | |
| | | return (entry.matchesBaseAndScope(getBaseDN(), scope) && |
| | | getFilter().matchesEntry(entry)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the provided object is equal to this LDAP URL. |
| | | * |
| | | * @param o The object for which to make the determination. |
| | |
| | | */ |
| | | public class SearchFilter |
| | | { |
| | | |
| | | |
| | | |
| | | // The attribute type for this filter. |
| | | private final AttributeType attributeType; |
| | | |
| | |
| | | * |
| | | * @return The constructed search filter. |
| | | */ |
| | | public static SearchFilter createANDFilter(List<SearchFilter> |
| | | public static SearchFilter createANDFilter(Collection<SearchFilter> |
| | | filterComponents) |
| | | { |
| | | return new SearchFilter(FilterType.AND, filterComponents, null, |
| | |
| | | * |
| | | * @return The constructed search filter. |
| | | */ |
| | | public static SearchFilter createORFilter(List<SearchFilter> |
| | | public static SearchFilter createORFilter(Collection<SearchFilter> |
| | | filterComponents) |
| | | { |
| | | return new SearchFilter(FilterType.OR, filterComponents, null, |
| | |
| | | |
| | | |
| | | /** |
| | | * The name of the standard "member" attribute type, formatted in all ' |
| | | * The name of the standard "member" attribute type, formatted in all |
| | | * lowercase characters. |
| | | */ |
| | | public static final String ATTR_MEMBER = "member"; |
| | |
| | | |
| | | |
| | | /** |
| | | * The name of the standard "memberURL" attribute type, formatted in camel |
| | | * case. |
| | | */ |
| | | public static final String ATTR_MEMBER_URL = "memberURL"; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The name of the standard "memberURL" attribute type, formatted in all |
| | | * lowercase characters. |
| | | */ |
| | | public static final String ATTR_MEMBER_URL_LC = "memberurl"; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The name of the monitor attribute that is used to hold a backend ID. |
| | | */ |
| | | public static final String ATTR_MONITOR_BACKEND_ID = "ds-backend-id"; |
| | |
| | | |
| | | |
| | | /** |
| | | * The name of the standard "groupOfURLs" object class, formatted in camel |
| | | * case. |
| | | */ |
| | | public static final String OC_GROUP_OF_URLS = "groupOfURLs"; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The name of the standard "groupOfURLs" object class, formatted in all |
| | | * lowercase characters. |
| | | */ |
| | | public static final String OC_GROUP_OF_URLS_LC = "groupofurls"; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The request OID for the cancel extended operation. |
| | | */ |
| | | public static final String OID_CANCEL_REQUEST = "1.3.6.1.1.8"; |
| | |
| | | |
| | | |
| | | |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | |
| | | import org.opends.server.core.DeleteOperation; |
| | | import org.opends.server.core.ModifyOperation; |
| | | import org.opends.server.core.ModifyDNOperation; |
| | | import org.opends.server.extensions.DynamicGroup; |
| | | import org.opends.server.extensions.StaticGroup; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AuthenticationInfo; |
| | | import org.opends.server.types.DereferencePolicy; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.MemberList; |
| | |
| | | { |
| | | GroupManager groupManager = DirectoryServer.getGroupManager(); |
| | | |
| | | assertTrue(groupManager.getGroupImplementations().iterator().hasNext()); |
| | | LinkedHashSet<Class> groupClasses = new LinkedHashSet<Class>(); |
| | | groupClasses.add(StaticGroup.class); |
| | | groupClasses.add(DynamicGroup.class); |
| | | |
| | | for (Group g : groupManager.getGroupImplementations()) |
| | | { |
| | | assertTrue(groupClasses.remove(g.getClass()), |
| | | "Group class " + g.getClass() + " isn't registered"); |
| | | } |
| | | |
| | | assertTrue(groupClasses.isEmpty(), |
| | | "Unexpected group class(es) registered: " + groupClasses); |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | Group groupInstance = groupManager.getGroupInstance(groupDN); |
| | | assertNotNull(groupInstance); |
| | | assertEquals(groupInstance.getGroupDN(), groupDN); |
| | | assertTrue(groupInstance.isMember(user1DN)); |
| | | assertTrue(groupInstance.isMember(user2DN)); |
| | | assertFalse(groupInstance.isMember(user3DN)); |
| | |
| | | assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); |
| | | assertNull(groupManager.getGroupInstance(groupDN)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Invokes general group API methods on a dynamic group. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test() |
| | | public void testGenericDynamicGroupAPI() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.initializeTestBackend(true); |
| | | |
| | | GroupManager groupManager = DirectoryServer.getGroupManager(); |
| | | groupManager.deregisterAllGroups(); |
| | | |
| | | TestCaseUtils.addEntries( |
| | | "dn: ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: People", |
| | | "", |
| | | "dn: ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: Groups", |
| | | "", |
| | | "dn: uid=user.1,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.1", |
| | | "givenName: User", |
| | | "sn: 1", |
| | | "cn: User 1", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.2,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.2", |
| | | "givenName: User", |
| | | "sn: 2", |
| | | "cn: User 2", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.3,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.3", |
| | | "givenName: User", |
| | | "sn: 3", |
| | | "cn: User 3", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: cn=Test Group of URLs,ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: groupOfURLs", |
| | | "cn: Test Group of URLs", |
| | | "memberURL: ldap:///o=test??sub?(sn<=2)"); |
| | | |
| | | DN groupDN = DN.decode("cn=Test Group of URLs,ou=Groups,o=test"); |
| | | DN user1DN = DN.decode("uid=user.1,ou=People,o=test"); |
| | | DN user2DN = DN.decode("uid=user.2,ou=People,o=test"); |
| | | DN user3DN = DN.decode("uid=user.3,ou=People,o=test"); |
| | | DN bogusDN = DN.decode("uid=bogus,ou=People,o=test"); |
| | | |
| | | Group groupInstance = groupManager.getGroupInstance(groupDN); |
| | | assertNotNull(groupInstance); |
| | | assertEquals(groupInstance.getGroupDN(), groupDN); |
| | | assertTrue(groupInstance.isMember(user1DN)); |
| | | assertTrue(groupInstance.isMember(user2DN)); |
| | | assertFalse(groupInstance.isMember(user3DN)); |
| | | assertFalse(groupInstance.isMember(bogusDN)); |
| | | |
| | | assertFalse(groupInstance.supportsNestedGroups()); |
| | | assertTrue(groupInstance.getNestedGroupDNs().isEmpty()); |
| | | |
| | | try |
| | | { |
| | | groupInstance.addNestedGroup(DN.decode("uid=test,ou=People,o=test")); |
| | | throw new AssertionError("Expected addNestedGroup to fail but it " + |
| | | "didn't"); |
| | | } catch (UnsupportedOperationException uoe) {} |
| | | |
| | | try |
| | | { |
| | | groupInstance.removeNestedGroup( |
| | | DN.decode("uid=test,ou=People,o=test")); |
| | | throw new AssertionError("Expected removeNestedGroup to fail but " + |
| | | "it didn't"); |
| | | } catch (UnsupportedOperationException uoe) {} |
| | | |
| | | |
| | | assertFalse(groupInstance.mayAlterMemberList()); |
| | | |
| | | try |
| | | { |
| | | Entry user3Entry = DirectoryServer.getEntry(user3DN); |
| | | groupInstance.addMember(user3Entry); |
| | | throw new AssertionError("Expected addMember to fail but it didn't"); |
| | | } catch (UnsupportedOperationException uoe) {} |
| | | |
| | | try |
| | | { |
| | | groupInstance.removeMember(user2DN); |
| | | throw new AssertionError("Expected removeMember to fail but it didn't"); |
| | | } catch (UnsupportedOperationException uoe) {} |
| | | |
| | | groupInstance.toString(new StringBuilder()); |
| | | |
| | | |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | DeleteOperation deleteOperation = conn.processDelete(groupDN); |
| | | assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); |
| | | assertNull(groupManager.getGroupInstance(groupDN)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Tests to ensure that an attempt to add a dynamic group with a malformed URL |
| | | * will cause it to be decoded as a group but any operations attempted with it |
| | | * will fail with an exception. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test() |
| | | public void testDynamicGroupMalformedURL() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.initializeTestBackend(true); |
| | | |
| | | GroupManager groupManager = DirectoryServer.getGroupManager(); |
| | | groupManager.deregisterAllGroups(); |
| | | |
| | | TestCaseUtils.addEntries( |
| | | "dn: ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: People", |
| | | "", |
| | | "dn: ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: Groups", |
| | | "", |
| | | "dn: uid=user.1,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.1", |
| | | "givenName: User", |
| | | "sn: 1", |
| | | "cn: User 1", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: cn=Test Malformed URL,ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: groupOfURLs", |
| | | "cn: Test Malformed URL", |
| | | "memberURL: ldap:///o=test??sub?(malformed)"); |
| | | |
| | | DN groupDN = DN.decode("cn=Test Malformed URL,ou=Groups,o=test"); |
| | | |
| | | Group groupInstance = groupManager.getGroupInstance(groupDN); |
| | | assertNotNull(groupInstance); |
| | | |
| | | DynamicGroup dynamicGroup = (DynamicGroup) groupInstance; |
| | | assertTrue(dynamicGroup.getMemberURLs().isEmpty()); |
| | | |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | DeleteOperation deleteOperation = conn.processDelete(groupDN); |
| | | assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); |
| | | assertNull(groupManager.getGroupInstance(groupDN)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Tests the {@code getMembers()} method for a dynamic group, using the |
| | | * variant that doesn't take any arguments. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test() |
| | | public void testGetMembersSimple() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.initializeTestBackend(true); |
| | | |
| | | GroupManager groupManager = DirectoryServer.getGroupManager(); |
| | | groupManager.deregisterAllGroups(); |
| | | |
| | | TestCaseUtils.addEntries( |
| | | "dn: ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: People", |
| | | "", |
| | | "dn: ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: Groups", |
| | | "", |
| | | "dn: uid=user.1,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.1", |
| | | "givenName: User", |
| | | "sn: 1", |
| | | "cn: User 1", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.2,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.2", |
| | | "givenName: User", |
| | | "sn: 2", |
| | | "cn: User 2", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.3,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.3", |
| | | "givenName: User", |
| | | "sn: 3", |
| | | "cn: User 3", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: cn=Test Group of URLs,ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: groupOfURLs", |
| | | "cn: Test Group of URLs", |
| | | "memberURL: ldap:///o=test??sub?(sn<=2)"); |
| | | |
| | | DN groupDN = DN.decode("cn=Test Group of URLs,ou=Groups,o=test"); |
| | | DN user1DN = DN.decode("uid=user.1,ou=People,o=test"); |
| | | DN user2DN = DN.decode("uid=user.2,ou=People,o=test"); |
| | | |
| | | Group groupInstance = groupManager.getGroupInstance(groupDN); |
| | | assertNotNull(groupInstance); |
| | | |
| | | |
| | | LinkedHashSet<DN> memberSet = new LinkedHashSet<DN>(); |
| | | memberSet.add(user1DN); |
| | | memberSet.add(user2DN); |
| | | |
| | | MemberList memberList = groupInstance.getMembers(); |
| | | assertNotNull(memberList); |
| | | while (memberList.hasMoreMembers()) |
| | | { |
| | | DN memberDN = memberList.nextMemberDN(); |
| | | assertTrue(memberSet.remove(memberDN), |
| | | "Returned unexpected member " + memberDN.toString()); |
| | | } |
| | | memberList.close(); |
| | | assertTrue(memberSet.isEmpty(), |
| | | "Expected member set to be empty but it was not: " + memberSet); |
| | | |
| | | |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | DeleteOperation deleteOperation = conn.processDelete(groupDN); |
| | | assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); |
| | | assertNull(groupManager.getGroupInstance(groupDN)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Tests the {@code getMembers()} method for a dynamic group, using the |
| | | * variant that takes base, scope, and filter arguments. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test() |
| | | public void testGetMembersComplex() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.initializeTestBackend(true); |
| | | |
| | | GroupManager groupManager = DirectoryServer.getGroupManager(); |
| | | groupManager.deregisterAllGroups(); |
| | | |
| | | TestCaseUtils.addEntries( |
| | | "dn: ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: People", |
| | | "", |
| | | "dn: ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: Groups", |
| | | "", |
| | | "dn: uid=user.1,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.1", |
| | | "givenName: User", |
| | | "sn: 1", |
| | | "cn: User 1", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.2,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.2", |
| | | "givenName: User", |
| | | "sn: 2", |
| | | "cn: User 2", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.3,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.3", |
| | | "givenName: User", |
| | | "sn: 3", |
| | | "cn: User 3", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: cn=Test Group of URLs,ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: groupOfURLs", |
| | | "cn: Test Group of URLs", |
| | | "memberURL: ldap:///o=test??sub?(sn<=2)"); |
| | | |
| | | DN groupDN = DN.decode("cn=Test Group of URLs,ou=Groups,o=test"); |
| | | DN user1DN = DN.decode("uid=user.1,ou=People,o=test"); |
| | | DN user2DN = DN.decode("uid=user.2,ou=People,o=test"); |
| | | |
| | | Group groupInstance = groupManager.getGroupInstance(groupDN); |
| | | assertNotNull(groupInstance); |
| | | |
| | | |
| | | LinkedHashSet<DN> memberSet = new LinkedHashSet<DN>(); |
| | | memberSet.add(user1DN); |
| | | |
| | | MemberList memberList = groupInstance.getMembers( |
| | | DN.decode("ou=people,o=test"), |
| | | SearchScope.SINGLE_LEVEL, |
| | | SearchFilter.createFilterFromString("(sn=1)")); |
| | | assertNotNull(memberList); |
| | | while (memberList.hasMoreMembers()) |
| | | { |
| | | DN memberDN = memberList.nextMemberDN(); |
| | | assertTrue(memberSet.remove(memberDN), |
| | | "Returned unexpected member " + memberDN.toString()); |
| | | } |
| | | memberList.close(); |
| | | assertTrue(memberSet.isEmpty(), |
| | | "Expected member set to be empty but it was not: " + memberSet); |
| | | |
| | | |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | DeleteOperation deleteOperation = conn.processDelete(groupDN); |
| | | assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); |
| | | assertNull(groupManager.getGroupInstance(groupDN)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Tests the {@code getMembers()} method for a dynamic group that contains |
| | | * multiple member URLs containing non-overlapping criteria. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test() |
| | | public void testGetMembersMultipleDistinctURLs() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.initializeTestBackend(true); |
| | | |
| | | GroupManager groupManager = DirectoryServer.getGroupManager(); |
| | | groupManager.deregisterAllGroups(); |
| | | |
| | | TestCaseUtils.addEntries( |
| | | "dn: ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: People", |
| | | "", |
| | | "dn: ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: Groups", |
| | | "", |
| | | "dn: uid=user.1,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.1", |
| | | "givenName: User", |
| | | "sn: 1", |
| | | "cn: User 1", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.2,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.2", |
| | | "givenName: User", |
| | | "sn: 2", |
| | | "cn: User 2", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.3,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.3", |
| | | "givenName: User", |
| | | "sn: 3", |
| | | "cn: User 3", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: cn=Test Group of URLs,ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: groupOfURLs", |
| | | "cn: Test Group of URLs", |
| | | "memberURL: ldap:///o=test??sub?(sn=1)", |
| | | "memberURL: ldap:///o=test??sub?(sn=2)"); |
| | | |
| | | DN groupDN = DN.decode("cn=Test Group of URLs,ou=Groups,o=test"); |
| | | DN user1DN = DN.decode("uid=user.1,ou=People,o=test"); |
| | | DN user2DN = DN.decode("uid=user.2,ou=People,o=test"); |
| | | |
| | | Group groupInstance = groupManager.getGroupInstance(groupDN); |
| | | assertNotNull(groupInstance); |
| | | groupInstance.toString(); |
| | | |
| | | |
| | | LinkedHashSet<DN> memberSet = new LinkedHashSet<DN>(); |
| | | memberSet.add(user1DN); |
| | | memberSet.add(user2DN); |
| | | |
| | | MemberList memberList = groupInstance.getMembers(); |
| | | assertNotNull(memberList); |
| | | while (memberList.hasMoreMembers()) |
| | | { |
| | | DN memberDN = memberList.nextMemberDN(); |
| | | assertTrue(memberSet.remove(memberDN), |
| | | "Returned unexpected member " + memberDN.toString()); |
| | | } |
| | | memberList.close(); |
| | | assertTrue(memberSet.isEmpty(), |
| | | "Expected member set to be empty but it was not: " + memberSet); |
| | | |
| | | |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | DeleteOperation deleteOperation = conn.processDelete(groupDN); |
| | | assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); |
| | | assertNull(groupManager.getGroupInstance(groupDN)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Tests the {@code getMembers()} method for a dynamic group that contains |
| | | * multiple member URLs containing overlapping criteria. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test() |
| | | public void testGetMembersMultipleOverlappingURLs() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.initializeTestBackend(true); |
| | | TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); |
| | | |
| | | GroupManager groupManager = DirectoryServer.getGroupManager(); |
| | | groupManager.deregisterAllGroups(); |
| | | |
| | | TestCaseUtils.addEntries( |
| | | "dn: ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: People", |
| | | "", |
| | | "dn: ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: organizationalUnit", |
| | | "ou: Groups", |
| | | "", |
| | | "dn: uid=user.1,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.1", |
| | | "givenName: User", |
| | | "sn: 1", |
| | | "cn: User 1", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.2,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.2", |
| | | "givenName: User", |
| | | "sn: 2", |
| | | "cn: User 2", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.3,ou=People,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: user.3", |
| | | "givenName: User", |
| | | "sn: 3", |
| | | "cn: User 3", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: cn=Test Group of URLs,ou=Groups,o=test", |
| | | "objectClass: top", |
| | | "objectClass: groupOfURLs", |
| | | "cn: Test Group of URLs", |
| | | "memberURL: ldap:///dc=example,dc=com??sub?(cn=nonexistent)", |
| | | "memberURL: ldap:///uid=user.2,ou=People,o=test??sub?(sn=2)", |
| | | "memberURL: ldap:///o=test??sub?(sn=1)", |
| | | "memberURL: ldap:///ou=People,o=test??subordinate?(!(sn=3))"); |
| | | |
| | | DN groupDN = DN.decode("cn=Test Group of URLs,ou=Groups,o=test"); |
| | | DN user1DN = DN.decode("uid=user.1,ou=People,o=test"); |
| | | DN user2DN = DN.decode("uid=user.2,ou=People,o=test"); |
| | | |
| | | Group groupInstance = groupManager.getGroupInstance(groupDN); |
| | | assertNotNull(groupInstance); |
| | | groupInstance.toString(); |
| | | |
| | | |
| | | LinkedHashSet<DN> memberSet = new LinkedHashSet<DN>(); |
| | | memberSet.add(user1DN); |
| | | memberSet.add(user2DN); |
| | | |
| | | MemberList memberList = |
| | | groupInstance.getMembers(DN.nullDN(), SearchScope.WHOLE_SUBTREE, |
| | | SearchFilter.createFilterFromString("(objectClass=*)")); |
| | | assertNotNull(memberList); |
| | | while (memberList.hasMoreMembers()) |
| | | { |
| | | DN memberDN = memberList.nextMemberDN(); |
| | | assertTrue(memberSet.remove(memberDN), |
| | | "Returned unexpected member " + memberDN.toString()); |
| | | } |
| | | memberList.close(); |
| | | assertTrue(memberSet.isEmpty(), |
| | | "Expected member set to be empty but it was not: " + memberSet); |
| | | |
| | | |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | DeleteOperation deleteOperation = conn.processDelete(groupDN); |
| | | assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); |
| | | assertNull(groupManager.getGroupInstance(groupDN)); |
| | | } |
| | | } |
| | | |