001/*
002 * Copyright 2011-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2011-2024 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) 2011-2024 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;
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.util.NotMutable;
046import com.unboundid.util.NotNull;
047import com.unboundid.util.Nullable;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050import com.unboundid.util.Validator;
051
052
053
054/**
055 * This class provides a mechanism for performing SASL authentication in a
056 * generic manner.  The caller is responsible for properly encoding the
057 * credentials (if any) and interpreting the result.  Further, if the requested
058 * SASL mechanism is one that requires multiple stages, then the caller is
059 * responsible for all processing in each stage.
060 */
061@NotMutable()
062@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
063public final class GenericSASLBindRequest
064       extends SASLBindRequest
065{
066  /**
067   * The serial version UID for this serializable class.
068   */
069  private static final long serialVersionUID = 7740968332104559230L;
070
071
072
073  // The SASL credentials that should be used for the bind request.
074  @Nullable private final ASN1OctetString credentials;
075
076  // The bind DN to use for the bind request.
077  @Nullable private final String bindDN;
078
079  // The name of the SASL mechanism that should be used for the bind request.
080  @NotNull private final String mechanism;
081
082
083
084  /**
085   * Creates a new generic SASL bind request with the provided information.
086   *
087   * @param  bindDN       The bind DN that should be used for the request.  It
088   *                      may be {@code null} if the target identity should be
089   *                      derived from the credentials or some other source.
090   * @param  mechanism    The name of the mechanism that should be used for the
091   *                      SASL bind.  It must not be {@code null}.
092   * @param  credentials  The credentials that should be used for the SASL bind.
093   *                      It may be {@code null} if no credentials should be
094   *                      used.
095   * @param  controls     The set of controls to include in the SASL bind
096   *                      request.  It may be {@code null} or empty if no
097   *                      request controls are needed.
098   */
099  public GenericSASLBindRequest(@Nullable final String bindDN,
100                                @NotNull final String mechanism,
101                                @Nullable final ASN1OctetString credentials,
102                                @Nullable final Control... controls)
103  {
104    super(controls);
105
106    Validator.ensureNotNull(mechanism);
107
108    this.bindDN      = bindDN;
109    this.mechanism   = mechanism;
110    this.credentials = credentials;
111  }
112
113
114
115  /**
116   * Retrieves the bind DN for this SASL bind request, if any.
117   *
118   * @return  The bind DN for this SASL bind request, or {@code null} if the
119   *          target identity should be determined from the credentials or some
120   *          other mechanism.
121   */
122  @Nullable()
123  public String getBindDN()
124  {
125    return bindDN;
126  }
127
128
129
130  /**
131   * {@inheritDoc}
132   */
133  @Override()
134  @NotNull()
135  public String getSASLMechanismName()
136  {
137    return mechanism;
138  }
139
140
141
142  /**
143   * Retrieves the credentials for the SASL bind request, if any.
144   *
145   * @return  The credentials for the SASL bind request, or {@code null} if
146   *          there are none.
147   */
148  @Nullable()
149  public ASN1OctetString getCredentials()
150  {
151    return credentials;
152  }
153
154
155
156  /**
157   * {@inheritDoc}
158   */
159  @Override()
160  @NotNull()
161  protected BindResult process(@NotNull final LDAPConnection connection,
162                               final int depth)
163            throws LDAPException
164  {
165    setReferralDepth(depth);
166
167    return sendBindRequest(connection, bindDN, credentials, getControls(),
168         getResponseTimeoutMillis(connection));
169  }
170
171
172
173  /**
174   * {@inheritDoc}
175   */
176  @Override()
177  @NotNull()
178  public GenericSASLBindRequest duplicate()
179  {
180    return duplicate(getControls());
181  }
182
183
184
185  /**
186   * {@inheritDoc}
187   */
188  @Override()
189  @NotNull()
190  public GenericSASLBindRequest duplicate(@Nullable final Control[] controls)
191  {
192    final GenericSASLBindRequest bindRequest =
193         new GenericSASLBindRequest(bindDN, mechanism, credentials, controls);
194    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
195    bindRequest.setIntermediateResponseListener(
196         getIntermediateResponseListener());
197    bindRequest.setReferralDepth(getReferralDepth());
198    bindRequest.setReferralConnector(getReferralConnectorInternal());
199    return bindRequest;
200  }
201
202
203
204  /**
205   * {@inheritDoc}
206   */
207  @Override()
208  public void toString(@NotNull final StringBuilder buffer)
209  {
210    buffer.append("GenericSASLBindRequest(mechanism='");
211    buffer.append(mechanism);
212    buffer.append('\'');
213
214    if (bindDN != null)
215    {
216      buffer.append(", bindDN='");
217      buffer.append(bindDN);
218      buffer.append('\'');
219    }
220
221    if (credentials != null)
222    {
223      buffer.append(", credentials=byte[");
224      buffer.append(credentials.getValueLength());
225      buffer.append(']');
226    }
227
228    final Control[] controls = getControls();
229    if (controls.length > 0)
230    {
231      buffer.append(", controls={");
232      for (int i=0; i < controls.length; i++)
233      {
234        if (i > 0)
235        {
236          buffer.append(", ");
237        }
238
239        buffer.append(controls[i]);
240      }
241      buffer.append('}');
242    }
243
244    buffer.append(')');
245  }
246
247
248
249  /**
250   * {@inheritDoc}
251   */
252  @Override()
253  public void toCode(@NotNull final List<String> lineList,
254                     @NotNull final String requestID,
255                     final int indentSpaces, final boolean includeProcessing)
256  {
257    // Create the request variable.
258    final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(4);
259    constructorArgs.add(ToCodeArgHelper.createString(bindDN, "Bind DN"));
260    constructorArgs.add(ToCodeArgHelper.createString(mechanism,
261         "SASL Mechanism Name"));
262    constructorArgs.add(ToCodeArgHelper.createByteArray(
263         "---redacted-SASL-credentials".getBytes(StandardCharsets.UTF_8), true,
264         "SASL Credentials"));
265
266    final Control[] controls = getControls();
267    if (controls.length > 0)
268    {
269      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
270           "Bind Controls"));
271    }
272
273    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
274         "GenericSASLBindRequest", requestID + "Request",
275         "new GenericSASLBindRequest", constructorArgs);
276
277
278    // Add lines for processing the request and obtaining the result.
279    if (includeProcessing)
280    {
281      // Generate a string with the appropriate indent.
282      final StringBuilder buffer = new StringBuilder();
283      for (int i=0; i < indentSpaces; i++)
284      {
285        buffer.append(' ');
286      }
287      final String indent = buffer.toString();
288
289      lineList.add("");
290      lineList.add(indent + '{');
291      lineList.add(indent + "  BindResult " + requestID +
292           "Result = connection.bind(" + requestID + "Request);");
293      lineList.add(indent + "  // The bind was processed successfully.");
294      lineList.add(indent + '}');
295      lineList.add(indent + "catch (SASLBindInProgressException e)");
296      lineList.add(indent + '{');
297      lineList.add(indent + "  // The SASL bind requires multiple stages.  " +
298           "Continue it here.");
299      lineList.add(indent + "  // Do not attempt to use the connection for " +
300           "any other purpose until bind processing has completed.");
301      lineList.add(indent + '}');
302      lineList.add(indent + "catch (LDAPException e)");
303      lineList.add(indent + '{');
304      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
305           "help explain why.");
306      lineList.add(indent + "  // Note that the connection is now likely in " +
307           "an unauthenticated state.");
308      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
309      lineList.add(indent + "  String message = e.getMessage();");
310      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
311      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
312      lineList.add(indent + "  Control[] responseControls = " +
313           "e.getResponseControls();");
314      lineList.add(indent + '}');
315    }
316  }
317}