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