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