001/* 002 * Copyright 2016-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2016-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) 2016-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.util.ArrayList; 041 042import com.unboundid.asn1.ASN1Element; 043import com.unboundid.asn1.ASN1OctetString; 044import com.unboundid.asn1.ASN1Sequence; 045import com.unboundid.ldap.sdk.Control; 046import com.unboundid.ldap.sdk.ExtendedRequest; 047import com.unboundid.ldap.sdk.LDAPException; 048import com.unboundid.ldap.sdk.ResultCode; 049import com.unboundid.util.Debug; 050import com.unboundid.util.NotMutable; 051import com.unboundid.util.StaticUtils; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054import com.unboundid.util.Validator; 055 056import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 057 058 059 060/** 061 * This class provides an implementation of an extended request that may be used 062 * to revoke one or all of the TOTP shared secrets for a user so that they may 063 * no longer be used to authenticate. 064 * <BR> 065 * <BLOCKQUOTE> 066 * <B>NOTE:</B> This class, and other classes within the 067 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 068 * supported for use against Ping Identity, UnboundID, and 069 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 070 * for proprietary functionality or for external specifications that are not 071 * considered stable or mature enough to be guaranteed to work in an 072 * interoperable way with other types of LDAP servers. 073 * </BLOCKQUOTE> 074 * <BR> 075 * This request may be invoked in one of following ways: 076 * <BR><BR> 077 * <UL> 078 * <LI> 079 * With a {@code null} authentication identity and a non-{@code null} 080 * TOTP shared secret. In this case, the authorization identity for the 081 * operation (typically the user as whom the underlying connection is 082 * authenticated, but possibly a different user if the request also includes 083 * a control like the proxied authorization or intermediate client request 084 * control that specifies and alternate authorization identity, or if the 085 * client authenticated with a SASL mechanism that included an alternate 086 * authorization identity) will be used as the authentication identity for 087 * this request, and only the specified TOTP shared secret will be removed 088 * from the user's entry while any other shared secrets that may be present 089 * in the user's entry will be preserved. If a static password is provided, 090 * then it will be verified, but if none is given then the provided TOTP 091 * shared secret will be considered sufficient proof of the user's identity. 092 * </LI> 093 * <LI> 094 * With a {@code null} authentication identity, a non-{@code null} static 095 * password, and a {@code null} TOTP shared secret. In this case, the 096 * authorization identity for the operation will be used as the 097 * authentication identity for this request, and, if the provided static 098 * password is valid, then all TOTP secrets contained in the user's entry 099 * will be revoked. 100 * </LI> 101 * <LI> 102 * With a non-{@code null} authentication identity and a non-{@code null} 103 * TOTP shared secret. In this case, only the provided TOTP shared secret 104 * will be removed from the specified user's account while any other shared 105 * secrets will be preserved. If a static password is provided, then it 106 * will be verified, but if none is given then the provided TOTP shared 107 * secret will be considered sufficient proof of the user's identity. 108 * </LI> 109 * <LI> 110 * With a non-{@code null} authentication identity a non-{@code null} 111 * static password, and a {@code null} TOTP shared secret. In this case, 112 * if the static password is valid for the specified user, then all TOTP 113 * shared secrets for that user will be revoked. 114 * </LI> 115 * <LI> 116 * With a non-{@code null} authentication identity a {@code null} static 117 * password, and a {@code null} TOTP shared secret. In this case, the 118 * authentication identity from the request must be different from the 119 * authorization identity for the operation, and the authorization identity 120 * must have the password-reset privilege. All TOTP shared secrets for 121 * the specified user will be revoked. 122 * </LI> 123 * </UL> 124 * <BR><BR> 125 * This extended request has an OID of 1.3.6.1.4.1.30221.2.6.58, and it must 126 * include a request value with the following encoding: 127 * <BR><BR> 128 * <PRE> 129 * RevokeTOTPSharedSecretRequest ::= SEQUENCE { 130 * authenticationID [0] OCTET STRING OPTIONAL, 131 * staticPassword [1] OCTET STRING OPTIONAL, 132 * totpSharedSecret [2] OCTET STRING OPTIONAL, 133 * ... } 134 * </PRE> 135 * 136 * 137 * @see GenerateTOTPSharedSecretExtendedRequest 138 */ 139@NotMutable() 140@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 141public final class RevokeTOTPSharedSecretExtendedRequest 142 extends ExtendedRequest 143{ 144 /** 145 * The OID (1.3.6.1.4.1.30221.2.6.58) for the revoke TOTP shared secret 146 * extended request. 147 */ 148 public static final String REVOKE_TOTP_SHARED_SECRET_REQUEST_OID = 149 "1.3.6.1.4.1.30221.2.6.58"; 150 151 152 153 /** 154 * The BER type for the authentication ID element of the request value 155 * sequence. 156 */ 157 private static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80; 158 159 160 161 /** 162 * The BER type for the static password element of the request value sequence. 163 */ 164 private static final byte TYPE_STATIC_PASSWORD = (byte) 0x81; 165 166 167 168 /** 169 * The BER type for the TOTP shared secret element of the request value 170 * sequence. 171 */ 172 private static final byte TYPE_TOTP_SHARED_SECRET = (byte) 0x82; 173 174 175 176 /** 177 * The serial version UID for this serializable class. 178 */ 179 private static final long serialVersionUID = 1437768898568182738L; 180 181 182 183 // The static password for the request. 184 private final ASN1OctetString staticPassword; 185 186 // The authentication ID for the request. 187 private final String authenticationID; 188 189 // The base32-encoded representation of the TOTP shared secret to revoke. 190 private final String totpSharedSecret; 191 192 193 194 /** 195 * Creates a new revoke TOTP shared secret extended request with the provided 196 * information. 197 * 198 * @param authenticationID The authentication ID to use to identify the user 199 * for whom to revoke the TOTP shared secret. It 200 * should be a string in the form "dn:" followed by 201 * the DN of the target user, or "u:" followed by 202 * the username. It may be {@code null} if the 203 * authorization identity for the operation should 204 * be used as the authentication identity for this 205 * request. 206 * @param staticPassword The static password of the user for whom the TOTP 207 * shared secrets are to be revoked. It may be 208 * {@code null} if the provided 209 * {@code totpSharedSecret} is non-{@code null}, or 210 * if the {@code authenticationID} is 211 * non-{@code null} and the operation's 212 * authorization identity has the password-reset 213 * privilege. 214 * @param totpSharedSecret The base32-encoded representation of the TOTP 215 * shared secret to revoke. It may be {@code null} 216 * if all TOTP shared secrets should be purged from 217 * the target user's entry. If it is {@code null}, 218 * then either the {@code staticPassword} element 219 * must be non-{@code null}, or the 220 * {@code authenticationID} element must be 221 * non-{@code null}, must be different from the 222 * operation's authorization identity, and the 223 * authorization identity must have the 224 * password-reset privilege. 225 * @param controls The set of controls to include in the request. 226 * It may be {@code null} or empty if there should 227 * not be any request controls. 228 */ 229 public RevokeTOTPSharedSecretExtendedRequest(final String authenticationID, 230 final String staticPassword, 231 final String totpSharedSecret, 232 final Control... controls) 233 { 234 this(authenticationID, encodePassword(staticPassword), totpSharedSecret, 235 controls); 236 } 237 238 239 240 /** 241 * Creates a new revoke TOTP shared secret extended request with the provided 242 * information. 243 * 244 * @param authenticationID The authentication ID to use to identify the user 245 * for whom to revoke the TOTP shared secret. It 246 * should be a string in the form "dn:" followed by 247 * the DN of the target user, or "u:" followed by 248 * the username. It may be {@code null} if the 249 * authorization identity for the operation should 250 * be used as the authentication identity for this 251 * request. 252 * @param staticPassword The static password of the user for whom the TOTP 253 * shared secrets are to be revoked. It may be 254 * {@code null} if the provided 255 * {@code totpSharedSecret} is non-{@code null}, or 256 * if the {@code authenticationID} is 257 * non-{@code null} and the operation's 258 * authorization identity has the password-reset 259 * privilege. 260 * @param totpSharedSecret The base32-encoded representation of the TOTP 261 * shared secret to revoke. It may be {@code null} 262 * if all TOTP shared secrets should be purged from 263 * the target user's entry. If it is {@code null}, 264 * then either the {@code staticPassword} element 265 * must be non-{@code null}, or the 266 * {@code authenticationID} element must be 267 * non-{@code null}, must be different from the 268 * operation's authorization identity, and the 269 * authorization identity must have the 270 * password-reset privilege. 271 * @param controls The set of controls to include in the request. 272 * It may be {@code null} or empty if there should 273 * not be any request controls. 274 */ 275 public RevokeTOTPSharedSecretExtendedRequest(final String authenticationID, 276 final byte[] staticPassword, 277 final String totpSharedSecret, 278 final Control... controls) 279 { 280 this(authenticationID, encodePassword(staticPassword), totpSharedSecret, 281 controls); 282 } 283 284 285 286 /** 287 * Creates a new revoke TOTP shared secret extended request with the provided 288 * information. 289 * 290 * @param authenticationID The authentication ID to use to identify the user 291 * for whom to revoke the TOTP shared secret. It 292 * should be a string in the form "dn:" followed by 293 * the DN of the target user, or "u:" followed by 294 * the username. It may be {@code null} if the 295 * authorization identity for the operation should 296 * be used as the authentication identity for this 297 * request. 298 * @param staticPassword The static password of the user for whom the TOTP 299 * shared secrets are to be revoked. It may be 300 * {@code null} if the provided 301 * {@code totpSharedSecret} is non-{@code null}, or 302 * if the {@code authenticationID} is 303 * non-{@code null} and the operation's 304 * authorization identity has the password-reset 305 * privilege. 306 * @param totpSharedSecret The base32-encoded representation of the TOTP 307 * shared secret to revoke. It may be {@code null} 308 * if all TOTP shared secrets should be purged from 309 * the target user's entry. If it is {@code null}, 310 * then either the {@code staticPassword} element 311 * must be non-{@code null}, or the 312 * {@code authenticationID} element must be 313 * non-{@code null}, must be different from the 314 * operation's authorization identity, and the 315 * authorization identity must have the 316 * password-reset privilege. 317 * @param controls The set of controls to include in the request. 318 * It may be {@code null} or empty if there should 319 * not be any request controls. 320 */ 321 public RevokeTOTPSharedSecretExtendedRequest(final String authenticationID, 322 final ASN1OctetString staticPassword, 323 final String totpSharedSecret, final Control... controls) 324 { 325 super(REVOKE_TOTP_SHARED_SECRET_REQUEST_OID, 326 encodeValue(authenticationID, staticPassword, totpSharedSecret), 327 controls); 328 329 this.authenticationID = authenticationID; 330 this.staticPassword = staticPassword; 331 this.totpSharedSecret = totpSharedSecret; 332 } 333 334 335 336 /** 337 * Creates a new revoke TOTP shared secret extended request that is decoded 338 * from the provided generic extended request. 339 * 340 * @param request The generic extended request to decode as a revoke TOTP 341 * shared secret request. 342 * 343 * @throws LDAPException If a problem is encountered while attempting to 344 * decode the provided request. 345 */ 346 public RevokeTOTPSharedSecretExtendedRequest(final ExtendedRequest request) 347 throws LDAPException 348 { 349 super(request); 350 351 final ASN1OctetString value = request.getValue(); 352 if (value == null) 353 { 354 throw new LDAPException(ResultCode.DECODING_ERROR, 355 ERR_REVOKE_TOTP_SECRET_REQUEST_NO_VALUE.get()); 356 } 357 358 try 359 { 360 String authID = null; 361 ASN1OctetString staticPW = null; 362 String totpSecret = null; 363 for (final ASN1Element e : 364 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 365 { 366 switch (e.getType()) 367 { 368 case TYPE_AUTHENTICATION_ID: 369 authID = ASN1OctetString.decodeAsOctetString(e).stringValue(); 370 break; 371 case TYPE_STATIC_PASSWORD: 372 staticPW = ASN1OctetString.decodeAsOctetString(e); 373 break; 374 case TYPE_TOTP_SHARED_SECRET: 375 totpSecret = ASN1OctetString.decodeAsOctetString(e).stringValue(); 376 break; 377 default: 378 throw new LDAPException(ResultCode.DECODING_ERROR, 379 ERR_REVOKE_TOTP_SECRET_REQUEST_UNRECOGNIZED_TYPE.get( 380 StaticUtils.toHex(e.getType()))); 381 } 382 } 383 384 if ((authID == null) && (staticPW == null) && (totpSecret == null)) 385 { 386 throw new LDAPException(ResultCode.DECODING_ERROR, 387 ERR_REVOKE_TOTP_SECRET_REQUEST_NO_AUTHN_ID_OR_PW_OR_SECRET.get()); 388 } 389 390 authenticationID = authID; 391 staticPassword = staticPW; 392 totpSharedSecret = totpSecret; 393 } 394 catch (final LDAPException le) 395 { 396 Debug.debugException(le); 397 throw le; 398 } 399 catch (final Exception e) 400 { 401 Debug.debugException(e); 402 throw new LDAPException(ResultCode.DECODING_ERROR, 403 ERR_REVOKE_TOTP_SECRET_REQUEST_ERROR_DECODING_VALUE.get( 404 StaticUtils.getExceptionMessage(e)), 405 e); 406 } 407 } 408 409 410 411 /** 412 * Encodes the provided password as an ASN.1 octet string suitable for 413 * inclusion in the encoded request. 414 * 415 * @param password The password to be encoded. It may be {@code null} if 416 * no password should be included. If it is 417 * non-{@code null}, then it must be a string or a byte 418 * array. 419 * 420 * @return The encoded password, or {@code null} if no password was given. 421 */ 422 private static ASN1OctetString encodePassword(final Object password) 423 { 424 if (password == null) 425 { 426 return null; 427 } 428 else if (password instanceof byte[]) 429 { 430 return new ASN1OctetString(TYPE_STATIC_PASSWORD, (byte[]) password); 431 } 432 else 433 { 434 return new ASN1OctetString(TYPE_STATIC_PASSWORD, 435 String.valueOf(password)); 436 } 437 } 438 439 440 441 /** 442 * Encodes the provided information into an ASN.1 octet string suitable for 443 * use as the value of this extended request. 444 * 445 * @param authenticationID The authentication ID to use to identify the user 446 * for whom to revoke the TOTP shared secret. It 447 * should be a string in the form "dn:" followed by 448 * the DN of the target user, or "u:" followed by 449 * the username. It may be {@code null} if the 450 * authorization identity for the operation should 451 * be used as the authentication identity for this 452 * request. 453 * @param staticPassword The static password of the user for whom the TOTP 454 * shared secrets are to be revoked. It may be 455 * {@code null} if the provided 456 * {@code totpSharedSecret} is non-{@code null}, or 457 * if the {@code authenticationID} is 458 * non-{@code null} and the operation's 459 * authorization identity has the password-reset 460 * privilege. 461 * @param totpSharedSecret The TOTP shared secret to revoke. It may be 462 * {@code null} if all TOTP shared secrets should be 463 * purged from the target user's entry. If it is 464 * {@code null}, then either the 465 * {@code staticPassword} element must be 466 * non-{@code null}, or the {@code authenticationID} 467 * element must be non-{@code null}, must be 468 * different from the operation's authorization 469 * identity, and the authorization identity must 470 * have the password-reset privilege. 471 * 472 * @return The ASN.1 octet string containing the encoded request value. 473 */ 474 private static ASN1OctetString encodeValue(final String authenticationID, 475 final ASN1OctetString staticPassword, 476 final String totpSharedSecret) 477 { 478 if (totpSharedSecret == null) 479 { 480 Validator.ensureTrue( 481 ((authenticationID != null) || (staticPassword != null)), 482 "If the TOTP shared secret is null, then at least one of the " + 483 "authentication ID and static password must be non-null."); 484 } 485 486 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 487 488 if (authenticationID != null) 489 { 490 elements.add( 491 new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID)); 492 } 493 494 if (staticPassword != null) 495 { 496 elements.add(staticPassword); 497 } 498 499 if (totpSharedSecret != null) 500 { 501 elements.add( 502 new ASN1OctetString(TYPE_TOTP_SHARED_SECRET, totpSharedSecret)); 503 } 504 505 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 506 } 507 508 509 510 /** 511 * Retrieves the authentication ID that identifies the user for whom to revoke 512 * the TOTP shared secrets, if provided. 513 * 514 * @return The authentication ID that identifies the target user, or 515 * {@code null} if the shared secrets are to be revoked for the 516 * operation's authorization identity. 517 */ 518 public String getAuthenticationID() 519 { 520 return authenticationID; 521 } 522 523 524 525 /** 526 * Retrieves the string representation of the static password for the target 527 * user, if provided. 528 * 529 * @return The string representation of the static password for the target 530 * user, or {@code null} if no static password was provided. 531 */ 532 public String getStaticPasswordString() 533 { 534 if (staticPassword == null) 535 { 536 return null; 537 } 538 else 539 { 540 return staticPassword.stringValue(); 541 } 542 } 543 544 545 546 /** 547 * Retrieves the bytes that comprise the static password for the target user, 548 * if provided. 549 * 550 * @return The bytes that comprise the static password for the target user, 551 * or {@code null} if no static password was provided. 552 */ 553 public byte[] getStaticPasswordBytes() 554 { 555 if (staticPassword == null) 556 { 557 return null; 558 } 559 else 560 { 561 return staticPassword.getValue(); 562 } 563 } 564 565 566 567 /** 568 * Retrieves the base32-encoded representation of the TOTP shared secret to be 569 * revoked, if provided. 570 * 571 * @return The base32-encoded representation of the TOTP shared secret to be 572 * revoked, or {@code null} if all of the user's TOTP shared secrets 573 * should be revoked. 574 */ 575 public String getTOTPSharedSecret() 576 { 577 return totpSharedSecret; 578 } 579 580 581 582 /** 583 * {@inheritDoc} 584 */ 585 @Override() 586 public RevokeTOTPSharedSecretExtendedRequest duplicate() 587 { 588 return duplicate(getControls()); 589 } 590 591 592 593 /** 594 * {@inheritDoc} 595 */ 596 @Override() 597 public RevokeTOTPSharedSecretExtendedRequest duplicate( 598 final Control[] controls) 599 { 600 final RevokeTOTPSharedSecretExtendedRequest r = 601 new RevokeTOTPSharedSecretExtendedRequest(authenticationID, 602 staticPassword, totpSharedSecret, controls); 603 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 604 return r; 605 } 606 607 608 609 /** 610 * {@inheritDoc} 611 */ 612 @Override() 613 public String getExtendedRequestName() 614 { 615 return INFO_REVOKE_TOTP_SECRET_REQUEST_NAME.get(); 616 } 617 618 619 620 /** 621 * {@inheritDoc} 622 */ 623 @Override() 624 public void toString(final StringBuilder buffer) 625 { 626 buffer.append("RevokeTOTPSharedSecretExtendedRequest("); 627 628 if (authenticationID != null) 629 { 630 buffer.append("authenticationID='"); 631 buffer.append(authenticationID); 632 buffer.append("', "); 633 } 634 635 buffer.append("staticPasswordProvided="); 636 buffer.append(staticPassword != null); 637 buffer.append(", totpSharedSecretProvided="); 638 buffer.append(totpSharedSecret != null); 639 640 final Control[] controls = getControls(); 641 if (controls.length > 0) 642 { 643 buffer.append(", controls={"); 644 for (int i=0; i < controls.length; i++) 645 { 646 if (i > 0) 647 { 648 buffer.append(", "); 649 } 650 651 buffer.append(controls[i]); 652 } 653 buffer.append('}'); 654 } 655 656 buffer.append(')'); 657 } 658}