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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2015 ForgeRock AS. 025 */ 026 027package org.forgerock.opendj.examples; 028 029import static org.forgerock.opendj.ldap.TrustManagers.checkHostName; 030import static org.forgerock.util.Utils.closeSilently; 031import static org.forgerock.opendj.ldap.LDAPConnectionFactory.SSL_USE_STARTTLS; 032import static org.forgerock.opendj.ldap.LDAPConnectionFactory.SSL_CONTEXT; 033 034import org.forgerock.opendj.ldap.Connection; 035import org.forgerock.opendj.ldap.LDAPConnectionFactory; 036import org.forgerock.opendj.ldap.LdapException; 037import org.forgerock.opendj.ldap.ResultCode; 038import org.forgerock.opendj.ldap.SSLContextBuilder; 039import org.forgerock.opendj.ldap.TrustManagers; 040import org.forgerock.opendj.ldap.requests.Requests; 041import org.forgerock.opendj.ldap.responses.BindResult; 042import org.forgerock.opendj.ldap.responses.Result; 043import org.forgerock.util.AsyncFunction; 044import org.forgerock.util.Options; 045import org.forgerock.util.promise.ExceptionHandler; 046import org.forgerock.util.promise.Promise; 047import org.forgerock.util.promise.ResultHandler; 048 049import javax.net.ssl.SSLContext; 050import javax.net.ssl.TrustManager; 051import java.io.File; 052import java.security.GeneralSecurityException; 053import java.util.concurrent.CountDownLatch; 054 055/** 056 * An example client application which performs simple authentication to a 057 * directory server using the asynchronous APIs. 058 * <br> 059 * This example takes the following command line parameters: 060 * <ul> 061 * <li>host - host name of the directory server</li> 062 * <li>port - port number of the directory server</li> 063 * <li>bind-dn - DN of the user to authenticate</li> 064 * <li>bind-password - Password of the user to authenticate</li> 065 * <li>use-starttls - (Optional) connect with StartTLS</li> 066 * <li>use-ssl - (Optional) connect over SSL</li> 067 * </ul> 068 * The host, port, bind-dn, and bind-password arguments are required. 069 * The use-starttls and use-ssl arguments are optional and mutually exclusive. 070 * <br> 071 * If the server certificate is self-signed, 072 * or otherwise not trusted out-of-the-box, 073 * then set the trust store by using the JSSE system property 074 * {@code -Djavax.net.ssl.trustStore=/path/to/opendj/config/keystore} 075 * and the trust store password if necessary by using the JSSE system property 076 * {@code -Djavax.net.ssl.trustStorePassword=`cat /path/to/opendj/config/keystore.pin`}. 077 */ 078public final class SimpleAuthAsync { 079 /** Connection to the LDAP server. */ 080 private static Connection connection; 081 /** Result for the modify operation. */ 082 private static int resultCode; 083 /** Count down latch to wait for modify operation to complete. */ 084 private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1); 085 086 /** 087 * Authenticate to the directory either over LDAP, over LDAPS, or using 088 * StartTLS. 089 * 090 * @param args 091 * The command line arguments 092 */ 093 public static void main(final String[] args) { 094 parseArgs(args); 095 096 // Connect and bind. 097 // Pass getTrustAllOptions() instead of getTrustOptions() 098 // to the connection factory constructor 099 // if you want to trust all certificates blindly. 100 new LDAPConnectionFactory(host, port, getTrustOptions(host, keystore, storepass)) 101 .getConnectionAsync() 102 .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() { 103 @Override 104 public Promise<BindResult, LdapException> apply(Connection connection) 105 throws LdapException { 106 SimpleAuthAsync.connection = connection; 107 return connection.bindAsync( 108 Requests.newSimpleBindRequest(bindDN, bindPassword.toCharArray())); 109 } 110 }) 111 .thenOnResult(new ResultHandler<Result>() { 112 @Override 113 public void handleResult(Result result) { 114 resultCode = result.getResultCode().intValue(); 115 System.out.println("Authenticated as " + bindDN + "."); 116 COMPLETION_LATCH.countDown(); 117 } 118 }) 119 .thenOnException(new ExceptionHandler<LdapException>() { 120 @Override 121 public void handleException(LdapException e) { 122 System.err.println(e.getMessage()); 123 resultCode = e.getResult().getResultCode().intValue(); 124 COMPLETION_LATCH.countDown(); 125 } 126 }); 127 128 try { 129 COMPLETION_LATCH.await(); 130 } catch (InterruptedException e) { 131 System.err.println(e.getMessage()); 132 System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue()); 133 return; 134 } 135 136 closeSilently(connection); 137 System.exit(resultCode); 138 } 139 140 /** 141 * For StartTLS and SSL the connection factory needs SSL context options. 142 * In the general case, a trust manager in the SSL context serves 143 * to check server certificates, and a key manager handles client keys 144 * when the server checks certificates from our client. 145 * <br> 146 * This sample checks the server certificate, 147 * verifying that the certificate is currently valid, 148 * and that the host name of the server matches that of the certificate, 149 * based on a Java Key Store-format trust store. 150 * This sample does not present a client certificate. 151 * 152 * @param hostname Host name expected in the server certificate 153 * @param truststore Path to trust store file for the trust manager 154 * @param storepass Password for the trust store 155 * @return SSL context options if SSL or StartTLS is used. 156 */ 157 private static Options getTrustOptions(final String hostname, 158 final String truststore, 159 final String storepass) { 160 Options options = Options.defaultOptions(); 161 if (useSSL || useStartTLS) { 162 try { 163 TrustManager trustManager = TrustManagers.checkValidityDates( 164 checkHostName(hostname, 165 TrustManagers.checkUsingTrustStore( 166 truststore, storepass.toCharArray(), null))); 167 if (trustManager != null) { 168 SSLContext sslContext = new SSLContextBuilder() 169 .setTrustManager(trustManager).getSSLContext(); 170 options.set(SSL_CONTEXT, sslContext); 171 } 172 options.set(SSL_USE_STARTTLS, useStartTLS); 173 } catch (Exception e) { 174 System.err.println(e.getMessage()); 175 System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue()); 176 return null; 177 } 178 } 179 return options; 180 } 181 182 /** 183 * For StartTLS and SSL the connection factory needs SSL context options. In 184 * the general case, a trust manager in the SSL context serves to check 185 * server certificates, and a key manager handles client keys when the 186 * server checks certificates from our client. 187 * <br> 188 * OpenDJ directory server lets you install by default with a self-signed 189 * certificate that is not in the system trust store. To simplify this 190 * implementation trusts all server certificates. 191 * 192 * @return SSL context options to trust all certificates without checking. 193 */ 194 private static Options getTrustAllOptions() { 195 try { 196 Options options = Options.defaultOptions(); 197 SSLContext sslContext = 198 new SSLContextBuilder().setTrustManager(TrustManagers.trustAll()) 199 .getSSLContext(); 200 options.set(SSL_CONTEXT, sslContext); 201 options.set(SSL_USE_STARTTLS, useStartTLS); 202 return options; 203 } catch (GeneralSecurityException e) { 204 System.err.println(e.getMessage()); 205 System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue()); 206 return null; 207 } 208 } 209 210 private static String host; 211 private static int port; 212 private static String bindDN; 213 private static String bindPassword; 214 private static boolean useStartTLS; 215 private static boolean useSSL; 216 private static String keystore; 217 private static String storepass; 218 219 /** 220 * Parse command line arguments. 221 * 222 * @param args 223 * host port bind-dn bind-password [ use-starttls | use-ssl ] 224 */ 225 private static void parseArgs(String[] args) { 226 if (args.length < 4 || args.length > 5) { 227 giveUp(); 228 } 229 230 host = args[0]; 231 port = Integer.parseInt(args[1]); 232 bindDN = args[2]; 233 bindPassword = args[3]; 234 235 if (args.length == 5) { 236 if ("use-starttls".equals(args[4].toLowerCase())) { 237 useStartTLS = true; 238 useSSL = false; 239 } else if ("use-ssl".equals(args[4].toLowerCase())) { 240 useStartTLS = false; 241 useSSL = true; 242 } else { 243 giveUp(); 244 } 245 } 246 247 keystore = System.getProperty("javax.net.ssl.trustStore"); 248 storepass = System.getProperty("javax.net.ssl.trustStorePassword"); 249 if (keystore == null) { // Try to use Java's cacerts trust store. 250 keystore = System.getProperty("java.home") + File.separator 251 + "lib" + File.separator 252 + "security" + File.separator 253 + "cacerts"; 254 storepass = "changeit"; // Default password 255 } 256 } 257 258 private static void giveUp() { 259 printUsage(); 260 System.exit(1); 261 } 262 263 private static void printUsage() { 264 System.err.println("Usage: host port bind-dn bind-password [ use-starttls | use-ssl ]"); 265 System.err.println("\thost, port, bind-dn, and bind-password arguments are required."); 266 System.err.println("\tuse-starttls and use-ssl are optional and mutually exclusive."); 267 System.err.println("\tOptionally set javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword."); 268 } 269 270 private SimpleAuthAsync() { 271 // Not used. 272 } 273}