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