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-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029import java.util.List;
030
031import org.forgerock.i18n.LocalizableMessage;
032import org.forgerock.i18n.slf4j.LocalizedLogger;
033import org.forgerock.opendj.config.server.ConfigException;
034import org.forgerock.opendj.ldap.ByteString;
035import org.forgerock.opendj.ldap.ConditionResult;
036import org.forgerock.opendj.ldap.ResultCode;
037import org.opends.server.admin.server.ConfigurationChangeListener;
038import org.opends.server.admin.std.server.MemberVirtualAttributeCfg;
039import org.opends.server.api.Group;
040import org.opends.server.api.VirtualAttributeProvider;
041import org.opends.server.core.DirectoryServer;
042import org.opends.server.types.Attribute;
043import org.opends.server.types.AttributeBuilder;
044import org.opends.server.types.Attributes;
045import org.opends.server.core.SearchOperation;
046import org.forgerock.opendj.config.server.ConfigChangeResult;
047import org.opends.server.types.DN;
048import org.opends.server.types.Entry;
049import org.opends.server.types.InitializationException;
050import org.opends.server.types.MemberList;
051import org.opends.server.types.MembershipException;
052import org.opends.server.types.VirtualAttributeRule;
053
054/**
055 * This class implements a virtual attribute provider that works in conjunction
056 * with virtual static groups to generate the values for the member or
057 * uniqueMember attribute.
058 */
059public class MemberVirtualAttributeProvider
060       extends VirtualAttributeProvider<MemberVirtualAttributeCfg>
061       implements ConfigurationChangeListener<MemberVirtualAttributeCfg>
062{
063  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
064
065  /** The current configuration for this member virtual attribute. */
066  private MemberVirtualAttributeCfg currentConfig;
067
068
069
070  /**
071   * Creates a new instance of this member virtual attribute provider.
072   */
073  public MemberVirtualAttributeProvider()
074  {
075    super();
076
077    // All initialization should be performed in the
078    // initializeVirtualAttributeProvider method.
079  }
080
081
082
083  /** {@inheritDoc} */
084  @Override
085  public void initializeVirtualAttributeProvider(
086                            MemberVirtualAttributeCfg configuration)
087         throws ConfigException, InitializationException
088  {
089    configuration.addMemberChangeListener(this);
090    currentConfig = configuration;
091  }
092
093
094
095  /** {@inheritDoc} */
096  @Override
097  public boolean isMultiValued()
098  {
099    return true;
100  }
101
102
103
104  /** {@inheritDoc} */
105  @Override
106  public Attribute getValues(Entry entry, VirtualAttributeRule rule)
107  {
108    if (! currentConfig.isAllowRetrievingMembership())
109    {
110      return Attributes.empty(rule.getAttributeType());
111    }
112
113    Group<?> g =
114      DirectoryServer.getGroupManager().getGroupInstance(entry.getName());
115    if (g == null)
116    {
117      return Attributes.empty(rule.getAttributeType());
118    }
119
120    AttributeBuilder builder = new AttributeBuilder(rule.getAttributeType());
121    try
122    {
123      MemberList memberList = g.getMembers();
124      while (memberList.hasMoreMembers())
125      {
126        try
127        {
128          DN memberDN = memberList.nextMemberDN();
129          if (memberDN != null)
130          {
131            builder.add(ByteString.valueOfUtf8(memberDN.toString()));
132          }
133        }
134        catch (MembershipException me)
135        {
136          if (! me.continueIterating())
137          {
138            break;
139          }
140        }
141      }
142    }
143    catch (Exception e)
144    {
145      logger.traceException(e);
146    }
147
148    return builder.toAttribute();
149  }
150
151
152
153  /** {@inheritDoc} */
154  @Override
155  public boolean hasValue(Entry entry, VirtualAttributeRule rule)
156  {
157    Group<?> g =
158      DirectoryServer.getGroupManager().getGroupInstance(entry.getName());
159    if (g == null)
160    {
161      return false;
162    }
163
164    try
165    {
166      MemberList memberList = g.getMembers();
167      while (memberList.hasMoreMembers())
168      {
169        try
170        {
171          DN memberDN = memberList.nextMemberDN();
172          if (memberDN != null)
173          {
174            memberList.close();
175            return true;
176          }
177        }
178        catch (MembershipException me)
179        {
180          if (! me.continueIterating())
181          {
182            break;
183          }
184        }
185      }
186    }
187    catch (Exception e)
188    {
189      logger.traceException(e);
190    }
191
192    return false;
193  }
194
195
196
197  /** {@inheritDoc} */
198  @Override
199  public boolean hasValue(Entry entry, VirtualAttributeRule rule, ByteString value)
200  {
201    Group<?> g =
202      DirectoryServer.getGroupManager().getGroupInstance(entry.getName());
203    if (g == null)
204    {
205      return false;
206    }
207
208    try
209    {
210      return g.isMember(DN.decode(value));
211    }
212    catch (Exception e)
213    {
214      logger.traceException(e);
215    }
216
217    return false;
218  }
219
220
221
222  /** {@inheritDoc} */
223  @Override
224  public ConditionResult matchesSubstring(Entry entry,
225                                          VirtualAttributeRule rule,
226                                          ByteString subInitial,
227                                          List<ByteString> subAny,
228                                          ByteString subFinal)
229  {
230    // DNs cannot be used in substring matching.
231    return ConditionResult.UNDEFINED;
232  }
233
234
235
236  /** {@inheritDoc} */
237  @Override
238  public ConditionResult greaterThanOrEqualTo(Entry entry,
239                              VirtualAttributeRule rule,
240                              ByteString value)
241  {
242    // DNs cannot be used in ordering matching.
243    return ConditionResult.UNDEFINED;
244  }
245
246
247
248  /** {@inheritDoc} */
249  @Override
250  public ConditionResult lessThanOrEqualTo(Entry entry,
251                              VirtualAttributeRule rule,
252                              ByteString value)
253  {
254    // DNs cannot be used in ordering matching.
255    return ConditionResult.UNDEFINED;
256  }
257
258
259
260  /** {@inheritDoc} */
261  @Override
262  public ConditionResult approximatelyEqualTo(Entry entry,
263                              VirtualAttributeRule rule,
264                              ByteString value)
265  {
266    // DNs cannot be used in approximate matching.
267    return ConditionResult.UNDEFINED;
268  }
269
270
271
272  /** {@inheritDoc} */
273  @Override
274  public boolean isSearchable(VirtualAttributeRule rule,
275                              SearchOperation searchOperation,
276                              boolean isPreIndexed)
277  {
278    return false;
279  }
280
281
282
283  /** {@inheritDoc} */
284  @Override
285  public void processSearch(VirtualAttributeRule rule,
286                            SearchOperation searchOperation)
287  {
288    searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
289    return;
290  }
291
292
293
294  /** {@inheritDoc} */
295  @Override
296  public boolean isConfigurationChangeAcceptable(
297                      MemberVirtualAttributeCfg configuration,
298                      List<LocalizableMessage> unacceptableReasons)
299  {
300    // The new configuration should always be acceptable.
301    return true;
302  }
303
304
305
306  /** {@inheritDoc} */
307  @Override
308  public ConfigChangeResult applyConfigurationChange(
309                                 MemberVirtualAttributeCfg configuration)
310  {
311    currentConfig = configuration;
312    return new ConfigChangeResult();
313  }
314}
315