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.opendj.server.config.meta.VirtualAttributeCfgDefn;
029import org.forgerock.opendj.server.config.server.VirtualAttributeCfg;
030import org.forgerock.util.Utils;
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    return (conflictBehavior != VirtualAttributeCfgDefn.ConflictBehavior.REAL_OVERRIDES_VIRTUAL
212            || !entry.hasAttribute(attributeType))
213        // If there are any base DNs defined, then the entry must be below one of them.
214        && (baseDNs.isEmpty() || matchesAnyBaseDN(entry.getName()))
215        // If there are any search filters defined, then the entry must match one of them.
216        && (filters.isEmpty() || matchesAnyFilter(entry))
217        // If there are any group memberships defined, then the entry must be a member of one of them.
218        && (groupDNs.isEmpty() || isMemberOfAnyGroup(entry));
219  }
220
221  private boolean matchesAnyBaseDN(DN entryDN)
222  {
223    for (DN dn : baseDNs)
224    {
225      if (entryDN.isInScopeOf(dn, scope))
226      {
227        return true;
228      }
229    }
230    return false;
231  }
232
233  private boolean matchesAnyFilter(Entry entry)
234  {
235    for (SearchFilter filter : filters)
236    {
237      try
238      {
239        if (filter.matchesEntry(entry))
240        {
241          return true;
242        }
243      }
244      catch (Exception e)
245      {
246        logger.traceException(e);
247      }
248    }
249    return false;
250  }
251
252  private boolean isMemberOfAnyGroup(Entry entry)
253  {
254    for (DN dn : groupDNs)
255    {
256      try
257      {
258        Group<?> group = DirectoryServer.getGroupManager().getGroupInstance(dn);
259        if (group != null && group.isMember(entry))
260        {
261          return true;
262        }
263      }
264      catch (Exception e)
265      {
266        logger.traceException(e);
267      }
268    }
269    return false;
270  }
271
272  @Override
273  public String toString()
274  {
275    StringBuilder buffer = new StringBuilder();
276    toString(buffer);
277    return buffer.toString();
278  }
279
280  /**
281   * Appends a string representation of this virtual attribute rule to
282   * the provided buffer.
283   *
284   * @param  buffer  The buffer to which the information should be written.
285   */
286  private void toString(StringBuilder buffer)
287  {
288    buffer.append("VirtualAttributeRule(attrType=");
289    buffer.append(attributeType.getNameOrOID());
290    buffer.append(", providerDN=\"").append(provider.getClass().getName());
291
292    buffer.append("\", baseDNs={");
293    append(buffer, baseDNs);
294
295    buffer.append("}, scope=").append(scope);
296
297    buffer.append(", groupDNs={");
298    append(buffer, groupDNs);
299    buffer.append("}, filters={");
300    append(buffer, filters);
301
302    buffer.append("}, conflictBehavior=").append(conflictBehavior);
303    buffer.append(")");
304  }
305
306  private void append(StringBuilder buffer, Collection<?> col)
307  {
308    if (!col.isEmpty())
309    {
310      buffer.append("\"");
311      Utils.joinAsString(buffer, "\", \"", col);
312      buffer.append("\"");
313    }
314  }
315}