001/* 002 * Copyright 2012-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2012-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) 2012-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.unboundidds; 037 038 039 040import java.util.ArrayList; 041import java.util.List; 042 043import com.unboundid.asn1.ASN1OctetString; 044import com.unboundid.ldap.sdk.BindResult; 045import com.unboundid.ldap.sdk.Control; 046import com.unboundid.ldap.sdk.InternalSDKHelper; 047import com.unboundid.ldap.sdk.LDAPConnection; 048import com.unboundid.ldap.sdk.LDAPException; 049import com.unboundid.ldap.sdk.SASLBindRequest; 050import com.unboundid.ldap.sdk.ToCodeArgHelper; 051import com.unboundid.ldap.sdk.ToCodeHelper; 052import com.unboundid.util.NotNull; 053import com.unboundid.util.Nullable; 054import com.unboundid.util.ThreadSafety; 055import com.unboundid.util.ThreadSafetyLevel; 056import com.unboundid.util.Validator; 057 058 059 060/** 061 * This class provides support for an UnboundID-proprietary SASL mechanism that 062 * provides multifactor authentication using the combination of a client 063 * certificate (presented during SSL/TLS negotiation) and a static password. 064 * <BR> 065 * <BLOCKQUOTE> 066 * <B>NOTE:</B> This class, and other classes within the 067 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 068 * supported for use against Ping Identity, UnboundID, and 069 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 070 * for proprietary functionality or for external specifications that are not 071 * considered stable or mature enough to be guaranteed to work in an 072 * interoperable way with other types of LDAP servers. 073 * </BLOCKQUOTE> 074 * <BR> 075 * The name for this SASL mechanism is "UNBOUNDID-CERTIFICATE-PLUS-PASSWORD". 076 * The SASL credentials consist simply of the static password for the user 077 * identified by the certificate, to make the SASL mechanism as easy as possible 078 * to use from other client APIs. 079 */ 080@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 081public final class UnboundIDCertificatePlusPasswordBindRequest 082 extends SASLBindRequest 083{ 084 /** 085 * The name for the UnboundID certificate plus password SASL mechanism. 086 */ 087 @NotNull public static final String UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME = 088 "UNBOUNDID-CERTIFICATE-PLUS-PASSWORD"; 089 090 091 092 /** 093 * The serial version UID for this serializable class. 094 */ 095 private static final long serialVersionUID = 8863298749835036708L; 096 097 098 099 // The password to use to authenticate. 100 @NotNull private final ASN1OctetString password; 101 102 // The message ID from the last LDAP message sent from this request. 103 private volatile int messageID = -1; 104 105 106 107 /** 108 * Creates a new certificate plus password bind request with the provided 109 * information. 110 * 111 * @param password The password to use to authenticate as user identified by 112 * the certificate. It must not be {@code null} or empty. 113 * @param controls The set of controls to include in the bind request. It 114 * may be {@code null} or empty if no request controls are 115 * needed. 116 */ 117 public UnboundIDCertificatePlusPasswordBindRequest( 118 @NotNull final String password, 119 @Nullable 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 public UnboundIDCertificatePlusPasswordBindRequest( 137 @NotNull final byte[] password, 138 @Nullable final Control... controls) 139 { 140 this(new ASN1OctetString(CRED_TYPE_SASL, password), controls); 141 } 142 143 144 145 /** 146 * Creates a new certificate plus password bind request with the provided 147 * information. 148 * 149 * @param password The password to use to authenticate as user identified by 150 * the certificate. It must not be {@code null} or empty. 151 * @param controls The set of controls to include in the bind request. It 152 * may be {@code null} or empty if no request controls are 153 * needed. 154 */ 155 private UnboundIDCertificatePlusPasswordBindRequest( 156 @NotNull final ASN1OctetString password, 157 @Nullable final Control... controls) 158 { 159 super(controls); 160 161 Validator.ensureFalse((password.getValueLength() == 0), 162 "The bind password must not be empty"); 163 164 this.password = password; 165 } 166 167 168 169 /** 170 * Retrieves the password to use to authenticate as the user identified by the 171 * certificate. 172 * 173 * @return The password to use to authenticate as the user identified by the 174 * certificate. 175 */ 176 @NotNull() 177 public ASN1OctetString getPassword() 178 { 179 return password; 180 } 181 182 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override() 188 @NotNull() 189 public String getSASLMechanismName() 190 { 191 return UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME; 192 } 193 194 195 196 /** 197 * {@inheritDoc} 198 */ 199 @Override() 200 @NotNull() 201 protected BindResult process(@NotNull final LDAPConnection connection, 202 final int depth) 203 throws LDAPException 204 { 205 setReferralDepth(depth); 206 207 messageID = InternalSDKHelper.nextMessageID(connection); 208 return sendBindRequest(connection, "", password, getControls(), 209 getResponseTimeoutMillis(connection)); 210 } 211 212 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override() 218 public int getLastMessageID() 219 { 220 return messageID; 221 } 222 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 @Override() 229 @NotNull() 230 public UnboundIDCertificatePlusPasswordBindRequest duplicate() 231 { 232 return duplicate(getControls()); 233 } 234 235 236 237 /** 238 * {@inheritDoc} 239 */ 240 @Override() 241 @NotNull() 242 public UnboundIDCertificatePlusPasswordBindRequest duplicate( 243 @Nullable final Control[] controls) 244 { 245 final UnboundIDCertificatePlusPasswordBindRequest bindRequest = 246 new UnboundIDCertificatePlusPasswordBindRequest(password, controls); 247 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 248 bindRequest.setIntermediateResponseListener( 249 getIntermediateResponseListener()); 250 bindRequest.setReferralDepth(getReferralDepth()); 251 bindRequest.setReferralConnector(getReferralConnectorInternal()); 252 return bindRequest; 253 } 254 255 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override() 261 @NotNull() 262 public UnboundIDCertificatePlusPasswordBindRequest getRebindRequest( 263 @NotNull final String host, final int port) 264 { 265 return duplicate(); 266 } 267 268 269 270 /** 271 * {@inheritDoc} 272 */ 273 @Override() 274 public void toString(@NotNull final StringBuilder buffer) 275 { 276 buffer.append("UnboundIDCertificatePlusPasswordBindRequest("); 277 278 final Control[] controls = getControls(); 279 if (controls.length > 0) 280 { 281 buffer.append("controls={"); 282 for (int i=0; i < controls.length; i++) 283 { 284 if (i > 0) 285 { 286 buffer.append(", "); 287 } 288 289 buffer.append(controls[i]); 290 } 291 buffer.append('}'); 292 } 293 294 buffer.append(')'); 295 } 296 297 298 299 /** 300 * {@inheritDoc} 301 */ 302 @Override() 303 public void toCode(@NotNull final List<String> lineList, 304 @NotNull final String requestID, 305 final int indentSpaces, final boolean includeProcessing) 306 { 307 // Create the request variable. 308 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2); 309 constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---", 310 "Bind Password")); 311 312 final Control[] controls = getControls(); 313 if (controls.length > 0) 314 { 315 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 316 "Bind Controls")); 317 } 318 319 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 320 "UnboundIDCertificatePlusPasswordBindRequest", requestID + "Request", 321 "new UnboundIDCertificatePlusPasswordBindRequest", constructorArgs); 322 323 324 // Add lines for processing the request and obtaining the result. 325 if (includeProcessing) 326 { 327 // Generate a string with the appropriate indent. 328 final StringBuilder buffer = new StringBuilder(); 329 for (int i=0; i < indentSpaces; i++) 330 { 331 buffer.append(' '); 332 } 333 final String indent = buffer.toString(); 334 335 lineList.add(""); 336 lineList.add(indent + "try"); 337 lineList.add(indent + '{'); 338 lineList.add(indent + " BindResult " + requestID + 339 "Result = connection.bind(" + requestID + "Request);"); 340 lineList.add(indent + " // The bind was processed successfully."); 341 lineList.add(indent + '}'); 342 lineList.add(indent + "catch (LDAPException e)"); 343 lineList.add(indent + '{'); 344 lineList.add(indent + " // The bind failed. Maybe the following will " + 345 "help explain why."); 346 lineList.add(indent + " // Note that the connection is now likely in " + 347 "an unauthenticated state."); 348 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 349 lineList.add(indent + " String message = e.getMessage();"); 350 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 351 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 352 lineList.add(indent + " Control[] responseControls = " + 353 "e.getResponseControls();"); 354 lineList.add(indent + '}'); 355 } 356 } 357}