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