001/* 002 * Copyright 2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 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.extensions; 022 023 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.List; 029 030import com.unboundid.asn1.ASN1Boolean; 031import com.unboundid.asn1.ASN1Element; 032import com.unboundid.asn1.ASN1OctetString; 033import com.unboundid.asn1.ASN1Sequence; 034import com.unboundid.ldap.sdk.LDAPException; 035import com.unboundid.ldap.sdk.ResultCode; 036import com.unboundid.util.Debug; 037import com.unboundid.util.NotMutable; 038import com.unboundid.util.StaticUtils; 039import com.unboundid.util.ThreadSafety; 040import com.unboundid.util.ThreadSafetyLevel; 041import com.unboundid.util.Validator; 042 043import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 044 045 046 047/** 048 * This class defines a data structure that holds information about a password 049 * generated by the server and returned to the client in a 050 * {@link GeneratePasswordExtendedResult}. 051 * <BR> 052 * <BLOCKQUOTE> 053 * <B>NOTE:</B> This class, and other classes within the 054 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 055 * supported for use against Ping Identity, UnboundID, and 056 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 057 * for proprietary functionality or for external specifications that are not 058 * considered stable or mature enough to be guaranteed to work in an 059 * interoperable way with other types of LDAP servers. 060 * </BLOCKQUOTE> 061 */ 062@NotMutable() 063@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 064public final class GeneratedPassword 065 implements Serializable 066{ 067 /** 068 * The BER type for the element that provides a list of validation errors for 069 * the generated password. 070 */ 071 private static final byte TYPE_VALIDATION_ERRORS = (byte) 0xA0; 072 073 074 075 /** 076 * The serial version UID for this serializable class. 077 */ 078 private static final long serialVersionUID = -240847847799966594L; 079 080 081 082 // The password that was generated. 083 private final ASN1OctetString password; 084 085 // Indicates whether the server attempted to perform any validation on the 086 // provided password. 087 private final boolean validationAttempted; 088 089 // A list of messages with information about any problems identified while the 090 // server was validating the quality of the generated password. 091 private final List<String> validationErrors; 092 093 094 095 /** 096 * Creates a generated password object with the provided information. 097 * 098 * @param password The password that was generated. It must not 099 * be @code null} or empty. 100 * @param validationAttempted Indicates whether the server attempted to 101 * validate the quality of the generated 102 * password. 103 * @param validationErrors An optional list of messages with information 104 * about any problems identified while the 105 * server was validating the quality of the 106 * generated password. 107 */ 108 public GeneratedPassword(final String password, 109 final boolean validationAttempted, 110 final List<String> validationErrors) 111 { 112 this(new ASN1OctetString(password), validationAttempted, validationErrors); 113 } 114 115 116 117 /** 118 * Creates a generated password object with the provided information. 119 * 120 * @param password The password that was generated. It must not 121 * be @code null} or empty. 122 * @param validationAttempted Indicates whether the server attempted to 123 * validate the quality of the generated 124 * password. 125 * @param validationErrors An optional list of messages with information 126 * about any problems identified while the 127 * server was validating the quality of the 128 * generated password. 129 */ 130 public GeneratedPassword(final byte[] password, 131 final boolean validationAttempted, 132 final List<String> validationErrors) 133 { 134 this(new ASN1OctetString(password), validationAttempted, validationErrors); 135 } 136 137 138 139 /** 140 * Creates a generated password object with the provided information. 141 * 142 * @param password The password that was generated. It must not 143 * be @code null} or empty. 144 * @param validationAttempted Indicates whether the server attempted to 145 * validate the quality of the generated 146 * password. 147 * @param validationErrors An optional list of messages with information 148 * about any problems identified while the 149 * server was validating the quality of the 150 * generated password. 151 */ 152 private GeneratedPassword(final ASN1OctetString password, 153 final boolean validationAttempted, 154 final List<String> validationErrors) 155 { 156 Validator.ensureTrue( 157 ((password != null) && (password.getValueLength() > 0)), 158 "GeneratedPassword.password must not be null or empty."); 159 160 this.password = password; 161 this.validationAttempted = validationAttempted; 162 163 if (validationErrors == null) 164 { 165 this.validationErrors = Collections.emptyList(); 166 } 167 else 168 { 169 this.validationErrors = Collections.unmodifiableList( 170 new ArrayList<>(validationErrors)); 171 } 172 } 173 174 175 176 /** 177 * Retrieves a string representation of the server-generated password. 178 * 179 * @return A string representation of the server-generated password. 180 */ 181 public String getPasswordString() 182 { 183 return password.stringValue(); 184 } 185 186 187 188 /** 189 * Retrieves the bytes that comprise the server-generated password. 190 * 191 * @return The bytes that comprise the server-generated password. 192 */ 193 public byte[] getPasswordBytes() 194 { 195 return password.getValue(); 196 } 197 198 199 200 /** 201 * Indicates whether the server attempted to validate the quality of the 202 * generated password. 203 * 204 * @return {@code true} if the server attempted to validate the quality of 205 * the generated password, or {@code false} if not. 206 */ 207 public boolean validationAttempted() 208 { 209 return validationAttempted; 210 } 211 212 213 214 /** 215 * Retrieves a list of problems identified while the server was validating the 216 * quality of the generated password. 217 * 218 * @return A list of problems identified while the server was validating the 219 * quality of the generated password, or an empty list if no 220 * validation was attempted or if the generated password satisfied 221 * all of the requirements for all of the appropriate password 222 * validators. 223 */ 224 public List<String> getValidationErrors() 225 { 226 return validationErrors; 227 } 228 229 230 231 /** 232 * Encodes this generated password to a sequence suitable for inclusion in the 233 * value of a {@link GeneratePasswordExtendedResult}. 234 * 235 * @return An ASN.1 sequence containing an encoded representation of this 236 * generated password object. 237 */ 238 public ASN1Sequence encode() 239 { 240 final List<ASN1Element> elements = new ArrayList<>(3); 241 elements.add(password); 242 elements.add(new ASN1Boolean(validationAttempted)); 243 244 if (! validationErrors.isEmpty()) 245 { 246 final List<ASN1Element> validationErrorElements = 247 new ArrayList<>(validationErrors.size()); 248 for (final String error : validationErrors) 249 { 250 validationErrorElements.add(new ASN1OctetString(error)); 251 } 252 253 elements.add(new ASN1Sequence(TYPE_VALIDATION_ERRORS, 254 validationErrorElements)); 255 } 256 257 return new ASN1Sequence(elements); 258 } 259 260 261 262 /** 263 * Decodes the provided ASN.1 element as a generated password object. 264 * 265 * @param element The ASN.1 element to be decoded. It must not be 266 * {@code null}. 267 * 268 * @return The generated password object that was decoded. 269 * 270 * @throws LDAPException If a problem is encountered while decoding the 271 * provided element as a generated password. 272 */ 273 public static GeneratedPassword decode(final ASN1Element element) 274 throws LDAPException 275 { 276 try 277 { 278 final ASN1Element[] elements = 279 ASN1Sequence.decodeAsSequence(element).elements(); 280 final ASN1OctetString password = elements[0].decodeAsOctetString(); 281 final boolean validationAttempted = 282 elements[1].decodeAsBoolean().booleanValue(); 283 284 final List<String> validationErrors = new ArrayList<>(5); 285 for (int i=2; i < elements.length; i++) 286 { 287 if (elements[i].getType() == TYPE_VALIDATION_ERRORS) 288 { 289 for (final ASN1Element errorElement : 290 elements[i].decodeAsSequence().elements()) 291 { 292 validationErrors.add( 293 errorElement.decodeAsOctetString().stringValue()); 294 } 295 } 296 } 297 298 return new GeneratedPassword(password, validationAttempted, 299 validationErrors); 300 } 301 catch (final Exception e) 302 { 303 Debug.debugException(e); 304 throw new LDAPException(ResultCode.DECODING_ERROR, 305 ERR_GENERATED_PASSWORD_DECODING_ERROR.get( 306 StaticUtils.getExceptionMessage(e)), 307 e); 308 } 309 } 310 311 312 313 /** 314 * Retrieves a string representation of this generated password object. 315 * 316 * @return A string representation of this generated password object. 317 */ 318 @Override() 319 public String toString() 320 { 321 final StringBuilder buffer = new StringBuilder(); 322 toString(buffer); 323 return buffer.toString(); 324 } 325 326 327 328 /** 329 * Appends a string representation of this generated password object to the 330 * provided buffer. 331 * 332 * @param buffer The buffer to which the information should be appended. 333 */ 334 public void toString(final StringBuilder buffer) 335 { 336 buffer.append("GeneratedPassword(passwordLength="); 337 buffer.append(password.getValueLength()); 338 buffer.append(", validationAttempted="); 339 buffer.append(validationAttempted); 340 341 if (! validationErrors.isEmpty()) 342 { 343 buffer.append(", validationErrors={"); 344 buffer.append('}'); 345 } 346 347 buffer.append(')'); 348 } 349}