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.controls; 022 023 024 025 import java.util.List; 026 import java.util.ArrayList; 027 028 import com.unboundid.asn1.ASN1Element; 029 import com.unboundid.asn1.ASN1Sequence; 030 import com.unboundid.ldap.matchingrules.BooleanMatchingRule; 031 import com.unboundid.ldap.matchingrules.OctetStringMatchingRule; 032 import com.unboundid.ldap.sdk.Control; 033 import com.unboundid.ldap.sdk.AddRequest; 034 import com.unboundid.ldap.sdk.Attribute; 035 import com.unboundid.ldap.sdk.LDAPException; 036 import com.unboundid.ldap.sdk.Modification; 037 import com.unboundid.ldap.sdk.ResultCode; 038 import com.unboundid.ldif.LDIFModifyChangeRecord; 039 import com.unboundid.util.Debug; 040 import com.unboundid.util.NotMutable; 041 import com.unboundid.util.StaticUtils; 042 import com.unboundid.util.ThreadSafety; 043 import com.unboundid.util.ThreadSafetyLevel; 044 045 import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 046 047 048 049 /** 050 * <BLOCKQUOTE> 051 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 052 * LDAP SDK for Java. It is not available for use in applications that 053 * include only the Standard Edition of the LDAP SDK, and is not supported for 054 * use in conjunction with non-UnboundID products. 055 * </BLOCKQUOTE> 056 * This class provides a request control which may be included in an add request 057 * to indicate that the contents of the resulting entry should come not from the 058 * data of the add request itself but instead from a soft-deleted entry. This 059 * can be used to recover an entry that was previously removed by a delete 060 * request containing the {@link SoftDeleteRequestControl}. 061 * <BR><BR> 062 * The criticality for this control should always be {@code TRUE}. The 063 * criticality will have no effect on servers that do support this control, but 064 * a criticality of {@code TRUE} will ensure that a server which does not 065 * support soft deletes does not attempt to process the add request. If the 066 * criticality were {@code FALSE}, then any server that does not support the 067 * control would simply ignore it and attempt to add the entry specified in the 068 * add request (which will have details about the undelete to be processed). 069 * <BR><BR> 070 * The control may optionally have a value. If a value is provided, then it 071 * must be the encoded representation of an empty ASN.1 sequence, like: 072 * <PRE> 073 * UndeleteRequestValue ::= SEQUENCE { 074 * ... } 075 * </PRE> 076 * In the future, the value sequence may allow one or more elements to customize 077 * the behavior of the undelete operation, but at present no such elements are 078 * defined. 079 * See the documentation for the {@link SoftDeleteRequestControl} class for an 080 * example demonstrating the use of this control. 081 * 082 * @see HardDeleteRequestControl 083 * @see SoftDeleteRequestControl 084 */ 085 @NotMutable() 086 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 087 public final class UndeleteRequestControl 088 extends Control 089 { 090 /** 091 * The OID (1.3.6.1.4.1.30221.2.5.23) for the undelete request control. 092 */ 093 public static final String UNDELETE_REQUEST_OID = 094 "1.3.6.1.4.1.30221.2.5.23"; 095 096 097 098 /** 099 * The name of the optional attribute used to specify a set of changes to 100 * apply to the soft-deleted entry during the course of the undelete. 101 */ 102 public static final String ATTR_CHANGES = "ds-undelete-changes"; 103 104 105 106 /** 107 * The name of the optional attribute used to indicate whether the 108 * newly-undeleted user account should be disabled and prevented from 109 * authenticating. 110 */ 111 public static final String ATTR_DISABLE_ACCOUNT = 112 "ds-undelete-disable-account"; 113 114 115 116 /** 117 * The name of the optional attribute used to indicate whether the 118 * newly-undeleted user will be required to change his/her password 119 * immediately after authenticating and before being required to request any 120 * other operations. 121 */ 122 public static final String ATTR_MUST_CHANGE_PASSWORD = 123 "ds-undelete-must-change-password"; 124 125 126 127 /** 128 * The name of the optional attribute used to specify the new password for use 129 * in the newly-undeleted entry. 130 */ 131 public static final String ATTR_NEW_PASSWORD = "ds-undelete-new-password"; 132 133 134 135 /** 136 * The name of the optional attribute used to specify the password currently 137 * contained in the soft-deleted entry, to be validated as part of the 138 * undelete process. 139 */ 140 public static final String ATTR_OLD_PASSWORD = "ds-undelete-old-password"; 141 142 143 144 /** 145 * The name of the required attribute used to specify the DN of the 146 * soft-deleted entry to be undeleted. 147 */ 148 public static final String ATTR_SOFT_DELETED_ENTRY_DN = "ds-undelete-from-dn"; 149 150 151 152 /** 153 * The serial version UID for this serializable class. 154 */ 155 private static final long serialVersionUID = 5338045977962112876L; 156 157 158 159 /** 160 * Creates a undelete request control with a criticality of TRUE and no value. 161 */ 162 public UndeleteRequestControl() 163 { 164 super(UNDELETE_REQUEST_OID, true, null); 165 } 166 167 168 169 /** 170 * Creates a new undelete request control which is decoded from the 171 * provided generic control. 172 * 173 * @param control The generic control to be decoded as an undelete request 174 * control. 175 * 176 * @throws LDAPException If the provided control cannot be decoded as an 177 * undelete request control. 178 */ 179 public UndeleteRequestControl(final Control control) 180 throws LDAPException 181 { 182 super(control); 183 184 if (control.hasValue()) 185 { 186 try 187 { 188 final ASN1Sequence valueSequence = 189 ASN1Sequence.decodeAsSequence(control.getValue().getValue()); 190 final ASN1Element[] elements = valueSequence.elements(); 191 if (elements.length > 0) 192 { 193 throw new LDAPException(ResultCode.DECODING_ERROR, 194 ERR_UNDELETE_REQUEST_UNSUPPORTED_VALUE_ELEMENT_TYPE.get( 195 StaticUtils.toHex(elements[0].getType()))); 196 } 197 } 198 catch (final LDAPException le) 199 { 200 Debug.debugException(le); 201 throw le; 202 } 203 catch (final Exception e) 204 { 205 Debug.debugException(e); 206 throw new LDAPException(ResultCode.DECODING_ERROR, 207 ERR_UNDELETE_REQUEST_CANNOT_DECODE_VALUE.get( 208 StaticUtils.getExceptionMessage(e)), 209 e); 210 } 211 } 212 } 213 214 215 216 /** 217 * Creates a new undelete request that may be used to recover the specified 218 * soft-deleted entry. 219 * 220 * @param targetDN The DN to use for the entry recovered 221 * from the soft-deleted entry contents. It must 222 * not be {@code null}. 223 * @param softDeletedEntryDN The DN of the soft-deleted entry to be used in 224 * the restore process. It must not be 225 * {@code null}. 226 * 227 * @return An add request with an appropriate set of content 228 */ 229 public static AddRequest createUndeleteRequest(final String targetDN, 230 final String softDeletedEntryDN) 231 { 232 return createUndeleteRequest(targetDN, softDeletedEntryDN, null, null, null, 233 null, null); 234 } 235 236 237 238 /** 239 * Creates a new undelete request that may be used to recover the specified 240 * soft-deleted entry. 241 * 242 * @param targetDN The DN to use for the entry recovered 243 * from the soft-deleted entry contents. It must 244 * not be {@code null}. 245 * @param softDeletedEntryDN The DN of the soft-deleted entry to be used in 246 * the restore process. It must not be 247 * {@code null}. 248 * @param changes An optional set of changes that should be 249 * applied to the entry during the course of 250 * undelete processing. It may be {@code null} or 251 * empty if this element should be omitted from 252 * the resulting add request. 253 * @param oldPassword An optional copy of the password currently 254 * contained in the soft-deleted entry to be 255 * recovered. If this is non-{@code null}, then 256 * this password will be required to match that 257 * contained in the target entry for the undelete 258 * to succeed. 259 * @param newPassword An optional new password to set for the user 260 * as part of the undelete processing. It may be 261 * {@code null} if no new password should be 262 * provided. 263 * @param mustChangePassword Indicates whether the recovered user will be 264 * required to change his/her password before 265 * being allowed to request any other operations. 266 * It may be {@code null} if this should be 267 * omitted from the resulting add request. 268 * @param disableAccount Indicates whether the undeleted entry should be 269 * made disabled so that it cannot be used to 270 * authenticate. It may be {@code null} if this 271 * should be omitted from the resulting add 272 * request. 273 * 274 * @return An add request with an appropriate set of content 275 */ 276 public static AddRequest createUndeleteRequest(final String targetDN, 277 final String softDeletedEntryDN, 278 final List<Modification> changes, 279 final String oldPassword, 280 final String newPassword, 281 final Boolean mustChangePassword, 282 final Boolean disableAccount) 283 { 284 final ArrayList<Attribute> attributes = new ArrayList<Attribute>(6); 285 attributes.add(new Attribute(ATTR_SOFT_DELETED_ENTRY_DN, 286 softDeletedEntryDN)); 287 288 if ((changes != null) && (! changes.isEmpty())) 289 { 290 // The changes attribute should be an LDIF-encoded representation of the 291 // modification, with the first two lines (the DN and changetype) 292 // removed. 293 final LDIFModifyChangeRecord changeRecord = 294 new LDIFModifyChangeRecord(targetDN, changes); 295 final String[] modLdifLines = changeRecord.toLDIF(0); 296 final StringBuilder modLDIFBuffer = new StringBuilder(); 297 for (int i=2; i < modLdifLines.length; i++) 298 { 299 modLDIFBuffer.append(modLdifLines[i]); 300 modLDIFBuffer.append(StaticUtils.EOL); 301 } 302 attributes.add(new Attribute(ATTR_CHANGES, 303 OctetStringMatchingRule.getInstance(), modLDIFBuffer.toString())); 304 } 305 306 if (oldPassword != null) 307 { 308 attributes.add(new Attribute(ATTR_OLD_PASSWORD, 309 OctetStringMatchingRule.getInstance(), oldPassword)); 310 } 311 312 if (newPassword != null) 313 { 314 attributes.add(new Attribute(ATTR_NEW_PASSWORD, 315 OctetStringMatchingRule.getInstance(), newPassword)); 316 } 317 318 if (mustChangePassword != null) 319 { 320 attributes.add(new Attribute(ATTR_MUST_CHANGE_PASSWORD, 321 BooleanMatchingRule.getInstance(), 322 (mustChangePassword ? "true" : "false"))); 323 } 324 325 if (disableAccount != null) 326 { 327 attributes.add(new Attribute(ATTR_DISABLE_ACCOUNT, 328 BooleanMatchingRule.getInstance(), 329 (disableAccount ? "true" : "false"))); 330 } 331 332 final Control[] controls = 333 { 334 new UndeleteRequestControl() 335 }; 336 337 return new AddRequest(targetDN, attributes, controls); 338 } 339 340 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override() 346 public String getControlName() 347 { 348 return INFO_CONTROL_NAME_UNDELETE_REQUEST.get(); 349 } 350 351 352 353 /** 354 * {@inheritDoc} 355 */ 356 @Override() 357 public void toString(final StringBuilder buffer) 358 { 359 buffer.append("UndeleteRequestControl(isCritical="); 360 buffer.append(isCritical()); 361 buffer.append(')'); 362 } 363 }