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.ASN1Element; 029import com.unboundid.asn1.ASN1OctetString; 030import com.unboundid.asn1.ASN1Sequence; 031import com.unboundid.ldap.sdk.Control; 032import com.unboundid.ldap.sdk.LDAPException; 033import com.unboundid.ldap.sdk.ResultCode; 034import com.unboundid.ldap.sdk.ToCodeArgHelper; 035import com.unboundid.ldap.sdk.ToCodeHelper; 036import com.unboundid.util.Debug; 037import com.unboundid.util.NotMutable; 038import com.unboundid.util.StaticUtils; 039import com.unboundid.util.ThreadSafety; 040import com.unboundid.util.ThreadSafetyLevel; 041import com.unboundid.util.Validator; 042 043import static com.unboundid.ldap.sdk.unboundidds.UnboundIDDSMessages.*; 044 045 046 047/** 048 * This class provides an implementation of the UNBOUNDID-TOTP SASL bind request 049 * that contains a point-in-time version of the one-time password and can be 050 * used for a single bind but is not suitable for repeated use. This version of 051 * the bind request should be used for authentication in which the one-time 052 * password is provided by an external source rather than being generated by 053 * the LDAP SDK. 054 * <BR> 055 * <BLOCKQUOTE> 056 * <B>NOTE:</B> This class, and other classes within the 057 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 058 * supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661 059 * server products. These classes provide support for proprietary 060 * functionality or for external specifications that are not considered stable 061 * or mature enough to be guaranteed to work in an interoperable way with 062 * other types of LDAP servers. 063 * </BLOCKQUOTE> 064 * <BR> 065 * Because the one-time password is provided rather than generated, this version 066 * of the bind request is not suitable for cases in which the authentication 067 * process may need to be repeated (e.g., for use in a connection pool, 068 * following referrals, or if the auto-reconnect feature is enabled), then the 069 * reusable variant (supported by the {@link ReusableTOTPBindRequest} class) 070 * which generates the one-time password should be used instead. 071 */ 072@NotMutable() 073@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 074public final class SingleUseTOTPBindRequest 075 extends UnboundIDTOTPBindRequest 076{ 077 /** 078 * The serial version UID for this serializable class. 079 */ 080 private static final long serialVersionUID = -4429898810534930296L; 081 082 083 084 // The hard-coded TOTP password to include in the bind request. 085 private final String totpPassword; 086 087 088 089 /** 090 * Creates a new SASL TOTP bind request with the provided information. 091 * 092 * @param authenticationID The authentication identity for the bind request. 093 * It must not be {@code null}, and must be in the 094 * form "u:" followed by a username, or "dn:" 095 * followed by a DN. 096 * @param authorizationID The authorization identity for the bind request. 097 * It may be {@code null} if the authorization 098 * identity should be the same as the authentication 099 * identity. If an authorization identity is 100 * specified, it must be in the form "u:" followed 101 * by a username, or "dn:" followed by a DN. The 102 * value "dn:" may indicate an authorization 103 * identity of the anonymous user. 104 * @param totpPassword The hard-coded TOTP password to include in the 105 * bind request. It must not be {@code null}. 106 * @param staticPassword The static password for the target user. It may 107 * be {@code null} if only the one-time password is 108 * to be used for authentication (which may or may 109 * not be allowed by the server). 110 * @param controls The set of controls to include in the bind 111 * request. 112 */ 113 public SingleUseTOTPBindRequest(final String authenticationID, 114 final String authorizationID, 115 final String totpPassword, 116 final String staticPassword, 117 final Control... controls) 118 { 119 super(authenticationID, authorizationID, staticPassword, controls); 120 121 Validator.ensureNotNull(totpPassword); 122 this.totpPassword = totpPassword; 123 } 124 125 126 127 /** 128 * Creates a new SASL TOTP bind request with the provided information. 129 * 130 * @param authenticationID The authentication identity for the bind request. 131 * It must not be {@code null}, and must be in the 132 * form "u:" followed by a username, or "dn:" 133 * followed by a DN. 134 * @param authorizationID The authorization identity for the bind request. 135 * It may be {@code null} if the authorization 136 * identity should be the same as the authentication 137 * identity. If an authorization identity is 138 * specified, it must be in the form "u:" followed 139 * by a username, or "dn:" followed by a DN. The 140 * value "dn:" may indicate an authorization 141 * identity of the anonymous user. 142 * @param totpPassword The hard-coded TOTP password to include in the 143 * bind request. It must not be {@code null}. 144 * @param staticPassword The static password for the target user. It may 145 * be {@code null} if only the one-time password is 146 * to be used for authentication (which may or may 147 * not be allowed by the server). 148 * @param controls The set of controls to include in the bind 149 * request. 150 */ 151 public SingleUseTOTPBindRequest(final String authenticationID, 152 final String authorizationID, 153 final String totpPassword, 154 final byte[] staticPassword, 155 final Control... controls) 156 { 157 super(authenticationID, authorizationID, staticPassword, controls); 158 159 Validator.ensureNotNull(totpPassword); 160 this.totpPassword = totpPassword; 161 } 162 163 164 165 /** 166 * Creates a new SASL TOTP bind request with the provided information. 167 * 168 * @param authenticationID The authentication identity for the bind request. 169 * It must not be {@code null}, and must be in the 170 * form "u:" followed by a username, or "dn:" 171 * followed by a DN. 172 * @param authorizationID The authorization identity for the bind request. 173 * It may be {@code null} if the authorization 174 * identity should be the same as the authentication 175 * identity. If an authorization identity is 176 * specified, it must be in the form "u:" followed 177 * by a username, or "dn:" followed by a DN. The 178 * value "dn:" may indicate an authorization 179 * identity of the anonymous user. 180 * @param totpPassword The hard-coded TOTP password to include in the 181 * bind request. It must not be {@code null}. 182 * @param staticPassword The static password for the target user. It may 183 * be {@code null} if only the one-time password is 184 * to be used for authentication (which may or may 185 * not be allowed by the server). 186 * @param controls The set of controls to include in the bind 187 * request. 188 */ 189 private SingleUseTOTPBindRequest(final String authenticationID, 190 final String authorizationID, 191 final String totpPassword, 192 final ASN1OctetString staticPassword, 193 final Control... controls) 194 { 195 super(authenticationID, authorizationID, staticPassword, controls); 196 197 Validator.ensureNotNull(totpPassword); 198 this.totpPassword = totpPassword; 199 } 200 201 202 203 /** 204 * Creates a new single-use TOTP bind request from the information contained 205 * in the provided encoded SASL credentials. 206 * 207 * @param saslCredentials The encoded SASL credentials to be decoded in 208 * order to create this single-use TOTP bind request. 209 * It must not be {@code null}. 210 * @param controls The set of controls to include in the bind 211 * request. 212 * 213 * @return The single-use TOTP bind request decoded from the provided 214 * credentials. 215 * 216 * @throws LDAPException If the provided credentials are not valid for an 217 * UNBOUNDID-TOTP bind request. 218 */ 219 public static SingleUseTOTPBindRequest 220 decodeSASLCredentials(final ASN1OctetString saslCredentials, 221 final Control... controls) 222 throws LDAPException 223 { 224 try 225 { 226 String authenticationID = null; 227 String authorizationID = null; 228 String totpPassword = null; 229 ASN1OctetString staticPassword = null; 230 231 final ASN1Sequence s = 232 ASN1Sequence.decodeAsSequence(saslCredentials.getValue()); 233 for (final ASN1Element e : s.elements()) 234 { 235 switch (e.getType()) 236 { 237 case TYPE_AUTHENTICATION_ID: 238 authenticationID = e.decodeAsOctetString().stringValue(); 239 break; 240 case TYPE_AUTHORIZATION_ID: 241 authorizationID = e.decodeAsOctetString().stringValue(); 242 break; 243 case TYPE_TOTP_PASSWORD: 244 totpPassword = e.decodeAsOctetString().stringValue(); 245 break; 246 case TYPE_STATIC_PASSWORD: 247 staticPassword = e.decodeAsOctetString(); 248 break; 249 default: 250 throw new LDAPException(ResultCode.DECODING_ERROR, 251 ERR_SINGLE_USE_TOTP_DECODE_INVALID_ELEMENT_TYPE.get( 252 StaticUtils.toHex(e.getType()))); 253 } 254 } 255 256 if (authenticationID == null) 257 { 258 throw new LDAPException(ResultCode.DECODING_ERROR, 259 ERR_SINGLE_USE_TOTP_DECODE_MISSING_AUTHN_ID.get()); 260 } 261 262 if (totpPassword == null) 263 { 264 throw new LDAPException(ResultCode.DECODING_ERROR, 265 ERR_SINGLE_USE_TOTP_DECODE_MISSING_TOTP_PW.get()); 266 } 267 268 return new SingleUseTOTPBindRequest(authenticationID, authorizationID, 269 totpPassword, staticPassword, controls); 270 } 271 catch (final Exception e) 272 { 273 Debug.debugException(e); 274 throw new LDAPException(ResultCode.DECODING_ERROR, 275 ERR_SINGLE_USE_TOTP_DECODE_ERROR.get( 276 StaticUtils.getExceptionMessage(e)), 277 e); 278 } 279 } 280 281 282 283 /** 284 * Retrieves the hard-coded TOTP password to include in the bind request. 285 * 286 * @return The hard-coded TOTP password to include in the bind request. 287 */ 288 public String getTOTPPassword() 289 { 290 return totpPassword; 291 } 292 293 294 295 /** 296 * {@inheritDoc} 297 */ 298 @Override() 299 protected ASN1OctetString getSASLCredentials() 300 { 301 return encodeCredentials(getAuthenticationID(), getAuthorizationID(), 302 totpPassword, getStaticPassword()); 303 } 304 305 306 307 /** 308 * {@inheritDoc} 309 */ 310 @Override() 311 public SingleUseTOTPBindRequest getRebindRequest(final String host, 312 final int port) 313 { 314 // Automatic rebinding is not supported for single-use TOTP binds. 315 return null; 316 } 317 318 319 320 /** 321 * {@inheritDoc} 322 */ 323 @Override() 324 public SingleUseTOTPBindRequest duplicate() 325 { 326 return duplicate(getControls()); 327 } 328 329 330 331 /** 332 * {@inheritDoc} 333 */ 334 @Override() 335 public SingleUseTOTPBindRequest duplicate(final Control[] controls) 336 { 337 final SingleUseTOTPBindRequest bindRequest = 338 new SingleUseTOTPBindRequest(getAuthenticationID(), 339 getAuthorizationID(), totpPassword, getStaticPassword(), 340 controls); 341 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 342 return bindRequest; 343 } 344 345 346 347 /** 348 * {@inheritDoc} 349 */ 350 @Override() 351 public void toCode(final List<String> lineList, final String requestID, 352 final int indentSpaces, final boolean includeProcessing) 353 { 354 // Create the request variable. 355 final ArrayList<ToCodeArgHelper> constructorArgs = 356 new ArrayList<ToCodeArgHelper>(5); 357 constructorArgs.add(ToCodeArgHelper.createString(getAuthenticationID(), 358 "Authentication ID")); 359 constructorArgs.add(ToCodeArgHelper.createString(getAuthorizationID(), 360 "Authorization ID")); 361 constructorArgs.add(ToCodeArgHelper.createString( 362 "---redacted-totp-password", "TOTP Password")); 363 constructorArgs.add(ToCodeArgHelper.createString( 364 ((getStaticPassword() == null) 365 ? "null" 366 : "---redacted-static-password---"), 367 "Static Password")); 368 369 final Control[] controls = getControls(); 370 if (controls.length > 0) 371 { 372 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 373 "Bind Controls")); 374 } 375 376 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 377 "SingleUseTOTPBindRequest", requestID + "Request", 378 "new SingleUseTOTPBindRequest", constructorArgs); 379 380 381 // Add lines for processing the request and obtaining the result. 382 if (includeProcessing) 383 { 384 // Generate a string with the appropriate indent. 385 final StringBuilder buffer = new StringBuilder(); 386 for (int i=0; i < indentSpaces; i++) 387 { 388 buffer.append(' '); 389 } 390 final String indent = buffer.toString(); 391 392 lineList.add(""); 393 lineList.add(indent + "try"); 394 lineList.add(indent + '{'); 395 lineList.add(indent + " BindResult " + requestID + 396 "Result = connection.bind(" + requestID + "Request);"); 397 lineList.add(indent + " // The bind was processed successfully."); 398 lineList.add(indent + '}'); 399 lineList.add(indent + "catch (LDAPException e)"); 400 lineList.add(indent + '{'); 401 lineList.add(indent + " // The bind failed. Maybe the following will " + 402 "help explain why."); 403 lineList.add(indent + " // Note that the connection is now likely in " + 404 "an unauthenticated state."); 405 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 406 lineList.add(indent + " String message = e.getMessage();"); 407 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 408 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 409 lineList.add(indent + " Control[] responseControls = " + 410 "e.getResponseControls();"); 411 lineList.add(indent + '}'); 412 } 413 } 414}