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