Skip to content

Commit 5880236

Browse files
authored
Add HTTP signature authentication support to Java (jersey2-experimental) (#6058)
* add fmt-maven-plugin to jersey2 exp * update samples * add http signature auth template * minor fix * fix http beaer auth, update sample * fix http signature auth * fix http signature auth * header support * add query string to path * undo changes in default codegen * ignore fake test * add serialize to string method * add serialzie to string method * add get mapper * auto format java source code * remove plugin * update pom.xml * change back AbstractOpenApiSchema to T * skip mvn code formatter in bin script * undo changes to spec * update samples * add back HttpSignatureAuth.java
1 parent 3b0bd36 commit 5880236

File tree

196 files changed

+9345
-10148
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+9345
-10148
lines changed

bin/java-petstore-jersey2-experimental.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ fi
2727

2828
# if you've executed sbt assembly previously it will use that instead.
2929
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
30-
ags="generate -i modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -g java -c bin/java-petstore-jersey2-experimental.json -o samples/client/petstore/java/jersey2-experimental -t modules/openapi-generator/src/main/resources/Java --additional-properties hideGenerationTimestamp=true $@"
30+
ags="generate -i modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml -g java -c bin/java-petstore-jersey2-experimental.json -o samples/client/petstore/java/jersey2-experimental -t modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental --additional-properties hideGenerationTimestamp=true $@"
3131

3232
echo "Removing files and folders under samples/client/petstore/java/jersey2-experimental/src/main"
3333
rm -rf samples/client/petstore/java/jersey2-experimental/src/main
3434
find samples/client/petstore/java/jersey2-experimental -maxdepth 1 -type f ! -name "README.md" -exec rm {} +
3535
java $JAVA_OPTS -jar $executable $ags
3636

37+
#mvn com.coveo:fmt-maven-plugin:format -f samples/client/petstore/java/jersey2-experimental/pom.xml
38+
3739
# copy additional manually written unit-tests
3840
#mkdir samples/client/petstore/java/jersey2/src/test/java/org/openapitools/client
3941
#mkdir samples/client/petstore/java/jersey2/src/test/java/org/openapitools/client/auth

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ public void processOpts() {
373373
supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java"));
374374
supportingFiles.add(new SupportingFile("ApiResponse.mustache", invokerFolder, "ApiResponse.java"));
375375
if (JERSEY2_EXPERIMENTAL.equals(getLibrary())) {
376+
supportingFiles.add(new SupportingFile("auth/HttpSignatureAuth.mustache", authFolder, "HttpSignatureAuth.java"));
376377
supportingFiles.add(new SupportingFile("AbstractOpenApiSchema.mustache", (sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar), "AbstractOpenApiSchema.java"));
377378
}
378379
forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON);

modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/ApiClient.mustache

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.glassfish.jersey.media.multipart.MultiPartFeature;
2323
import java.io.IOException;
2424
import java.io.InputStream;
2525

26+
import java.net.URI;
2627
{{^supportJava6}}
2728
import java.nio.file.Files;
2829
import java.nio.file.StandardCopyOption;
@@ -56,6 +57,7 @@ import java.util.regex.Pattern;
5657
import {{invokerPackage}}.auth.Authentication;
5758
import {{invokerPackage}}.auth.HttpBasicAuth;
5859
import {{invokerPackage}}.auth.HttpBearerAuth;
60+
import {{invokerPackage}}.auth.HttpSignatureAuth;
5961
import {{invokerPackage}}.auth.ApiKeyAuth;
6062
import {{invokerPackage}}.model.AbstractOpenApiSchema;
6163

@@ -159,11 +161,26 @@ public class ApiClient {
159161
setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}");
160162
161163
// Setup authentications (key: authentication name, value: authentication).
162-
authentications = new HashMap<String, Authentication>();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
163-
authentications.put("{{name}}", new HttpBasicAuth());{{/isBasicBasic}}{{^isBasicBasic}}
164-
authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));{{/isBasicBasic}}{{/isBasic}}{{#isApiKey}}
165-
authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}}
166-
authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}
164+
authentications = new HashMap<String, Authentication>();
165+
{{#authMethods}}
166+
{{#isBasic}}
167+
{{#isBasicBasic}}
168+
authentications.put("{{name}}", new HttpBasicAuth());
169+
{{/isBasicBasic}}
170+
{{#isBasicBearer}}
171+
authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));
172+
{{/isBasicBearer}}
173+
{{#isHttpSignature}}
174+
authentications.put("{{name}}", new HttpSignatureAuth("{{name}}", null, null));
175+
{{/isHttpSignature}}
176+
{{/isBasic}}
177+
{{#isApiKey}}
178+
authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));
179+
{{/isApiKey}}
180+
{{#isOAuth}}
181+
authentications.put("{{name}}", new OAuth());
182+
{{/isOAuth}}
183+
{{/authMethods}}
167184
// Prevent the authentications from being modified.
168185
authentications = Collections.unmodifiableMap(authentications);
169186

@@ -701,6 +718,38 @@ public class ApiClient {
701718
return entity;
702719
}
703720

721+
/**
722+
* Serialize the given Java object into string according the given
723+
* Content-Type (only JSON, HTTP form is supported for now).
724+
* @param obj Object
725+
* @param formParams Form parameters
726+
* @param contentType Context type
727+
* @return String
728+
* @throws ApiException API exception
729+
*/
730+
public String serializeToString(Object obj, Map<String, Object> formParams, String contentType) throws ApiException {
731+
try {
732+
if (contentType.startsWith("multipart/form-data")) {
733+
throw new ApiException("multipart/form-data not yet supported for serializeToString (http signature authentication)");
734+
} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
735+
String formString = "";
736+
for (Entry<String, Object> param : formParams.entrySet()) {
737+
formString = param.getKey() + "=" + URLEncoder.encode(parameterToString(param.getValue()), "UTF-8") + "&";
738+
}
739+
740+
if (formString.length() == 0) { // empty string
741+
return formString;
742+
} else {
743+
return formString.substring(0, formString.length() - 1);
744+
}
745+
} else {
746+
return json.getMapper().writeValueAsString(obj);
747+
}
748+
} catch (Exception ex) {
749+
throw new ApiException("Failed to perform serializeToString: " + ex.toString());
750+
}
751+
}
752+
704753
public AbstractOpenApiSchema deserializeSchemas(Response response, AbstractOpenApiSchema schema) throws ApiException{
705754
706755
Object result = null;
@@ -862,7 +911,6 @@ public class ApiClient {
862911
* @throws ApiException API exception
863912
*/
864913
public <T> ApiResponse<T> invokeAPI(String operation, String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames, GenericType<T> returnType, AbstractOpenApiSchema schema) throws ApiException {
865-
updateParamsForAuth(authNames, queryParams, headerParams, cookieParams);
866914
867915
// Not using `.target(targetURL).path(path)` below,
868916
// to support (constant) query string in `path`, e.g. "/posts?draft=1"
@@ -935,6 +983,14 @@ public class ApiClient {
935983

936984
Entity<?> entity = serialize(body, formParams, contentType);
937985

986+
// put all headers in one place
987+
Map<String, String> allHeaderParams = new HashMap<>();
988+
allHeaderParams.putAll(defaultHeaderMap);
989+
allHeaderParams.putAll(headerParams);
990+
991+
// update different parameters (e.g. headers) for authentication
992+
updateParamsForAuth(authNames, queryParams, allHeaderParams, cookieParams, serializeToString(body, formParams, contentType), method, target.getUri());
993+
938994
Response response = null;
939995

940996
try {
@@ -1061,12 +1117,17 @@ public class ApiClient {
10611117
* @param queryParams List of query parameters
10621118
* @param headerParams Map of header parameters
10631119
* @param cookieParams Map of cookie parameters
1120+
* @param method HTTP method (e.g. POST)
1121+
* @param uri HTTP URI
10641122
*/
1065-
protected void updateParamsForAuth(String[] authNames, List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) {
1123+
protected void updateParamsForAuth(String[] authNames, List<Pair> queryParams, Map<String, String> headerParams,
1124+
Map<String, String> cookieParams, String payload, String method, URI uri) throws ApiException {
10661125
for (String authName : authNames) {
10671126
Authentication auth = authentications.get(authName);
1068-
if (auth == null) throw new RuntimeException("Authentication undefined: " + authName);
1069-
auth.applyToParams(queryParams, headerParams, cookieParams);
1127+
if (auth == null) {
1128+
throw new RuntimeException("Authentication undefined: " + authName);
1129+
}
1130+
auth.applyToParams(queryParams, headerParams, cookieParams, payload, method, uri);
10701131
}
10711132
}
10721133
}

modules/openapi-generator/src/main/resources/Java/libraries/jersey2-experimental/JSON.mustache

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,6 @@ public class JSON implements ContextResolver<ObjectMapper> {
6262
public ObjectMapper getContext(Class<?> type) {
6363
return mapper;
6464
}
65+
66+
public ObjectMapper getMapper() { return mapper; }
6567
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{{>licenseInfo}}
2+
3+
package {{invokerPackage}}.auth;
4+
5+
import {{invokerPackage}}.Pair;
6+
import {{invokerPackage}}.ApiException;
7+
8+
import java.net.URI;
9+
import java.util.Map;
10+
import java.util.List;
11+
12+
{{>generatedAnnotation}}
13+
public class ApiKeyAuth implements Authentication {
14+
private final String location;
15+
private final String paramName;
16+
17+
private String apiKey;
18+
private String apiKeyPrefix;
19+
20+
public ApiKeyAuth(String location, String paramName) {
21+
this.location = location;
22+
this.paramName = paramName;
23+
}
24+
25+
public String getLocation() {
26+
return location;
27+
}
28+
29+
public String getParamName() {
30+
return paramName;
31+
}
32+
33+
public String getApiKey() {
34+
return apiKey;
35+
}
36+
37+
public void setApiKey(String apiKey) {
38+
this.apiKey = apiKey;
39+
}
40+
41+
public String getApiKeyPrefix() {
42+
return apiKeyPrefix;
43+
}
44+
45+
public void setApiKeyPrefix(String apiKeyPrefix) {
46+
this.apiKeyPrefix = apiKeyPrefix;
47+
}
48+
49+
@Override
50+
public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams, String payload, String method, URI uri) throws ApiException {
51+
if (apiKey == null) {
52+
return;
53+
}
54+
String value;
55+
if (apiKeyPrefix != null) {
56+
value = apiKeyPrefix + " " + apiKey;
57+
} else {
58+
value = apiKey;
59+
}
60+
if ("query".equals(location)) {
61+
queryParams.add(new Pair(paramName, value));
62+
} else if ("header".equals(location)) {
63+
headerParams.put(paramName, value);
64+
} else if ("cookie".equals(location)) {
65+
cookieParams.put(paramName, value);
66+
}
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{{>licenseInfo}}
2+
3+
package {{invokerPackage}}.auth;
4+
5+
import {{invokerPackage}}.Pair;
6+
import {{invokerPackage}}.ApiException;
7+
8+
import java.net.URI;
9+
import java.util.Map;
10+
import java.util.List;
11+
12+
public interface Authentication {
13+
/**
14+
* Apply authentication settings to header and query params.
15+
*
16+
* @param queryParams List of query parameters
17+
* @param headerParams Map of header parameters
18+
* @param cookieParams Map of cookie parameters
19+
*/
20+
void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams, String payload, String method, URI uri) throws ApiException;
21+
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{{>licenseInfo}}
2+
3+
package {{invokerPackage}}.auth;
4+
5+
import {{invokerPackage}}.Pair;
6+
import {{invokerPackage}}.ApiException;
7+
8+
{{^java8}}
9+
import com.migcomponents.migbase64.Base64;
10+
{{/java8}}
11+
{{#java8}}
12+
import java.util.Base64;
13+
import java.nio.charset.StandardCharsets;
14+
{{/java8}}
15+
16+
import java.net.URI;
17+
import java.util.Map;
18+
import java.util.List;
19+
20+
{{^java8}}
21+
import java.io.UnsupportedEncodingException;
22+
{{/java8}}
23+
24+
{{>generatedAnnotation}}
25+
public class HttpBasicAuth implements Authentication {
26+
private String username;
27+
private String password;
28+
29+
public String getUsername() {
30+
return username;
31+
}
32+
33+
public void setUsername(String username) {
34+
this.username = username;
35+
}
36+
37+
public String getPassword() {
38+
return password;
39+
}
40+
41+
public void setPassword(String password) {
42+
this.password = password;
43+
}
44+
45+
@Override
46+
public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams, String payload, String method, URI uri) throws ApiException {
47+
if (username == null && password == null) {
48+
return;
49+
}
50+
String str = (username == null ? "" : username) + ":" + (password == null ? "" : password);
51+
{{^java8}}
52+
try {
53+
headerParams.put("Authorization", "Basic " + Base64.encodeToString(str.getBytes("UTF-8"), false));
54+
} catch (UnsupportedEncodingException e) {
55+
throw new RuntimeException(e);
56+
}
57+
{{/java8}}
58+
{{#java8}}
59+
headerParams.put("Authorization", "Basic " + Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8)));
60+
{{/java8}}
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{{>licenseInfo}}
2+
3+
package {{invokerPackage}}.auth;
4+
5+
import {{invokerPackage}}.Pair;
6+
import {{invokerPackage}}.ApiException;
7+
8+
import java.net.URI;
9+
import java.util.Map;
10+
import java.util.List;
11+
12+
{{>generatedAnnotation}}
13+
public class HttpBearerAuth implements Authentication {
14+
private final String scheme;
15+
private String bearerToken;
16+
17+
public HttpBearerAuth(String scheme) {
18+
this.scheme = scheme;
19+
}
20+
21+
/**
22+
* Gets the token, which together with the scheme, will be sent as the value of the Authorization header.
23+
*
24+
* @return The bearer token
25+
*/
26+
public String getBearerToken() {
27+
return bearerToken;
28+
}
29+
30+
/**
31+
* Sets the token, which together with the scheme, will be sent as the value of the Authorization header.
32+
*
33+
* @param bearerToken The bearer token to send in the Authorization header
34+
*/
35+
public void setBearerToken(String bearerToken) {
36+
this.bearerToken = bearerToken;
37+
}
38+
39+
@Override
40+
public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams, String payload, String method, URI uri) throws ApiException {
41+
if(bearerToken == null) {
42+
return;
43+
}
44+
45+
headerParams.put("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken);
46+
}
47+
48+
private static String upperCaseBearer(String scheme) {
49+
return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
50+
}
51+
}

0 commit comments

Comments
 (0)