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