001/* 002 * Copyright 2012-2022 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2012-2022 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-2022 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 messageID = InternalSDKHelper.nextMessageID(connection); 206 return sendBindRequest(connection, "", password, getControls(), 207 getResponseTimeoutMillis(connection)); 208 } 209 210 211 212 /** 213 * {@inheritDoc} 214 */ 215 @Override() 216 public int getLastMessageID() 217 { 218 return messageID; 219 } 220 221 222 223 /** 224 * {@inheritDoc} 225 */ 226 @Override() 227 @NotNull() 228 public UnboundIDCertificatePlusPasswordBindRequest duplicate() 229 { 230 return duplicate(getControls()); 231 } 232 233 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override() 239 @NotNull() 240 public UnboundIDCertificatePlusPasswordBindRequest duplicate( 241 @Nullable final Control[] controls) 242 { 243 final UnboundIDCertificatePlusPasswordBindRequest bindRequest = 244 new UnboundIDCertificatePlusPasswordBindRequest(password, controls); 245 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 246 return bindRequest; 247 } 248 249 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override() 255 @NotNull() 256 public UnboundIDCertificatePlusPasswordBindRequest getRebindRequest( 257 @NotNull final String host, final int port) 258 { 259 return duplicate(); 260 } 261 262 263 264 /** 265 * {@inheritDoc} 266 */ 267 @Override() 268 public void toString(@NotNull final StringBuilder buffer) 269 { 270 buffer.append("UnboundIDCertificatePlusPasswordBindRequest("); 271 272 final Control[] controls = getControls(); 273 if (controls.length > 0) 274 { 275 buffer.append("controls={"); 276 for (int i=0; i < controls.length; i++) 277 { 278 if (i > 0) 279 { 280 buffer.append(", "); 281 } 282 283 buffer.append(controls[i]); 284 } 285 buffer.append('}'); 286 } 287 288 buffer.append(')'); 289 } 290 291 292 293 /** 294 * {@inheritDoc} 295 */ 296 @Override() 297 public void toCode(@NotNull final List<String> lineList, 298 @NotNull final String requestID, 299 final int indentSpaces, final boolean includeProcessing) 300 { 301 // Create the request variable. 302 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2); 303 constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---", 304 "Bind Password")); 305 306 final Control[] controls = getControls(); 307 if (controls.length > 0) 308 { 309 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 310 "Bind Controls")); 311 } 312 313 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 314 "UnboundIDCertificatePlusPasswordBindRequest", requestID + "Request", 315 "new UnboundIDCertificatePlusPasswordBindRequest", constructorArgs); 316 317 318 // Add lines for processing the request and obtaining the result. 319 if (includeProcessing) 320 { 321 // Generate a string with the appropriate indent. 322 final StringBuilder buffer = new StringBuilder(); 323 for (int i=0; i < indentSpaces; i++) 324 { 325 buffer.append(' '); 326 } 327 final String indent = buffer.toString(); 328 329 lineList.add(""); 330 lineList.add(indent + "try"); 331 lineList.add(indent + '{'); 332 lineList.add(indent + " BindResult " + requestID + 333 "Result = connection.bind(" + requestID + "Request);"); 334 lineList.add(indent + " // The bind was processed successfully."); 335 lineList.add(indent + '}'); 336 lineList.add(indent + "catch (LDAPException e)"); 337 lineList.add(indent + '{'); 338 lineList.add(indent + " // The bind failed. Maybe the following will " + 339 "help explain why."); 340 lineList.add(indent + " // Note that the connection is now likely in " + 341 "an unauthenticated state."); 342 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 343 lineList.add(indent + " String message = e.getMessage();"); 344 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 345 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 346 lineList.add(indent + " Control[] responseControls = " + 347 "e.getResponseControls();"); 348 lineList.add(indent + '}'); 349 } 350 } 351}