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 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.types;
028
029import static org.forgerock.util.Reject.*;
030
031import java.util.Collection;
032import java.util.Set;
033
034import org.forgerock.i18n.slf4j.LocalizedLogger;
035import org.forgerock.opendj.ldap.SearchScope;
036import org.forgerock.util.Utils;
037import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
038import org.opends.server.admin.std.server.VirtualAttributeCfg;
039import org.opends.server.api.Group;
040import org.opends.server.api.VirtualAttributeProvider;
041import org.opends.server.core.DirectoryServer;
042
043/**
044 * This class defines a virtual attribute rule, which associates a
045 * virtual attribute provider with its associated configuration,
046 * including the attribute type for which the values should be
047 * generated; the base DN(s), group DN(s), and search filter(s) that
048 * should be used to identify which entries should have the virtual
049 * attribute, and how conflicts between real and virtual values should
050 * be handled.
051 */
052@org.opends.server.types.PublicAPI(
053     stability=org.opends.server.types.StabilityLevel.VOLATILE,
054     mayInstantiate=false,
055     mayExtend=false,
056     mayInvoke=true)
057public final class VirtualAttributeRule
058{
059  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
060
061  /** The attribute type for which the values should be generated. */
062  private final AttributeType attributeType;
063  /** The set of base DNs for branches that are eligible to have this virtual attribute. */
064  private final Set<DN> baseDNs;
065  /** The scope of entries eligible to have this virtual attribute, under the base DNs. */
066  private final SearchScope scope;
067  /** The set of DNs for groups whose members are eligible to have this virtual attribute. */
068  private final Set<DN> groupDNs;
069  /** The set of search filters for entries that are eligible to have this virtual attribute. */
070  private final Set<SearchFilter> filters;
071  /** The virtual attribute provider used to generate the values. */
072  private final VirtualAttributeProvider<? extends VirtualAttributeCfg> provider;
073  /**
074   * The behavior that should be exhibited for entries that already have real
075   * values for the target attribute.
076   */
077  private final VirtualAttributeCfgDefn.ConflictBehavior conflictBehavior;
078
079  /**
080   * Creates a new virtual attribute rule with the provided information.
081   *
082   * @param  attributeType     The attribute type for which the values
083   *                           should be generated.
084   * @param  provider          The virtual attribute provider to use
085   *                           to generate the values.
086   * @param  baseDNs           The set of base DNs for branches that
087   *                           are eligible to have this virtual attribute.
088   * @param  scope             The scope of entries, related to the
089   *                           base DNs, that are eligible to have
090   *                           this virtual attribute.
091   * @param  groupDNs          The set of DNs for groups whose members
092   *                           are eligible to have this virtual attribute.
093   * @param  filters           The set of search filters for entries
094   *                           that are eligible to have this virtual attribute.
095   * @param  conflictBehavior  The behavior that the server should
096   *                           exhibit for entries that already have
097   *                           one or more real values for the target
098   *                           attribute.
099   */
100  public VirtualAttributeRule(AttributeType attributeType,
101              VirtualAttributeProvider<? extends VirtualAttributeCfg>
102                   provider,
103              Set<DN> baseDNs, SearchScope scope, Set<DN> groupDNs,
104              Set<SearchFilter> filters,
105              VirtualAttributeCfgDefn.ConflictBehavior
106                   conflictBehavior)
107  {
108    ifNull(attributeType, provider, baseDNs, groupDNs);
109    ifNull(filters, conflictBehavior);
110
111    this.attributeType    = attributeType;
112    this.provider         = provider;
113    this.baseDNs          = baseDNs;
114    this.scope            = scope;
115    this.groupDNs         = groupDNs;
116    this.filters          = filters;
117    this.conflictBehavior = conflictBehavior;
118  }
119
120  /**
121   * Retrieves the attribute type for which the values should be generated.
122   *
123   * @return  The attribute type for which the values should be generated.
124   */
125  public AttributeType getAttributeType()
126  {
127    return attributeType;
128  }
129
130  /**
131   * Retrieves the virtual attribute provider used to generate the values.
132   *
133   * @return  The virtual attribute provider to use to generate the values.
134   */
135  public VirtualAttributeProvider<? extends VirtualAttributeCfg>
136              getProvider()
137  {
138    return provider;
139  }
140
141  /**
142   * Retrieves the set of base DNs for branches that are eligible to
143   * have this virtual attribute.
144   *
145   * @return  The set of base DNs for branches that are eligible to
146   *          have this virtual attribute.
147   */
148  public Set<DN> getBaseDNs()
149  {
150    return baseDNs;
151  }
152
153  /**
154   * Retrieves the scope of entries in the base DNs that are eligible
155   * to have this virtual attribute.
156   *
157   * @return  The scope of entries that are eligible to
158   *          have this virtual attribute.
159   */
160  public SearchScope getScope()
161  {
162    return scope;
163  }
164
165  /**
166   * Retrieves the set of DNs for groups whose members are eligible to
167   * have this virtual attribute.
168   *
169   * @return  The set of DNs for groups whose members are eligible to
170   *          have this virtual attribute.
171   */
172  public Set<DN> getGroupDNs()
173  {
174    return groupDNs;
175  }
176
177  /**
178   * Retrieves the set of search filters for entries that are eligible
179   * to have this virtual attribute.
180   *
181   * @return  The set of search filters for entries that are eligible
182   *          to have this virtual attribute.
183   */
184  public Set<SearchFilter> getFilters()
185  {
186    return filters;
187  }
188
189  /**
190   * Retrieves the behavior that the server should exhibit for entries
191   * that already have one or more real values for the target attribute.
192   *
193   * @return  The behavior that the server should exhibit for entries
194   *          that already have one or more real values for the target
195   *          attribute.
196   */
197  public VirtualAttributeCfgDefn.ConflictBehavior
198              getConflictBehavior()
199  {
200    return conflictBehavior;
201  }
202
203  /**
204   * Indicates whether this virtual attribute rule applies to the
205   * provided entry, taking into account the eligibility requirements
206   * defined in the rule.
207   *
208   * @param  entry  The entry for which to make the determination.
209   *
210   * @return  {@code true} if this virtual attribute rule may be used
211   *          to generate values for the entry, or {@code false} if not.
212   */
213  public boolean appliesToEntry(Entry entry)
214  {
215    // We'll do this in order of expense so that the checks which are
216    // potentially most expensive are done last.  First, check to see
217    // if real values should override virtual ones and if so whether
218    // the entry already has virtual values.
219    if (conflictBehavior == VirtualAttributeCfgDefn.ConflictBehavior.
220                                REAL_OVERRIDES_VIRTUAL
221        && entry.hasAttribute(attributeType))
222    {
223      return false;
224    }
225
226    // If there are any base DNs defined, then the entry must be below one of them.
227    if (!baseDNs.isEmpty() && !matchesAnyBaseDN(entry.getName()))
228    {
229      return false;
230    }
231
232    // If there are any search filters defined, then the entry must match one of them.
233    if (!filters.isEmpty() && !matchesAnyFilter(entry))
234    {
235      return false;
236    }
237
238    // If there are any group memberships defined, then the entry must
239    // be a member of one of them.
240    if (!groupDNs.isEmpty() && !isMemberOfAnyGroup(entry))
241    {
242      return false;
243    }
244
245    // If we've gotten here, then the rule is applicable.
246    return true;
247  }
248
249  private boolean matchesAnyBaseDN(DN entryDN)
250  {
251    for (DN dn : baseDNs)
252    {
253      if (entryDN.matchesBaseAndScope(dn, scope))
254      {
255        return true;
256      }
257    }
258    return false;
259  }
260
261  private boolean matchesAnyFilter(Entry entry)
262  {
263    for (SearchFilter filter : filters)
264    {
265      try
266      {
267        if (filter.matchesEntry(entry))
268        {
269          return true;
270        }
271      }
272      catch (Exception e)
273      {
274        logger.traceException(e);
275      }
276    }
277    return false;
278  }
279
280  private boolean isMemberOfAnyGroup(Entry entry)
281  {
282    for (DN dn : groupDNs)
283    {
284      try
285      {
286        Group<?> group = DirectoryServer.getGroupManager().getGroupInstance(dn);
287        if (group != null && group.isMember(entry))
288        {
289          return true;
290        }
291      }
292      catch (Exception e)
293      {
294        logger.traceException(e);
295      }
296    }
297    return false;
298  }
299
300  @Override
301  public String toString()
302  {
303    StringBuilder buffer = new StringBuilder();
304    toString(buffer);
305    return buffer.toString();
306  }
307
308  /**
309   * Appends a string representation of this virtual attribute rule to
310   * the provided buffer.
311   *
312   * @param  buffer  The buffer to which the information should be written.
313   */
314  public void toString(StringBuilder buffer)
315  {
316    buffer.append("VirtualAttributeRule(attrType=");
317    buffer.append(attributeType.getNameOrOID());
318    buffer.append(", providerDN=\"").append(provider.getClass().getName());
319
320    buffer.append("\", baseDNs={");
321    append(buffer, baseDNs);
322
323    buffer.append("}, scope=").append(scope);
324
325    buffer.append(", groupDNs={");
326    append(buffer, groupDNs);
327    buffer.append("}, filters={");
328    append(buffer, filters);
329
330    buffer.append("}, conflictBehavior=").append(conflictBehavior);
331    buffer.append(")");
332  }
333
334  private void append(StringBuilder buffer, Collection<?> col)
335  {
336    if (!col.isEmpty())
337    {
338      buffer.append("\"");
339      Utils.joinAsString(buffer, "\", \"", col);
340      buffer.append("\"");
341    }
342  }
343}