001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * docs/licenses/cddl.txt 011 * or http://www.opensource.org/licenses/cddl1.php. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * docs/licenses/cddl.txt. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Portions Copyright 2013-2024 Ping Identity Corporation 026 */ 027package com.unboundid.directory.sdk.ds.api; 028 029 030 031import java.util.Collections; 032import java.util.List; 033import java.util.Map; 034 035import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider; 036import com.unboundid.directory.sdk.common.internal.Reconfigurable; 037import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 038import com.unboundid.directory.sdk.common.types.Entry; 039import com.unboundid.directory.sdk.ds.config. 040 OneTimePasswordDeliveryMechanismConfig; 041import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension; 042import com.unboundid.directory.sdk.ds.types.DirectoryServerContext; 043import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 044import com.unboundid.ldap.sdk.LDAPException; 045import com.unboundid.ldap.sdk.ResultCode; 046import com.unboundid.ldap.sdk.unboundidds.extensions. 047 SupportedOTPDeliveryMechanismInfo; 048import com.unboundid.util.Extensible; 049import com.unboundid.util.ThreadSafety; 050import com.unboundid.util.ThreadSafetyLevel; 051import com.unboundid.util.args.ArgumentException; 052import com.unboundid.util.args.ArgumentParser; 053 054 055 056/** 057 * This class defines an API that must be implemented by extensions which 058 * attempt to deliver one-time passwords (OTPs) to end users through some 059 * out-of-band mechanism. The one-time passwords will have been generated by 060 * the deliver one-time password extended request, and may be used to 061 * authenticate to the server through the UNBOUNDID-DELIVERED-OTP SASL 062 * mechanism. 063 * <BR> 064 * <H2>Configuring One-Time Password Delivery Mechanisms</H2> 065 * In order to configure a one-time password delivery mechanism created using 066 * this API, use a command like: 067 * <PRE> 068 * dsconfig create-otp-delivery-mechanism \ 069 * --mechanism-name "<I>{mechanism-name}</I>" \ 070 * --type third-party \ 071 * --set enabled:true \ 072 * --set "extension-class:<I>{class-name}</I>" \ 073 * --set "extension-argument:<I>{name=value}</I>" 074 * </PRE> 075 * where "<I>{mechanism-name}</I>" is the name to use for the one-time password 076 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class 077 * that extends 078 * {@code com.unboundid.directory.sdk.ds.api.OneTimePasswordDeliveryMechanism}, 079 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to 080 * provide to the one-time password delivery mechanism. If multiple arguments 081 * should be provided to the OTP delivery mechanism, then the 082 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 083 * provided multiple times. 084 */ 085@Extensible() 086@DirectoryServerExtension() 087@DirectoryProxyServerExtension(appliesToLocalContent=true, 088 appliesToRemoteContent=true) 089@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 090public abstract class OneTimePasswordDeliveryMechanism 091 implements UnboundIDExtension, 092 Reconfigurable<OneTimePasswordDeliveryMechanismConfig>, 093 ExampleUsageProvider 094{ 095 /** 096 * Creates a new instance of this one-time password delivery mechanism. All 097 * OTP delivery mechanism implementations must include a default constructor, 098 * but any initialization should generally be done in the 099 * {@code initializeOTPDeliveryMechanism} method. 100 */ 101 public OneTimePasswordDeliveryMechanism() 102 { 103 // No implementation is required. 104 } 105 106 107 108 /** 109 * {@inheritDoc} 110 */ 111 @Override() 112 public abstract String getExtensionName(); 113 114 115 116 /** 117 * {@inheritDoc} 118 */ 119 @Override() 120 public abstract String[] getExtensionDescription(); 121 122 123 124 /** 125 * {@inheritDoc} 126 */ 127 @Override() 128 public void defineConfigArguments(final ArgumentParser parser) 129 throws ArgumentException 130 { 131 // No arguments will be allowed by default. 132 } 133 134 135 136 /** 137 * Initializes this one-time password delivery mechanism. 138 * 139 * @param serverContext A handle to the server context for the server in 140 * which this extension is running. 141 * @param config The general configuration for this OTP delivery 142 * mechanism. 143 * @param parser The argument parser which has been initialized from 144 * the configuration for this OTP delivery mechanism. 145 * 146 * @throws LDAPException If a problem occurs while initializing this OTP 147 * delivery mechanism. 148 */ 149 public void initializeOTPDeliveryMechanism( 150 final DirectoryServerContext serverContext, 151 final OneTimePasswordDeliveryMechanismConfig config, 152 final ArgumentParser parser) 153 throws LDAPException 154 { 155 // No initialization will be performed by default. 156 } 157 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override() 164 public boolean isConfigurationAcceptable( 165 final OneTimePasswordDeliveryMechanismConfig config, 166 final ArgumentParser parser, 167 final List<String> unacceptableReasons) 168 { 169 // No extended validation will be performed by default. 170 return true; 171 } 172 173 174 175 /** 176 * {@inheritDoc} 177 */ 178 @Override() 179 public ResultCode applyConfiguration( 180 final OneTimePasswordDeliveryMechanismConfig config, 181 final ArgumentParser parser, 182 final List<String> adminActionsRequired, 183 final List<String> messages) 184 { 185 // By default, no configuration changes will be applied. If there are any 186 // arguments, then add an admin action message indicating that the extension 187 // needs to be restarted for any changes to take effect. 188 if (! parser.getNamedArguments().isEmpty()) 189 { 190 adminActionsRequired.add( 191 "No configuration change has actually been applied. The new " + 192 "configuration will not take effect until this one-time " + 193 "password delivery mechanism is disabled and re-enabled or " + 194 "until the server is restarted."); 195 } 196 197 return ResultCode.SUCCESS; 198 } 199 200 201 202 /** 203 * Performs any cleanup which may be necessary when this one-time password 204 * delivery mechanism is to be taken out of service. 205 */ 206 public void finalizeOTPDeliveryMechanism() 207 { 208 // No implementation is required. 209 } 210 211 212 213 /** 214 * Attempts to deliver the provided one-time password to the specified user. 215 * 216 * @param oneTimePassword The one-time password to be delivered. 217 * @param userEntry The entry for the user to whom the one-time 218 * password should be delivered. 219 * @param targetRecipientID The target recipient ID that should be used for 220 * the delivery, if possible. It may be 221 * {@code null} if there is no appropriate 222 * identifier for this delivery mechanism or if 223 * the delivery mechanism may choose which 224 * recipient ID should be used. However, if it is 225 * non-{@code null}, then the delivery mechanism 226 * must verify that the provided recipient ID is 227 * valid for the associated user (e.g., for an 228 * e-mail delivery mechanism, the recipient ID 229 * would probably be an e-mail address, and the 230 * delivery mechanism must verify that the provided 231 * e-mail address is associated with the given 232 * user account). 233 * @param resultMessage A buffer to which a message may be appended with 234 * additional information about the password 235 * delivery. 236 * 237 * @return An identifier for the user in a format appropriate to the delivery 238 * mechanism (e.g., an e-mail address for an e-mail delivery 239 * mechanism, a phone number for an SMS or voice call delivery 240 * mechanism, etc.). It may be {@code null} if there is no 241 * appropriate identifier for this delivery mechanism that is needed 242 * beyond the name of the delivery mechanism and the DN of the 243 * recipient. 244 * 245 * @throws LDAPException If a problem is encountered while attempting to 246 * send the one-time password to the user. The 247 * exception should have a result code of 248 * {@code TOKEN_DELIVERY_INVALID_RECIPIENT_ID} if the 249 * client specified a recipient ID that is not valid 250 * for the target user, 251 * {@code TOKEN_DELIVERY_MECHANISM_UNAVAILABLE} if 252 * this delivery mechanism is not supported for the 253 * target user, or 254 * {@code TOKEN_DELIVERY_ATTEMPT_FAILED} if the 255 * delivery mechanism should be supported but the 256 * attempt to deliver the one-time password failed. 257 */ 258 public abstract String deliverOneTimePassword(final String oneTimePassword, 259 final Entry userEntry, 260 final String targetRecipientID, 261 final StringBuilder resultMessage) 262 throws LDAPException; 263 264 265 266 /** 267 * Indicates whether this one-time password delivery mechanism supports 268 * general purpose message delivery via the {@link #deliverMessage} method. 269 * 270 * @return {@code true} if the {@code deliverMessage} method can be used for 271 * general-purpose message delivery, or {@code false} if only the 272 * {@code deliverOneTimePassword} method can be used. 273 */ 274 public boolean supportsGenericMessageDelivery() 275 { 276 return false; 277 } 278 279 280 281 /** 282 * Attempts to deliver a message (containing a single-use token) to the 283 * specified user. 284 * 285 * @param config The general configuration for this OTP delivery 286 * mechanism. 287 * @param tokenID The token ID for the single-use token contained 288 * in the message to be delivered. 289 * @param tokenValue The value for the single-use token contained 290 * in the message to be delivered. 291 * @param messageSubject The message subject that should be used, if 292 * appropriate. It may be {@code null} if no 293 * subject was provided. 294 * @param fullMessage The message that should be delivered if this 295 * delivery mechanism does not impose a 296 * significant constraint on message size. 297 * @param compactMessage The message that should be delivered if this 298 * delivery mechanism does impose a significant 299 * constraint on message size. 300 * @param userEntry The entry for the user to whom the token should 301 * be delivered. 302 * @param targetRecipientID The target recipient ID that should be used for 303 * the delivery, if possible. It may be 304 * {@code null} if there is no appropriate 305 * identifier for this delivery mechanism or if 306 * the delivery mechanism may choose which 307 * recipient ID should be used. However, if it is 308 * non-{@code null}, then the delivery mechanism 309 * must verify that the provided recipient ID is 310 * valid for the associated user (e.g., for an 311 * e-mail delivery mechanism, the recipient ID 312 * would probably be an e-mail address, and the 313 * delivery mechanism must verify that the 314 * provided e-mail address is associated with the 315 * given user account). 316 * @param resultMessage A buffer to which a message may be appended 317 * with additional information about the token 318 * delivery. 319 * 320 * @return An identifier for the user in a format appropriate to the delivery 321 * mechanism (e.g., an e-mail address for an e-mail delivery 322 * mechanism, a phone number for an SMS or voice call delivery 323 * mechanism, etc.). It may be {@code null} if there is no 324 * appropriate identifier for this delivery mechanism that is needed 325 * beyond the name of the delivery mechanism and the DN of the 326 * recipient. 327 * 328 * @throws LDAPException If a problem is encountered while attempting to 329 * deliver the single-use token message to the user. 330 * The exception should have a result code of 331 * {@code TOKEN_DELIVERY_INVALID_RECIPIENT_ID} if the 332 * client specified a recipient ID that is not valid 333 * for the target user, 334 * {@code TOKEN_DELIVERY_MECHANISM_UNAVAILABLE} if 335 * this delivery mechanism is not supported for the 336 * target user, or 337 * {@code TOKEN_DELIVERY_ATTEMPT_FAILED} if the 338 * delivery mechanism should be supported but the 339 * attempt to deliver the token failed. 340 */ 341 public String deliverMessage( 342 final OneTimePasswordDeliveryMechanismConfig config, 343 final String tokenID, final String tokenValue, 344 final String messageSubject, final String fullMessage, 345 final String compactMessage, final Entry userEntry, 346 final String targetRecipientID, 347 final StringBuilder resultMessage) 348 throws LDAPException 349 { 350 throw new LDAPException(ResultCode.OTHER, 351 "The '" + config.getConfigObjectName() + "' OTP delivery mechanism " + 352 "does not support arbitrary message delivery."); 353 } 354 355 356 357 /** 358 * Updates the provided list with one or more 359 * {@code SupportedOTPDeliveryMechanismInfo} objects indicating whether this 360 * OTP delivery mechanism is supported for the specified user. 361 * 362 * @param config The general configuration for this OTP 363 * delivery mechanism. 364 * @param userEntry The entry of the user for whom to make the 365 * determination. 366 * @param deliveryMechanismInfo The list to which any objects may be 367 * appended with information about the level 368 * of support this OTP delivery mechanism has 369 * for use in conjunction with the specified 370 * user. 371 */ 372 public void getSupportedDeliveryMechanismInfo( 373 final OneTimePasswordDeliveryMechanismConfig config, 374 final Entry userEntry, 375 final List<SupportedOTPDeliveryMechanismInfo> deliveryMechanismInfo) 376 { 377 deliveryMechanismInfo.add( 378 new SupportedOTPDeliveryMechanismInfo(config.getConfigObjectName(), 379 null, null)); 380 } 381 382 383 384 /** 385 * {@inheritDoc} 386 */ 387 @Override() 388 public Map<List<String>,String> getExamplesArgumentSets() 389 { 390 return Collections.emptyMap(); 391 } 392}