|
1 | 1 | package com.docusign.esign.client; |
2 | 2 |
|
3 | 3 | import com.fasterxml.jackson.annotation.*; |
| 4 | +import com.fasterxml.jackson.core.JsonParseException; |
4 | 5 | import com.fasterxml.jackson.databind.*; |
5 | 6 | import com.fasterxml.jackson.datatype.joda.*; |
6 | 7 | import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; |
7 | 8 |
|
8 | 9 | import com.sun.jersey.api.client.Client; |
9 | 10 | import com.sun.jersey.api.client.ClientResponse; |
10 | 11 | import com.sun.jersey.api.client.GenericType; |
| 12 | +import com.sun.jersey.api.client.WebResource; |
11 | 13 | import com.sun.jersey.api.client.config.DefaultClientConfig; |
12 | 14 | import com.sun.jersey.api.client.filter.LoggingFilter; |
| 15 | +import com.sun.jersey.core.util.MultivaluedMapImpl; |
13 | 16 | import com.sun.jersey.api.client.WebResource.Builder; |
14 | 17 |
|
15 | 18 | import com.sun.jersey.multipart.FormDataMultiPart; |
|
20 | 23 | import org.apache.oltu.oauth2.common.exception.OAuthSystemException; |
21 | 24 |
|
22 | 25 | import javax.ws.rs.core.Response.Status.Family; |
| 26 | +import javax.ws.rs.core.UriBuilder; |
23 | 27 | import javax.ws.rs.core.MediaType; |
| 28 | +import javax.ws.rs.core.MultivaluedMap; |
24 | 29 |
|
25 | 30 | import java.util.Collection; |
26 | | -import java.util.Collections; |
27 | 31 | import java.util.Map; |
28 | 32 | import java.util.Map.Entry; |
29 | 33 | import java.util.HashMap; |
30 | 34 | import java.util.List; |
31 | 35 | import java.util.ArrayList; |
32 | 36 | import java.util.Date; |
33 | 37 | import java.util.TimeZone; |
34 | | - |
35 | 38 | import java.net.URLEncoder; |
36 | | - |
37 | 39 | import java.io.File; |
| 40 | +import java.io.IOException; |
38 | 41 | import java.io.UnsupportedEncodingException; |
39 | 42 |
|
40 | 43 | import java.text.DateFormat; |
41 | 44 | import java.text.SimpleDateFormat; |
42 | 45 |
|
43 | 46 | import com.docusign.esign.client.auth.Authentication; |
44 | 47 | import com.docusign.esign.client.auth.HttpBasicAuth; |
| 48 | +import com.docusign.esign.client.auth.JWTUtils; |
45 | 49 | import com.docusign.esign.client.auth.ApiKeyAuth; |
46 | 50 | import com.docusign.esign.client.auth.OAuth; |
47 | 51 | import com.docusign.esign.client.auth.AccessTokenListener; |
@@ -252,14 +256,20 @@ public void updateAccessToken() { |
252 | 256 | * @param accessToken OAuth access token |
253 | 257 | * @param expiresIn Validity period in seconds |
254 | 258 | */ |
255 | | - public void setAccessToken(String accessToken, Long expiresIn) { |
| 259 | + public void setAccessToken(final String accessToken, Long expiresIn) { |
256 | 260 | for (Authentication auth : authentications.values()) { |
257 | 261 | if (auth instanceof OAuth) { |
258 | 262 | ((OAuth) auth).setAccessToken(accessToken, expiresIn); |
259 | 263 | return; |
260 | 264 | } |
261 | 265 | } |
262 | | - throw new RuntimeException("No OAuth2 authentication configured!"); |
| 266 | + addAuthorization("docusignAccessCode", new Authentication() { |
| 267 | + @Override |
| 268 | + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams) { |
| 269 | + headerParams.put("Authorization", "Bearer " + accessToken); |
| 270 | + } |
| 271 | + }); |
| 272 | + //throw new RuntimeException("No OAuth2 authentication configured!"); |
263 | 273 | } |
264 | 274 |
|
265 | 275 | /** |
@@ -396,6 +406,65 @@ public void registerAccessTokenListener(AccessTokenListener accessTokenListener) |
396 | 406 | } |
397 | 407 | } |
398 | 408 |
|
| 409 | + /** |
| 410 | + * Helper method to build the OAuth JWT grant uri (used once to get a user consent for impersonation) |
| 411 | + * @param clientId OAuth2 client ID |
| 412 | + * @param redirectURI OAuth2 redirect uri |
| 413 | + * @return the OAuth JWT grant uri as a String |
| 414 | + */ |
| 415 | + public String getJWTUri(String clientId, String redirectURI, String oAuthBasePath) { |
| 416 | + return UriBuilder.fromUri(oAuthBasePath) |
| 417 | + .scheme("https") |
| 418 | + .path("/oauth/auth") |
| 419 | + .queryParam("response_type", "code") |
| 420 | + .queryParam("scope", "signature%20impersonation") |
| 421 | + .queryParam("client_id", clientId) |
| 422 | + .queryParam("redirect_uri", redirectURI) |
| 423 | + .build().toString(); |
| 424 | + } |
| 425 | + |
| 426 | + /** |
| 427 | + * Configures the current instance of ApiClient with a fresh OAuth JWT access token from DocuSign |
| 428 | + * @param publicKeyFilename the filename of the RSA public key |
| 429 | + * @param privateKeyFilename the filename of the RSA private key |
| 430 | + * @param oAuthBasePath DocuSign OAuth base path (account-d.docusign.com for the developer sandbox |
| 431 | + and account.docusign.com for the production platform) |
| 432 | + * @param clientId DocuSign OAuth Client Id (AKA Integrator Key) |
| 433 | + * @param userId DocuSign user Id to be impersonated (This is a UUID) |
| 434 | + * @param expiresIn in seconds for the token time-to-live |
| 435 | + * @throws IOException if there is an issue with either the public or private file |
| 436 | + * @throws ApiException if there is an error while exchanging the JWT with an access token |
| 437 | + */ |
| 438 | + public void configureJWTAuthorizationFlow(String publicKeyFilename, String privateKeyFilename, String oAuthBasePath, String clientId, String userId, long expiresIn) throws IOException, ApiException { |
| 439 | + try { |
| 440 | + String assertion = JWTUtils.generateJWTAssertion(publicKeyFilename, privateKeyFilename, oAuthBasePath, clientId, userId, expiresIn); |
| 441 | + MultivaluedMap<String, String> form = new MultivaluedMapImpl(); |
| 442 | + form.add("assertion", assertion); |
| 443 | + form.add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"); |
| 444 | + |
| 445 | + Client client = Client.create(); |
| 446 | + WebResource webResource = client.resource("https://" + oAuthBasePath + "/oauth/token"); |
| 447 | + ClientResponse response = webResource |
| 448 | + .type(MediaType.APPLICATION_FORM_URLENCODED_TYPE) |
| 449 | + .post(ClientResponse.class, form); |
| 450 | + |
| 451 | + ObjectMapper mapper = new ObjectMapper(); |
| 452 | + JsonNode responseJson = mapper.readValue(response.getEntityInputStream(), JsonNode.class); |
| 453 | + if (!responseJson.has("access_token") || !responseJson.has("expires_in")) { |
| 454 | + throw new ApiException("Error while requesting an access token: " + responseJson); |
| 455 | + } |
| 456 | + String accessToken = responseJson.get("access_token").asText(); |
| 457 | + expiresIn = responseJson.get("expires_in").asLong(); |
| 458 | + setAccessToken(accessToken, expiresIn); |
| 459 | + } catch (JsonParseException e) { |
| 460 | + throw new ApiException("Error while parsing the response for the access token."); |
| 461 | + } catch (JsonMappingException e) { |
| 462 | + throw e; |
| 463 | + } catch (IOException e) { |
| 464 | + throw e; |
| 465 | + } |
| 466 | + } |
| 467 | + |
399 | 468 | /** |
400 | 469 | * Parse the given string into Date object. |
401 | 470 | */ |
|
0 commit comments