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