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