001/*
002 * Copyright 2012-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds;
022
023
024
025import java.util.ArrayList;
026import java.util.List;
027
028import com.unboundid.asn1.ASN1OctetString;
029import com.unboundid.ldap.sdk.BindResult;
030import com.unboundid.ldap.sdk.Control;
031import com.unboundid.ldap.sdk.InternalSDKHelper;
032import com.unboundid.ldap.sdk.LDAPConnection;
033import com.unboundid.ldap.sdk.LDAPException;
034import com.unboundid.ldap.sdk.SASLBindRequest;
035import com.unboundid.ldap.sdk.ToCodeArgHelper;
036import com.unboundid.ldap.sdk.ToCodeHelper;
037import com.unboundid.util.ThreadSafety;
038import com.unboundid.util.ThreadSafetyLevel;
039import com.unboundid.util.Validator;
040
041
042
043/**
044 * This class provides support for an UnboundID-proprietary SASL mechanism that
045 * provides multifactor authentication using the combination of a client
046 * certificate (presented during SSL/TLS negotiation) and a static password.
047 * <BR>
048 * <BLOCKQUOTE>
049 *   <B>NOTE:</B>  This class, and other classes within the
050 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
051 *   supported for use against Ping Identity, UnboundID, and
052 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
053 *   for proprietary functionality or for external specifications that are not
054 *   considered stable or mature enough to be guaranteed to work in an
055 *   interoperable way with other types of LDAP servers.
056 * </BLOCKQUOTE>
057 * <BR>
058 * The name for this SASL mechanism is "UNBOUNDID-CERTIFICATE-PLUS-PASSWORD".
059 * The SASL credentials consist simply of the static password for the user
060 * identified by the certificate, to make the SASL mechanism as easy as possible
061 * to use from other client APIs.
062 */
063@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
064public final class UnboundIDCertificatePlusPasswordBindRequest
065       extends SASLBindRequest
066{
067  /**
068   * The name for the UnboundID certificate plus password SASL mechanism.
069   */
070  public static final String UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME =
071       "UNBOUNDID-CERTIFICATE-PLUS-PASSWORD";
072
073
074
075  /**
076   * The serial version UID for this serializable class.
077   */
078  private static final long serialVersionUID = 8863298749835036708L;
079
080
081
082  // The password to use to authenticate.
083  private final ASN1OctetString password;
084
085  // The message ID from the last LDAP message sent from this request.
086  private volatile int messageID = -1;
087
088
089
090  /**
091   * Creates a new certificate plus password bind request with the provided
092   * information.
093   *
094   * @param  password  The password to use to authenticate as user identified by
095   *                   the certificate.  It must not be {@code null} or empty.
096   * @param  controls  The set of controls to include in the bind request.  It
097   *                   may be {@code null} or empty if no request controls are
098   *                   needed.
099   */
100  public UnboundIDCertificatePlusPasswordBindRequest(final String password,
101                                                     final Control... controls)
102  {
103    this(new ASN1OctetString(CRED_TYPE_SASL, password), controls);
104  }
105
106
107
108  /**
109   * Creates a new certificate plus password bind request with the provided
110   * information.
111   *
112   * @param  password  The password to use to authenticate as user identified by
113   *                   the certificate.  It must not be {@code null} or empty.
114   * @param  controls  The set of controls to include in the bind request.  It
115   *                   may be {@code null} or empty if no request controls are
116   *                   needed.
117   */
118  public UnboundIDCertificatePlusPasswordBindRequest(final byte[] password,
119                                                     final Control... controls)
120  {
121    this(new ASN1OctetString(CRED_TYPE_SASL, password), controls);
122  }
123
124
125
126  /**
127   * Creates a new certificate plus password bind request with the provided
128   * information.
129   *
130   * @param  password  The password to use to authenticate as user identified by
131   *                   the certificate.  It must not be {@code null} or empty.
132   * @param  controls  The set of controls to include in the bind request.  It
133   *                   may be {@code null} or empty if no request controls are
134   *                   needed.
135   */
136  private UnboundIDCertificatePlusPasswordBindRequest(
137               final ASN1OctetString password, final Control... controls)
138  {
139    super(controls);
140
141    Validator.ensureFalse((password.getValueLength() == 0),
142         "The bind password must not be empty");
143
144    this.password = password;
145  }
146
147
148
149  /**
150   * Retrieves the password to use to authenticate as the user identified by the
151   * certificate.
152   *
153   * @return  The password to use to authenticate as the user identified by the
154   *          certificate.
155   */
156  public ASN1OctetString getPassword()
157  {
158    return password;
159  }
160
161
162
163  /**
164   * {@inheritDoc}
165   */
166  @Override()
167  public String getSASLMechanismName()
168  {
169    return UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME;
170  }
171
172
173
174  /**
175   * {@inheritDoc}
176   */
177  @Override()
178  protected BindResult process(final LDAPConnection connection, final int depth)
179            throws LDAPException
180  {
181    messageID = InternalSDKHelper.nextMessageID(connection);
182    return sendBindRequest(connection, "", password, getControls(),
183         getResponseTimeoutMillis(connection));
184  }
185
186
187
188  /**
189   * {@inheritDoc}
190   */
191  @Override()
192  public int getLastMessageID()
193  {
194    return messageID;
195  }
196
197
198
199  /**
200   * {@inheritDoc}
201   */
202  @Override()
203  public UnboundIDCertificatePlusPasswordBindRequest duplicate()
204  {
205    return duplicate(getControls());
206  }
207
208
209
210  /**
211   * {@inheritDoc}
212   */
213  @Override()
214  public UnboundIDCertificatePlusPasswordBindRequest duplicate(
215              final Control[] controls)
216  {
217    final UnboundIDCertificatePlusPasswordBindRequest bindRequest =
218         new UnboundIDCertificatePlusPasswordBindRequest(password, controls);
219    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
220    return bindRequest;
221  }
222
223
224
225  /**
226   * {@inheritDoc}
227   */
228  @Override()
229  public UnboundIDCertificatePlusPasswordBindRequest getRebindRequest(
230              final String host, final int port)
231  {
232    return duplicate();
233  }
234
235
236
237  /**
238   * {@inheritDoc}
239   */
240  @Override()
241  public void toString(final StringBuilder buffer)
242  {
243    buffer.append("UnboundIDCertificatePlusPasswordBindRequest(");
244
245    final Control[] controls = getControls();
246    if (controls.length > 0)
247    {
248      buffer.append("controls={");
249      for (int i=0; i < controls.length; i++)
250      {
251        if (i > 0)
252        {
253          buffer.append(", ");
254        }
255
256        buffer.append(controls[i]);
257      }
258      buffer.append('}');
259    }
260
261    buffer.append(')');
262  }
263
264
265
266  /**
267   * {@inheritDoc}
268   */
269  @Override()
270  public void toCode(final List<String> lineList, final String requestID,
271                     final int indentSpaces, final boolean includeProcessing)
272  {
273    // Create the request variable.
274    final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2);
275    constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---",
276         "Bind Password"));
277
278    final Control[] controls = getControls();
279    if (controls.length > 0)
280    {
281      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
282           "Bind Controls"));
283    }
284
285    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
286         "UnboundIDCertificatePlusPasswordBindRequest", requestID + "Request",
287         "new UnboundIDCertificatePlusPasswordBindRequest", constructorArgs);
288
289
290    // Add lines for processing the request and obtaining the result.
291    if (includeProcessing)
292    {
293      // Generate a string with the appropriate indent.
294      final StringBuilder buffer = new StringBuilder();
295      for (int i=0; i < indentSpaces; i++)
296      {
297        buffer.append(' ');
298      }
299      final String indent = buffer.toString();
300
301      lineList.add("");
302      lineList.add(indent + "try");
303      lineList.add(indent + '{');
304      lineList.add(indent + "  BindResult " + requestID +
305           "Result = connection.bind(" + requestID + "Request);");
306      lineList.add(indent + "  // The bind was processed successfully.");
307      lineList.add(indent + '}');
308      lineList.add(indent + "catch (LDAPException e)");
309      lineList.add(indent + '{');
310      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
311           "help explain why.");
312      lineList.add(indent + "  // Note that the connection is now likely in " +
313           "an unauthenticated state.");
314      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
315      lineList.add(indent + "  String message = e.getMessage();");
316      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
317      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
318      lineList.add(indent + "  Control[] responseControls = " +
319           "e.getResponseControls();");
320      lineList.add(indent + '}');
321    }
322  }
323}