From 779b17664896531c3755aab3eb8559fdba5ad6aa Mon Sep 17 00:00:00 2001 From: Christophe Bornet Date: Mon, 11 May 2020 09:18:47 +0200 Subject: [PATCH] Add full OAuth2 support to jersey2-experimental codegen (#6183) * Add full Oauth2 support to Jersey client * Regenerate jersey2-experimental sample * Regenerate all java clients --- .../main/resources/Java/StringUtil.mustache | 22 ++ .../jersey2-experimental/ApiClient.mustache | 285 ++++++++++++------ .../jersey2-experimental/auth/OAuth.mustache | 158 +++++++++- .../jersey2-experimental/pom.mustache | 7 + .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../java/jersey2-experimental/pom.xml | 5 + .../org/openapitools/client/ApiClient.java | 283 +++++++++++------ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/auth/OAuth.java | 158 +++++++++- .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ .../org/openapitools/client/StringUtil.java | 22 ++ 27 files changed, 1158 insertions(+), 200 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/Java/StringUtil.mustache b/modules/openapi-generator/src/main/resources/Java/StringUtil.mustache index ce52c82384..e3d5d6e908 100644 --- a/modules/openapi-generator/src/main/resources/Java/StringUtil.mustache +++ b/modules/openapi-generator/src/main/resources/Java/StringUtil.mustache @@ -2,6 +2,9 @@ package {{invokerPackage}}; +import java.util.Collection; +import java.util.Iterator; + {{>generatedAnnotation}} public class StringUtil { /** @@ -47,4 +50,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/ApiClient.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/ApiClient.mustache index 10a976f6d7..c0b67407f6 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/ApiClient.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/ApiClient.mustache @@ -11,6 +11,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +{{#hasOAuthMethods}} +import com.github.scribejava.core.model.OAuth2AccessToken; +{{/hasOAuthMethods}} import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.HttpUrlConnectorProvider; @@ -43,7 +46,6 @@ import java.util.List; import java.util.Arrays; import java.util.ArrayList; import java.util.Date; -import java.util.TimeZone; import java.net.URLEncoder; @@ -59,11 +61,10 @@ import {{invokerPackage}}.auth.HttpBasicAuth; import {{invokerPackage}}.auth.HttpBearerAuth; import {{invokerPackage}}.auth.HttpSignatureAuth; import {{invokerPackage}}.auth.ApiKeyAuth; -import {{invokerPackage}}.model.AbstractOpenApiSchema; - {{#hasOAuthMethods}} import {{invokerPackage}}.auth.OAuth; {{/hasOAuthMethods}} +import {{invokerPackage}}.model.AbstractOpenApiSchema; {{>generatedAnnotation}} public class ApiClient { @@ -178,7 +179,7 @@ public class ApiClient { authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}")); {{/isApiKey}} {{#isOAuth}} - authentications.put("{{name}}", new OAuth()); + authentications.put("{{name}}", new OAuth(basePath, "{{tokenUrl}}")); {{/isOAuth}} {{/authMethods}} // Prevent the authentications from being modified. @@ -191,6 +192,7 @@ public class ApiClient { /** * Gets the JSON instance to do JSON serialization and deserialization. + * * @return JSON */ public JSON getJSON() { @@ -212,6 +214,7 @@ public class ApiClient { public ApiClient setBasePath(String basePath) { this.basePath = basePath; + setOauthBasePath(basePath); return this; } @@ -221,6 +224,7 @@ public class ApiClient { public ApiClient setServers(List servers) { this.servers = servers; + updateBasePath(); return this; } @@ -230,6 +234,7 @@ public class ApiClient { public ApiClient setServerIndex(Integer serverIndex) { this.serverIndex = serverIndex; + updateBasePath(); return this; } @@ -239,11 +244,25 @@ public class ApiClient { public ApiClient setServerVariables(Map serverVariables) { this.serverVariables = serverVariables; + updateBasePath(); return this; } + private void updateBasePath() { + setBasePath(servers.get(serverIndex).URL(serverVariables)); + } + + private void setOauthBasePath(String basePath) { + for(Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setBasePath(basePath); + } + } + } + /** * Get authentications (key: authentication name, value: authentication). + * * @return Map of authentication object */ public Map getAuthentications() { @@ -262,13 +281,14 @@ public class ApiClient { /** * Helper method to set username for the first HTTP basic authentication. + * * @param username Username */ - public void setUsername(String username) { + public ApiClient setUsername(String username) { for (Authentication auth : authentications.values()) { if (auth instanceof HttpBasicAuth) { ((HttpBasicAuth) auth).setUsername(username); - return; + return this; } } throw new RuntimeException("No HTTP basic authentication configured!"); @@ -276,13 +296,14 @@ public class ApiClient { /** * Helper method to set password for the first HTTP basic authentication. + * * @param password Password */ - public void setPassword(String password) { + public ApiClient setPassword(String password) { for (Authentication auth : authentications.values()) { if (auth instanceof HttpBasicAuth) { ((HttpBasicAuth) auth).setPassword(password); - return; + return this; } } throw new RuntimeException("No HTTP basic authentication configured!"); @@ -290,13 +311,14 @@ public class ApiClient { /** * Helper method to set API key value for the first API key authentication. + * * @param apiKey API key */ - public void setApiKey(String apiKey) { + public ApiClient setApiKey(String apiKey) { for (Authentication auth : authentications.values()) { if (auth instanceof ApiKeyAuth) { ((ApiKeyAuth) auth).setApiKey(apiKey); - return; + return this; } } throw new RuntimeException("No API key authentication configured!"); @@ -307,29 +329,31 @@ public class ApiClient { * * @param secrets Hash map from authentication name to its secret. */ - public void configureApiKeys(HashMap secrets) { + public ApiClient configureApiKeys(HashMap secrets) { for (Map.Entry authEntry : authentications.entrySet()) { Authentication auth = authEntry.getValue(); if (auth instanceof ApiKeyAuth) { String name = authEntry.getKey(); // respect x-auth-id-alias property - name = authenticationLookup.getOrDefault(name, name); + name = authenticationLookup.containsKey(name) ? authenticationLookup.get(name) : name; if (secrets.containsKey(name)) { ((ApiKeyAuth) auth).setApiKey(secrets.get(name)); } } } + return this; } /** * Helper method to set API key prefix for the first API key authentication. + * * @param apiKeyPrefix API key prefix */ - public void setApiKeyPrefix(String apiKeyPrefix) { + public ApiClient setApiKeyPrefix(String apiKeyPrefix) { for (Authentication auth : authentications.values()) { if (auth instanceof ApiKeyAuth) { ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); - return; + return this; } } throw new RuntimeException("No API key authentication configured!"); @@ -337,28 +361,92 @@ public class ApiClient { /** * Helper method to set bearer token for the first Bearer authentication. + * * @param bearerToken Bearer token */ - public void setBearerToken(String bearerToken) { + public ApiClient setBearerToken(String bearerToken) { for (Authentication auth : authentications.values()) { if (auth instanceof HttpBearerAuth) { ((HttpBearerAuth) auth).setBearerToken(bearerToken); - return; + return this; } } throw new RuntimeException("No Bearer authentication configured!"); } + {{#hasOAuthMethods}} /** * Helper method to set access token for the first OAuth2 authentication. * @param accessToken Access token */ - public void setAccessToken(String accessToken) { + public ApiClient setAccessToken(String accessToken) { for (Authentication auth : authentications.values()) { if (auth instanceof OAuth) { ((OAuth) auth).setAccessToken(accessToken); - return; + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set the credentials for the first OAuth2 authentication. + * + * @param clientId the client ID + * @param clientSecret the client secret + */ + public ApiClient setOauthCredentials(String clientId, String clientSecret) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setCredentials(clientId, clientSecret); + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set the password flow for the first OAuth2 authentication. + * + * @param username the user name + * @param password the user password + */ + public ApiClient setOauthPasswordFlow(String username, String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).usePasswordFlow(username, password); + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set the authorization code flow for the first OAuth2 authentication. + * + * @param code the authorization code + */ + public ApiClient setOauthAuthorizationCodeFlow(String code) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).useAuthorizationCodeFlow(code); + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set the scopes for the first OAuth2 authentication. + * + * @param scope the oauth scope + */ + public ApiClient setOauthScope(String scope) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setScope(scope); + return this; } } throw new RuntimeException("No OAuth2 authentication configured!"); @@ -787,9 +875,9 @@ public class ApiClient { } if (matchCounter > 1 && "oneOf".equals(schema.getSchemaType())) {// more than 1 match for oneOf - throw new ApiException("Response body is invalid as it matches more than one schema (" + String.join(", ", matchSchemas) + ") defined in the oneOf model: " + schema.getClass().getName()); + throw new ApiException("Response body is invalid as it matches more than one schema (" + StringUtil.join(matchSchemas, ", ") + ") defined in the oneOf model: " + schema.getClass().getName()); } else if (matchCounter == 0) { // fail to match any in oneOf/anyOf schemas - throw new ApiException("Response body is invalid as it doens't match any schemas (" + String.join(", ", schema.getSchemas().keySet()) + ") defined in the oneOf/anyOf model: " + schema.getClass().getName()); + throw new ApiException("Response body is invalid as it doens't match any schemas (" + StringUtil.join(schema.getSchemas().keySet(), ", ") + ") defined in the oneOf/anyOf model: " + schema.getClass().getName()); } else { // only one matched schema.setActualInstance(result); return schema; @@ -910,29 +998,35 @@ public class ApiClient { * @return The response body in type of string * @throws ApiException API exception */ - public ApiResponse invokeAPI(String operation, String path, String method, List queryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String accept, String contentType, String[] authNames, GenericType returnType, AbstractOpenApiSchema schema) throws ApiException { + public ApiResponse invokeAPI( + String operation, + String path, + String method, + List queryParams, + Object body, + Map headerParams, + Map cookieParams, + Map formParams, + String accept, + String contentType, + String[] authNames, + GenericType returnType, + AbstractOpenApiSchema schema) + throws ApiException { // Not using `.target(targetURL).path(path)` below, // to support (constant) query string in `path`, e.g. "/posts?draft=1" String targetURL; - if (serverIndex != null) { - Integer index; - List serverConfigurations; - Map variables; - - if (operationServers.containsKey(operation)) { - index = operationServerIndex.getOrDefault(operation, serverIndex); - variables = operationServerVariables.getOrDefault(operation, serverVariables); - serverConfigurations = operationServers.get(operation); - } else { - index = serverIndex; - variables = serverVariables; - serverConfigurations = servers; - } + if (operationServers.containsKey(operation)) { + Integer index = operationServerIndex.containsKey(operation) ? operationServerIndex.get(operation) : serverIndex; + Map variables = operationServerVariables.containsKey(operation) ? + operationServerVariables.get(operation) : serverVariables; + List serverConfigurations = operationServers.get(operation); if (index < 0 || index >= serverConfigurations.size()) { - throw new ArrayIndexOutOfBoundsException(String.format( - "Invalid index %d when selecting the host settings. Must be less than %d", index, serverConfigurations.size() - )); + throw new ArrayIndexOutOfBoundsException( + String.format( + "Invalid index %d when selecting the host settings. Must be less than %d", + index, serverConfigurations.size())); } targetURL = serverConfigurations.get(index).URL(variables) + path; } else { @@ -950,13 +1044,6 @@ public class ApiClient { Invocation.Builder invocationBuilder = target.request().accept(accept); - for (Entry entry : headerParams.entrySet()) { - String value = entry.getValue(); - if (value != null) { - invocationBuilder = invocationBuilder.header(entry.getKey(), value); - } - } - for (Entry entry : cookieParams.entrySet()) { String value = entry.getValue(); if (value != null) { @@ -971,63 +1058,63 @@ public class ApiClient { } } - for (Entry entry : defaultHeaderMap.entrySet()) { - String key = entry.getKey(); - if (!headerParams.containsKey(key)) { - String value = entry.getValue(); - if (value != null) { - invocationBuilder = invocationBuilder.header(key, value); - } - } - } - Entity entity = serialize(body, formParams, contentType); // put all headers in one place - Map allHeaderParams = new HashMap<>(); - allHeaderParams.putAll(defaultHeaderMap); + Map allHeaderParams = new HashMap<>(defaultHeaderMap); allHeaderParams.putAll(headerParams); - + // update different parameters (e.g. headers) for authentication - updateParamsForAuth(authNames, queryParams, allHeaderParams, cookieParams, serializeToString(body, formParams, contentType), method, target.getUri()); + updateParamsForAuth( + authNames, + queryParams, + allHeaderParams, + cookieParams, + serializeToString(body, formParams, contentType), + method, + target.getUri()); + + for (Entry entry : allHeaderParams.entrySet()) { + String value = entry.getValue(); + if (value != null) { + invocationBuilder = invocationBuilder.header(entry.getKey(), value); + } + } Response response = null; try { - if ("GET".equals(method)) { - response = invocationBuilder.get(); - } else if ("POST".equals(method)) { - response = invocationBuilder.post(entity); - } else if ("PUT".equals(method)) { - response = invocationBuilder.put(entity); - } else if ("DELETE".equals(method)) { - response = invocationBuilder.method("DELETE", entity); - } else if ("PATCH".equals(method)) { - response = invocationBuilder.method("PATCH", entity); - } else if ("HEAD".equals(method)) { - response = invocationBuilder.head(); - } else if ("OPTIONS".equals(method)) { - response = invocationBuilder.options(); - } else if ("TRACE".equals(method)) { - response = invocationBuilder.trace(); - } else { - throw new ApiException(500, "unknown method type " + method); + response = sendRequest(method, invocationBuilder, entity); + + // If OAuth is used and a status 401 is received, renew the access token and retry the request + if (response.getStatusInfo() == Status.UNAUTHORIZED) { + for (String authName : authNames) { + Authentication authentication = authentications.get(authName); + if (authentication instanceof OAuth) { + OAuth2AccessToken accessToken = ((OAuth) authentication).renewAccessToken(); + if (accessToken != null) { + invocationBuilder.header("Authorization", null); + invocationBuilder.header("Authorization", "Bearer " + accessToken.getAccessToken()); + response = sendRequest(method, invocationBuilder, entity); + } + break; + } + } } int statusCode = response.getStatusInfo().getStatusCode(); Map> responseHeaders = buildResponseHeaders(response); - if (response.getStatus() == Status.NO_CONTENT.getStatusCode()) { - return new ApiResponse<{{#supportJava6}}T{{/supportJava6}}>(statusCode, responseHeaders); + if (response.getStatusInfo() == Status.NO_CONTENT) { + return new ApiResponse(statusCode, responseHeaders); } else if (response.getStatusInfo().getFamily() == Status.Family.SUCCESSFUL) { - if (returnType == null) - return new ApiResponse<{{#supportJava6}}T{{/supportJava6}}>(statusCode, responseHeaders); - else - if (schema == null) { - return new ApiResponse<>(statusCode, responseHeaders, deserialize(response, returnType)); - } else { // oneOf/anyOf - return new ApiResponse<>(statusCode, responseHeaders, (T)deserializeSchemas(response, schema)); - } + if (returnType == null) return new ApiResponse(statusCode, responseHeaders); + else if (schema == null) { + return new ApiResponse(statusCode, responseHeaders, deserialize(response, returnType)); + } else { // oneOf/anyOf + return new ApiResponse( + statusCode, responseHeaders, (T) deserializeSchemas(response, schema)); + } } else { String message = "error"; String respBody = null; @@ -1040,20 +1127,34 @@ public class ApiClient { } } throw new ApiException( - response.getStatus(), - message, - buildResponseHeaders(response), - respBody); + response.getStatus(), message, buildResponseHeaders(response), respBody); } } finally { try { response.close(); } catch (Exception e) { - // it's not critical, since the response object is local in method invokeAPI; that's fine, just continue + // it's not critical, since the response object is local in method invokeAPI; that's fine, + // just continue } } } + private Response sendRequest(String method, Invocation.Builder invocationBuilder, Entity entity) { + Response response; + if ("POST".equals(method)) { + response = invocationBuilder.post(entity); + } else if ("PUT".equals(method)) { + response = invocationBuilder.put(entity); + } else if ("DELETE".equals(method)) { + response = invocationBuilder.method("DELETE", entity); + } else if ("PATCH".equals(method)) { + response = invocationBuilder.method("PATCH", entity); + } else { + response = invocationBuilder.method(method); + } + return response; + } + /** * @deprecated Add qualified name of the operation as a first parameter. */ diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/auth/OAuth.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/auth/OAuth.mustache index 8622798ad3..d18b41b0e4 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/auth/OAuth.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/auth/OAuth.mustache @@ -4,27 +4,169 @@ package {{invokerPackage}}.auth; import {{invokerPackage}}.Pair; import {{invokerPackage}}.ApiException; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.oauth.OAuth20Service; +import javax.ws.rs.core.UriBuilder; +import java.io.IOException; +import java.net.MalformedURLException; import java.net.URI; -import java.util.Map; import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; {{>generatedAnnotation}} public class OAuth implements Authentication { - private String accessToken; + private static final Logger log = Logger.getLogger(OAuth.class.getName()); - public String getAccessToken() { - return accessToken; + private String tokenUrl; + private String absoluteTokenUrl; + private OAuthFlow flow = OAuthFlow.application; + private OAuth20Service service; + private DefaultApi20 authApi; + private String scope; + private String username; + private String password; + private String code; + private volatile OAuth2AccessToken accessToken; + + public OAuth(String basePath, String tokenUrl) { + this.tokenUrl = tokenUrl; + this.absoluteTokenUrl = createAbsoluteTokenUrl(basePath, tokenUrl); + authApi = new DefaultApi20() { + @Override + public String getAccessTokenEndpoint() { + return absoluteTokenUrl; + } + + @Override + protected String getAuthorizationBaseUrl() { + throw new UnsupportedOperationException("Shouldn't get there !"); + } + }; } - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; + private static String createAbsoluteTokenUrl(String basePath, String tokenUrl) { + if (!URI.create(tokenUrl).isAbsolute()) { + try { + return UriBuilder.fromPath(basePath).path(tokenUrl).build().toURL().toString(); + } catch (MalformedURLException e) { + log.log(Level.SEVERE, "Couldn't create absolute token URL", e); + } + } + return tokenUrl; } @Override - public void applyToParams(List queryParams, Map headerParams, Map cookieParams, String payload, String method, URI uri) throws ApiException { + public void applyToParams( + List queryParams, + Map headerParams, + Map cookieParams, + String payload, + String method, + URI uri) + throws ApiException { + + if (accessToken == null) { + obtainAccessToken(null); + } if (accessToken != null) { - headerParams.put("Authorization", "Bearer " + accessToken); + headerParams.put("Authorization", "Bearer " + accessToken.getAccessToken()); } } + + public OAuth2AccessToken renewAccessToken() throws ApiException { + String refreshToken = null; + if (accessToken != null) { + refreshToken = accessToken.getRefreshToken(); + accessToken = null; + } + return obtainAccessToken(refreshToken); + } + + public synchronized OAuth2AccessToken obtainAccessToken(String refreshToken) throws ApiException { + if (service == null) { + return null; + } + try { + if (refreshToken != null) { + return service.refreshAccessToken(refreshToken); + } + } catch (OAuthException | InterruptedException | ExecutionException | IOException e) { + log.log(Level.FINE, "Refreshing the access token using the refresh token failed", e); + } + try { + switch (flow) { + case password: + if (username != null && password != null) { + accessToken = service.getAccessTokenPasswordGrant(username, password, scope); + } + break; + case accessCode: + if (code != null) { + accessToken = service.getAccessToken(code); + code = null; + } + break; + case application: + accessToken = service.getAccessTokenClientCredentialsGrant(scope); + } + } catch (OAuthException | InterruptedException | ExecutionException | IOException e) { + throw new ApiException(e); + } + return accessToken; + } + + public OAuth2AccessToken getAccessToken() { + return accessToken; + } + + public OAuth setAccessToken(OAuth2AccessToken accessToken) { + this.accessToken = accessToken; + return this; + } + + public OAuth setAccessToken(String accessToken) { + this.accessToken = new OAuth2AccessToken(accessToken); + return this; + } + + public OAuth setScope(String scope) { + this.scope = scope; + return this; + } + + public OAuth setCredentials(String clientId, String clientSecret) { + service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .build(authApi); + return this; + } + + public OAuth usePasswordFlow(String username, String password) { + this.flow = OAuthFlow.password; + this.username = username; + this.password = password; + return this; + } + + public OAuth useAuthorizationCodeFlow(String code) { + this.flow = OAuthFlow.accessCode; + this.code = code; + return this; + } + + public OAuth setFlow(OAuthFlow flow) { + this.flow = flow; + return this; + } + + public void setBasePath(String basePath) { + this.absoluteTokenUrl = createAbsoluteTokenUrl(basePath, tokenUrl); + } } diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/pom.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/pom.mustache index a71ab26386..ce5855067d 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/pom.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/pom.mustache @@ -343,6 +343,13 @@ tomitribe-http-signatures ${http-signature-version} + {{#hasOAuthMethods}} + + com.github.scribejava + scribejava-apis + 6.9.0 + + {{/hasOAuthMethods}} {{#useBeanValidation}} diff --git a/samples/client/petstore/java/feign/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/feign/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/feign/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/feign/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/feign10x/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/feign10x/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/feign10x/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/feign10x/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/google-api-client/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/google-api-client/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/google-api-client/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/google-api-client/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/jersey1/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/jersey1/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/jersey1/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/jersey1/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/jersey2-experimental/pom.xml b/samples/client/petstore/java/jersey2-experimental/pom.xml index 84d5d5ce18..72a26531e7 100644 --- a/samples/client/petstore/java/jersey2-experimental/pom.xml +++ b/samples/client/petstore/java/jersey2-experimental/pom.xml @@ -284,6 +284,11 @@ tomitribe-http-signatures ${http-signature-version} + + com.github.scribejava + scribejava-apis + 6.9.0 + junit diff --git a/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/ApiClient.java index ea977a7db5..e5df2bfcac 100644 --- a/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/ApiClient.java @@ -11,6 +11,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import com.github.scribejava.core.model.OAuth2AccessToken; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.HttpUrlConnectorProvider; @@ -37,7 +38,6 @@ import java.util.List; import java.util.Arrays; import java.util.ArrayList; import java.util.Date; -import java.util.TimeZone; import java.net.URLEncoder; @@ -53,9 +53,8 @@ import org.openapitools.client.auth.HttpBasicAuth; import org.openapitools.client.auth.HttpBearerAuth; import org.openapitools.client.auth.HttpSignatureAuth; import org.openapitools.client.auth.ApiKeyAuth; -import org.openapitools.client.model.AbstractOpenApiSchema; - import org.openapitools.client.auth.OAuth; +import org.openapitools.client.model.AbstractOpenApiSchema; public class ApiClient { @@ -168,7 +167,7 @@ public class ApiClient { authentications.put("bearer_test", new HttpBearerAuth("bearer")); authentications.put("http_basic_test", new HttpBasicAuth()); authentications.put("http_signature_test", new HttpSignatureAuth("http_signature_test", null, null)); - authentications.put("petstore_auth", new OAuth()); + authentications.put("petstore_auth", new OAuth(basePath, "")); // Prevent the authentications from being modified. authentications = Collections.unmodifiableMap(authentications); @@ -178,6 +177,7 @@ public class ApiClient { /** * Gets the JSON instance to do JSON serialization and deserialization. + * * @return JSON */ public JSON getJSON() { @@ -199,6 +199,7 @@ public class ApiClient { public ApiClient setBasePath(String basePath) { this.basePath = basePath; + setOauthBasePath(basePath); return this; } @@ -208,6 +209,7 @@ public class ApiClient { public ApiClient setServers(List servers) { this.servers = servers; + updateBasePath(); return this; } @@ -217,6 +219,7 @@ public class ApiClient { public ApiClient setServerIndex(Integer serverIndex) { this.serverIndex = serverIndex; + updateBasePath(); return this; } @@ -226,11 +229,25 @@ public class ApiClient { public ApiClient setServerVariables(Map serverVariables) { this.serverVariables = serverVariables; + updateBasePath(); return this; } + private void updateBasePath() { + setBasePath(servers.get(serverIndex).URL(serverVariables)); + } + + private void setOauthBasePath(String basePath) { + for(Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setBasePath(basePath); + } + } + } + /** * Get authentications (key: authentication name, value: authentication). + * * @return Map of authentication object */ public Map getAuthentications() { @@ -249,13 +266,14 @@ public class ApiClient { /** * Helper method to set username for the first HTTP basic authentication. + * * @param username Username */ - public void setUsername(String username) { + public ApiClient setUsername(String username) { for (Authentication auth : authentications.values()) { if (auth instanceof HttpBasicAuth) { ((HttpBasicAuth) auth).setUsername(username); - return; + return this; } } throw new RuntimeException("No HTTP basic authentication configured!"); @@ -263,13 +281,14 @@ public class ApiClient { /** * Helper method to set password for the first HTTP basic authentication. + * * @param password Password */ - public void setPassword(String password) { + public ApiClient setPassword(String password) { for (Authentication auth : authentications.values()) { if (auth instanceof HttpBasicAuth) { ((HttpBasicAuth) auth).setPassword(password); - return; + return this; } } throw new RuntimeException("No HTTP basic authentication configured!"); @@ -277,13 +296,14 @@ public class ApiClient { /** * Helper method to set API key value for the first API key authentication. + * * @param apiKey API key */ - public void setApiKey(String apiKey) { + public ApiClient setApiKey(String apiKey) { for (Authentication auth : authentications.values()) { if (auth instanceof ApiKeyAuth) { ((ApiKeyAuth) auth).setApiKey(apiKey); - return; + return this; } } throw new RuntimeException("No API key authentication configured!"); @@ -294,29 +314,31 @@ public class ApiClient { * * @param secrets Hash map from authentication name to its secret. */ - public void configureApiKeys(HashMap secrets) { + public ApiClient configureApiKeys(HashMap secrets) { for (Map.Entry authEntry : authentications.entrySet()) { Authentication auth = authEntry.getValue(); if (auth instanceof ApiKeyAuth) { String name = authEntry.getKey(); // respect x-auth-id-alias property - name = authenticationLookup.getOrDefault(name, name); + name = authenticationLookup.containsKey(name) ? authenticationLookup.get(name) : name; if (secrets.containsKey(name)) { ((ApiKeyAuth) auth).setApiKey(secrets.get(name)); } } } + return this; } /** * Helper method to set API key prefix for the first API key authentication. + * * @param apiKeyPrefix API key prefix */ - public void setApiKeyPrefix(String apiKeyPrefix) { + public ApiClient setApiKeyPrefix(String apiKeyPrefix) { for (Authentication auth : authentications.values()) { if (auth instanceof ApiKeyAuth) { ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); - return; + return this; } } throw new RuntimeException("No API key authentication configured!"); @@ -324,27 +346,91 @@ public class ApiClient { /** * Helper method to set bearer token for the first Bearer authentication. + * * @param bearerToken Bearer token */ - public void setBearerToken(String bearerToken) { + public ApiClient setBearerToken(String bearerToken) { for (Authentication auth : authentications.values()) { if (auth instanceof HttpBearerAuth) { ((HttpBearerAuth) auth).setBearerToken(bearerToken); - return; + return this; } } throw new RuntimeException("No Bearer authentication configured!"); } + /** * Helper method to set access token for the first OAuth2 authentication. * @param accessToken Access token */ - public void setAccessToken(String accessToken) { + public ApiClient setAccessToken(String accessToken) { for (Authentication auth : authentications.values()) { if (auth instanceof OAuth) { ((OAuth) auth).setAccessToken(accessToken); - return; + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set the credentials for the first OAuth2 authentication. + * + * @param clientId the client ID + * @param clientSecret the client secret + */ + public ApiClient setOauthCredentials(String clientId, String clientSecret) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setCredentials(clientId, clientSecret); + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set the password flow for the first OAuth2 authentication. + * + * @param username the user name + * @param password the user password + */ + public ApiClient setOauthPasswordFlow(String username, String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).usePasswordFlow(username, password); + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set the authorization code flow for the first OAuth2 authentication. + * + * @param code the authorization code + */ + public ApiClient setOauthAuthorizationCodeFlow(String code) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).useAuthorizationCodeFlow(code); + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set the scopes for the first OAuth2 authentication. + * + * @param scope the oauth scope + */ + public ApiClient setOauthScope(String scope) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setScope(scope); + return this; } } throw new RuntimeException("No OAuth2 authentication configured!"); @@ -772,9 +858,9 @@ public class ApiClient { } if (matchCounter > 1 && "oneOf".equals(schema.getSchemaType())) {// more than 1 match for oneOf - throw new ApiException("Response body is invalid as it matches more than one schema (" + String.join(", ", matchSchemas) + ") defined in the oneOf model: " + schema.getClass().getName()); + throw new ApiException("Response body is invalid as it matches more than one schema (" + StringUtil.join(matchSchemas, ", ") + ") defined in the oneOf model: " + schema.getClass().getName()); } else if (matchCounter == 0) { // fail to match any in oneOf/anyOf schemas - throw new ApiException("Response body is invalid as it doens't match any schemas (" + String.join(", ", schema.getSchemas().keySet()) + ") defined in the oneOf/anyOf model: " + schema.getClass().getName()); + throw new ApiException("Response body is invalid as it doens't match any schemas (" + StringUtil.join(schema.getSchemas().keySet(), ", ") + ") defined in the oneOf/anyOf model: " + schema.getClass().getName()); } else { // only one matched schema.setActualInstance(result); return schema; @@ -889,29 +975,35 @@ public class ApiClient { * @return The response body in type of string * @throws ApiException API exception */ - public ApiResponse invokeAPI(String operation, String path, String method, List queryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String accept, String contentType, String[] authNames, GenericType returnType, AbstractOpenApiSchema schema) throws ApiException { + public ApiResponse invokeAPI( + String operation, + String path, + String method, + List queryParams, + Object body, + Map headerParams, + Map cookieParams, + Map formParams, + String accept, + String contentType, + String[] authNames, + GenericType returnType, + AbstractOpenApiSchema schema) + throws ApiException { // Not using `.target(targetURL).path(path)` below, // to support (constant) query string in `path`, e.g. "/posts?draft=1" String targetURL; - if (serverIndex != null) { - Integer index; - List serverConfigurations; - Map variables; - - if (operationServers.containsKey(operation)) { - index = operationServerIndex.getOrDefault(operation, serverIndex); - variables = operationServerVariables.getOrDefault(operation, serverVariables); - serverConfigurations = operationServers.get(operation); - } else { - index = serverIndex; - variables = serverVariables; - serverConfigurations = servers; - } + if (operationServers.containsKey(operation)) { + Integer index = operationServerIndex.containsKey(operation) ? operationServerIndex.get(operation) : serverIndex; + Map variables = operationServerVariables.containsKey(operation) ? + operationServerVariables.get(operation) : serverVariables; + List serverConfigurations = operationServers.get(operation); if (index < 0 || index >= serverConfigurations.size()) { - throw new ArrayIndexOutOfBoundsException(String.format( - "Invalid index %d when selecting the host settings. Must be less than %d", index, serverConfigurations.size() - )); + throw new ArrayIndexOutOfBoundsException( + String.format( + "Invalid index %d when selecting the host settings. Must be less than %d", + index, serverConfigurations.size())); } targetURL = serverConfigurations.get(index).URL(variables) + path; } else { @@ -929,13 +1021,6 @@ public class ApiClient { Invocation.Builder invocationBuilder = target.request().accept(accept); - for (Entry entry : headerParams.entrySet()) { - String value = entry.getValue(); - if (value != null) { - invocationBuilder = invocationBuilder.header(entry.getKey(), value); - } - } - for (Entry entry : cookieParams.entrySet()) { String value = entry.getValue(); if (value != null) { @@ -950,63 +1035,63 @@ public class ApiClient { } } - for (Entry entry : defaultHeaderMap.entrySet()) { - String key = entry.getKey(); - if (!headerParams.containsKey(key)) { - String value = entry.getValue(); - if (value != null) { - invocationBuilder = invocationBuilder.header(key, value); - } - } - } - Entity entity = serialize(body, formParams, contentType); // put all headers in one place - Map allHeaderParams = new HashMap<>(); - allHeaderParams.putAll(defaultHeaderMap); + Map allHeaderParams = new HashMap<>(defaultHeaderMap); allHeaderParams.putAll(headerParams); - + // update different parameters (e.g. headers) for authentication - updateParamsForAuth(authNames, queryParams, allHeaderParams, cookieParams, serializeToString(body, formParams, contentType), method, target.getUri()); + updateParamsForAuth( + authNames, + queryParams, + allHeaderParams, + cookieParams, + serializeToString(body, formParams, contentType), + method, + target.getUri()); + + for (Entry entry : allHeaderParams.entrySet()) { + String value = entry.getValue(); + if (value != null) { + invocationBuilder = invocationBuilder.header(entry.getKey(), value); + } + } Response response = null; try { - if ("GET".equals(method)) { - response = invocationBuilder.get(); - } else if ("POST".equals(method)) { - response = invocationBuilder.post(entity); - } else if ("PUT".equals(method)) { - response = invocationBuilder.put(entity); - } else if ("DELETE".equals(method)) { - response = invocationBuilder.method("DELETE", entity); - } else if ("PATCH".equals(method)) { - response = invocationBuilder.method("PATCH", entity); - } else if ("HEAD".equals(method)) { - response = invocationBuilder.head(); - } else if ("OPTIONS".equals(method)) { - response = invocationBuilder.options(); - } else if ("TRACE".equals(method)) { - response = invocationBuilder.trace(); - } else { - throw new ApiException(500, "unknown method type " + method); + response = sendRequest(method, invocationBuilder, entity); + + // If OAuth is used and a status 401 is received, renew the access token and retry the request + if (response.getStatusInfo() == Status.UNAUTHORIZED) { + for (String authName : authNames) { + Authentication authentication = authentications.get(authName); + if (authentication instanceof OAuth) { + OAuth2AccessToken accessToken = ((OAuth) authentication).renewAccessToken(); + if (accessToken != null) { + invocationBuilder.header("Authorization", null); + invocationBuilder.header("Authorization", "Bearer " + accessToken.getAccessToken()); + response = sendRequest(method, invocationBuilder, entity); + } + break; + } + } } int statusCode = response.getStatusInfo().getStatusCode(); Map> responseHeaders = buildResponseHeaders(response); - if (response.getStatus() == Status.NO_CONTENT.getStatusCode()) { - return new ApiResponse<>(statusCode, responseHeaders); + if (response.getStatusInfo() == Status.NO_CONTENT) { + return new ApiResponse(statusCode, responseHeaders); } else if (response.getStatusInfo().getFamily() == Status.Family.SUCCESSFUL) { - if (returnType == null) - return new ApiResponse<>(statusCode, responseHeaders); - else - if (schema == null) { - return new ApiResponse<>(statusCode, responseHeaders, deserialize(response, returnType)); - } else { // oneOf/anyOf - return new ApiResponse<>(statusCode, responseHeaders, (T)deserializeSchemas(response, schema)); - } + if (returnType == null) return new ApiResponse(statusCode, responseHeaders); + else if (schema == null) { + return new ApiResponse(statusCode, responseHeaders, deserialize(response, returnType)); + } else { // oneOf/anyOf + return new ApiResponse( + statusCode, responseHeaders, (T) deserializeSchemas(response, schema)); + } } else { String message = "error"; String respBody = null; @@ -1019,20 +1104,34 @@ public class ApiClient { } } throw new ApiException( - response.getStatus(), - message, - buildResponseHeaders(response), - respBody); + response.getStatus(), message, buildResponseHeaders(response), respBody); } } finally { try { response.close(); } catch (Exception e) { - // it's not critical, since the response object is local in method invokeAPI; that's fine, just continue + // it's not critical, since the response object is local in method invokeAPI; that's fine, + // just continue } } } + private Response sendRequest(String method, Invocation.Builder invocationBuilder, Entity entity) { + Response response; + if ("POST".equals(method)) { + response = invocationBuilder.post(entity); + } else if ("PUT".equals(method)) { + response = invocationBuilder.put(entity); + } else if ("DELETE".equals(method)) { + response = invocationBuilder.method("DELETE", entity); + } else if ("PATCH".equals(method)) { + response = invocationBuilder.method("PATCH", entity); + } else { + response = invocationBuilder.method(method); + } + return response; + } + /** * @deprecated Add qualified name of the operation as a first parameter. */ diff --git a/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/auth/OAuth.java b/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/auth/OAuth.java index 77061fb4ac..5751094fa1 100644 --- a/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/auth/OAuth.java +++ b/samples/client/petstore/java/jersey2-experimental/src/main/java/org/openapitools/client/auth/OAuth.java @@ -15,27 +15,169 @@ package org.openapitools.client.auth; import org.openapitools.client.Pair; import org.openapitools.client.ApiException; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.oauth.OAuth20Service; +import javax.ws.rs.core.UriBuilder; +import java.io.IOException; +import java.net.MalformedURLException; import java.net.URI; -import java.util.Map; import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; public class OAuth implements Authentication { - private String accessToken; + private static final Logger log = Logger.getLogger(OAuth.class.getName()); - public String getAccessToken() { - return accessToken; + private String tokenUrl; + private String absoluteTokenUrl; + private OAuthFlow flow = OAuthFlow.application; + private OAuth20Service service; + private DefaultApi20 authApi; + private String scope; + private String username; + private String password; + private String code; + private volatile OAuth2AccessToken accessToken; + + public OAuth(String basePath, String tokenUrl) { + this.tokenUrl = tokenUrl; + this.absoluteTokenUrl = createAbsoluteTokenUrl(basePath, tokenUrl); + authApi = new DefaultApi20() { + @Override + public String getAccessTokenEndpoint() { + return absoluteTokenUrl; + } + + @Override + protected String getAuthorizationBaseUrl() { + throw new UnsupportedOperationException("Shouldn't get there !"); + } + }; } - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; + private static String createAbsoluteTokenUrl(String basePath, String tokenUrl) { + if (!URI.create(tokenUrl).isAbsolute()) { + try { + return UriBuilder.fromPath(basePath).path(tokenUrl).build().toURL().toString(); + } catch (MalformedURLException e) { + log.log(Level.SEVERE, "Couldn't create absolute token URL", e); + } + } + return tokenUrl; } @Override - public void applyToParams(List queryParams, Map headerParams, Map cookieParams, String payload, String method, URI uri) throws ApiException { + public void applyToParams( + List queryParams, + Map headerParams, + Map cookieParams, + String payload, + String method, + URI uri) + throws ApiException { + + if (accessToken == null) { + obtainAccessToken(null); + } if (accessToken != null) { - headerParams.put("Authorization", "Bearer " + accessToken); + headerParams.put("Authorization", "Bearer " + accessToken.getAccessToken()); } } + + public OAuth2AccessToken renewAccessToken() throws ApiException { + String refreshToken = null; + if (accessToken != null) { + refreshToken = accessToken.getRefreshToken(); + accessToken = null; + } + return obtainAccessToken(refreshToken); + } + + public synchronized OAuth2AccessToken obtainAccessToken(String refreshToken) throws ApiException { + if (service == null) { + return null; + } + try { + if (refreshToken != null) { + return service.refreshAccessToken(refreshToken); + } + } catch (OAuthException | InterruptedException | ExecutionException | IOException e) { + log.log(Level.FINE, "Refreshing the access token using the refresh token failed", e); + } + try { + switch (flow) { + case password: + if (username != null && password != null) { + accessToken = service.getAccessTokenPasswordGrant(username, password, scope); + } + break; + case accessCode: + if (code != null) { + accessToken = service.getAccessToken(code); + code = null; + } + break; + case application: + accessToken = service.getAccessTokenClientCredentialsGrant(scope); + } + } catch (OAuthException | InterruptedException | ExecutionException | IOException e) { + throw new ApiException(e); + } + return accessToken; + } + + public OAuth2AccessToken getAccessToken() { + return accessToken; + } + + public OAuth setAccessToken(OAuth2AccessToken accessToken) { + this.accessToken = accessToken; + return this; + } + + public OAuth setAccessToken(String accessToken) { + this.accessToken = new OAuth2AccessToken(accessToken); + return this; + } + + public OAuth setScope(String scope) { + this.scope = scope; + return this; + } + + public OAuth setCredentials(String clientId, String clientSecret) { + service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .build(authApi); + return this; + } + + public OAuth usePasswordFlow(String username, String password) { + this.flow = OAuthFlow.password; + this.username = username; + this.password = password; + return this; + } + + public OAuth useAuthorizationCodeFlow(String code) { + this.flow = OAuthFlow.accessCode; + this.code = code; + return this; + } + + public OAuth setFlow(OAuthFlow flow) { + this.flow = flow; + return this; + } + + public void setBasePath(String basePath) { + this.absoluteTokenUrl = createAbsoluteTokenUrl(basePath, tokenUrl); + } } diff --git a/samples/client/petstore/java/jersey2-java6/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/jersey2-java6/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/jersey2-java6/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/jersey2-java6/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/jersey2-java8/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/jersey2-java8/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/jersey2-java8/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/jersey2-java8/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/jersey2/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/jersey2/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/jersey2/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/jersey2/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/okhttp-gson-parcelableModel/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/okhttp-gson-parcelableModel/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/okhttp-gson-parcelableModel/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/okhttp-gson-parcelableModel/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/resteasy/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/resteasy/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/resteasy/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/resteasy/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/retrofit/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/retrofit/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/retrofit/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/retrofit/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/retrofit2-play24/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/retrofit2-play24/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/retrofit2-play24/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/retrofit2-play24/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/retrofit2-play25/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/retrofit2-play25/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/retrofit2-play25/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/retrofit2-play25/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/retrofit2-play26/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/retrofit2-play26/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/retrofit2-play26/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/retrofit2-play26/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/retrofit2/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/retrofit2/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/retrofit2/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/retrofit2/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/retrofit2rx/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/retrofit2rx/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/retrofit2rx/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/retrofit2rx/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/retrofit2rx2/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/retrofit2rx2/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/retrofit2rx2/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/retrofit2rx2/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/vertx/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/vertx/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/vertx/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/vertx/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } } diff --git a/samples/client/petstore/java/webclient/src/main/java/org/openapitools/client/StringUtil.java b/samples/client/petstore/java/webclient/src/main/java/org/openapitools/client/StringUtil.java index 266c26be3a..747a23f5bf 100644 --- a/samples/client/petstore/java/webclient/src/main/java/org/openapitools/client/StringUtil.java +++ b/samples/client/petstore/java/webclient/src/main/java/org/openapitools/client/StringUtil.java @@ -13,6 +13,9 @@ package org.openapitools.client; +import java.util.Collection; +import java.util.Iterator; + public class StringUtil { /** @@ -58,4 +61,23 @@ public class StringUtil { } return out.toString(); } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } }