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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Set;
034
035import org.forgerock.i18n.LocalizableMessage;
036import org.opends.server.admin.std.server.VirtualStaticGroupImplementationCfg;
037import org.opends.server.api.Group;
038import org.opends.server.core.DirectoryServer;
039import org.opends.server.core.ServerContext;
040import org.forgerock.opendj.config.server.ConfigException;
041import org.forgerock.i18n.slf4j.LocalizedLogger;
042import org.opends.server.types.Attribute;
043import org.opends.server.types.AttributeType;
044import org.forgerock.opendj.ldap.ByteString;
045import org.opends.server.types.DirectoryException;
046import org.opends.server.types.DN;
047import org.opends.server.types.Entry;
048import org.opends.server.types.InitializationException;
049import org.opends.server.types.MemberList;
050import org.opends.server.types.ObjectClass;
051import org.forgerock.opendj.ldap.ResultCode;
052import org.opends.server.types.SearchFilter;
053import org.forgerock.opendj.ldap.SearchScope;
054
055import static org.opends.messages.ExtensionMessages.*;
056import static org.opends.server.config.ConfigConstants.*;
057import static org.opends.server.util.ServerConstants.*;
058import static org.forgerock.util.Reject.*;
059
060
061
062/**
063 * This class provides a virtual static group implementation, in which
064 * membership is based on membership of another group.
065 */
066public class VirtualStaticGroup
067       extends Group<VirtualStaticGroupImplementationCfg>
068{
069  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
070
071  /** The DN of the entry that holds the definition for this group. */
072  private DN groupEntryDN;
073
074  /** The DN of the target group that will provide membership information. */
075  private DN targetGroupDN;
076
077
078
079  /**
080   * Creates a new, uninitialized virtual static group instance.  This is
081   * intended for internal use only.
082   */
083  public VirtualStaticGroup()
084  {
085    super();
086
087    // No initialization is required here.
088  }
089
090
091
092  /**
093   * Creates a new virtual static group instance with the provided information.
094   *
095   * @param  groupEntryDN   The DN of the entry that holds the definition for
096   *                        this group.  It must not be {@code null}.
097   * @param  targetGroupDN  The DN of the target group that will provide
098   *                        membership information.  It must not be
099   *                        {@code null}.
100   */
101  public VirtualStaticGroup(DN groupEntryDN, DN targetGroupDN)
102  {
103    super();
104
105    ifNull(groupEntryDN, targetGroupDN);
106
107    this.groupEntryDN  = groupEntryDN;
108    this.targetGroupDN = targetGroupDN;
109  }
110
111
112
113  /** {@inheritDoc} */
114  @Override
115  public void initializeGroupImplementation(
116                   VirtualStaticGroupImplementationCfg configuration)
117         throws ConfigException, InitializationException
118  {
119    // No additional initialization is required.
120  }
121
122
123
124
125  /** {@inheritDoc} */
126  @Override
127  public VirtualStaticGroup newInstance(ServerContext serverContext, Entry groupEntry)
128         throws DirectoryException
129  {
130    ifNull(groupEntry);
131
132
133    // Get the target group DN attribute from the entry, if there is one.
134    DN targetDN = null;
135    AttributeType targetType = DirectoryServer.getAttributeTypeOrDefault(ATTR_TARGET_GROUP_DN);
136    List<Attribute> attrList = groupEntry.getAttribute(targetType);
137    if (attrList != null)
138    {
139      for (Attribute a : attrList)
140      {
141        for (ByteString v : a)
142        {
143          if (targetDN != null)
144          {
145            LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS.get(groupEntry.getName());
146            throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message);
147          }
148
149          try
150          {
151            targetDN = DN.decode(v);
152          }
153          catch (DirectoryException de)
154          {
155            logger.traceException(de);
156
157            LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET.
158                get(v, groupEntry.getName(), de.getMessageObject());
159            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
160                                         message, de);
161          }
162        }
163      }
164    }
165
166    if (targetDN == null)
167    {
168      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET.get(groupEntry.getName());
169      throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message);
170    }
171
172    return new VirtualStaticGroup(groupEntry.getName(), targetDN);
173  }
174
175
176
177  /** {@inheritDoc} */
178  @Override
179  public SearchFilter getGroupDefinitionFilter()
180         throws DirectoryException
181  {
182    // FIXME -- This needs to exclude enhanced groups once we have support for
183    // them.
184    return SearchFilter.createFilterFromString("(" + ATTR_OBJECTCLASS + "=" +
185                                               OC_VIRTUAL_STATIC_GROUP + ")");
186  }
187
188
189
190  /** {@inheritDoc} */
191  @Override
192  public boolean isGroupDefinition(Entry entry)
193  {
194    ifNull(entry);
195
196    // FIXME -- This needs to exclude enhanced groups once we have support for
197    //them.
198    ObjectClass virtualStaticGroupClass =
199         DirectoryServer.getObjectClass(OC_VIRTUAL_STATIC_GROUP, true);
200    return entry.hasObjectClass(virtualStaticGroupClass);
201  }
202
203
204
205  /** {@inheritDoc} */
206  @Override
207  public DN getGroupDN()
208  {
209    return groupEntryDN;
210  }
211
212
213
214  /** {@inheritDoc} */
215  @Override
216  public void setGroupDN(DN groupDN)
217  {
218    groupEntryDN = groupDN;
219  }
220
221
222
223  /**
224   * Retrieves the DN of the target group for this virtual static group.
225   *
226   * @return  The DN of the target group for this virtual static group.
227   */
228  public DN getTargetGroupDN()
229  {
230    return targetGroupDN;
231  }
232
233
234
235  /** {@inheritDoc} */
236  @Override
237  public boolean supportsNestedGroups()
238  {
239    // Virtual static groups don't support nesting.
240    return false;
241  }
242
243
244
245  /** {@inheritDoc} */
246  @Override
247  public List<DN> getNestedGroupDNs()
248  {
249    // Virtual static groups don't support nesting.
250    return Collections.<DN>emptyList();
251  }
252
253
254
255  /** {@inheritDoc} */
256  @Override
257  public void addNestedGroup(DN nestedGroupDN)
258         throws UnsupportedOperationException, DirectoryException
259  {
260    // Virtual static groups don't support nesting.
261    LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED.get();
262    throw new UnsupportedOperationException(message.toString());
263  }
264
265
266
267  /** {@inheritDoc} */
268  @Override
269  public void removeNestedGroup(DN nestedGroupDN)
270         throws UnsupportedOperationException, DirectoryException
271  {
272    // Virtual static groups don't support nesting.
273    LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED.get();
274    throw new UnsupportedOperationException(message.toString());
275  }
276
277
278
279  /** {@inheritDoc} */
280  @Override
281  public boolean isMember(DN userDN, Set<DN> examinedGroups)
282         throws DirectoryException
283  {
284    if (! examinedGroups.add(getGroupDN()))
285    {
286      return false;
287    }
288
289    Group targetGroup =
290         DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
291    if (targetGroup == null)
292    {
293      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(targetGroupDN, groupEntryDN);
294      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
295    }
296    else if (targetGroup instanceof VirtualStaticGroup)
297    {
298      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(groupEntryDN, targetGroupDN);
299      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
300    }
301    else
302    {
303      return targetGroup.isMember(userDN);
304    }
305  }
306
307
308
309  /** {@inheritDoc} */
310  @Override
311  public boolean isMember(Entry userEntry, Set<DN> examinedGroups)
312         throws DirectoryException
313  {
314    if (! examinedGroups.add(getGroupDN()))
315    {
316      return false;
317    }
318
319    Group targetGroup =
320         DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
321    if (targetGroup == null)
322    {
323      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(targetGroupDN, groupEntryDN);
324      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
325    }
326    else if (targetGroup instanceof VirtualStaticGroup)
327    {
328      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(groupEntryDN, targetGroupDN);
329      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
330    }
331    else
332    {
333      return targetGroup.isMember(userEntry);
334    }
335  }
336
337
338
339  /** {@inheritDoc} */
340  @Override
341  public MemberList getMembers()
342         throws DirectoryException
343  {
344    Group targetGroup =
345         DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
346    if (targetGroup == null)
347    {
348      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(targetGroupDN, groupEntryDN);
349      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
350    }
351    else if (targetGroup instanceof VirtualStaticGroup)
352    {
353      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(groupEntryDN, targetGroupDN);
354      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
355    }
356    else
357    {
358      return targetGroup.getMembers();
359    }
360  }
361
362
363
364  /** {@inheritDoc} */
365  @Override
366  public MemberList getMembers(DN baseDN, SearchScope scope,
367                               SearchFilter filter)
368         throws DirectoryException
369  {
370    Group targetGroup =
371         DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
372    if (targetGroup == null)
373    {
374      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP.get(targetGroupDN, groupEntryDN);
375      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
376    }
377    else if (targetGroup instanceof VirtualStaticGroup)
378    {
379      LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL.get(groupEntryDN, targetGroupDN);
380      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
381    }
382    else
383    {
384      return targetGroup.getMembers(baseDN, scope, filter);
385    }
386  }
387
388
389
390  /** {@inheritDoc} */
391  @Override
392  public boolean mayAlterMemberList()
393  {
394    return false;
395  }
396
397
398
399  /** {@inheritDoc} */
400  @Override
401  public void addMember(Entry userEntry)
402         throws UnsupportedOperationException, DirectoryException
403  {
404    // Virtual static groups don't support altering the member list.
405    LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get(groupEntryDN);
406    throw new UnsupportedOperationException(message.toString());
407  }
408
409
410
411  /** {@inheritDoc} */
412  @Override
413  public void removeMember(DN userDN)
414         throws UnsupportedOperationException, DirectoryException
415  {
416    // Virtual static groups don't support altering the member list.
417    LocalizableMessage message = ERR_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get(groupEntryDN);
418    throw new UnsupportedOperationException(message.toString());
419  }
420
421
422
423  /** {@inheritDoc} */
424  @Override
425  public void toString(StringBuilder buffer)
426  {
427    buffer.append("VirtualStaticGroup(dn=");
428    buffer.append(groupEntryDN);
429    buffer.append(",targetGroupDN=");
430    buffer.append(targetGroupDN);
431    buffer.append(")");
432  }
433}
434