001/*
002 * Copyright 2008-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.controls;
022
023
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.List;
030import java.util.Iterator;
031
032import com.unboundid.asn1.ASN1Element;
033import com.unboundid.asn1.ASN1OctetString;
034import com.unboundid.asn1.ASN1Sequence;
035import com.unboundid.ldap.sdk.Control;
036import com.unboundid.ldap.sdk.LDAPException;
037import com.unboundid.ldap.sdk.ResultCode;
038import com.unboundid.util.Debug;
039import com.unboundid.util.NotMutable;
040import com.unboundid.util.StaticUtils;
041import com.unboundid.util.ThreadSafety;
042import com.unboundid.util.ThreadSafetyLevel;
043import com.unboundid.util.Validator;
044
045import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
046
047
048
049/**
050 * This class provides a request control which may be used to request that
051 * entries below one or more base DNs be excluded from the results returned to
052 * a client while processing a search operation.  For example, this may be
053 * useful in cases where you want to perform a search below "dc=example,dc=com",
054 * but want to exclude all entries below "ou=private,dc=example,dc=com".
055 * <BR>
056 * <BLOCKQUOTE>
057 *   <B>NOTE:</B>  This class, and other classes within the
058 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
059 *   supported for use against Ping Identity, UnboundID, and
060 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
061 *   for proprietary functionality or for external specifications that are not
062 *   considered stable or mature enough to be guaranteed to work in an
063 *   interoperable way with other types of LDAP servers.
064 * </BLOCKQUOTE>
065 * <BR>
066 * The criticality for this control may be either {@code true} or {@code false}.
067 * It must have a value with the following encoding:
068 * <PRE>
069 *   ExcludeBranchRequest ::= SEQUENCE {
070 *        baseDNs     [0] SEQUENCE OF LDAPDN,
071 *        ... }
072 * </PRE>
073 */
074@NotMutable()
075@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
076public final class ExcludeBranchRequestControl
077       extends Control
078{
079  /**
080   * The OID (1.3.6.1.4.1.30221.2.5.17) for the exclude branch request control.
081   */
082  public static final String EXCLUDE_BRANCH_REQUEST_OID =
083       "1.3.6.1.4.1.30221.2.5.17";
084
085
086
087  /**
088   * The BER type for the base DNs element.
089   */
090  private static final byte TYPE_BASE_DNS = (byte) 0xA0;
091
092
093
094  /**
095   * The serial version UID for this serializable class.
096   */
097  private static final long serialVersionUID = -8599554860060612417L;
098
099
100
101  // The list of base DNs to be excluded from the search results.
102  private final List<String> baseDNs;
103
104
105
106  /**
107   * Creates a new exclude branch request control with the provided set of base
108   * DNs.  It will be marked critical.
109   *
110   * @param  baseDNs  The base DNs for entries to be excluded from search
111   *                  results.  It must not be {@code null} or empty.
112   */
113  public ExcludeBranchRequestControl(final Collection<String> baseDNs)
114  {
115    this(true, baseDNs);
116  }
117
118
119
120  /**
121   * Creates a new exclude branch request control with the provided set of base
122   * DNs.  It will be marked critical.
123   *
124   * @param  baseDNs  The base DNs for entries to be excluded from search
125   *                  results.  It must not be {@code null} or empty.
126   */
127  public ExcludeBranchRequestControl(final String... baseDNs)
128  {
129    this(true, baseDNs);
130  }
131
132
133
134  /**
135   * Creates a new exclude branch request control with the provided information.
136   *
137   * @param  isCritical  Indicates whether the control should be marked
138   *                     critical.
139   * @param  baseDNs     The base DNs for entries to be excluded from search
140   *                     results.  It must not be {@code null} or empty.
141   */
142  public ExcludeBranchRequestControl(final boolean isCritical,
143                                     final String... baseDNs)
144  {
145    super(EXCLUDE_BRANCH_REQUEST_OID, isCritical, encodeValue(baseDNs));
146
147    this.baseDNs = Collections.unmodifiableList(Arrays.asList(baseDNs));
148  }
149
150
151
152  /**
153   * Creates a new exclude branch request control with the provided information.
154   *
155   * @param  isCritical  Indicates whether the control should be marked
156   *                     critical.
157   * @param  baseDNs     The base DNs for entries to be excluded from search
158   *                     results.  It must not be {@code null} or empty.
159   */
160  public ExcludeBranchRequestControl(final boolean isCritical,
161                                     final Collection<String> baseDNs)
162  {
163    super(EXCLUDE_BRANCH_REQUEST_OID, isCritical, encodeValue(baseDNs));
164
165    this.baseDNs = Collections.unmodifiableList(new ArrayList<>(baseDNs));
166  }
167
168
169
170  /**
171   * Creates a new exclude branch request control which is decoded from the
172   * provided generic control.
173   *
174   * @param  control  The generic control to be decoded as an exclude branch
175   *                  request control.
176   *
177   * @throws  LDAPException  If the provided control cannot be decoded as an
178   *                         exclude branch request control.
179   */
180  public ExcludeBranchRequestControl(final Control control)
181         throws LDAPException
182  {
183    super(control);
184
185    final ASN1OctetString value = control.getValue();
186    if (value == null)
187    {
188      throw new LDAPException(ResultCode.DECODING_ERROR,
189           ERR_EXCLUDE_BRANCH_MISSING_VALUE.get());
190    }
191
192    final ASN1Sequence valueSequence;
193    try
194    {
195      valueSequence = ASN1Sequence.decodeAsSequence(value.getValue());
196    }
197    catch (final Exception e)
198    {
199      Debug.debugException(e);
200      throw new LDAPException(ResultCode.DECODING_ERROR,
201           ERR_EXCLUDE_BRANCH_VALUE_NOT_SEQUENCE.get(
202                StaticUtils.getExceptionMessage(e)), e);
203    }
204
205    try
206    {
207      final ASN1Element[] elements = valueSequence.elements();
208
209      final ASN1Element[] dnElements =
210           ASN1Sequence.decodeAsSequence(elements[0]).elements();
211      final ArrayList<String> dnList = new ArrayList<>(dnElements.length);
212      for (final ASN1Element e : dnElements)
213      {
214        dnList.add(ASN1OctetString.decodeAsOctetString(e).stringValue());
215      }
216      baseDNs = Collections.unmodifiableList(dnList);
217
218      if (baseDNs.isEmpty())
219      {
220        throw new LDAPException(ResultCode.DECODING_ERROR,
221             ERR_EXCLUDE_BRANCH_NO_BASE_DNS.get());
222      }
223    }
224    catch (final LDAPException le)
225    {
226      Debug.debugException(le);
227      throw le;
228    }
229    catch (final Exception e)
230    {
231      Debug.debugException(e);
232      throw new LDAPException(ResultCode.DECODING_ERROR,
233           ERR_EXCLUDE_BRANCH_ERROR_PARSING_VALUE.get(
234                StaticUtils.getExceptionMessage(e)), e);
235    }
236  }
237
238
239
240  /**
241   * Encodes the provided information into a form suitable for use as the value
242   * of this control.
243   *
244   * @param  baseDNs  The base DNs for entries to be excluded from search
245   *                  results.  It must not be {@code null} or empty.
246   *
247   * @return  The encoded value for this control.
248   */
249  private static ASN1OctetString encodeValue(final String... baseDNs)
250  {
251    Validator.ensureNotNull(baseDNs);
252    return encodeValue(Arrays.asList(baseDNs));
253  }
254
255
256
257  /**
258   * Encodes the provided information into a form suitable for use as the value
259   * of this control.
260   *
261   * @param  baseDNs  The base DNs for entries to be excluded from search
262   *                  results.  It must not be {@code null} or empty.
263   *
264   * @return  The encoded value for this control.
265   */
266  private static ASN1OctetString encodeValue(final Collection<String> baseDNs)
267  {
268    Validator.ensureNotNull(baseDNs);
269    Validator.ensureFalse(baseDNs.isEmpty());
270
271    final ArrayList<ASN1Element> dnElements = new ArrayList<>(baseDNs.size());
272    for (final String s : baseDNs)
273    {
274      dnElements.add(new ASN1OctetString(s));
275    }
276
277    final ASN1Sequence baseDNSequence =
278         new ASN1Sequence(TYPE_BASE_DNS, dnElements);
279    final ASN1Sequence valueSequence = new ASN1Sequence(baseDNSequence);
280    return new ASN1OctetString(valueSequence.encode());
281  }
282
283
284
285  /**
286   * Retrieves a list of the base DNs for entries to exclude from the search
287   * results.
288   *
289   * @return  A list of the base DNs for entries to exclude from the search
290   *          results.
291   */
292  public List<String> getBaseDNs()
293  {
294    return baseDNs;
295  }
296
297
298
299  /**
300   * {@inheritDoc}
301   */
302  @Override()
303  public String getControlName()
304  {
305    return INFO_CONTROL_NAME_EXCLUDE_BRANCH.get();
306  }
307
308
309
310  /**
311   * {@inheritDoc}
312   */
313  @Override()
314  public void toString(final StringBuilder buffer)
315  {
316    buffer.append("ExcludeBranchRequestControl(isCritical=");
317    buffer.append(isCritical());
318    buffer.append(", baseDNs={");
319
320    final Iterator<String> iterator = baseDNs.iterator();
321    while (iterator.hasNext())
322    {
323      buffer.append('\'');
324      buffer.append(iterator.next());
325      buffer.append('\'');
326
327      if (iterator.hasNext())
328      {
329        buffer.append(", ");
330      }
331    }
332
333    buffer.append("})");
334  }
335}