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