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