001/*
002 * Copyright 2012-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2012-2020 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2015-2020 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.unboundidds.controls;
037
038
039
040import java.util.ArrayList;
041
042import com.unboundid.asn1.ASN1Boolean;
043import com.unboundid.asn1.ASN1Element;
044import com.unboundid.asn1.ASN1OctetString;
045import com.unboundid.asn1.ASN1Sequence;
046import com.unboundid.ldap.sdk.Control;
047import com.unboundid.ldap.sdk.LDAPException;
048import com.unboundid.ldap.sdk.ResultCode;
049import com.unboundid.util.Debug;
050import com.unboundid.util.NotMutable;
051import com.unboundid.util.StaticUtils;
052import com.unboundid.util.ThreadSafety;
053import com.unboundid.util.ThreadSafetyLevel;
054
055import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
056
057
058
059/**
060 * This class provides a request control which may be included in a search
061 * request to indicate that soft-deleted entries may be included in the results,
062 * or it may be included in a compare or modify request to indicate that the
063 * operation should operate against the target entry even if it is a
064 * soft-deleted entry.
065 * <BR>
066 * <BLOCKQUOTE>
067 *   <B>NOTE:</B>  This class, and other classes within the
068 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
069 *   supported for use against Ping Identity, UnboundID, and
070 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
071 *   for proprietary functionality or for external specifications that are not
072 *   considered stable or mature enough to be guaranteed to work in an
073 *   interoperable way with other types of LDAP servers.
074 * </BLOCKQUOTE>
075 * <BR>
076 * The criticality for this control may be either {@code TRUE} or {@code FALSE},
077 * but this will only impact how the delete request is to be handled by servers
078 * which do not support this control.  A criticality of {@code TRUE} will cause
079 * any server which does not support this control to reject the request, while
080 * a criticality of {@code FALSE} should cause the request to be processed as if
081 * the control had not been included.
082 * <BR><BR>
083 * The control may optionally have a value.  If a value is provided, then it
084 * must be the encoded representation of the following ASN.1 element:
085 * <PRE>
086 *   SoftDeleteAccessRequestValue ::= SEQUENCE {
087 *     includeNonSoftDeletedEntries     [0] BOOLEAN DEFAULT TRUE,
088 *     returnEntriesInUndeletedForm     [1] BOOLEAN DEFAULT FALSE,
089 *     ... }
090 * </PRE>
091 * See the documentation for the {@link SoftDeleteRequestControl} class for an
092 * example demonstrating the use of this control.
093 *
094 * @see  SoftDeleteResponseControl
095 */
096@NotMutable()
097@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
098public final class SoftDeletedEntryAccessRequestControl
099       extends Control
100{
101  /**
102   * The OID (1.3.6.1.4.1.30221.2.5.24) for the soft-deleted entry access
103   * request control.
104   */
105  public static final String SOFT_DELETED_ENTRY_ACCESS_REQUEST_OID =
106       "1.3.6.1.4.1.30221.2.5.24";
107
108
109
110  /**
111   * The BER type for the include non-soft-deleted entries element.
112   */
113  private static final byte TYPE_INCLUDE_NON_SOFT_DELETED_ENTRIES = (byte) 0x80;
114
115
116
117  /**
118   * The BER type for the return entries in undeleted form element.
119   */
120  private static final byte TYPE_RETURN_ENTRIES_IN_UNDELETED_FORM = (byte) 0x81;
121
122
123
124  /**
125   * The serial version UID for this serializable class.
126   */
127  private static final long serialVersionUID = -3633807543861389512L;
128
129
130
131  // Indicates whether to include non-soft-deleted entries in search results.
132  private final boolean includeNonSoftDeletedEntries;
133
134  // Indicates whether to return soft-deleted entries in the form they appeared
135  // before they were deleted.
136  private final boolean returnEntriesInUndeletedForm;
137
138
139
140  /**
141   * Creates a new soft-deleted entry access request control with the default
142   * settings for all elements.  It will not be marked critical.
143   */
144  public SoftDeletedEntryAccessRequestControl()
145  {
146    this(false, true, false);
147  }
148
149
150
151  /**
152   * Creates a new soft delete request control with the provided information.
153   *
154   * @param  isCritical                    Indicates whether this control should
155   *                                       be marked critical.  This will only
156   *                                       have an effect on the way the
157   *                                       associated delete operation is
158   *                                       handled by servers which do NOT
159   *                                       support the soft-deleted entry access
160   *                                       request control.  For such servers, a
161   *                                       control that is critical will cause
162   *                                       associated request to be rejected,
163   *                                       while a control that is not critical
164   *                                       will be processed as if the control
165   *                                       was not included in the request.
166   * @param  includeNonSoftDeletedEntries  Indicates whether search results
167   *                                       should include non-soft-deleted
168   *                                       entries if they match the criteria
169   *                                       for the associated search request.
170   * @param  returnEntriesInUndeletedForm  Indicates whether soft-deleted
171   *                                       entries returned in search results
172   *                                       should be returned in the form in
173   *                                       which they would appear if they were
174   *                                       undeleted.  Note that if soft-deleted
175   *                                       entries should be returned in their
176   *                                       undeleted form, then it may be
177   *                                       possible for multiple entries to be
178   *                                       returned with the same DN (if
179   *                                       multiple soft-deleted entries with
180   *                                       the same original DN match the
181   *                                       criteria, or if at least one
182   *                                       soft-deleted entry and one normal
183   *                                       entry with the same DN both match the
184   *                                       search criteria).
185   */
186  public SoftDeletedEntryAccessRequestControl(final boolean isCritical,
187              final boolean includeNonSoftDeletedEntries,
188              final boolean returnEntriesInUndeletedForm)
189  {
190    super(SOFT_DELETED_ENTRY_ACCESS_REQUEST_OID, isCritical,
191         encodeValue(includeNonSoftDeletedEntries,
192              returnEntriesInUndeletedForm));
193
194    this.includeNonSoftDeletedEntries = includeNonSoftDeletedEntries;
195    this.returnEntriesInUndeletedForm = returnEntriesInUndeletedForm;
196  }
197
198
199
200  /**
201   * Creates a new soft-deleted entry access request control which is decoded
202   * from the provided generic control.
203   *
204   * @param  control  The generic control to be decoded as a soft-deleted entry
205   *                  access request control.
206   *
207   * @throws  LDAPException  If the provided control cannot be decoded as a
208   *                         soft-deleted entry access request control.
209   */
210  public SoftDeletedEntryAccessRequestControl(final Control control)
211         throws LDAPException
212  {
213    super(control);
214
215    boolean includeNonSoftDeleted = true;
216    boolean returnAsUndeleted     = false;
217
218    if (control.hasValue())
219    {
220      try
221      {
222        final ASN1Sequence valueSequence =
223             ASN1Sequence.decodeAsSequence(control.getValue().getValue());
224        for (final ASN1Element e : valueSequence.elements())
225        {
226          switch (e.getType())
227          {
228            case TYPE_INCLUDE_NON_SOFT_DELETED_ENTRIES:
229              includeNonSoftDeleted =
230                   ASN1Boolean.decodeAsBoolean(e).booleanValue();
231              break;
232            case TYPE_RETURN_ENTRIES_IN_UNDELETED_FORM:
233              returnAsUndeleted = ASN1Boolean.decodeAsBoolean(e).booleanValue();
234              break;
235            default:
236              throw new LDAPException(ResultCode.DECODING_ERROR,
237                   ERR_SOFT_DELETED_ACCESS_REQUEST_UNSUPPORTED_ELEMENT_TYPE.get(
238                        StaticUtils.toHex(e.getType())));
239          }
240        }
241      }
242      catch (final LDAPException le)
243      {
244        Debug.debugException(le);
245        throw le;
246      }
247      catch (final Exception e)
248      {
249        Debug.debugException(e);
250        throw new LDAPException(ResultCode.DECODING_ERROR,
251             ERR_SOFT_DELETED_ACCESS_REQUEST_CANNOT_DECODE_VALUE.get(
252                  StaticUtils.getExceptionMessage(e)),
253             e);
254      }
255    }
256
257    includeNonSoftDeletedEntries = includeNonSoftDeleted;
258    returnEntriesInUndeletedForm = returnAsUndeleted;
259  }
260
261
262
263  /**
264   * Encodes the provided information into an ASN.1 octet string suitable for
265   * use as the value of a soft-deleted entry access request control.
266   *
267   * @param  includeNonSoftDeletedEntries  Indicates whether search results
268   *                                       should include non-soft-deleted
269   *                                       entries if they match the criteria
270   *                                       for the associated search request.
271   * @param  returnEntriesInUndeletedForm  Indicates whether soft-deleted
272   *                                       entries returned in search results
273   *                                       should be returned in the form in
274   *                                       which they would appear if they were
275   *                                       undeleted.  Note that if soft-deleted
276   *                                       entries should be returned in their
277   *                                       undeleted form, then it may be
278   *                                       possible for multiple entries to be
279   *                                       returned with the same DN (if
280   *                                       multiple soft-deleted entries with
281   *                                       the same original DN match the
282   *                                       criteria, or if at least one
283   *                                       soft-deleted entry and one normal
284   *                                       entry with the same DN both match the
285   *                                       search criteria).
286   *
287   * @return  An ASN.1 octet string with an encoding suitable for use as the
288   *          value of a soft-deleted entry access request control, or
289   *          {@code null} if no value is needed for the control.
290   */
291  private static ASN1OctetString encodeValue(
292                      final boolean includeNonSoftDeletedEntries,
293                      final boolean returnEntriesInUndeletedForm)
294  {
295    if (includeNonSoftDeletedEntries && (! returnEntriesInUndeletedForm))
296    {
297      return null;
298    }
299
300    final ArrayList<ASN1Element> elements = new ArrayList<>(2);
301    if (! includeNonSoftDeletedEntries)
302    {
303      elements.add(new ASN1Boolean(TYPE_INCLUDE_NON_SOFT_DELETED_ENTRIES,
304           false));
305    }
306
307    if (returnEntriesInUndeletedForm)
308    {
309      elements.add(new ASN1Boolean(TYPE_RETURN_ENTRIES_IN_UNDELETED_FORM,
310           true));
311    }
312
313    return new ASN1OctetString(new ASN1Sequence(elements).encode());
314  }
315
316
317
318  /**
319   * Indicates whether search results should include non-soft-deleted entries
320   * if they match the criteria for the associated search request.
321   *
322   * @return  {@code true} if the server should return any "normal"
323   *          non-soft-deleted entries that match the search criteria, or
324   *          {@code false} if the server should only return soft-deleted
325   *          entries that match the search criteria.
326   */
327  public boolean includeNonSoftDeletedEntries()
328  {
329    return includeNonSoftDeletedEntries;
330  }
331
332
333
334  /**
335   * Indicates whether soft-deleted entries returned in search results should be
336   * returned in the form in which they would appear if they were undeleted.
337   * Note that if soft-deleted entries should be returned in their undeleted
338   * form, then it may be possible for multiple entries to be returned with the
339   * same DN (if multiple soft-deleted entries with the same original DN match
340   * the criteria, or if at least one soft-deleted entry and one normal entry
341   * with the same DN both match the search criteria).
342   *
343   * @return  {@code false} if soft-deleted entries should be returned in their
344   *          current form as soft-deleted entries, or {@code true} if they
345   *          should be returned in the form in which they would appear if they
346   *          were undeleted (e.g., using the original DN for the entry and
347   *          without all the additional meta-attributes added during the
348   *          soft delete process).
349   */
350  public boolean returnEntriesInUndeletedForm()
351  {
352    return returnEntriesInUndeletedForm;
353  }
354
355
356
357  /**
358   * {@inheritDoc}
359   */
360  @Override()
361  public String getControlName()
362  {
363    return INFO_CONTROL_NAME_SOFT_DELETED_ACCESS_REQUEST.get();
364  }
365
366
367
368  /**
369   * {@inheritDoc}
370   */
371  @Override()
372  public void toString(final StringBuilder buffer)
373  {
374    buffer.append("SoftDeletedEntryAccessRequestControl(isCritical=");
375    buffer.append(isCritical());
376    buffer.append(", includeNonSoftDeletedEntries=");
377    buffer.append(includeNonSoftDeletedEntries);
378    buffer.append(", returnEntriesInUndeletedForm=");
379    buffer.append(returnEntriesInUndeletedForm);
380    buffer.append(')');
381  }
382}