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.List;
033    import java.util.Set;
034    
035    import org.opends.messages.Message;
036    import org.opends.server.admin.std.server.VirtualStaticGroupImplementationCfg;
037    import org.opends.server.api.Group;
038    import org.opends.server.core.DirectoryServer;
039    import org.opends.server.config.ConfigException;
040    import org.opends.server.loggers.debug.DebugTracer;
041    import org.opends.server.types.Attribute;
042    import org.opends.server.types.AttributeType;
043    import org.opends.server.types.AttributeValue;
044    import org.opends.server.types.DebugLogLevel;
045    import org.opends.server.types.DirectoryException;
046    import org.opends.server.types.DN;
047    import org.opends.server.types.Entry;
048    import org.opends.server.types.InitializationException;
049    import org.opends.server.types.MemberList;
050    import org.opends.server.types.ObjectClass;
051    import org.opends.server.types.ResultCode;
052    import org.opends.server.types.SearchFilter;
053    import org.opends.server.types.SearchScope;
054    
055    import static org.opends.messages.ExtensionMessages.*;
056    import static org.opends.server.config.ConfigConstants.*;
057    import static org.opends.server.loggers.debug.DebugLogger.*;
058    import static org.opends.server.util.ServerConstants.*;
059    import static org.opends.server.util.Validator.*;
060    
061    
062    
063    /**
064     * This class provides a virtual static group implementation, in which
065     * membership is based on membership of another group.
066     */
067    public class VirtualStaticGroup
068           extends Group<VirtualStaticGroupImplementationCfg>
069    {
070      /**
071       * The tracer object for the debug logger.
072       */
073      private static final DebugTracer TRACER = getTracer();
074    
075      // The DN of the entry that holds the definition for this group.
076      private DN groupEntryDN;
077    
078      // The DN of the target group that will provide membership information.
079      private DN targetGroupDN;
080    
081    
082    
083      /**
084       * Creates a new, uninitialized virtual static group instance.  This is
085       * intended for internal use only.
086       */
087      public VirtualStaticGroup()
088      {
089        super();
090    
091        // No initialization is required here.
092      }
093    
094    
095    
096      /**
097       * Creates a new virtual static group instance with the provided information.
098       *
099       * @param  groupEntryDN   The DN of the entry that holds the definition for
100       *                        this group.  It must not be {@code null}.
101       * @param  targetGroupDN  The DN of the target group that will provide
102       *                        membership information.  It must not be
103       *                        {@code null}.
104       */
105      public VirtualStaticGroup(DN groupEntryDN, DN targetGroupDN)
106      {
107        super();
108    
109        ensureNotNull(groupEntryDN, targetGroupDN);
110    
111        this.groupEntryDN  = groupEntryDN;
112        this.targetGroupDN = targetGroupDN;
113      }
114    
115    
116    
117      /**
118       * {@inheritDoc}
119       */
120      @Override()
121      public void initializeGroupImplementation(
122                       VirtualStaticGroupImplementationCfg configuration)
123             throws ConfigException, InitializationException
124      {
125        // No additional initialization is required.
126      }
127    
128    
129    
130    
131      /**
132       * {@inheritDoc}
133       */
134      @Override()
135      public VirtualStaticGroup newInstance(Entry groupEntry)
136             throws DirectoryException
137      {
138        ensureNotNull(groupEntry);
139    
140    
141        // Get the target group DN attribute from the entry, if there is one.
142        DN targetDN = null;
143        AttributeType targetType =
144             DirectoryServer.getAttributeType(ATTR_TARGET_GROUP_DN, true);
145        List<Attribute> attrList = groupEntry.getAttribute(targetType);
146        if (attrList != null)
147        {
148          for (Attribute a : attrList)
149          {
150            for (AttributeValue v : a.getValues())
151            {
152              if (targetDN != null)
153              {
154                Message message = ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS.get(
155                    String.valueOf(groupEntry.getDN()));
156                throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
157                                             message);
158              }
159    
160              try
161              {
162                targetDN = DN.decode(v.getValue());
163              }
164              catch (DirectoryException de)
165              {
166                if (debugEnabled())
167                {
168                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
169                }
170    
171                Message message = ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET.
172                    get(v.getStringValue(), String.valueOf(groupEntry.getDN()),
173                        de.getMessageObject());
174                throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
175                                             message, de);
176              }
177            }
178          }
179        }
180    
181        if (targetDN == null)
182        {
183          Message message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET.get(
184              String.valueOf(groupEntry.getDN()));
185          throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message);
186        }
187    
188        return new VirtualStaticGroup(groupEntry.getDN(), targetDN);
189      }
190    
191    
192    
193      /**
194       * {@inheritDoc}
195       */
196      @Override()
197      public SearchFilter getGroupDefinitionFilter()
198             throws DirectoryException
199      {
200        // FIXME -- This needs to exclude enhanced groups once we have support for
201        // them.
202        return SearchFilter.createFilterFromString("(" + ATTR_OBJECTCLASS + "=" +
203                                                   OC_VIRTUAL_STATIC_GROUP + ")");
204      }
205    
206    
207    
208      /**
209       * {@inheritDoc}
210       */
211      @Override()
212      public boolean isGroupDefinition(Entry entry)
213      {
214        ensureNotNull(entry);
215    
216        // FIXME -- This needs to exclude enhanced groups once we have support for
217        //them.
218        ObjectClass virtualStaticGroupClass =
219             DirectoryServer.getObjectClass(OC_VIRTUAL_STATIC_GROUP, true);
220        return entry.hasObjectClass(virtualStaticGroupClass);
221      }
222    
223    
224    
225      /**
226       * {@inheritDoc}
227       */
228      @Override()
229      public DN getGroupDN()
230      {
231        return groupEntryDN;
232      }
233    
234    
235    
236      /**
237       * Retrieves the DN of the target group for this virtual static group.
238       *
239       * @return  The DN of the target group for this virtual static group.
240       */
241      public DN getTargetGroupDN()
242      {
243        return targetGroupDN;
244      }
245    
246    
247    
248      /**
249       * {@inheritDoc}
250       */
251      @Override()
252      public boolean supportsNestedGroups()
253      {
254        // Virtual static groups don't support nesting.
255        return false;
256      }
257    
258    
259    
260      /**
261       * {@inheritDoc}
262       */
263      @Override()
264      public List<DN> getNestedGroupDNs()
265      {
266        // Virtual static groups don't support nesting.
267        return Collections.<DN>emptyList();
268      }
269    
270    
271    
272      /**
273       * {@inheritDoc}
274       */
275      @Override()
276      public void addNestedGroup(DN nestedGroupDN)
277             throws UnsupportedOperationException, DirectoryException
278      {
279        // Virtual static groups don't support nesting.
280        Message message = ERR_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED.get();
281        throw new UnsupportedOperationException(message.toString());
282      }
283    
284    
285    
286      /**
287       * {@inheritDoc}
288       */
289      @Override()
290      public void removeNestedGroup(DN nestedGroupDN)
291             throws UnsupportedOperationException, DirectoryException
292      {
293        // Virtual static groups don't support nesting.
294        Message message = ERR_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED.get();
295        throw new UnsupportedOperationException(message.toString());
296      }
297    
298    
299    
300      /**
301       * {@inheritDoc}
302       */
303      @Override()
304      public boolean isMember(DN userDN, Set<DN> examinedGroups)
305             throws DirectoryException
306      {
307        if (! examinedGroups.add(getGroupDN()))
308        {
309          return false;
310        }
311    
312        Group targetGroup =
313             DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
314        if (targetGroup == null)
315        {
316          Message message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(
317              String.valueOf(targetGroupDN), String.valueOf(groupEntryDN));
318          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
319                                       message);
320        }
321        else if (targetGroup instanceof VirtualStaticGroup)
322        {
323          Message message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(
324              String.valueOf(groupEntryDN), String.valueOf(targetGroupDN));
325          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
326        }
327        else
328        {
329          return targetGroup.isMember(userDN);
330        }
331      }
332    
333    
334    
335      /**
336       * {@inheritDoc}
337       */
338      @Override()
339      public boolean isMember(Entry userEntry, Set<DN> examinedGroups)
340             throws DirectoryException
341      {
342        if (! examinedGroups.add(getGroupDN()))
343        {
344          return false;
345        }
346    
347        Group targetGroup =
348             DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
349        if (targetGroup == null)
350        {
351          Message message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(
352              String.valueOf(targetGroupDN), String.valueOf(groupEntryDN));
353          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
354                                       message);
355        }
356        else if (targetGroup instanceof VirtualStaticGroup)
357        {
358          Message message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(
359              String.valueOf(groupEntryDN), String.valueOf(targetGroupDN));
360          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
361        }
362        else
363        {
364          return targetGroup.isMember(userEntry);
365        }
366      }
367    
368    
369    
370      /**
371       * {@inheritDoc}
372       */
373      @Override()
374      public MemberList getMembers()
375             throws DirectoryException
376      {
377        Group targetGroup =
378             DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
379        if (targetGroup == null)
380        {
381          Message message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(
382              String.valueOf(targetGroupDN), String.valueOf(groupEntryDN));
383          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
384                                       message);
385        }
386        else if (targetGroup instanceof VirtualStaticGroup)
387        {
388          Message message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(
389              String.valueOf(groupEntryDN), String.valueOf(targetGroupDN));
390          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
391        }
392        else
393        {
394          return targetGroup.getMembers();
395        }
396      }
397    
398    
399    
400      /**
401       * {@inheritDoc}
402       */
403      @Override()
404      public MemberList getMembers(DN baseDN, SearchScope scope,
405                                   SearchFilter filter)
406             throws DirectoryException
407      {
408        Group targetGroup =
409             DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
410        if (targetGroup == null)
411        {
412          Message message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(
413              String.valueOf(targetGroupDN), String.valueOf(groupEntryDN));
414          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
415                                       message);
416        }
417        else if (targetGroup instanceof VirtualStaticGroup)
418        {
419          Message message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(
420              String.valueOf(groupEntryDN), String.valueOf(targetGroupDN));
421          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
422        }
423        else
424        {
425          return targetGroup.getMembers(baseDN, scope, filter);
426        }
427      }
428    
429    
430    
431      /**
432       * {@inheritDoc}
433       */
434      @Override()
435      public boolean mayAlterMemberList()
436      {
437        return false;
438      }
439    
440    
441    
442      /**
443       * {@inheritDoc}
444       */
445      @Override()
446      public void addMember(Entry userEntry)
447             throws UnsupportedOperationException, DirectoryException
448      {
449        // Virtual static groups don't support altering the member list.
450        Message message = ERR_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED.
451            get(String.valueOf(groupEntryDN));
452        throw new UnsupportedOperationException(message.toString());
453      }
454    
455    
456    
457      /**
458       * {@inheritDoc}
459       */
460      @Override()
461      public void removeMember(DN userDN)
462             throws UnsupportedOperationException, DirectoryException
463      {
464        // Virtual static groups don't support altering the member list.
465        Message message = ERR_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED.
466            get(String.valueOf(groupEntryDN));
467        throw new UnsupportedOperationException(message.toString());
468      }
469    
470    
471    
472      /**
473       * {@inheritDoc}
474       */
475      @Override()
476      public void toString(StringBuilder buffer)
477      {
478        buffer.append("VirtualStaticGroup(dn=");
479        buffer.append(groupEntryDN);
480        buffer.append(",targetGroupDN=");
481        buffer.append(targetGroupDN);
482        buffer.append(")");
483      }
484    }
485