001/*
002 * Copyright 2016-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-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.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 register a YubiKey OTP device with the Directory Server so that it may be
048 * used to authenticate using the UNBOUNDID-YUBIKEY-OTP SASL mechanism.
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 Alcatel-Lucent 8661
054 *   server products.  These classes provide support for proprietary
055 *   functionality or for external specifications that are not considered stable
056 *   or mature enough to be guaranteed to work in an interoperable way with
057 *   other types of LDAP servers.
058 * </BLOCKQUOTE>
059 * <BR>
060 * This extended request has an OID of 1.3.6.1.4.1.30221.2.6.54, and it must
061 * include a request value with the following encoding:
062 * <BR><BR>
063 * <PRE>
064 *   RegisterYubiKeyOTPDeviceRequest ::= SEQUENCE {
065 *        authenticationID     [0] OCTET STRING OPTIONAL,
066 *        staticPassword       [1] OCTET STRING OPTIONAL,
067 *        yubiKeyOTP           [2] OCTET STRING,
068 *        ... }
069 * </PRE>
070 *
071 *
072 * @see  DeregisterYubiKeyOTPDeviceExtendedRequest
073 * @see  com.unboundid.ldap.sdk.unboundidds.UnboundIDYubiKeyOTPBindRequest
074 * @see  com.unboundid.ldap.sdk.unboundidds.RegisterYubiKeyOTPDevice
075 */
076@NotMutable()
077@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
078public final class RegisterYubiKeyOTPDeviceExtendedRequest
079       extends ExtendedRequest
080{
081  /**
082   * The OID (1.3.6.1.4.1.30221.2.6.54) for the register YubiKey OTP device
083   * extended request.
084   */
085  public static final String REGISTER_YUBIKEY_OTP_DEVICE_REQUEST_OID =
086       "1.3.6.1.4.1.30221.2.6.54";
087
088
089
090  /**
091   * The BER type for the authentication ID element of the request value
092   * sequence.
093   */
094  private static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80;
095
096
097
098  /**
099   * The BER type for the static password element of the request value sequence.
100   */
101  private static final byte TYPE_STATIC_PASSWORD = (byte) 0x81;
102
103
104
105  /**
106   * The BER type for the YubiKey OTP element of the request value sequence.
107   */
108  private static final byte TYPE_YUBIKEY_OTP = (byte) 0x82;
109
110
111
112  /**
113   * The serial version UID for this serializable class.
114   */
115  private static final long serialVersionUID = 4833523148133015294L;
116
117
118
119  // The static password for the request.
120  private final ASN1OctetString staticPassword;
121
122  // The authentication ID for the request.
123  private final String authenticationID;
124
125  // The YubiKey OTP for the request.
126  private final String yubiKeyOTP;
127
128
129
130  /**
131   * Creates a new register YubiKey OTP device extended request that will be
132   * used to register a new device for the user as whom the underlying
133   * connection is authenticated.
134   *
135   * @param  yubiKeyOTP  A one-time password generated by the YubiKey device to
136   *                     be registered.  It must not be {@code null}.
137   * @param  controls    The set of controls to include in the request.  It may
138   *                     be {@code null} or empty if there should not be any
139   *                     request controls.
140   */
141  public RegisterYubiKeyOTPDeviceExtendedRequest(final String yubiKeyOTP,
142                                                 final Control... controls)
143  {
144    this(null, (ASN1OctetString) null, yubiKeyOTP, controls);
145  }
146
147
148
149  /**
150   * Creates a new register YubiKey OTP device extended request with the
151   * provided information.
152   *
153   * @param  authenticationID  The authentication ID that identifies the user
154   *                           for whom the YubiKey OTP device is to be
155   *                           registered.  It may be {@code null} if the device
156   *                           is to be registered for the user as whom the
157   *                           underlying connection is authenticated.
158   * @param  staticPassword    The static password of the user for whom the
159   *                           device is to be registered.  It may be
160   *                           {@code null} if the device is to be registered
161   *                           for a user other than the user authenticated on
162   *                           the underlying connection and the server is
163   *                           configured to not require the target user's
164   *                           static password in this case.
165   * @param  yubiKeyOTP        A one-time password generated by the YubiKey
166   *                           device to be registered.  It must not be
167   *                           {@code null}.
168   * @param  controls          The set of controls to include in the request.
169   *                           It may be {@code null} or empty if there should
170   *                           not be any request controls.
171   */
172  public RegisterYubiKeyOTPDeviceExtendedRequest(final String authenticationID,
173                                                 final String staticPassword,
174                                                 final String yubiKeyOTP,
175                                                 final Control... controls)
176  {
177    this(authenticationID, encodePassword(staticPassword), yubiKeyOTP,
178         controls);
179  }
180
181
182
183  /**
184   * Creates a new register YubiKey OTP device extended request with the
185   * provided information.
186   *
187   * @param  authenticationID  The authentication ID that identifies the user
188   *                           for whom the YubiKey OTP device is to be
189   *                           registered.  It may be {@code null} if the device
190   *                           is to be registered for the user as whom the
191   *                           underlying connection is authenticated.
192   * @param  staticPassword    The static password of the user for whom the
193   *                           device is to be registered.  It may be
194   *                           {@code null} if the device is to be registered
195   *                           for a user other than the user authenticated on
196   *                           the underlying connection and the server is
197   *                           configured to not require the target user's
198   *                           static password in this case.
199   * @param  yubiKeyOTP        A one-time password generated by the YubiKey
200   *                           device to be registered.  It must not be
201   *                           {@code null}.
202   * @param  controls          The set of controls to include in the request.
203   *                           It may be {@code null} or empty if there should
204   *                           not be any request controls.
205   */
206  public RegisterYubiKeyOTPDeviceExtendedRequest(final String authenticationID,
207                                                 final byte[] staticPassword,
208                                                 final String yubiKeyOTP,
209                                                 final Control... controls)
210  {
211    this(authenticationID, encodePassword(staticPassword), yubiKeyOTP,
212         controls);
213  }
214
215
216
217  /**
218   * Creates a new register YubiKey OTP device extended request with the
219   * provided information.
220   *
221   * @param  authenticationID  The authentication ID that identifies the user
222   *                           for whom the YubiKey OTP device is to be
223   *                           registered.  It may be {@code null} if the device
224   *                           is to be registered for the user as whom the
225   *                           underlying connection is authenticated.
226   * @param  staticPassword    The static password of the user for whom the
227   *                           device is to be registered.  It may be
228   *                           {@code null} if the device is to be registered
229   *                           for a user other than the user authenticated on
230   *                           the underlying connection and the server is
231   *                           configured to not require the target user's
232   *                           static password in this case.
233   * @param  yubiKeyOTP        A one-time password generated by the YubiKey
234   *                           device to be registered.  It must not be
235   *                           {@code null}.
236   * @param  controls          The set of controls to include in the request.
237   *                           It may be {@code null} or empty if there should
238   *                           not be any request controls.
239   */
240  private RegisterYubiKeyOTPDeviceExtendedRequest(final String authenticationID,
241               final ASN1OctetString staticPassword, final String yubiKeyOTP,
242               final Control... controls)
243  {
244    super(REGISTER_YUBIKEY_OTP_DEVICE_REQUEST_OID,
245         encodeValue(authenticationID, staticPassword, yubiKeyOTP), controls);
246
247    this.authenticationID = authenticationID;
248    this.staticPassword   = staticPassword;
249    this.yubiKeyOTP       = yubiKeyOTP;
250  }
251
252
253
254  /**
255   * Creates a new register YubiKey OTP device extended request that is decoded
256   * from the provided generic extended request.
257   *
258   * @param  request  The generic extended request to decode as a register
259   *                  YubiKey OTP device request.
260   *
261   * @throws  LDAPException  If a problem is encountered while attempting to
262   *                         decode the provided request.
263   */
264  public RegisterYubiKeyOTPDeviceExtendedRequest(final ExtendedRequest request)
265         throws LDAPException
266  {
267    super(request);
268
269    final ASN1OctetString value = request.getValue();
270    if (value == null)
271    {
272      throw new LDAPException(ResultCode.DECODING_ERROR,
273           ERR_REGISTER_YUBIKEY_OTP_REQUEST_NO_VALUE.get());
274    }
275
276    try
277    {
278      String authID = null;
279      ASN1OctetString staticPW = null;
280      String otp = null;
281      for (final ASN1Element e :
282           ASN1Sequence.decodeAsSequence(value.getValue()).elements())
283      {
284        switch (e.getType())
285        {
286          case TYPE_AUTHENTICATION_ID:
287            authID = ASN1OctetString.decodeAsOctetString(e).stringValue();
288            break;
289          case TYPE_STATIC_PASSWORD:
290            staticPW = ASN1OctetString.decodeAsOctetString(e);
291            break;
292          case TYPE_YUBIKEY_OTP:
293            otp = ASN1OctetString.decodeAsOctetString(e).stringValue();
294            break;
295          default:
296            throw new LDAPException(ResultCode.DECODING_ERROR,
297                 ERR_REGISTER_YUBIKEY_OTP_REQUEST_UNRECOGNIZED_TYPE.get(
298                      StaticUtils.toHex(e.getType())));
299        }
300      }
301
302      if (otp == null)
303      {
304        throw new LDAPException(ResultCode.DECODING_ERROR,
305             ERR_REGISTER_YUBIKEY_OTP_REQUEST_MISSING_OTP.get());
306      }
307
308      authenticationID = authID;
309      staticPassword   = staticPW;
310      yubiKeyOTP       = otp;
311    }
312    catch (final LDAPException le)
313    {
314      Debug.debugException(le);
315      throw le;
316    }
317    catch (final Exception e)
318    {
319      Debug.debugException(e);
320      throw new LDAPException(ResultCode.DECODING_ERROR,
321           ERR_REGISTER_YUBIKEY_OTP_REQUEST_ERROR_DECODING_VALUE.get(
322                StaticUtils.getExceptionMessage(e)),
323           e);
324    }
325  }
326
327
328
329  /**
330   * Encodes the provided password as an ASN.1 octet string suitable for
331   * inclusion in the encoded request.
332   *
333   * @param  password  The password to be encoded.  It may be {@code null} if
334   *                   no password should be included.  If it is
335   *                   non-{@code null}, then it must be a string or a byte
336   *                   array.
337   *
338   * @return  The encoded password, or {@code null} if no password was given.
339   */
340  static ASN1OctetString encodePassword(final Object password)
341  {
342    if (password == null)
343    {
344      return null;
345    }
346    else if (password instanceof byte[])
347    {
348      return new ASN1OctetString(TYPE_STATIC_PASSWORD, (byte[]) password);
349    }
350    else
351    {
352      return new ASN1OctetString(TYPE_STATIC_PASSWORD,
353           String.valueOf(password));
354    }
355  }
356
357
358
359  /**
360   * Encodes the provided information into an ASN.1 octet string suitable for
361   * use as the value of this extended request.
362   *
363   * @param  authenticationID  The authentication ID that identifies the user
364   *                           for whom the YubiKey OTP device is to be
365   *                           registered.  It may be {@code null} if the device
366   *                           is to be registered for the user as whom the
367   *                           underlying connection is authenticated.
368   * @param  staticPassword    The static password of the user for whom the
369   *                           device is to be registered.  It may be
370   *                           {@code null} if the device is to be registered
371   *                           for a user other than the user authenticated on
372   *                           the underlying connection and the server is
373   *                           configured to not require the target user's
374   *                           static password in this case.
375   * @param  yubiKeyOTP        A one-time password generated by the YubiKey
376   *                           device to be registered.  It must not be
377   *                           {@code null}.
378   *
379   * @return  The ASN.1 octet string containing the encoded request value.
380   */
381  private static ASN1OctetString encodeValue(final String authenticationID,
382                                      final ASN1OctetString staticPassword,
383                                      final String yubiKeyOTP)
384  {
385    Validator.ensureNotNull(yubiKeyOTP);
386
387    final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
388
389    if (authenticationID != null)
390    {
391      elements.add(
392           new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID));
393    }
394
395    if (staticPassword != null)
396    {
397      elements.add(staticPassword);
398    }
399
400    elements.add(new ASN1OctetString(TYPE_YUBIKEY_OTP, yubiKeyOTP));
401    return new ASN1OctetString(new ASN1Sequence(elements).encode());
402  }
403
404
405
406  /**
407   * Retrieves the authentication ID that identifies the user for whom the
408   * YubiKey OTP device is to be registered, if provided.
409   *
410   * @return  The authentication ID that identifies the target user, or
411   *          {@code null} if the device is to be registered as the user as
412   *          whom the underlying connection is authenticated.
413   */
414  public String getAuthenticationID()
415  {
416    return authenticationID;
417  }
418
419
420
421  /**
422   * Retrieves the string representation of the static password for the target
423   * user, if provided.
424   *
425   * @return  The string representation of the static password for the target
426   *          user, or {@code null} if no static password was provided.
427   */
428  public String getStaticPasswordString()
429  {
430    if (staticPassword == null)
431    {
432      return null;
433    }
434    else
435    {
436      return staticPassword.stringValue();
437    }
438  }
439
440
441
442  /**
443   * Retrieves the bytes that comprise the static password for the target user,
444   * if provided.
445   *
446   * @return  The bytes that comprise the static password for the target user,
447   *          or {@code null} if no static password was provided.
448   */
449  public byte[] getStaticPasswordBytes()
450  {
451    if (staticPassword == null)
452    {
453      return null;
454    }
455    else
456    {
457      return staticPassword.getValue();
458    }
459  }
460
461
462
463  /**
464   * Retrieves a one-time password generated by the YubiKey device to be
465   * registered.
466   *
467   * @return  A one-time password generated by the YubiKey device to be
468   *          registered.
469   */
470  public String getYubiKeyOTP()
471  {
472    return yubiKeyOTP;
473  }
474
475
476
477  /**
478   * {@inheritDoc}
479   */
480  @Override()
481  public RegisterYubiKeyOTPDeviceExtendedRequest duplicate()
482  {
483    return duplicate(getControls());
484  }
485
486
487
488  /**
489   * {@inheritDoc}
490   */
491  @Override()
492  public RegisterYubiKeyOTPDeviceExtendedRequest duplicate(
493                                                      final Control[] controls)
494  {
495    final RegisterYubiKeyOTPDeviceExtendedRequest r =
496         new RegisterYubiKeyOTPDeviceExtendedRequest(authenticationID,
497              staticPassword, yubiKeyOTP, controls);
498    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
499    return r;
500  }
501
502
503
504  /**
505   * {@inheritDoc}
506   */
507  @Override()
508  public String getExtendedRequestName()
509  {
510    return INFO_REGISTER_YUBIKEY_OTP_REQUEST_NAME.get();
511  }
512
513
514
515  /**
516   * {@inheritDoc}
517   */
518  @Override()
519  public void toString(final StringBuilder buffer)
520  {
521    buffer.append("RegisterYubiKeyOTPDeviceExtendedRequest(");
522
523    if (authenticationID != null)
524    {
525      buffer.append("authenticationID='");
526      buffer.append(authenticationID);
527      buffer.append("', ");
528    }
529
530    buffer.append("staticPasswordProvided=");
531    buffer.append(staticPassword != null);
532
533    final Control[] controls = getControls();
534    if (controls.length > 0)
535    {
536      buffer.append(", controls={");
537      for (int i=0; i < controls.length; i++)
538      {
539        if (i > 0)
540        {
541          buffer.append(", ");
542        }
543
544        buffer.append(controls[i]);
545      }
546      buffer.append('}');
547    }
548
549    buffer.append(')');
550  }
551}