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