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}