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