001/* 002 * Copyright (c) 2011-2017 Nexmo Inc 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in 012 * all copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 020 * THE SOFTWARE. 021 */ 022package com.nexmo.client; 023 024 025import com.nexmo.client.account.AccountClient; 026import com.nexmo.client.application.ApplicationClient; 027import com.nexmo.client.auth.*; 028import com.nexmo.client.conversion.ConversionClient; 029import com.nexmo.client.insight.InsightClient; 030import com.nexmo.client.numbers.NumbersClient; 031import com.nexmo.client.redact.RedactClient; 032import com.nexmo.client.sms.SmsClient; 033import com.nexmo.client.sns.SnsClient; 034import com.nexmo.client.verify.VerifyClient; 035import com.nexmo.client.voice.VoiceClient; 036import org.apache.http.client.HttpClient; 037 038import java.io.IOException; 039import java.nio.file.Files; 040import java.nio.file.Path; 041import java.nio.file.Paths; 042 043/** 044 * Top-level Nexmo API client object. 045 * <p> 046 * Construct an instance of this object with one or more {@link AuthMethod}s (providing all the authentication methods 047 * for the APIs you wish to use), and then call {@link #getVoiceClient()} to obtain a client for the Nexmo Voice API. 048 * <p> 049 * Currently this object only constructs and provides access to {@link VoiceClient}. In the future it will manage 050 * clients for all of the Nexmo APIs. 051 */ 052public class NexmoClient { 053 private AccountClient account; 054 private ApplicationClient application; 055 private InsightClient insight; 056 private NumbersClient numbers; 057 private SmsClient sms; 058 private VoiceClient voice; 059 private VerifyClient verify; 060 private SnsClient sns; 061 private ConversionClient conversion; 062 private RedactClient redact; 063 private HttpWrapper httpWrapper; 064 065 private NexmoClient(Builder builder) { 066 this.httpWrapper = new HttpWrapper(builder.httpConfig, builder.authCollection); 067 this.httpWrapper.setHttpClient(builder.httpClient); 068 069 this.account = new AccountClient(this.httpWrapper); 070 this.application = new ApplicationClient(this.httpWrapper); 071 this.insight = new InsightClient(this.httpWrapper); 072 this.numbers = new NumbersClient(this.httpWrapper); 073 this.verify = new VerifyClient(this.httpWrapper); 074 this.voice = new VoiceClient(this.httpWrapper); 075 this.sms = new SmsClient(this.httpWrapper); 076 this.sns = new SnsClient(this.httpWrapper); 077 this.conversion = new ConversionClient(this.httpWrapper); 078 this.redact = new RedactClient(this.httpWrapper); 079 } 080 081 public AccountClient getAccountClient() { 082 return this.account; 083 } 084 085 public ApplicationClient getApplicationClient() { 086 return this.application; 087 } 088 089 public InsightClient getInsightClient() { 090 return this.insight; 091 } 092 093 public NumbersClient getNumbersClient() { 094 return this.numbers; 095 } 096 097 public SmsClient getSmsClient() { 098 return this.sms; 099 } 100 101 public SnsClient getSnsClient() { 102 return this.sns; 103 } 104 105 public VerifyClient getVerifyClient() { 106 return this.verify; 107 } 108 109 public VoiceClient getVoiceClient() { 110 return this.voice; 111 } 112 113 public ConversionClient getConversionClient() { 114 return this.conversion; 115 } 116 117 public RedactClient getRedactClient() { 118 return this.redact; 119 } 120 121 /** 122 * Generate a JWT for the application the client has been configured with. 123 * 124 * @return A String containing the token data. 125 * 126 * @throws NexmoUnacceptableAuthException if no {@link JWTAuthMethod} is available 127 */ 128 public String generateJwt() throws NexmoUnacceptableAuthException { 129 JWTAuthMethod authMethod = this.httpWrapper.getAuthCollection().getAuth(JWTAuthMethod.class); 130 return authMethod.generateToken(); 131 } 132 133 /** 134 * @return The {@link HttpWrapper} 135 */ 136 HttpWrapper getHttpWrapper() { 137 return httpWrapper; 138 } 139 140 public static Builder builder() { 141 return new Builder(); 142 } 143 144 public static class Builder { 145 private AuthCollection authCollection; 146 private HttpConfig httpConfig = HttpConfig.defaultConfig(); 147 private HttpClient httpClient; 148 private String applicationId; 149 private String apiKey; 150 private String apiSecret; 151 private String signatureSecret; 152 private byte[] privateKeyContents; 153 154 /** 155 * @param httpConfig Configuration options for the {@link HttpWrapper} 156 * 157 * @return The {@link Builder} to keep building. 158 */ 159 public Builder httpConfig(HttpConfig httpConfig) { 160 this.httpConfig = httpConfig; 161 return this; 162 } 163 164 /** 165 * @param httpClient Custom implementation of {@link HttpClient}. 166 * 167 * @return The {@link Builder} to keep building. 168 */ 169 public Builder httpClient(HttpClient httpClient) { 170 this.httpClient = httpClient; 171 return this; 172 } 173 174 /** 175 * When setting an applicationId, it is also expected that the {@link #privateKeyContents} will also be set. 176 * 177 * @param applicationId Used to identify each application. 178 * 179 * @return The {@link Builder} to keep building. 180 */ 181 public Builder applicationId(String applicationId) { 182 this.applicationId = applicationId; 183 return this; 184 } 185 186 /** 187 * When setting an apiKey, it is also expected that {@link #apiSecret(String)} and/or {@link 188 * #signatureSecret(String)} will also be set. 189 * 190 * @param apiKey The API Key found in the dashboard for your account. 191 * 192 * @return The {@link Builder} to keep building. 193 */ 194 public Builder apiKey(String apiKey) { 195 this.apiKey = apiKey; 196 return this; 197 } 198 199 /** 200 * When setting an apiSecret, it is also expected that {@link #apiKey(String)} will also be set. 201 * 202 * @param apiSecret The API Secret found in the dashboard for your account. 203 * 204 * @return The {@link Builder} to keep building. 205 */ 206 public Builder apiSecret(String apiSecret) { 207 this.apiSecret = apiSecret; 208 return this; 209 } 210 211 /** 212 * When setting a signatureSecret, it is also expected that {@link #apiKey(String)} will also be set. 213 * 214 * @param signatureSecret The Signature Secret found in the dashboard for your account. 215 * 216 * @return The {@link Builder} to keep building. 217 */ 218 public Builder signatureSecret(String signatureSecret) { 219 this.signatureSecret = signatureSecret; 220 return this; 221 } 222 223 /** 224 * When setting the contents of your private key, it is also expected that {@link #applicationId(String)} will 225 * also be set. 226 * 227 * @param privateKeyContents The contents of your private key used for JWT generation. 228 * 229 * @return The {@link Builder} to keep building. 230 */ 231 public Builder privateKeyContents(byte[] privateKeyContents) { 232 this.privateKeyContents = privateKeyContents; 233 return this; 234 } 235 236 /** 237 * When setting the contents of your private key, it is also expected that {@link #applicationId(String)} will 238 * also be set. 239 * 240 * @param privateKeyContents The contents of your private key used for JWT generation. 241 * 242 * @return The {@link Builder} to keep building. 243 */ 244 public Builder privateKeyContents(String privateKeyContents) { 245 return privateKeyContents(privateKeyContents.getBytes()); 246 } 247 248 /** 249 * When setting the path of your private key, it is also expected that {@link #applicationId(String)} will also 250 * be set. 251 * 252 * @param privateKeyPath The path to your private key used for JWT generation. 253 * 254 * @return The {@link Builder} to keep building. 255 * 256 * @throws NexmoUnableToReadPrivateKeyException if the private key could not be read from the file system. 257 */ 258 public Builder privateKeyPath(Path privateKeyPath) throws NexmoUnableToReadPrivateKeyException { 259 try { 260 return privateKeyContents(Files.readAllBytes(privateKeyPath)); 261 } catch (IOException e) { 262 throw new NexmoUnableToReadPrivateKeyException("Unable to read private key at " + privateKeyPath, e); 263 } 264 } 265 266 /** 267 * When setting the path of your private key, it is also expected that {@link #applicationId(String)} will also 268 * be set. 269 * 270 * @param privateKeyPath The path to your private key used for JWT generation. 271 * 272 * @return The {@link Builder} to keep building. 273 * 274 * @throws NexmoUnableToReadPrivateKeyException if the private key could not be read from the file system. 275 */ 276 public Builder privateKeyPath(String privateKeyPath) throws NexmoUnableToReadPrivateKeyException { 277 return privateKeyPath(Paths.get(privateKeyPath)); 278 } 279 280 /** 281 * @return a new {@link NexmoClient} from the stored builder options. 282 * 283 * @throws NexmoClientCreationException if credentials aren't provided in a valid pairing or there were issues 284 * generating an {@link JWTAuthMethod} with the provided credentials. 285 */ 286 public NexmoClient build() { 287 this.authCollection = generateAuthCollection(this.applicationId, 288 this.apiKey, 289 this.apiSecret, 290 this.signatureSecret, 291 this.privateKeyContents 292 ); 293 return new NexmoClient(this); 294 } 295 296 private AuthCollection generateAuthCollection(String applicationId, String key, String secret, String signature, byte[] privateKeyContents) { 297 AuthCollection authMethods = new AuthCollection(); 298 299 try { 300 validateAuthParameters(applicationId, key, secret, signature, privateKeyContents); 301 } catch (IllegalStateException e) { 302 throw new NexmoClientCreationException("Failed to generate authentication methods.", e); 303 } 304 305 if (key != null && secret != null) { 306 authMethods.add(new TokenAuthMethod(key, secret)); 307 } 308 309 if (key != null && signature != null) { 310 authMethods.add(new SignatureAuthMethod(key, signature)); 311 } 312 313 if (applicationId != null && privateKeyContents != null) { 314 authMethods.add(new JWTAuthMethod(applicationId, privateKeyContents)); 315 } 316 317 return authMethods; 318 } 319 320 private void validateAuthParameters(String applicationId, String key, String secret, String signature, byte[] privateKeyContents) { 321 if (key != null && secret == null && signature == null) { 322 throw new IllegalStateException( 323 "You must provide an API secret or signature secret in addition to your API key."); 324 } 325 326 if (secret != null && key == null) { 327 throw new IllegalStateException("You must provide an API key in addition to your API secret."); 328 } 329 330 if (signature != null && key == null) { 331 throw new IllegalStateException("You must provide an API key in addition to your signature secret."); 332 } 333 334 if (applicationId == null && privateKeyContents != null) { 335 throw new IllegalStateException("You must provide an application ID in addition to your private key."); 336 } 337 338 if (applicationId != null && privateKeyContents == null) { 339 throw new IllegalStateException("You must provide a private key in addition to your application id."); 340 } 341 } 342 } 343}