001 /* 002 * Copyright 2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2015 UnboundID Corp. 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 */ 021 package com.unboundid.ldap.sdk.unboundidds; 022 023 024 025 import java.io.OutputStream; 026 import java.io.Serializable; 027 import java.util.ArrayList; 028 import java.util.LinkedHashMap; 029 import java.util.List; 030 031 import com.unboundid.ldap.sdk.LDAPConnection; 032 import com.unboundid.ldap.sdk.LDAPException; 033 import com.unboundid.ldap.sdk.ResultCode; 034 import com.unboundid.ldap.sdk.Version; 035 import com.unboundid.ldap.sdk.unboundidds.extensions. 036 DeliverPasswordResetTokenExtendedRequest; 037 import com.unboundid.ldap.sdk.unboundidds.extensions. 038 DeliverPasswordResetTokenExtendedResult; 039 import com.unboundid.util.Debug; 040 import com.unboundid.util.LDAPCommandLineTool; 041 import com.unboundid.util.ObjectPair; 042 import com.unboundid.util.StaticUtils; 043 import com.unboundid.util.ThreadSafety; 044 import com.unboundid.util.ThreadSafetyLevel; 045 import com.unboundid.util.args.ArgumentException; 046 import com.unboundid.util.args.ArgumentParser; 047 import com.unboundid.util.args.DNArgument; 048 import com.unboundid.util.args.StringArgument; 049 050 import static com.unboundid.ldap.sdk.unboundidds.UnboundIDDSMessages.*; 051 052 053 054 /** 055 * <BLOCKQUOTE> 056 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 057 * LDAP SDK for Java. It is not available for use in applications that 058 * include only the Standard Edition of the LDAP SDK, and is not supported for 059 * use in conjunction with non-UnboundID products. 060 * </BLOCKQUOTE> 061 * This class provides a utility that may be used to request that the Directory 062 * Server deliver a single-use password reset token to a user through some 063 * out-of-band mechanism. 064 */ 065 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 066 public final class DeliverPasswordResetToken 067 extends LDAPCommandLineTool 068 implements Serializable 069 { 070 /** 071 * The serial version UID for this serializable class. 072 */ 073 private static final long serialVersionUID = 5793619963770997266L; 074 075 076 077 // The DN of the user to whom the password reset token should be sent. 078 private DNArgument userDN; 079 080 // The text to include after the password reset token in the "compact" 081 // message. 082 private StringArgument compactTextAfterToken; 083 084 // The text to include before the password reset token in the "compact" 085 // message. 086 private StringArgument compactTextBeforeToken; 087 088 // The name of the mechanism through which the one-time password should be 089 // delivered. 090 private StringArgument deliveryMechanism; 091 092 // The text to include after the password reset token in the "full" message. 093 private StringArgument fullTextAfterToken; 094 095 // The text to include before the password reset token in the "full" message. 096 private StringArgument fullTextBeforeToken; 097 098 // The subject to use for the message containing the delivered token. 099 private StringArgument messageSubject; 100 101 102 103 /** 104 * Parse the provided command line arguments and perform the appropriate 105 * processing. 106 * 107 * @param args The command line arguments provided to this program. 108 */ 109 public static void main(final String... args) 110 { 111 final ResultCode resultCode = main(args, System.out, System.err); 112 if (resultCode != ResultCode.SUCCESS) 113 { 114 System.exit(resultCode.intValue()); 115 } 116 } 117 118 119 120 /** 121 * Parse the provided command line arguments and perform the appropriate 122 * processing. 123 * 124 * @param args The command line arguments provided to this program. 125 * @param outStream The output stream to which standard out should be 126 * written. It may be {@code null} if output should be 127 * suppressed. 128 * @param errStream The output stream to which standard error should be 129 * written. It may be {@code null} if error messages 130 * should be suppressed. 131 * 132 * @return A result code indicating whether the processing was successful. 133 */ 134 public static ResultCode main(final String[] args, 135 final OutputStream outStream, 136 final OutputStream errStream) 137 { 138 final DeliverPasswordResetToken tool = 139 new DeliverPasswordResetToken(outStream, errStream); 140 return tool.runTool(args); 141 } 142 143 144 145 /** 146 * Creates a new instance of this tool. 147 * 148 * @param outStream The output stream to which standard out should be 149 * written. It may be {@code null} if output should be 150 * suppressed. 151 * @param errStream The output stream to which standard error should be 152 * written. It may be {@code null} if error messages 153 * should be suppressed. 154 */ 155 public DeliverPasswordResetToken(final OutputStream outStream, 156 final OutputStream errStream) 157 { 158 super(outStream, errStream); 159 160 userDN = null; 161 compactTextAfterToken = null; 162 compactTextBeforeToken = null; 163 deliveryMechanism = null; 164 fullTextAfterToken = null; 165 fullTextBeforeToken = null; 166 messageSubject = null; 167 } 168 169 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override() 175 public String getToolName() 176 { 177 return "deliver-password-reset-token"; 178 } 179 180 181 182 /** 183 * {@inheritDoc} 184 */ 185 @Override() 186 public String getToolDescription() 187 { 188 return INFO_DELIVER_PW_RESET_TOKEN_TOOL_DESCRIPTION.get(); 189 } 190 191 192 193 /** 194 * {@inheritDoc} 195 */ 196 @Override() 197 public String getToolVersion() 198 { 199 return Version.NUMERIC_VERSION_STRING; 200 } 201 202 203 204 /** 205 * {@inheritDoc} 206 */ 207 @Override() 208 public void addNonLDAPArguments(final ArgumentParser parser) 209 throws ArgumentException 210 { 211 userDN = new DNArgument('b', "userDN", true, 1, 212 INFO_DELIVER_PW_RESET_TOKEN_PLACEHOLDER_DN.get(), 213 INFO_DELIVER_PW_RESET_TOKEN_DESCRIPTION_USER_DN.get()); 214 parser.addArgument(userDN); 215 216 deliveryMechanism = new StringArgument('m', "deliveryMechanism", false, 0, 217 INFO_DELIVER_PW_RESET_TOKEN_PLACEHOLDER_NAME.get(), 218 INFO_DELIVER_PW_RESET_TOKEN_DESCRIPTION_MECH.get()); 219 parser.addArgument(deliveryMechanism); 220 221 messageSubject = new StringArgument('s', "messageSubject", false, 1, 222 INFO_DELIVER_PW_RESET_TOKEN_PLACEHOLDER_SUBJECT.get(), 223 INFO_DELIVER_PW_RESET_TOKEN_DESCRIPTION_SUBJECT.get()); 224 parser.addArgument(messageSubject); 225 226 fullTextBeforeToken = new StringArgument('f', "fullTextBeforeToken", false, 227 1, INFO_DELIVER_PW_RESET_TOKEN_PLACEHOLDER_FULL_BEFORE.get(), 228 INFO_DELIVER_PW_RESET_TOKEN_DESCRIPTION_FULL_BEFORE.get()); 229 parser.addArgument(fullTextBeforeToken); 230 231 fullTextAfterToken = new StringArgument('F', "fullTextAfterToken", false, 232 1, INFO_DELIVER_PW_RESET_TOKEN_PLACEHOLDER_FULL_AFTER.get(), 233 INFO_DELIVER_PW_RESET_TOKEN_DESCRIPTION_FULL_AFTER.get()); 234 parser.addArgument(fullTextAfterToken); 235 236 compactTextBeforeToken = new StringArgument('c', "compactTextBeforeToken", 237 false, 1, INFO_DELIVER_PW_RESET_TOKEN_PLACEHOLDER_COMPACT_BEFORE.get(), 238 INFO_DELIVER_PW_RESET_TOKEN_DESCRIPTION_COMPACT_BEFORE.get()); 239 parser.addArgument(compactTextBeforeToken); 240 241 compactTextAfterToken = new StringArgument('C', "compactTextAfterToken", 242 false, 1, INFO_DELIVER_PW_RESET_TOKEN_PLACEHOLDER_COMPACT_AFTER.get(), 243 INFO_DELIVER_PW_RESET_TOKEN_DESCRIPTION_COMPACT_AFTER.get()); 244 parser.addArgument(compactTextAfterToken); 245 } 246 247 248 249 /** 250 * {@inheritDoc} 251 */ 252 @Override() 253 public ResultCode doToolProcessing() 254 { 255 // Get the set of preferred delivery mechanisms. 256 final ArrayList<ObjectPair<String,String>> preferredDeliveryMechanisms; 257 if (deliveryMechanism.isPresent()) 258 { 259 final List<String> dmList = deliveryMechanism.getValues(); 260 preferredDeliveryMechanisms = 261 new ArrayList<ObjectPair<String,String>>(dmList.size()); 262 for (final String s : dmList) 263 { 264 preferredDeliveryMechanisms.add(new ObjectPair<String,String>(s, null)); 265 } 266 } 267 else 268 { 269 preferredDeliveryMechanisms = null; 270 } 271 272 273 // Get a connection to the directory server. 274 final LDAPConnection conn; 275 try 276 { 277 conn = getConnection(); 278 } 279 catch (final LDAPException le) 280 { 281 Debug.debugException(le); 282 err(ERR_DELIVER_PW_RESET_TOKEN_CANNOT_GET_CONNECTION.get( 283 StaticUtils.getExceptionMessage(le))); 284 return le.getResultCode(); 285 } 286 287 try 288 { 289 // Create and send the extended request 290 final DeliverPasswordResetTokenExtendedRequest request = 291 new DeliverPasswordResetTokenExtendedRequest(userDN.getStringValue(), 292 messageSubject.getValue(), fullTextBeforeToken.getValue(), 293 fullTextAfterToken.getValue(), 294 compactTextBeforeToken.getValue(), 295 compactTextAfterToken.getValue(), preferredDeliveryMechanisms); 296 final DeliverPasswordResetTokenExtendedResult result; 297 try 298 { 299 result = (DeliverPasswordResetTokenExtendedResult) 300 conn.processExtendedOperation(request); 301 } 302 catch (final LDAPException le) 303 { 304 Debug.debugException(le); 305 err(ERR_DELIVER_PW_RESET_TOKEN_ERROR_PROCESSING_EXTOP.get( 306 StaticUtils.getExceptionMessage(le))); 307 return le.getResultCode(); 308 } 309 310 if (result.getResultCode() == ResultCode.SUCCESS) 311 { 312 final String mechanism = result.getDeliveryMechanism(); 313 final String id = result.getRecipientID(); 314 if (id == null) 315 { 316 out(INFO_DELIVER_PW_RESET_TOKEN_SUCCESS_RESULT_WITHOUT_ID.get( 317 mechanism)); 318 } 319 else 320 { 321 out(INFO_DELIVER_PW_RESET_TOKEN_SUCCESS_RESULT_WITH_ID.get(mechanism, 322 id)); 323 } 324 325 final String message = result.getDeliveryMessage(); 326 if (message != null) 327 { 328 out(INFO_DELIVER_PW_RESET_TOKEN_SUCCESS_MESSAGE.get(message)); 329 } 330 } 331 else 332 { 333 if (result.getDiagnosticMessage() == null) 334 { 335 err(ERR_DELIVER_PW_RESET_TOKEN_ERROR_RESULT_NO_MESSAGE.get( 336 String.valueOf(result.getResultCode()))); 337 } 338 else 339 { 340 err(ERR_DELIVER_PW_RESET_TOKEN_ERROR_RESULT.get( 341 String.valueOf(result.getResultCode()), 342 result.getDiagnosticMessage())); 343 } 344 } 345 346 return result.getResultCode(); 347 } 348 finally 349 { 350 conn.close(); 351 } 352 } 353 354 355 356 /** 357 * {@inheritDoc} 358 */ 359 @Override() 360 public LinkedHashMap<String[],String> getExampleUsages() 361 { 362 final LinkedHashMap<String[],String> exampleMap = 363 new LinkedHashMap<String[],String>(1); 364 365 final String[] args = 366 { 367 "--hostname", "server.example.com", 368 "--port", "389", 369 "--bindDN", "uid=password.admin,ou=People,dc=example,dc=com", 370 "--bindPassword", "password", 371 "--userDN", "uid=test.user,ou=People,dc=example,dc=com", 372 "--deliveryMechanism", "SMS", 373 "--deliveryMechanism", "E-Mail", 374 "--messageSubject", "Your password reset token", 375 "--fullTextBeforeToken", "Your single-use password reset token is '", 376 "--fullTextAfterToken", "'.", 377 "--compactTextBeforeToken", "Your single-use password reset token is '", 378 "--compactTextAfterToken", "'.", 379 }; 380 exampleMap.put(args, 381 INFO_DELIVER_PW_RESET_TOKEN_EXAMPLE.get()); 382 383 return exampleMap; 384 } 385 }