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;
037
038
039
040import java.util.Date;
041
042import com.unboundid.ldap.sdk.Entry;
043import com.unboundid.ldap.sdk.LDAPException;
044import com.unboundid.ldap.sdk.ReadOnlyEntry;
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.ldap.sdk.unboundidds.controls.
047            SoftDeletedEntryAccessRequestControl;
048import com.unboundid.util.NotMutable;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051
052import static com.unboundid.ldap.sdk.unboundidds.UnboundIDDSMessages.*;
053
054
055
056/**
057 * This class provides a data structure for representing information about a
058 * soft-deleted entry, which results from a soft delete operation that has
059 * caused the entry to be hidden so that it is not accessible to clients under
060 * normal circumstances, rather than causing the entry to be completely removed
061 * from the server.
062 * <BR>
063 * <BLOCKQUOTE>
064 *   <B>NOTE:</B>  This class, and other classes within the
065 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
066 *   supported for use against Ping Identity, UnboundID, and
067 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
068 *   for proprietary functionality or for external specifications that are not
069 *   considered stable or mature enough to be guaranteed to work in an
070 *   interoperable way with other types of LDAP servers.
071 * </BLOCKQUOTE>
072 * <BR>
073 * A soft-deleted entry will have its RDN altered to include the entryUUID for
074 * the original entry, will be updated to include the "ds-soft-delete-entry"
075 * auxiliary object class, and will have additional metadata attributes added to
076 * it which may include:
077 * <UL>
078 *   <LI>
079 *     ds-soft-delete-from-dn -- This specifies the DN assigned to the entry
080 *     before it was converted to a soft-deleted entry.
081 *   </LI>
082 *   <LI>
083 *     ds-soft-delete-timestamp -- This specifies the time that the entry was
084 *     converted to a soft-deleted entry.
085 *   </LI>
086 *   <LI>
087 *     ds-soft-delete-requester-dn -- This specifies the DN of the user who
088 *     requested the soft delete operation.
089 *   </LI>
090 *   <LI>
091 *     ds-soft-delete-requester-ip-address -- This specifies the IP address of
092 *     the client that requested the soft delete operation.
093 *   </LI>
094 * </UL>
095 * <BR><BR>
096 * Soft-deleted entries may only be retrieved by users who have the
097 * soft-delete-read privilege, and then only by clients who issue a search
098 * request with one or more of the following characteristics:
099 * <UL>
100 *   <LI>
101 *     The search operation has a scope of baseObject and a base DN which
102 *     specifically targets a soft-deleted entry.
103 *   </LI>
104 *   <LI>
105 *     The search operation includes a filter with a component that will
106 *     specifically match entries that have the ds-soft-delete-entry object
107 *     class (e.g., "(objectClass=ds-soft-delete-entry)").
108 *   </LI>
109 *   <LI>
110 *     The search operation includes a
111 *     {@link SoftDeletedEntryAccessRequestControl}.
112 *   </LI>
113 * </UL>
114 */
115@NotMutable()
116@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
117public final class SoftDeletedEntry
118       extends ReadOnlyEntry
119{
120  /**
121   * The name of the attribute that will be included in a soft-deleted entry to
122   * indicate the original DN the entry held before it was converted to a
123   * soft-deleted entry.
124   */
125  public static final String ATTR_SOFT_DELETE_FROM_DN =
126       "ds-soft-delete-from-dn";
127
128
129
130  /**
131   * The name of the attribute that will be included in a soft-deleted entry to
132   * indicate the DN of the user that requested the soft delete operation.
133   */
134  public static final String ATTR_SOFT_DELETE_REQUESTER_DN =
135       "ds-soft-delete-requester-dn";
136
137
138
139  /**
140   * The name of the attribute that will be included in a soft-deleted entry to
141   * indicate the IP address of the client that requested the soft delete
142   * operation.
143   */
144  public static final String ATTR_SOFT_DELETE_REQUESTER_IP_ADDRESS =
145       "ds-soft-delete-requester-ip-address";
146
147
148
149  /**
150   * The name of the attribute that will be included in a soft-deleted entry to
151   * indicate the time it was converted to a soft-deleted entry.
152   */
153  public static final String ATTR_SOFT_DELETE_TIMESTAMP =
154       "ds-soft-delete-timestamp";
155
156
157
158  /**
159   * The name of the auxiliary object class that will be used to mark
160   * soft-deleted entries.
161   */
162  public static final String OC_SOFT_DELETED_ENTRY = "ds-soft-delete-entry";
163
164
165
166  /**
167   * The serial version UID for this serializable class.
168   */
169  private static final long serialVersionUID = -3450703461178674797L;
170
171
172
173  // The time the entry was converted to a soft-deleted entry.
174  private final Date softDeleteTimestamp;
175
176  // The DN held by the entry at the time it was converted to a soft-deleted
177  // entry.
178  private final String softDeleteFromDN;
179
180  // The DN of the user that requested the soft delete operation.
181  private final String softDeleteRequesterDN;
182
183  // The IP address of the client that requested the soft delete operation.
184  private final String softDeleteRequesterIPAddress;
185
186
187
188  /**
189   * Creates a soft-deleted entry from the provided entry.
190   *
191   * @param  entry  The entry to be processed as a soft-deleted entry.  It must
192   *                not be {@code null}.
193   *
194   * @throws  LDAPException  If the provided entry does not represent a valid
195   *                         soft-deleted entry.
196   */
197  public SoftDeletedEntry(final Entry entry)
198         throws LDAPException
199  {
200    super(entry);
201
202    if (! entry.hasObjectClass(OC_SOFT_DELETED_ENTRY))
203    {
204      throw new LDAPException(ResultCode.LOCAL_ERROR,
205           ERR_SOFT_DELETED_ENTRY_MISSING_OC.get(entry.getDN()));
206    }
207
208    softDeleteFromDN = entry.getAttributeValue(ATTR_SOFT_DELETE_FROM_DN);
209    softDeleteTimestamp =
210         entry.getAttributeValueAsDate(ATTR_SOFT_DELETE_TIMESTAMP);
211    softDeleteRequesterDN =
212         entry.getAttributeValue(ATTR_SOFT_DELETE_REQUESTER_DN);
213    softDeleteRequesterIPAddress =
214         entry.getAttributeValue(ATTR_SOFT_DELETE_REQUESTER_IP_ADDRESS);
215
216    if (softDeleteFromDN == null)
217    {
218      throw new LDAPException(ResultCode.LOCAL_ERROR,
219           ERR_SOFT_DELETED_ENTRY_MISSING_FROM_DN.get(entry.getDN()));
220    }
221  }
222
223
224
225  /**
226   * Retrieves the DN held by the entry at the time it was converted to a
227   * soft-deleted entry.
228   *
229   * @return  The DN held by the entry at the time it was converted to a
230   *          soft-deleted entry.
231   */
232  public String getSoftDeleteFromDN()
233  {
234    return softDeleteFromDN;
235  }
236
237
238
239  /**
240   * Retrieves the time that the entry was converted to a soft-deleted entry,
241   * if available.
242   *
243   * @return  The time that the entry was converted to a soft-deleted entry, or
244   *          {@code null} if this is not available in the entry.
245   */
246  public Date getSoftDeleteTimestamp()
247  {
248    return softDeleteTimestamp;
249  }
250
251
252
253  /**
254   * Retrieves the DN of the user that requested the soft delete operation,
255   * if available.
256   *
257   * @return  The DN of the user that requested the soft delete operation, or
258   *          {@code null} if this is not available in the entry.
259   */
260  public String getSoftDeleteRequesterDN()
261  {
262    return softDeleteRequesterDN;
263  }
264
265
266
267  /**
268   * Retrieves the IP address of the client that requested the soft delete
269   * operation, if available.
270   *
271   * @return  The IP address of the client that requested the soft delete
272   *          operation, or {@code null} if this is not available in the entry.
273   */
274  public String getSoftDeleteRequesterIPAddress()
275  {
276    return softDeleteRequesterIPAddress;
277  }
278
279
280
281  /**
282   * Retrieves a copy of the original entry as it appeared before the soft
283   * delete operation was processed.  It will have its original DN and all
284   * soft delete metadata attributes and auxiliary object class removed.
285   *
286   * @return  A copy of the original entry as it appeared before the soft delete
287   *          operation was processed.
288   */
289  public ReadOnlyEntry getUndeletedEntry()
290  {
291    final Entry e = duplicate();
292
293    e.setDN(softDeleteFromDN);
294
295    e.removeAttributeValue("objectClass", OC_SOFT_DELETED_ENTRY);
296    e.removeAttribute(ATTR_SOFT_DELETE_FROM_DN);
297    e.removeAttribute(ATTR_SOFT_DELETE_TIMESTAMP);
298    e.removeAttribute(ATTR_SOFT_DELETE_REQUESTER_DN);
299    e.removeAttribute(ATTR_SOFT_DELETE_REQUESTER_IP_ADDRESS);
300
301    return new ReadOnlyEntry(e);
302  }
303
304
305
306  /**
307   * Indicates whether the provided entry may be parsed as a valid soft-deleted
308   * entry.
309   *
310   * @param  entry  The entry to be examined.  It must not be {@code null}.
311   *
312   * @return  {@code true} if the provided entry contains at least a
313   *          ds-soft-delete-entry object class and a ds-soft-delete-from-dn
314   *          attribute.
315   */
316  public static boolean isSoftDeletedEntry(final Entry entry)
317  {
318    return (entry.hasObjectClass(OC_SOFT_DELETED_ENTRY) &&
319         entry.hasAttribute(ATTR_SOFT_DELETE_FROM_DN));
320  }
321}