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.NotMutable; 042import com.unboundid.util.ThreadSafety; 043import com.unboundid.util.ThreadSafetyLevel; 044 045import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 046import static com.unboundid.util.Debug.*; 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 Alcatel-Lucent 8661 Directory Server. It is 054 * 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 Alcatel-Lucent 8661 063 * server products. These classes provide support for proprietary 064 * functionality or for external specifications that are not considered stable 065 * or mature enough to be guaranteed to work in an interoperable way with 066 * 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 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<Integer,PasswordPolicyStateOperation>(); 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 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<Integer,PasswordPolicyStateOperation>( 208 operations.length); 209 for (final PasswordPolicyStateOperation o : operations) 210 { 211 ops.put(o.getOperationType(), o); 212 } 213 this.operations = Collections.unmodifiableMap(ops); 214 } 215 } 216 217 218 219 /** 220 * Encodes the provided information into a suitable value for this control. 221 * 222 * @param userDN The user DN from the response. 223 * @param operations The set of operations from the response, mapped 224 * from operation type to the corresponding 225 * operation data. 226 * 227 * @return An ASN.1 octet string containing the appropriately-encoded value 228 * for this control, or {@code null} if there should not be a value. 229 */ 230 private static ASN1OctetString encodeValue(final String userDN, 231 final PasswordPolicyStateOperation[] operations) 232 { 233 if ((userDN == null) && ((operations == null) || (operations.length == 0))) 234 { 235 return null; 236 } 237 238 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2); 239 elements.add(new ASN1OctetString(userDN)); 240 241 if ((operations != null) && (operations.length > 0)) 242 { 243 final ASN1Element[] opElements = new ASN1Element[operations.length]; 244 for (int i=0; i < operations.length; i++) 245 { 246 opElements[i] = operations[i].encode(); 247 } 248 249 elements.add(new ASN1Sequence(opElements)); 250 } 251 252 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 253 } 254 255 256 257 258 /** 259 * Retrieves the user DN included in the response. 260 * 261 * @return The user DN included in the response, or {@code null} if the user 262 * DN is not available (e.g., if this is an error response). 263 */ 264 public String getUserDN() 265 { 266 return userDN; 267 } 268 269 270 271 /** 272 * Retrieves the set of password policy operations included in the response. 273 * 274 * @return The set of password policy operations included in the response. 275 */ 276 public Iterable<PasswordPolicyStateOperation> getOperations() 277 { 278 return operations.values(); 279 } 280 281 282 283 /** 284 * Retrieves the specified password policy state operation from the response. 285 * 286 * @param opType The operation type for the password policy state operation 287 * to retrieve. 288 * 289 * @return The requested password policy state operation, or {@code null} if 290 * no such operation was included in the response. 291 */ 292 public PasswordPolicyStateOperation getOperation(final int opType) 293 { 294 return operations.get(opType); 295 } 296 297 298 299 /** 300 * Retrieves the value for the specified password policy state operation as a 301 * string. 302 * 303 * @param opType The operation type for the password policy state operation 304 * to retrieve. 305 * 306 * @return The string value of the requested password policy state operation, 307 * or {@code null} if the specified operation was not included in the 308 * response or did not have any values. 309 */ 310 public String getStringValue(final int opType) 311 { 312 final PasswordPolicyStateOperation op = operations.get(opType); 313 if (op == null) 314 { 315 return null; 316 } 317 318 return op.getStringValue(); 319 } 320 321 322 323 /** 324 * Retrieves the set of string values for the specified password policy state 325 * operation. 326 * 327 * @param opType The operation type for the password policy state operation 328 * to retrieve. 329 * 330 * @return The set of string values for the requested password policy state 331 * operation, or {@code null} if the specified operation was not 332 * included in the response. 333 */ 334 public String[] getStringValues(final int opType) 335 { 336 final PasswordPolicyStateOperation op = operations.get(opType); 337 if (op == null) 338 { 339 return null; 340 } 341 342 return op.getStringValues(); 343 } 344 345 346 347 /** 348 * Retrieves the value of the specified password policy state operation as a 349 * boolean. 350 * 351 * @param opType The operation type for the password policy state operation 352 * to retrieve. 353 * 354 * @return The boolean value of the requested password policy state 355 * operation. 356 * 357 * @throws NoSuchElementException If the specified operation was not 358 * included in the response. 359 * 360 * @throws IllegalStateException If the specified password policy state 361 * operation does not have exactly one value, 362 * or if the value cannot be parsed as a 363 * boolean value. 364 */ 365 public boolean getBooleanValue(final int opType) 366 throws NoSuchElementException, IllegalStateException 367 { 368 final PasswordPolicyStateOperation op = operations.get(opType); 369 if (op == null) 370 { 371 throw new NoSuchElementException( 372 ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get()); 373 } 374 375 return op.getBooleanValue(); 376 } 377 378 379 380 /** 381 * Retrieves the value of the specified password policy state operation as an 382 * integer. 383 * 384 * @param opType The operation type for the password policy state operation 385 * to retrieve. 386 * 387 * @return The integer value of the requested password policy state 388 * operation. 389 * 390 * @throws NoSuchElementException If the specified operation was not 391 * included in the response. 392 * 393 * @throws IllegalStateException If the value of the specified password 394 * policy state operation cannot be parsed as 395 * an integer value. 396 */ 397 public int getIntValue(final int opType) 398 throws NoSuchElementException, IllegalStateException 399 { 400 final PasswordPolicyStateOperation op = operations.get(opType); 401 if (op == null) 402 { 403 throw new NoSuchElementException( 404 ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get()); 405 } 406 407 return op.getIntValue(); 408 } 409 410 411 412 /** 413 * Retrieves the value for the specified password policy state operation as a 414 * {@code Date} in generalized time format. 415 * 416 * @param opType The operation type for the password policy state operation 417 * to retrieve. 418 * 419 * @return The value of the requested password policy state operation as a 420 * {@code Date}, or {@code null} if the specified operation was not 421 * included in the response or did not have any values. 422 * 423 * @throws ParseException If the value cannot be parsed as a date in 424 * generalized time format. 425 */ 426 public Date getGeneralizedTimeValue(final int opType) 427 throws ParseException 428 { 429 final PasswordPolicyStateOperation op = operations.get(opType); 430 if (op == null) 431 { 432 return null; 433 } 434 435 return op.getGeneralizedTimeValue(); 436 } 437 438 439 440 /** 441 * Retrieves the set of values for the specified password policy state 442 * operation as {@code Date}s in generalized time format. 443 * 444 * @param opType The operation type for the password policy state operation 445 * to retrieve. 446 * 447 * @return The set of values of the requested password policy state operation 448 * as {@code Date}s. 449 * 450 * @throws ParseException If any of the values cannot be parsed as a date in 451 * generalized time format. 452 */ 453 public Date[] getGeneralizedTimeValues(final int opType) 454 throws ParseException 455 { 456 final PasswordPolicyStateOperation op = operations.get(opType); 457 if (op == null) 458 { 459 return null; 460 } 461 462 return op.getGeneralizedTimeValues(); 463 } 464 465 466 467 /** 468 * {@inheritDoc} 469 */ 470 @Override() 471 public String getExtendedResultName() 472 { 473 return INFO_EXTENDED_RESULT_NAME_PW_POLICY_STATE.get(); 474 } 475 476 477 478 /** 479 * Appends a string representation of this extended result to the provided 480 * buffer. 481 * 482 * @param buffer The buffer to which a string representation of this 483 * extended result will be appended. 484 */ 485 @Override() 486 public void toString(final StringBuilder buffer) 487 { 488 buffer.append("PasswordPolicyStateExtendedResult(resultCode="); 489 buffer.append(getResultCode()); 490 491 final int messageID = getMessageID(); 492 if (messageID >= 0) 493 { 494 buffer.append(", messageID="); 495 buffer.append(messageID); 496 } 497 498 buffer.append(", userDN='"); 499 buffer.append(userDN); 500 buffer.append("', operations={"); 501 502 final Iterator<PasswordPolicyStateOperation> iterator = 503 operations.values().iterator(); 504 while (iterator.hasNext()) 505 { 506 iterator.next().toString(buffer); 507 if (iterator.hasNext()) 508 { 509 buffer.append(", "); 510 } 511 } 512 buffer.append('}'); 513 514 final String diagnosticMessage = getDiagnosticMessage(); 515 if (diagnosticMessage != null) 516 { 517 buffer.append(", diagnosticMessage='"); 518 buffer.append(diagnosticMessage); 519 buffer.append('\''); 520 } 521 522 final String matchedDN = getMatchedDN(); 523 if (matchedDN != null) 524 { 525 buffer.append(", matchedDN='"); 526 buffer.append(matchedDN); 527 buffer.append('\''); 528 } 529 530 final String[] referralURLs = getReferralURLs(); 531 if (referralURLs.length > 0) 532 { 533 buffer.append(", referralURLs={"); 534 for (int i=0; i < referralURLs.length; i++) 535 { 536 if (i > 0) 537 { 538 buffer.append(", "); 539 } 540 541 buffer.append('\''); 542 buffer.append(referralURLs[i]); 543 buffer.append('\''); 544 } 545 buffer.append('}'); 546 } 547 548 final Control[] responseControls = getResponseControls(); 549 if (responseControls.length > 0) 550 { 551 buffer.append(", responseControls={"); 552 for (int i=0; i < responseControls.length; i++) 553 { 554 if (i > 0) 555 { 556 buffer.append(", "); 557 } 558 559 buffer.append(responseControls[i]); 560 } 561 buffer.append('}'); 562 } 563 564 buffer.append(')'); 565 } 566}