001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.extensions;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.Iterator;
033    import java.util.LinkedList;
034    import java.util.Set;
035    
036    import org.opends.server.types.DirectoryConfig;
037    import org.opends.server.types.DirectoryException;
038    import org.opends.server.types.DN;
039    import org.opends.server.types.Entry;
040    import org.opends.server.types.MemberList;
041    import org.opends.server.types.MembershipException;
042    import org.opends.server.types.SearchFilter;
043    import org.opends.server.types.SearchScope;
044    
045    import static org.opends.server.loggers.debug.DebugLogger.*;
046    import org.opends.server.loggers.debug.DebugTracer;
047    import org.opends.server.types.DebugLogLevel;
048    import static org.opends.messages.ExtensionMessages.*;
049    import static org.opends.server.util.Validator.*;
050    
051    
052    
053    /**
054     * This class provides an implementation of the {@code MemberList} class that
055     * may be used in conjunction when static groups when additional criteria is to
056     * be used to select a subset of the group members.
057     */
058    public class FilteredStaticGroupMemberList
059           extends MemberList
060    {
061      /**
062       * The tracer object for the debug logger.
063       */
064      private static final DebugTracer TRACER = getTracer();
065    
066    
067    
068    
069      // The base DN below which all returned members should exist.
070      private DN baseDN;
071    
072      // The DN of the static group with which this member list is associated.
073      private DN groupDN;
074    
075      // The entry of the next entry that matches the member list criteria.
076      private Entry nextMatchingEntry;
077    
078      // The iterator used to traverse the set of member DNs.
079      private Iterator<DN> memberDNIterator;
080    
081      // The set of DNs for the users that are members of the associated static
082      // group.
083      private LinkedList<DN> memberDNs;
084    
085      // The membership exception that should be thrown the next time a member is
086      // requested.
087      private MembershipException nextMembershipException;
088    
089      // The search filter that all returned members should match.
090      private SearchFilter filter;
091    
092      // The search scope to apply against the base DN for the member subset.
093      private SearchScope scope;
094    
095    
096    
097      /**
098       * Creates a new filtered static group member list with the provided
099       * information.
100       *
101       * @param  groupDN    The DN of the static group with which this member list
102       *                    is associated.
103       * @param  memberDNs  The set of DNs for the users that are members of the
104       *                    associated static group.
105       * @param  baseDN     The base DN below which all returned members should
106       *                    exist.  If this is {@code null}, then all members will
107       *                    be considered to match the base and scope criteria.
108       * @param  scope      The search scope to apply against the base DN when
109       *                    selecting eligible members.
110       * @param  filter     The search filter which all returned members should
111       *                    match.  If this is {@code null}, then all members will
112       *                    be considered eligible.
113       */
114      public FilteredStaticGroupMemberList(DN groupDN, Set<DN> memberDNs, DN baseDN,
115                                           SearchScope scope, SearchFilter filter)
116      {
117        ensureNotNull(groupDN, memberDNs);
118    
119        this.groupDN   = groupDN;
120        this.memberDNs = new LinkedList<DN>(memberDNs);
121        memberDNIterator = memberDNs.iterator();
122    
123        this.baseDN = baseDN;
124        this.filter = filter;
125    
126        if (scope == null)
127        {
128          this.scope = SearchScope.WHOLE_SUBTREE;
129        }
130        else
131        {
132          this.scope = scope;
133        }
134    
135        nextMatchingEntry       = null;
136        nextMembershipException = null;
137        nextMemberInternal();
138      }
139    
140    
141    
142      /**
143       * Attempts to find the next member that matches the associated criteria.
144       * When this method returns, if {@code nextMembershipException} is
145       * non-{@code null}, then that exception should be thrown on the next attempt
146       * to retrieve a member.  If {@code nextMatchingEntry} is non-{@code null},
147       * then that entry should be returned on the next attempt to retrieve a
148       * member.  If both are {@code null}, then there are no more members to
149       * return.
150       */
151      private void nextMemberInternal()
152      {
153        while (memberDNIterator.hasNext())
154        {
155          DN nextDN = memberDNIterator.next();
156    
157    
158          // Check to see if we can eliminate the entry as a possible match purely
159          // based on base DN and scope.
160          if (baseDN != null)
161          {
162            switch (scope)
163            {
164              case BASE_OBJECT:
165                if (! baseDN.equals(nextDN))
166                {
167                  continue;
168                }
169                break;
170    
171              case SINGLE_LEVEL:
172                if (! baseDN.equals(nextDN.getParent()))
173                {
174                  continue;
175                }
176                break;
177    
178              case SUBORDINATE_SUBTREE:
179                if (baseDN.equals(nextDN) || (! baseDN.isAncestorOf(nextDN)))
180                {
181                  continue;
182                }
183                break;
184    
185              default:
186                if (! baseDN.isAncestorOf(nextDN))
187                {
188                  continue;
189                }
190                break;
191            }
192          }
193    
194    
195          // Get the entry for the potential member.  If we can't, then populate
196          // the next membership exception.
197          try
198          {
199            Entry memberEntry = DirectoryConfig.getEntry(nextDN);
200            if (memberEntry == null)
201            {
202              Message message = ERR_STATICMEMBERS_NO_SUCH_ENTRY.get(
203                  String.valueOf(nextDN), String.valueOf(groupDN));
204              nextMembershipException =
205                   new MembershipException(message, true);
206              return;
207            }
208    
209            if (filter == null)
210            {
211              nextMatchingEntry = memberEntry;
212              return;
213            }
214            else
215            {
216              if (filter.matchesEntry(memberEntry))
217              {
218                nextMatchingEntry = memberEntry;
219                return;
220              }
221              else
222              {
223                continue;
224              }
225            }
226          }
227          catch (DirectoryException de)
228          {
229            if (debugEnabled())
230            {
231              TRACER.debugCaught(DebugLogLevel.ERROR, de);
232            }
233    
234            Message message = ERR_STATICMEMBERS_CANNOT_GET_ENTRY.
235                get(String.valueOf(nextDN), String.valueOf(groupDN),
236                    String.valueOf(de.getMessageObject()));
237            nextMembershipException =
238                 new MembershipException(message, true, de);
239            return;
240          }
241        }
242    
243    
244        // If we've gotten here, then there are no more members.
245        nextMatchingEntry       = null;
246        nextMembershipException = null;
247      }
248    
249    
250    
251      /**
252       * {@inheritDoc}
253       */
254      @Override()
255      public boolean hasMoreMembers()
256      {
257        if (! memberDNIterator.hasNext())
258        {
259          return false;
260        }
261    
262        return ((nextMatchingEntry != null) || (nextMembershipException != null));
263      }
264    
265    
266    
267      /**
268       * {@inheritDoc}
269       */
270      @Override()
271      public DN nextMemberDN()
272             throws MembershipException
273      {
274        if (! memberDNIterator.hasNext())
275        {
276          return null;
277        }
278    
279        Entry e = nextMemberEntry();
280        if (e == null)
281        {
282          return null;
283        }
284        else
285        {
286          return e.getDN();
287        }
288      }
289    
290    
291    
292      /**
293       * {@inheritDoc}
294       */
295      @Override()
296      public Entry nextMemberEntry()
297             throws MembershipException
298      {
299        if (! memberDNIterator.hasNext())
300        {
301          return null;
302        }
303    
304        if (nextMembershipException == null)
305        {
306          Entry e = nextMatchingEntry;
307          nextMatchingEntry = null;
308          nextMemberInternal();
309          return e;
310        }
311        else
312        {
313          MembershipException me = nextMembershipException;
314          nextMembershipException = null;
315          nextMemberInternal();
316          throw me;
317        }
318      }
319    
320    
321    
322      /**
323       * {@inheritDoc}
324       */
325      @Override()
326      public void close()
327      {
328        // No implementation is required.
329      }
330    }
331