001/* 002 * Copyright 2012-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-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; 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 Alcatel-Lucent 8661 052 * server products. These classes provide support for proprietary 053 * functionality or for external specifications that are not considered stable 054 * or mature enough to be guaranteed to work in an interoperable way with 055 * 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 = 275 new ArrayList<ToCodeArgHelper>(2); 276 constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---", 277 "Bind Password")); 278 279 final Control[] controls = getControls(); 280 if (controls.length > 0) 281 { 282 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 283 "Bind Controls")); 284 } 285 286 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 287 "UnboundIDCertificatePlusPasswordBindRequest", requestID + "Request", 288 "new UnboundIDCertificatePlusPasswordBindRequest", constructorArgs); 289 290 291 // Add lines for processing the request and obtaining the result. 292 if (includeProcessing) 293 { 294 // Generate a string with the appropriate indent. 295 final StringBuilder buffer = new StringBuilder(); 296 for (int i=0; i < indentSpaces; i++) 297 { 298 buffer.append(' '); 299 } 300 final String indent = buffer.toString(); 301 302 lineList.add(""); 303 lineList.add(indent + "try"); 304 lineList.add(indent + '{'); 305 lineList.add(indent + " BindResult " + requestID + 306 "Result = connection.bind(" + requestID + "Request);"); 307 lineList.add(indent + " // The bind was processed successfully."); 308 lineList.add(indent + '}'); 309 lineList.add(indent + "catch (LDAPException e)"); 310 lineList.add(indent + '{'); 311 lineList.add(indent + " // The bind failed. Maybe the following will " + 312 "help explain why."); 313 lineList.add(indent + " // Note that the connection is now likely in " + 314 "an unauthenticated state."); 315 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 316 lineList.add(indent + " String message = e.getMessage();"); 317 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 318 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 319 lineList.add(indent + " Control[] responseControls = " + 320 "e.getResponseControls();"); 321 lineList.add(indent + '}'); 322 } 323 } 324}