001/* 002 * Copyright 2008-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2018 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.text.ParseException; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.Date; 029import java.util.Iterator; 030import java.util.LinkedHashMap; 031import java.util.Map; 032import java.util.NoSuchElementException; 033 034import com.unboundid.asn1.ASN1Element; 035import com.unboundid.asn1.ASN1OctetString; 036import com.unboundid.asn1.ASN1Sequence; 037import com.unboundid.ldap.sdk.Control; 038import com.unboundid.ldap.sdk.ExtendedResult; 039import com.unboundid.ldap.sdk.LDAPException; 040import com.unboundid.ldap.sdk.ResultCode; 041import com.unboundid.util.Debug; 042import com.unboundid.util.NotMutable; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045 046import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 047 048 049 050/** 051 * This class implements a data structure for storing the information from an 052 * extended result for the password policy state extended request as used in the 053 * Ping Identity, UnboundID, or Nokia/Alcatel-Lucent 8661 Directory Server. It 054 * is able to decode a generic extended result to obtain the user DN and 055 * operations. See the documentation in the 056 * {@link PasswordPolicyStateExtendedRequest} class for an example that 057 * demonstrates the use of the password policy state extended operation. 058 * <BR> 059 * <BLOCKQUOTE> 060 * <B>NOTE:</B> This class, and other classes within the 061 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 062 * supported for use against Ping Identity, UnboundID, and 063 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 064 * for proprietary functionality or for external specifications that are not 065 * considered stable or mature enough to be guaranteed to work in an 066 * interoperable way with other types of LDAP servers. 067 * </BLOCKQUOTE> 068 * <BR> 069 * This extended result does not have an OID. If the request was processed 070 * successfully, then the result will have a value that has the same encoding as 071 * the request, which was described in the class-level documentation for the 072 * {@link PasswordPolicyStateExtendedRequest} class. 073 */ 074@NotMutable() 075@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 076public final class PasswordPolicyStateExtendedResult 077 extends ExtendedResult 078{ 079 /** 080 * The serial version UID for this serializable class. 081 */ 082 private static final long serialVersionUID = 7140468768443263344L; 083 084 085 086 // A map containing all of the response operations, indexed by operation type. 087 private final Map<Integer,PasswordPolicyStateOperation> operations; 088 089 // The user DN from the response. 090 private final String userDN; 091 092 093 094 /** 095 * Creates a new password policy state extended result from the provided 096 * extended result. 097 * 098 * @param extendedResult The extended result to be decoded as a password 099 * policy state extended result. It must not be 100 * {@code null}. 101 * 102 * @throws LDAPException If the provided extended result cannot be decoded 103 * as a password policy state extended result. 104 */ 105 public PasswordPolicyStateExtendedResult(final ExtendedResult extendedResult) 106 throws LDAPException 107 { 108 super(extendedResult); 109 110 final ASN1OctetString value = extendedResult.getValue(); 111 if (value == null) 112 { 113 userDN = null; 114 operations = Collections.emptyMap(); 115 return; 116 } 117 118 final ASN1Element[] elements; 119 try 120 { 121 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 122 elements = ASN1Sequence.decodeAsSequence(valueElement).elements(); 123 } 124 catch (final Exception e) 125 { 126 Debug.debugException(e); 127 throw new LDAPException(ResultCode.DECODING_ERROR, 128 ERR_PWP_STATE_RESPONSE_VALUE_NOT_SEQUENCE.get(e), 129 e); 130 } 131 132 if ((elements.length < 1) || (elements.length > 2)) 133 { 134 throw new LDAPException(ResultCode.DECODING_ERROR, 135 ERR_PWP_STATE_RESPONSE_INVALID_ELEMENT_COUNT.get( 136 elements.length)); 137 } 138 139 userDN = ASN1OctetString.decodeAsOctetString(elements[0]).stringValue(); 140 141 final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops = 142 new LinkedHashMap<>(20); 143 if (elements.length == 2) 144 { 145 try 146 { 147 final ASN1Element[] opElements = 148 ASN1Sequence.decodeAsSequence(elements[1]).elements(); 149 for (final ASN1Element e : opElements) 150 { 151 final PasswordPolicyStateOperation op = 152 PasswordPolicyStateOperation.decode(e); 153 ops.put(op.getOperationType(), op); 154 } 155 } 156 catch (final Exception e) 157 { 158 Debug.debugException(e); 159 throw new LDAPException(ResultCode.DECODING_ERROR, 160 ERR_PWP_STATE_RESPONSE_CANNOT_DECODE_OPS.get(e), 161 e); 162 } 163 } 164 165 operations = Collections.unmodifiableMap(ops); 166 } 167 168 169 170 /** 171 * Creates a new password policy state extended result with the provided 172 * information. 173 * @param messageID The message ID for the LDAP message that is 174 * associated with this LDAP result. 175 * @param resultCode The result code from the response. 176 * @param diagnosticMessage The diagnostic message from the response, if 177 * available. 178 * @param matchedDN The matched DN from the response, if available. 179 * @param referralURLs The set of referral URLs from the response, if 180 * available. 181 * @param userDN The user DN from the response. 182 * @param operations The set of operations from the response, mapped 183 * from operation type to the corresponding 184 * operation data. 185 * @param responseControls The set of controls from the response, if 186 * available. 187 */ 188 public PasswordPolicyStateExtendedResult(final int messageID, 189 final ResultCode resultCode, final String diagnosticMessage, 190 final String matchedDN, final String[] referralURLs, 191 final String userDN, 192 final PasswordPolicyStateOperation[] operations, 193 final Control[] responseControls) 194 { 195 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 196 null, encodeValue(userDN, operations), responseControls); 197 198 this.userDN = userDN; 199 200 if ((operations == null) || (operations.length == 0)) 201 { 202 this.operations = Collections.emptyMap(); 203 } 204 else 205 { 206 final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops = 207 new LinkedHashMap<>(operations.length); 208 for (final PasswordPolicyStateOperation o : operations) 209 { 210 ops.put(o.getOperationType(), o); 211 } 212 this.operations = Collections.unmodifiableMap(ops); 213 } 214 } 215 216 217 218 /** 219 * Encodes the provided information into a suitable value for this control. 220 * 221 * @param userDN The user DN from the response. 222 * @param operations The set of operations from the response, mapped 223 * from operation type to the corresponding 224 * operation data. 225 * 226 * @return An ASN.1 octet string containing the appropriately-encoded value 227 * for this control, or {@code null} if there should not be a value. 228 */ 229 private static ASN1OctetString encodeValue(final String userDN, 230 final PasswordPolicyStateOperation[] operations) 231 { 232 if ((userDN == null) && ((operations == null) || (operations.length == 0))) 233 { 234 return null; 235 } 236 237 final ArrayList<ASN1Element> elements = new ArrayList<>(2); 238 elements.add(new ASN1OctetString(userDN)); 239 240 if ((operations != null) && (operations.length > 0)) 241 { 242 final ASN1Element[] opElements = new ASN1Element[operations.length]; 243 for (int i=0; i < operations.length; i++) 244 { 245 opElements[i] = operations[i].encode(); 246 } 247 248 elements.add(new ASN1Sequence(opElements)); 249 } 250 251 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 252 } 253 254 255 256 /** 257 * Retrieves the user DN included in the response. 258 * 259 * @return The user DN included in the response, or {@code null} if the user 260 * DN is not available (e.g., if this is an error response). 261 */ 262 public String getUserDN() 263 { 264 return userDN; 265 } 266 267 268 269 /** 270 * Retrieves the set of password policy operations included in the response. 271 * 272 * @return The set of password policy operations included in the response. 273 */ 274 public Iterable<PasswordPolicyStateOperation> getOperations() 275 { 276 return operations.values(); 277 } 278 279 280 281 /** 282 * Retrieves the specified password policy state operation from the response. 283 * 284 * @param opType The operation type for the password policy state operation 285 * to retrieve. 286 * 287 * @return The requested password policy state operation, or {@code null} if 288 * no such operation was included in the response. 289 */ 290 public PasswordPolicyStateOperation getOperation(final int opType) 291 { 292 return operations.get(opType); 293 } 294 295 296 297 /** 298 * Retrieves the value for the specified password policy state operation as a 299 * string. 300 * 301 * @param opType The operation type for the password policy state operation 302 * to retrieve. 303 * 304 * @return The string value of the requested password policy state operation, 305 * or {@code null} if the specified operation was not included in the 306 * response or did not have any values. 307 */ 308 public String getStringValue(final int opType) 309 { 310 final PasswordPolicyStateOperation op = operations.get(opType); 311 if (op == null) 312 { 313 return null; 314 } 315 316 return op.getStringValue(); 317 } 318 319 320 321 /** 322 * Retrieves the set of string values for the specified password policy state 323 * operation. 324 * 325 * @param opType The operation type for the password policy state operation 326 * to retrieve. 327 * 328 * @return The set of string values for the requested password policy state 329 * operation, or {@code null} if the specified operation was not 330 * included in the response. 331 */ 332 public String[] getStringValues(final int opType) 333 { 334 final PasswordPolicyStateOperation op = operations.get(opType); 335 if (op == null) 336 { 337 return null; 338 } 339 340 return op.getStringValues(); 341 } 342 343 344 345 /** 346 * Retrieves the value of the specified password policy state operation as a 347 * boolean. 348 * 349 * @param opType The operation type for the password policy state operation 350 * to retrieve. 351 * 352 * @return The boolean value of the requested password policy state 353 * operation. 354 * 355 * @throws NoSuchElementException If the specified operation was not 356 * included in the response. 357 * 358 * @throws IllegalStateException If the specified password policy state 359 * operation does not have exactly one value, 360 * or if the value cannot be parsed as a 361 * boolean value. 362 */ 363 public boolean getBooleanValue(final int opType) 364 throws NoSuchElementException, IllegalStateException 365 { 366 final PasswordPolicyStateOperation op = operations.get(opType); 367 if (op == null) 368 { 369 throw new NoSuchElementException( 370 ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get()); 371 } 372 373 return op.getBooleanValue(); 374 } 375 376 377 378 /** 379 * Retrieves the value of the specified password policy state operation as an 380 * integer. 381 * 382 * @param opType The operation type for the password policy state operation 383 * to retrieve. 384 * 385 * @return The integer value of the requested password policy state 386 * operation. 387 * 388 * @throws NoSuchElementException If the specified operation was not 389 * included in the response. 390 * 391 * @throws IllegalStateException If the value of the specified password 392 * policy state operation cannot be parsed as 393 * an integer value. 394 */ 395 public int getIntValue(final int opType) 396 throws NoSuchElementException, IllegalStateException 397 { 398 final PasswordPolicyStateOperation op = operations.get(opType); 399 if (op == null) 400 { 401 throw new NoSuchElementException( 402 ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get()); 403 } 404 405 return op.getIntValue(); 406 } 407 408 409 410 /** 411 * Retrieves the value for the specified password policy state operation as a 412 * {@code Date} in generalized time format. 413 * 414 * @param opType The operation type for the password policy state operation 415 * to retrieve. 416 * 417 * @return The value of the requested password policy state operation as a 418 * {@code Date}, or {@code null} if the specified operation was not 419 * included in the response or did not have any values. 420 * 421 * @throws ParseException If the value cannot be parsed as a date in 422 * generalized time format. 423 */ 424 public Date getGeneralizedTimeValue(final int opType) 425 throws ParseException 426 { 427 final PasswordPolicyStateOperation op = operations.get(opType); 428 if (op == null) 429 { 430 return null; 431 } 432 433 return op.getGeneralizedTimeValue(); 434 } 435 436 437 438 /** 439 * Retrieves the set of values for the specified password policy state 440 * operation as {@code Date}s in generalized time format. 441 * 442 * @param opType The operation type for the password policy state operation 443 * to retrieve. 444 * 445 * @return The set of values of the requested password policy state operation 446 * as {@code Date}s. 447 * 448 * @throws ParseException If any of the values cannot be parsed as a date in 449 * generalized time format. 450 */ 451 public Date[] getGeneralizedTimeValues(final int opType) 452 throws ParseException 453 { 454 final PasswordPolicyStateOperation op = operations.get(opType); 455 if (op == null) 456 { 457 return null; 458 } 459 460 return op.getGeneralizedTimeValues(); 461 } 462 463 464 465 /** 466 * {@inheritDoc} 467 */ 468 @Override() 469 public String getExtendedResultName() 470 { 471 return INFO_EXTENDED_RESULT_NAME_PW_POLICY_STATE.get(); 472 } 473 474 475 476 /** 477 * Appends a string representation of this extended result to the provided 478 * buffer. 479 * 480 * @param buffer The buffer to which a string representation of this 481 * extended result will be appended. 482 */ 483 @Override() 484 public void toString(final StringBuilder buffer) 485 { 486 buffer.append("PasswordPolicyStateExtendedResult(resultCode="); 487 buffer.append(getResultCode()); 488 489 final int messageID = getMessageID(); 490 if (messageID >= 0) 491 { 492 buffer.append(", messageID="); 493 buffer.append(messageID); 494 } 495 496 buffer.append(", userDN='"); 497 buffer.append(userDN); 498 buffer.append("', operations={"); 499 500 final Iterator<PasswordPolicyStateOperation> iterator = 501 operations.values().iterator(); 502 while (iterator.hasNext()) 503 { 504 iterator.next().toString(buffer); 505 if (iterator.hasNext()) 506 { 507 buffer.append(", "); 508 } 509 } 510 buffer.append('}'); 511 512 final String diagnosticMessage = getDiagnosticMessage(); 513 if (diagnosticMessage != null) 514 { 515 buffer.append(", diagnosticMessage='"); 516 buffer.append(diagnosticMessage); 517 buffer.append('\''); 518 } 519 520 final String matchedDN = getMatchedDN(); 521 if (matchedDN != null) 522 { 523 buffer.append(", matchedDN='"); 524 buffer.append(matchedDN); 525 buffer.append('\''); 526 } 527 528 final String[] referralURLs = getReferralURLs(); 529 if (referralURLs.length > 0) 530 { 531 buffer.append(", referralURLs={"); 532 for (int i=0; i < referralURLs.length; i++) 533 { 534 if (i > 0) 535 { 536 buffer.append(", "); 537 } 538 539 buffer.append('\''); 540 buffer.append(referralURLs[i]); 541 buffer.append('\''); 542 } 543 buffer.append('}'); 544 } 545 546 final Control[] responseControls = getResponseControls(); 547 if (responseControls.length > 0) 548 { 549 buffer.append(", responseControls={"); 550 for (int i=0; i < responseControls.length; i++) 551 { 552 if (i > 0) 553 { 554 buffer.append(", "); 555 } 556 557 buffer.append(responseControls[i]); 558 } 559 buffer.append('}'); 560 } 561 562 buffer.append(')'); 563 } 564}