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