001/* 002 * Copyright 2016-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2016-2020 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) 2016-2020 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.extensions; 037 038 039 040import java.util.ArrayList; 041 042import com.unboundid.asn1.ASN1Element; 043import com.unboundid.asn1.ASN1OctetString; 044import com.unboundid.asn1.ASN1Sequence; 045import com.unboundid.ldap.sdk.Control; 046import com.unboundid.ldap.sdk.ExtendedRequest; 047import com.unboundid.ldap.sdk.LDAPException; 048import com.unboundid.ldap.sdk.ResultCode; 049import com.unboundid.util.Debug; 050import com.unboundid.util.NotMutable; 051import com.unboundid.util.StaticUtils; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054import com.unboundid.util.Validator; 055 056import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 057 058 059 060/** 061 * This class provides an implementation of an extended request that may be used 062 * to register a YubiKey OTP device with the Directory Server so that it may be 063 * used to authenticate using the UNBOUNDID-YUBIKEY-OTP SASL mechanism. 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 * This extended request has an OID of 1.3.6.1.4.1.30221.2.6.54, and it must 076 * include a request value with the following encoding: 077 * <BR><BR> 078 * <PRE> 079 * RegisterYubiKeyOTPDeviceRequest ::= SEQUENCE { 080 * authenticationID [0] OCTET STRING OPTIONAL, 081 * staticPassword [1] OCTET STRING OPTIONAL, 082 * yubiKeyOTP [2] OCTET STRING, 083 * ... } 084 * </PRE> 085 * 086 * 087 * @see DeregisterYubiKeyOTPDeviceExtendedRequest 088 * @see com.unboundid.ldap.sdk.unboundidds.UnboundIDYubiKeyOTPBindRequest 089 * @see com.unboundid.ldap.sdk.unboundidds.RegisterYubiKeyOTPDevice 090 */ 091@NotMutable() 092@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 093public final class RegisterYubiKeyOTPDeviceExtendedRequest 094 extends ExtendedRequest 095{ 096 /** 097 * The OID (1.3.6.1.4.1.30221.2.6.54) for the register YubiKey OTP device 098 * extended request. 099 */ 100 public static final String REGISTER_YUBIKEY_OTP_DEVICE_REQUEST_OID = 101 "1.3.6.1.4.1.30221.2.6.54"; 102 103 104 105 /** 106 * The BER type for the authentication ID element of the request value 107 * sequence. 108 */ 109 private static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80; 110 111 112 113 /** 114 * The BER type for the static password element of the request value sequence. 115 */ 116 private static final byte TYPE_STATIC_PASSWORD = (byte) 0x81; 117 118 119 120 /** 121 * The BER type for the YubiKey OTP element of the request value sequence. 122 */ 123 private static final byte TYPE_YUBIKEY_OTP = (byte) 0x82; 124 125 126 127 /** 128 * The serial version UID for this serializable class. 129 */ 130 private static final long serialVersionUID = 4833523148133015294L; 131 132 133 134 // The static password for the request. 135 private final ASN1OctetString staticPassword; 136 137 // The authentication ID for the request. 138 private final String authenticationID; 139 140 // The YubiKey OTP for the request. 141 private final String yubiKeyOTP; 142 143 144 145 /** 146 * Creates a new register YubiKey OTP device extended request that will be 147 * used to register a new device for the user as whom the underlying 148 * connection is authenticated. 149 * 150 * @param yubiKeyOTP A one-time password generated by the YubiKey device to 151 * be registered. It must not be {@code null}. 152 * @param controls The set of controls to include in the request. It may 153 * be {@code null} or empty if there should not be any 154 * request controls. 155 */ 156 public RegisterYubiKeyOTPDeviceExtendedRequest(final String yubiKeyOTP, 157 final Control... controls) 158 { 159 this(null, (ASN1OctetString) null, yubiKeyOTP, controls); 160 } 161 162 163 164 /** 165 * Creates a new register YubiKey OTP device extended request with the 166 * provided information. 167 * 168 * @param authenticationID The authentication ID that identifies the user 169 * for whom the YubiKey OTP device is to be 170 * registered. It may be {@code null} if the device 171 * is to be registered for the user as whom the 172 * underlying connection is authenticated. 173 * @param staticPassword The static password of the user for whom the 174 * device is to be registered. It may be 175 * {@code null} if the device is to be registered 176 * for a user other than the user authenticated on 177 * the underlying connection and the server is 178 * configured to not require the target user's 179 * static password in this case. 180 * @param yubiKeyOTP A one-time password generated by the YubiKey 181 * device to be registered. It must not be 182 * {@code null}. 183 * @param controls The set of controls to include in the request. 184 * It may be {@code null} or empty if there should 185 * not be any request controls. 186 */ 187 public RegisterYubiKeyOTPDeviceExtendedRequest(final String authenticationID, 188 final String staticPassword, 189 final String yubiKeyOTP, 190 final Control... controls) 191 { 192 this(authenticationID, encodePassword(staticPassword), yubiKeyOTP, 193 controls); 194 } 195 196 197 198 /** 199 * Creates a new register YubiKey OTP device extended request with the 200 * provided information. 201 * 202 * @param authenticationID The authentication ID that identifies the user 203 * for whom the YubiKey OTP device is to be 204 * registered. It may be {@code null} if the device 205 * is to be registered for the user as whom the 206 * underlying connection is authenticated. 207 * @param staticPassword The static password of the user for whom the 208 * device is to be registered. It may be 209 * {@code null} if the device is to be registered 210 * for a user other than the user authenticated on 211 * the underlying connection and the server is 212 * configured to not require the target user's 213 * static password in this case. 214 * @param yubiKeyOTP A one-time password generated by the YubiKey 215 * device to be registered. It must not be 216 * {@code null}. 217 * @param controls The set of controls to include in the request. 218 * It may be {@code null} or empty if there should 219 * not be any request controls. 220 */ 221 public RegisterYubiKeyOTPDeviceExtendedRequest(final String authenticationID, 222 final byte[] staticPassword, 223 final String yubiKeyOTP, 224 final Control... controls) 225 { 226 this(authenticationID, encodePassword(staticPassword), yubiKeyOTP, 227 controls); 228 } 229 230 231 232 /** 233 * Creates a new register YubiKey OTP device extended request with the 234 * provided information. 235 * 236 * @param authenticationID The authentication ID that identifies the user 237 * for whom the YubiKey OTP device is to be 238 * registered. It may be {@code null} if the device 239 * is to be registered for the user as whom the 240 * underlying connection is authenticated. 241 * @param staticPassword The static password of the user for whom the 242 * device is to be registered. It may be 243 * {@code null} if the device is to be registered 244 * for a user other than the user authenticated on 245 * the underlying connection and the server is 246 * configured to not require the target user's 247 * static password in this case. 248 * @param yubiKeyOTP A one-time password generated by the YubiKey 249 * device to be registered. It must not be 250 * {@code null}. 251 * @param controls The set of controls to include in the request. 252 * It may be {@code null} or empty if there should 253 * not be any request controls. 254 */ 255 private RegisterYubiKeyOTPDeviceExtendedRequest(final String authenticationID, 256 final ASN1OctetString staticPassword, final String yubiKeyOTP, 257 final Control... controls) 258 { 259 super(REGISTER_YUBIKEY_OTP_DEVICE_REQUEST_OID, 260 encodeValue(authenticationID, staticPassword, yubiKeyOTP), controls); 261 262 this.authenticationID = authenticationID; 263 this.staticPassword = staticPassword; 264 this.yubiKeyOTP = yubiKeyOTP; 265 } 266 267 268 269 /** 270 * Creates a new register YubiKey OTP device extended request that is decoded 271 * from the provided generic extended request. 272 * 273 * @param request The generic extended request to decode as a register 274 * YubiKey OTP device request. 275 * 276 * @throws LDAPException If a problem is encountered while attempting to 277 * decode the provided request. 278 */ 279 public RegisterYubiKeyOTPDeviceExtendedRequest(final ExtendedRequest request) 280 throws LDAPException 281 { 282 super(request); 283 284 final ASN1OctetString value = request.getValue(); 285 if (value == null) 286 { 287 throw new LDAPException(ResultCode.DECODING_ERROR, 288 ERR_REGISTER_YUBIKEY_OTP_REQUEST_NO_VALUE.get()); 289 } 290 291 try 292 { 293 String authID = null; 294 ASN1OctetString staticPW = null; 295 String otp = null; 296 for (final ASN1Element e : 297 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 298 { 299 switch (e.getType()) 300 { 301 case TYPE_AUTHENTICATION_ID: 302 authID = ASN1OctetString.decodeAsOctetString(e).stringValue(); 303 break; 304 case TYPE_STATIC_PASSWORD: 305 staticPW = ASN1OctetString.decodeAsOctetString(e); 306 break; 307 case TYPE_YUBIKEY_OTP: 308 otp = ASN1OctetString.decodeAsOctetString(e).stringValue(); 309 break; 310 default: 311 throw new LDAPException(ResultCode.DECODING_ERROR, 312 ERR_REGISTER_YUBIKEY_OTP_REQUEST_UNRECOGNIZED_TYPE.get( 313 StaticUtils.toHex(e.getType()))); 314 } 315 } 316 317 if (otp == null) 318 { 319 throw new LDAPException(ResultCode.DECODING_ERROR, 320 ERR_REGISTER_YUBIKEY_OTP_REQUEST_MISSING_OTP.get()); 321 } 322 323 authenticationID = authID; 324 staticPassword = staticPW; 325 yubiKeyOTP = otp; 326 } 327 catch (final LDAPException le) 328 { 329 Debug.debugException(le); 330 throw le; 331 } 332 catch (final Exception e) 333 { 334 Debug.debugException(e); 335 throw new LDAPException(ResultCode.DECODING_ERROR, 336 ERR_REGISTER_YUBIKEY_OTP_REQUEST_ERROR_DECODING_VALUE.get( 337 StaticUtils.getExceptionMessage(e)), 338 e); 339 } 340 } 341 342 343 344 /** 345 * Encodes the provided password as an ASN.1 octet string suitable for 346 * inclusion in the encoded request. 347 * 348 * @param password The password to be encoded. It may be {@code null} if 349 * no password should be included. If it is 350 * non-{@code null}, then it must be a string or a byte 351 * array. 352 * 353 * @return The encoded password, or {@code null} if no password was given. 354 */ 355 static ASN1OctetString encodePassword(final Object password) 356 { 357 if (password == null) 358 { 359 return null; 360 } 361 else if (password instanceof byte[]) 362 { 363 return new ASN1OctetString(TYPE_STATIC_PASSWORD, (byte[]) password); 364 } 365 else 366 { 367 return new ASN1OctetString(TYPE_STATIC_PASSWORD, 368 String.valueOf(password)); 369 } 370 } 371 372 373 374 /** 375 * Encodes the provided information into an ASN.1 octet string suitable for 376 * use as the value of this extended request. 377 * 378 * @param authenticationID The authentication ID that identifies the user 379 * for whom the YubiKey OTP device is to be 380 * registered. It may be {@code null} if the device 381 * is to be registered for the user as whom the 382 * underlying connection is authenticated. 383 * @param staticPassword The static password of the user for whom the 384 * device is to be registered. It may be 385 * {@code null} if the device is to be registered 386 * for a user other than the user authenticated on 387 * the underlying connection and the server is 388 * configured to not require the target user's 389 * static password in this case. 390 * @param yubiKeyOTP A one-time password generated by the YubiKey 391 * device to be registered. It must not be 392 * {@code null}. 393 * 394 * @return The ASN.1 octet string containing the encoded request value. 395 */ 396 private static ASN1OctetString encodeValue(final String authenticationID, 397 final ASN1OctetString staticPassword, 398 final String yubiKeyOTP) 399 { 400 Validator.ensureNotNull(yubiKeyOTP); 401 402 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 403 404 if (authenticationID != null) 405 { 406 elements.add( 407 new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID)); 408 } 409 410 if (staticPassword != null) 411 { 412 elements.add(staticPassword); 413 } 414 415 elements.add(new ASN1OctetString(TYPE_YUBIKEY_OTP, yubiKeyOTP)); 416 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 417 } 418 419 420 421 /** 422 * Retrieves the authentication ID that identifies the user for whom the 423 * YubiKey OTP device is to be registered, if provided. 424 * 425 * @return The authentication ID that identifies the target user, or 426 * {@code null} if the device is to be registered as the user as 427 * whom the underlying connection is authenticated. 428 */ 429 public String getAuthenticationID() 430 { 431 return authenticationID; 432 } 433 434 435 436 /** 437 * Retrieves the string representation of the static password for the target 438 * user, if provided. 439 * 440 * @return The string representation of the static password for the target 441 * user, or {@code null} if no static password was provided. 442 */ 443 public String getStaticPasswordString() 444 { 445 if (staticPassword == null) 446 { 447 return null; 448 } 449 else 450 { 451 return staticPassword.stringValue(); 452 } 453 } 454 455 456 457 /** 458 * Retrieves the bytes that comprise the static password for the target user, 459 * if provided. 460 * 461 * @return The bytes that comprise the static password for the target user, 462 * or {@code null} if no static password was provided. 463 */ 464 public byte[] getStaticPasswordBytes() 465 { 466 if (staticPassword == null) 467 { 468 return null; 469 } 470 else 471 { 472 return staticPassword.getValue(); 473 } 474 } 475 476 477 478 /** 479 * Retrieves a one-time password generated by the YubiKey device to be 480 * registered. 481 * 482 * @return A one-time password generated by the YubiKey device to be 483 * registered. 484 */ 485 public String getYubiKeyOTP() 486 { 487 return yubiKeyOTP; 488 } 489 490 491 492 /** 493 * {@inheritDoc} 494 */ 495 @Override() 496 public RegisterYubiKeyOTPDeviceExtendedRequest duplicate() 497 { 498 return duplicate(getControls()); 499 } 500 501 502 503 /** 504 * {@inheritDoc} 505 */ 506 @Override() 507 public RegisterYubiKeyOTPDeviceExtendedRequest duplicate( 508 final Control[] controls) 509 { 510 final RegisterYubiKeyOTPDeviceExtendedRequest r = 511 new RegisterYubiKeyOTPDeviceExtendedRequest(authenticationID, 512 staticPassword, yubiKeyOTP, controls); 513 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 514 return r; 515 } 516 517 518 519 /** 520 * {@inheritDoc} 521 */ 522 @Override() 523 public String getExtendedRequestName() 524 { 525 return INFO_REGISTER_YUBIKEY_OTP_REQUEST_NAME.get(); 526 } 527 528 529 530 /** 531 * {@inheritDoc} 532 */ 533 @Override() 534 public void toString(final StringBuilder buffer) 535 { 536 buffer.append("RegisterYubiKeyOTPDeviceExtendedRequest("); 537 538 if (authenticationID != null) 539 { 540 buffer.append("authenticationID='"); 541 buffer.append(authenticationID); 542 buffer.append("', "); 543 } 544 545 buffer.append("staticPasswordProvided="); 546 buffer.append(staticPassword != null); 547 548 final Control[] controls = getControls(); 549 if (controls.length > 0) 550 { 551 buffer.append(", controls={"); 552 for (int i=0; i < controls.length; i++) 553 { 554 if (i > 0) 555 { 556 buffer.append(", "); 557 } 558 559 buffer.append(controls[i]); 560 } 561 buffer.append('}'); 562 } 563 564 buffer.append(')'); 565 } 566}