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}