001/*
002 * Copyright 2012-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2012-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) 2015-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;
037
038
039
040import java.nio.charset.StandardCharsets;
041import java.util.ArrayList;
042import java.util.List;
043
044import com.unboundid.asn1.ASN1OctetString;
045import com.unboundid.ldap.sdk.Control;
046import com.unboundid.ldap.sdk.LDAPException;
047import com.unboundid.ldap.sdk.ToCodeArgHelper;
048import com.unboundid.ldap.sdk.ToCodeHelper;
049import com.unboundid.util.NotMutable;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052import com.unboundid.util.Validator;
053
054
055
056/**
057 * This class provides an implementation of the UNBOUNDID-TOTP SASL bind request
058 * that may be used to repeatedly generate one-time password values.  Because it
059 * is configured with the shared secret rather than a point-in-time version of
060 * the password, it can be used for cases in which the authentication process
061 * may need to be repeated (e.g., for use in a connection pool, following
062 * referrals, or if the auto-reconnect feature is enabled).  If the shared
063 * secret is not known and the one-time password will be provided from an
064 * external source (e.g., entered by a user), then the
065 * {@link SingleUseTOTPBindRequest} variant should be used instead.
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 */
077@NotMutable()
078@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
079public final class ReusableTOTPBindRequest
080       extends UnboundIDTOTPBindRequest
081{
082  /**
083   * The serial version UID for this serializable class.
084   */
085  private static final long serialVersionUID = -8283436883838802510L;
086
087
088
089  // The shared secret key to use when generating the TOTP password.
090  private final byte[] sharedSecret;
091
092  // The duration (in seconds) of the time interval to use when generating the
093  // TOTP password.
094  private final int totpIntervalDurationSeconds;
095
096  // The number of digits to include in the generated TOTP password.
097  private final int totpNumDigits;
098
099
100
101  /**
102   * Creates a new SASL TOTP bind request with the provided information.
103   *
104   * @param  authenticationID  The authentication identity for the bind request.
105   *                           It must not be {@code null}, and must be in the
106   *                           form "u:" followed by a username, or "dn:"
107   *                           followed by a DN.
108   * @param  authorizationID   The authorization identity for the bind request.
109   *                           It may be {@code null} if the authorization
110   *                           identity should be the same as the authentication
111   *                           identity.  If an authorization identity is
112   *                           specified, it must be in the form "u:" followed
113   *                           by a username, or "dn:" followed by a DN.  The
114   *                           value "dn:" may indicate an authorization
115   *                           identity of the anonymous user.
116   * @param  sharedSecret      The shared secret key to use when generating the
117   *                           TOTP password.
118   * @param  staticPassword    The static password for the target user.  It may
119   *                           be {@code null} if only the one-time password is
120   *                           to be used for authentication (which may or may
121   *                           not be allowed by the server).
122   * @param  controls          The set of controls to include in the bind
123   *                           request.
124   */
125  public ReusableTOTPBindRequest(final String authenticationID,
126                                 final String authorizationID,
127                                 final byte[] sharedSecret,
128                                 final String staticPassword,
129                                 final Control... controls)
130  {
131    this(authenticationID, authorizationID, sharedSecret, staticPassword,
132         OneTimePassword.DEFAULT_TOTP_INTERVAL_DURATION_SECONDS,
133         OneTimePassword.DEFAULT_TOTP_NUM_DIGITS, controls);
134  }
135
136
137
138  /**
139   * Creates a new SASL TOTP bind request with the provided information.
140   *
141   * @param  authenticationID  The authentication identity for the bind request.
142   *                           It must not be {@code null}, and must be in the
143   *                           form "u:" followed by a username, or "dn:"
144   *                           followed by a DN.
145   * @param  authorizationID   The authorization identity for the bind request.
146   *                           It may be {@code null} if the authorization
147   *                           identity should be the same as the authentication
148   *                           identity.  If an authorization identity is
149   *                           specified, it must be in the form "u:" followed
150   *                           by a username, or "dn:" followed by a DN.  The
151   *                           value "dn:" may indicate an authorization
152   *                           identity of the anonymous user.
153   * @param  sharedSecret      The shared secret key to use when generating the
154   *                           TOTP password.
155   * @param  staticPassword    The static password for the target user.  It may
156   *                           be {@code null} if only the one-time password is
157   *                           to be used for authentication (which may or may
158   *                           not be allowed by the server).
159   * @param  controls          The set of controls to include in the bind
160   *                           request.
161   */
162  public ReusableTOTPBindRequest(final String authenticationID,
163                                 final String authorizationID,
164                                 final byte[] sharedSecret,
165                                 final byte[] staticPassword,
166                                 final Control... controls)
167  {
168    this(authenticationID, authorizationID, sharedSecret, staticPassword,
169         OneTimePassword.DEFAULT_TOTP_INTERVAL_DURATION_SECONDS,
170         OneTimePassword.DEFAULT_TOTP_NUM_DIGITS, controls);
171  }
172
173
174
175  /**
176   * Creates a new SASL TOTP bind request with the provided information.
177   *
178   * @param  authenticationID             The authentication identity for the
179   *                                      bind request.  It must not be
180   *                                      {@code null}, and must be in the form
181   *                                      "u:" followed by a username, or "dn:"
182   *                                      followed by a DN.
183   * @param  authorizationID              The authorization identity for the
184   *                                      bind request.  It may be {@code null}
185   *                                      if the authorization identity should
186   *                                      be the same as the authentication
187   *                                      identity.  If an authorization
188   *                                      identity is specified, it must be in
189   *                                      the form "u:" followed by a username,
190   *                                      or "dn:" followed by a DN.  The value
191   *                                      "dn:" may indicate an authorization
192   *                                      identity of the anonymous user.
193   * @param  sharedSecret                 The shared secret key to use when
194   *                                      generating the TOTP password.
195   * @param  staticPassword               The static password for the target
196   *                                      user.  It may be {@code null} if only
197   *                                      the one-time password is to be used
198   *                                      for authentication (which may or may
199   *                                      not be allowed by the server).
200   * @param  totpIntervalDurationSeconds  The duration (in seconds) of the time
201   *                                      interval to use for TOTP processing.
202   *                                      It must be greater than zero.
203   * @param  totpNumDigits                The number of digits to include in the
204   *                                      generated TOTP password.  It must be
205   *                                      greater than or equal to six and less
206   *                                      than or equal to eight.
207   * @param  controls                     The set of controls to include in the
208   *                                      bind request.
209   */
210  public ReusableTOTPBindRequest(final String authenticationID,
211                                 final String authorizationID,
212                                 final byte[] sharedSecret,
213                                 final String staticPassword,
214                                 final int totpIntervalDurationSeconds,
215                                 final int totpNumDigits,
216                                 final Control... controls)
217  {
218    super(authenticationID, authorizationID, staticPassword, controls);
219
220    Validator.ensureTrue(totpIntervalDurationSeconds > 0);
221    Validator.ensureTrue((totpNumDigits >= 6) && (totpNumDigits <= 8));
222
223    this.sharedSecret                = sharedSecret;
224    this.totpIntervalDurationSeconds = totpIntervalDurationSeconds;
225    this.totpNumDigits               = totpNumDigits;
226  }
227
228
229
230  /**
231   * Creates a new SASL TOTP bind request with the provided information.
232   *
233   * @param  authenticationID             The authentication identity for the
234   *                                      bind request.  It must not be
235   *                                      {@code null}, and must be in the form
236   *                                      "u:" followed by a username, or "dn:"
237   *                                      followed by a DN.
238   * @param  authorizationID              The authorization identity for the
239   *                                      bind request.  It may be {@code null}
240   *                                      if the authorization identity should
241   *                                      be the same as the authentication
242   *                                      identity.  If an authorization
243   *                                      identity is specified, it must be in
244   *                                      the form "u:" followed by a username,
245   *                                      or "dn:" followed by a DN.  The value
246   *                                      "dn:" may indicate an authorization
247   *                                      identity of the anonymous user.
248   * @param  sharedSecret                 The shared secret key to use when
249   *                                      generating the TOTP password.
250   * @param  staticPassword               The static password for the target
251   *                                      user.  It may be {@code null} if only
252   *                                      the one-time password is to be used
253   *                                      for authentication (which may or may
254   *                                      not be allowed by the server).
255   * @param  totpIntervalDurationSeconds  The duration (in seconds) of the time
256   *                                      interval to use for TOTP processing.
257   *                                      It must be greater than zero.
258   * @param  totpNumDigits                The number of digits to include in the
259   *                                      generated TOTP password.  It must be
260   *                                      greater than or equal to six and less
261   *                                      than or equal to eight.
262   * @param  controls                     The set of controls to include in the
263   *                                      bind request.
264   */
265  public ReusableTOTPBindRequest(final String authenticationID,
266                                 final String authorizationID,
267                                 final byte[] sharedSecret,
268                                 final byte[] staticPassword,
269                                 final int totpIntervalDurationSeconds,
270                                 final int totpNumDigits,
271                                 final Control... controls)
272  {
273    super(authenticationID, authorizationID, staticPassword, controls);
274
275    Validator.ensureTrue(totpIntervalDurationSeconds > 0);
276    Validator.ensureTrue((totpNumDigits >= 6) && (totpNumDigits <= 8));
277
278    this.sharedSecret                = sharedSecret;
279    this.totpIntervalDurationSeconds = totpIntervalDurationSeconds;
280    this.totpNumDigits               = totpNumDigits;
281  }
282
283
284
285  /**
286   * Creates a new SASL TOTP bind request with the provided information.
287   *
288   * @param  authenticationID             The authentication identity for the
289   *                                      bind request.  It must not be
290   *                                      {@code null}, and must be in the form
291   *                                      "u:" followed by a username, or "dn:"
292   *                                      followed by a DN.
293   * @param  authorizationID              The authorization identity for the
294   *                                      bind request.  It may be {@code null}
295   *                                      if the authorization identity should
296   *                                      be the same as the authentication
297   *                                      identity.  If an authorization
298   *                                      identity is specified, it must be in
299   *                                      the form "u:" followed by a username,
300   *                                      or "dn:" followed by a DN.  The value
301   *                                      "dn:" may indicate an authorization
302   *                                      identity of the anonymous user.
303   * @param  sharedSecret                 The shared secret key to use when
304   *                                      generating the TOTP password.
305   * @param  staticPassword               The static password for the target
306   *                                      user.  It may be {@code null} if only
307   *                                      the one-time password is to be used
308   *                                      for authentication (which may or may
309   *                                      not be allowed by the server).
310   * @param  totpIntervalDurationSeconds  The duration (in seconds) of the time
311   *                                      interval to use when generating the
312   *                                      TOTP password.  It must be greater
313   *                                      than zero.
314   * @param  totpNumDigits                The number of digits to include in the
315   *                                      generated TOTP password.  It must be
316   *                                      greater than or equal to six and less
317   *                                      than or equal to eight.
318   * @param  controls                     The set of controls to include in the
319   *                                      bind request.
320   */
321  private ReusableTOTPBindRequest(final String authenticationID,
322                                  final String authorizationID,
323                                  final byte[] sharedSecret,
324                                  final ASN1OctetString staticPassword,
325                                  final int totpIntervalDurationSeconds,
326                                  final int totpNumDigits,
327                                  final Control... controls)
328  {
329    super(authenticationID, authorizationID, staticPassword, controls);
330
331    this.sharedSecret                = sharedSecret;
332    this.totpIntervalDurationSeconds = totpIntervalDurationSeconds;
333    this.totpNumDigits               = totpNumDigits;
334  }
335
336
337
338  /**
339   * Retrieves the shared secret key to use when generating the TOTP password.
340   *
341   * @return  The shared secret key to use when generating the TOTP password.
342   */
343  public byte[] getSharedSecret()
344  {
345    return sharedSecret;
346  }
347
348
349
350  /**
351   * Retrieves the duration (in seconds) of the time interval to use when
352   * generating the TOTP password.
353   *
354   * @return  The duration (in seconds) of the time interval to use when
355   *          generating the TOTP password.
356   */
357  public int getTOTPIntervalDurationSeconds()
358  {
359    return totpIntervalDurationSeconds;
360  }
361
362
363
364  /**
365   * Retrieves the number of digits to include in the generated TOTP password.
366   *
367   * @return  The number of digits to include in the generated TOTP password.
368   */
369  public int getTOTPNumDigits()
370  {
371    return totpNumDigits;
372  }
373
374
375
376  /**
377   * {@inheritDoc}
378   */
379  @Override()
380  protected ASN1OctetString getSASLCredentials()
381            throws LDAPException
382  {
383    // Generate the TOTP password.
384    final String totpPassword = OneTimePassword.totp(sharedSecret,
385         System.currentTimeMillis(), totpIntervalDurationSeconds,
386         totpNumDigits);
387
388    return encodeCredentials(getAuthenticationID(), getAuthorizationID(),
389         totpPassword, getStaticPassword());
390  }
391
392
393
394  /**
395   * {@inheritDoc}
396   */
397  @Override()
398  public ReusableTOTPBindRequest getRebindRequest(final String host,
399                                                  final int port)
400  {
401    return duplicate();
402  }
403
404
405
406  /**
407   * {@inheritDoc}
408   */
409  @Override()
410  public ReusableTOTPBindRequest duplicate()
411  {
412    return duplicate(getControls());
413  }
414
415
416
417  /**
418   * {@inheritDoc}
419   */
420  @Override()
421  public ReusableTOTPBindRequest duplicate(final Control[] controls)
422  {
423    final ReusableTOTPBindRequest bindRequest =
424         new ReusableTOTPBindRequest(getAuthenticationID(),
425              getAuthorizationID(), sharedSecret, getStaticPassword(),
426              totpIntervalDurationSeconds, totpNumDigits, controls);
427    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
428    return bindRequest;
429  }
430
431
432
433  /**
434   * {@inheritDoc}
435   */
436  @Override()
437  public void toCode(final List<String> lineList, final String requestID,
438                     final int indentSpaces, final boolean includeProcessing)
439  {
440    // Create the request variable.
441    final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(7);
442    constructorArgs.add(ToCodeArgHelper.createString(getAuthenticationID(),
443         "Authentication ID"));
444    constructorArgs.add(ToCodeArgHelper.createString(getAuthorizationID(),
445         "Authorization ID"));
446    constructorArgs.add(ToCodeArgHelper.createByteArray(
447         "---redacted-secret---".getBytes(StandardCharsets.UTF_8), true,
448         "Shared Secret"));
449    constructorArgs.add(ToCodeArgHelper.createString(
450         ((getStaticPassword() == null) ? "null" : "---redacted-password---"),
451         "Static Password"));
452    constructorArgs.add(ToCodeArgHelper.createInteger(
453         totpIntervalDurationSeconds, "Interval Duration (seconds)"));
454    constructorArgs.add(ToCodeArgHelper.createInteger(totpNumDigits,
455         "Number of TOTP Digits"));
456
457    final Control[] controls = getControls();
458    if (controls.length > 0)
459    {
460      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
461           "Bind Controls"));
462    }
463
464    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
465         "ReusableTOTPBindRequest", requestID + "Request",
466         "new ReusableTOTPBindRequest", constructorArgs);
467
468
469    // Add lines for processing the request and obtaining the result.
470    if (includeProcessing)
471    {
472      // Generate a string with the appropriate indent.
473      final StringBuilder buffer = new StringBuilder();
474      for (int i=0; i < indentSpaces; i++)
475      {
476        buffer.append(' ');
477      }
478      final String indent = buffer.toString();
479
480      lineList.add("");
481      lineList.add(indent + "try");
482      lineList.add(indent + '{');
483      lineList.add(indent + "  BindResult " + requestID +
484           "Result = connection.bind(" + requestID + "Request);");
485      lineList.add(indent + "  // The bind was processed successfully.");
486      lineList.add(indent + '}');
487      lineList.add(indent + "catch (LDAPException e)");
488      lineList.add(indent + '{');
489      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
490           "help explain why.");
491      lineList.add(indent + "  // Note that the connection is now likely in " +
492           "an unauthenticated state.");
493      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
494      lineList.add(indent + "  String message = e.getMessage();");
495      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
496      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
497      lineList.add(indent + "  Control[] responseControls = " +
498           "e.getResponseControls();");
499      lineList.add(indent + '}');
500    }
501  }
502}