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