Skip to content

Commit 8a565a4

Browse files
committed
post review
Signed-off-by: Dmitrii Tikhomirov <[email protected]>
1 parent beb676d commit 8a565a4

File tree

20 files changed

+357
-163
lines changed

20 files changed

+357
-163
lines changed

api/src/test/java/io/serverlessworkflow/api/ApiTest.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,6 @@ void testCallHTTPAPI() throws IOException {
6363
}
6464
}
6565

66-
@Test
67-
void testCallHttpOauthAPI() throws IOException {
68-
Workflow workflow = readWorkflowFromClasspath("features/authentication-oauth2.yaml");
69-
assertThat(workflow.getDo()).isNotEmpty();
70-
assertThat(workflow.getDo().get(0).getName()).isNotNull();
71-
assertThat(workflow.getDo().get(0).getTask()).isNotNull();
72-
Task task = workflow.getDo().get(0).getTask();
73-
if (task.get() instanceof CallTask) {
74-
CallTask callTask = task.getCallTask();
75-
assertThat(callTask).isNotNull();
76-
assertThat(task.getDoTask()).isNull();
77-
CallHTTP httpCall = callTask.getCallHTTP();
78-
assertThat(httpCall).isNotNull();
79-
assertThat(httpCall.getWith().getMethod()).isEqualTo("get");
80-
}
81-
}
82-
8366
@Test
8467
void testCallFunctionAPIWithoutArguments() throws IOException {
8568
Workflow workflow = readWorkflowFromClasspath("features/callFunction.yaml");

impl/http/pom.xml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
<groupId>io.serverlessworkflow</groupId>
1717
<artifactId>serverlessworkflow-impl-core</artifactId>
1818
</dependency>
19+
<dependency>
20+
<groupId>io.serverlessworkflow</groupId>
21+
<artifactId>serverlessworkflow-jwt</artifactId>
22+
</dependency>
1923
<dependency>
2024
<groupId>org.glassfish.jersey.media</groupId>
2125
<artifactId>jersey-media-json-jackson</artifactId>
@@ -24,10 +28,6 @@
2428
<groupId>org.glassfish.jersey.core</groupId>
2529
<artifactId>jersey-client</artifactId>
2630
</dependency>
27-
<dependency>
28-
<groupId>com.fasterxml.jackson.core</groupId>
29-
<artifactId>jackson-databind</artifactId>
30-
</dependency>
3131
<dependency>
3232
<groupId>io.serverlessworkflow</groupId>
3333
<artifactId>serverlessworkflow-api</artifactId>
@@ -38,6 +38,11 @@
3838
<artifactId>serverlessworkflow-impl-jackson</artifactId>
3939
<scope>test</scope>
4040
</dependency>
41+
<dependency>
42+
<groupId>io.serverlessworkflow</groupId>
43+
<artifactId>serverlessworkflow-impl-jwt</artifactId>
44+
<scope>test</scope>
45+
</dependency>
4146
<dependency>
4247
<groupId>org.junit.jupiter</groupId>
4348
<artifactId>junit-jupiter-api</artifactId>

impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/OAuth2AuthProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy;
1919
import io.serverlessworkflow.api.types.Oauth2;
2020
import io.serverlessworkflow.api.types.Workflow;
21+
import io.serverlessworkflow.http.jwt.JWT;
2122
import io.serverlessworkflow.impl.TaskContext;
2223
import io.serverlessworkflow.impl.WorkflowApplication;
2324
import io.serverlessworkflow.impl.WorkflowContext;
2425
import io.serverlessworkflow.impl.WorkflowModel;
25-
import io.serverlessworkflow.impl.executors.http.oauth.JWT;
2626
import io.serverlessworkflow.impl.executors.http.oauth.OAuthRequestBuilder;
2727
import jakarta.ws.rs.client.Invocation;
2828
import jakarta.ws.rs.client.Invocation.Builder;

impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/oauth/AccessTokenProvider.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,44 @@
1616

1717
package io.serverlessworkflow.impl.executors.http.oauth;
1818

19-
import com.fasterxml.jackson.core.JsonProcessingException;
20-
import com.fasterxml.jackson.databind.JsonNode;
19+
import io.serverlessworkflow.http.jwt.JWT;
20+
import io.serverlessworkflow.http.jwt.JWTConverter;
2121
import io.serverlessworkflow.impl.TaskContext;
22-
import java.net.http.HttpRequest;
2322
import java.util.List;
23+
import java.util.Map;
24+
import java.util.ServiceLoader;
2425

2526
public class AccessTokenProvider {
2627

2728
private final TokenResponseHandler tokenResponseHandler = new TokenResponseHandler();
2829

2930
private final TaskContext context;
3031
private final List<String> issuers;
31-
private final HttpRequest requestBuilder;
32+
private final InvocationHolder invocation;
3233

33-
public AccessTokenProvider(
34-
HttpRequest requestBuilder, TaskContext context, List<String> issuers) {
35-
this.requestBuilder = requestBuilder;
34+
private final JWTConverter jwtConverter;
35+
36+
AccessTokenProvider(InvocationHolder invocation, TaskContext context, List<String> issuers) {
37+
this.invocation = invocation;
3638
this.issuers = issuers;
3739
this.context = context;
40+
41+
ServiceLoader<JWTConverter> jwtConverters =
42+
ServiceLoader.load(JWTConverter.class, AccessTokenProvider.class.getClassLoader());
43+
44+
if (jwtConverters.iterator().hasNext()) {
45+
this.jwtConverter = jwtConverters.iterator().next();
46+
} else {
47+
throw new RuntimeException("No JWTConverter implementation found");
48+
}
3849
}
3950

4051
public JWT validateAndGet() {
41-
JsonNode token = tokenResponseHandler.apply(requestBuilder, context);
52+
Map<String, Object> token = tokenResponseHandler.apply(invocation, context);
4253
JWT jwt;
4354
try {
44-
jwt = JWT.fromString(token.get("access_token").asText());
45-
} catch (JsonProcessingException e) {
55+
jwt = jwtConverter.fromToken((String) token.get("access_token"));
56+
} catch (IllegalArgumentException e) {
4657
throw new RuntimeException("Failed to parse JWT token: " + e.getMessage(), e);
4758
}
4859
if (!(issuers == null || issuers.isEmpty())) {

impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/oauth/ClientSecretBasic.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public void execute(HttpRequestBuilder requestBuilder) {
4444
}
4545
}
4646

47-
private void clientCredentials(HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
47+
private void clientCredentials(
48+
HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
4849
if (authenticationData.getClient() == null
4950
|| authenticationData.getClient().getId() == null
5051
|| authenticationData.getClient().getSecret() == null) {
@@ -58,11 +59,12 @@ private void clientCredentials(HttpRequestBuilder requestBuilder, OAuth2Autenthi
5859

5960
requestBuilder
6061
.addHeader("Authorization", "Basic " + encodedAuth)
61-
.withMethod("POST")
62-
.addQueryParam("grant_type", "client_credentials");
62+
.withRequestContentType(authenticationData.getRequest())
63+
.withGrantType(authenticationData.getGrant());
6364
}
6465

65-
private void password(HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
66+
private void password(
67+
HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
6668
if (authenticationData.getUsername() == null || authenticationData.getPassword() == null) {
6769
throw new IllegalArgumentException(
6870
"Username and password must be provided for password grant type");
@@ -79,9 +81,9 @@ private void password(HttpRequestBuilder requestBuilder, OAuth2AutenthicationDat
7981
String encodedAuth = Base64.getEncoder().encodeToString(idAndSecret.getBytes());
8082

8183
requestBuilder
82-
.withMethod("POST")
84+
.withGrantType(authenticationData.getGrant())
85+
.withRequestContentType(authenticationData.getRequest())
8386
.addHeader("Authorization", "Basic " + encodedAuth)
84-
.addQueryParam("grant_type", "password")
8587
.addQueryParam("username", authenticationData.getUsername())
8688
.addQueryParam("password", authenticationData.getPassword());
8789
}

impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/oauth/ClientSecretPostStep.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public void execute(HttpRequestBuilder requestBuilder) {
4343
}
4444
}
4545

46-
private void clientCredentials(HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
46+
private void clientCredentials(
47+
HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
4748
if (authenticationData.getClient() == null
4849
|| authenticationData.getClient().getId() == null
4950
|| authenticationData.getClient().getSecret() == null) {
@@ -52,13 +53,14 @@ private void clientCredentials(HttpRequestBuilder requestBuilder, OAuth2Autenthi
5253
}
5354

5455
requestBuilder
55-
.withMethod("POST")
56-
.addQueryParam("grant_type", "client_credentials")
56+
.withGrantType(authenticationData.getGrant())
57+
.withRequestContentType(authenticationData.getRequest())
5758
.addQueryParam("client_id", authenticationData.getClient().getId())
5859
.addQueryParam("client_secret", authenticationData.getClient().getSecret());
5960
}
6061

61-
private void password(HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
62+
private void password(
63+
HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
6264
if (authenticationData.getUsername() == null || authenticationData.getPassword() == null) {
6365
throw new IllegalArgumentException(
6466
"Username and password must be provided for password grant type");
@@ -71,8 +73,8 @@ private void password(HttpRequestBuilder requestBuilder, OAuth2AutenthicationDat
7173
}
7274

7375
requestBuilder
74-
.withMethod("POST")
75-
.addQueryParam("grant_type", "password")
76+
.withGrantType(authenticationData.getGrant())
77+
.withRequestContentType(authenticationData.getRequest())
7678
.addQueryParam("client_id", authenticationData.getClient().getId())
7779
.addQueryParam("client_secret", authenticationData.getClient().getSecret())
7880
.addQueryParam("username", authenticationData.getUsername())

impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/oauth/HttpRequestBuilder.java

Lines changed: 73 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,31 @@
1616

1717
package io.serverlessworkflow.impl.executors.http.oauth;
1818

19+
import static io.serverlessworkflow.api.types.OAuth2TokenRequest.Oauth2TokenRequestEncoding;
20+
import static io.serverlessworkflow.api.types.OAuth2TokenRequest.Oauth2TokenRequestEncoding.APPLICATION_X_WWW_FORM_URLENCODED;
21+
22+
import io.serverlessworkflow.api.types.OAuth2AutenthicationData;
23+
import io.serverlessworkflow.api.types.OAuth2TokenRequest;
1924
import io.serverlessworkflow.impl.TaskContext;
2025
import io.serverlessworkflow.impl.WorkflowApplication;
2126
import io.serverlessworkflow.impl.WorkflowContext;
2227
import io.serverlessworkflow.impl.WorkflowModel;
2328
import io.serverlessworkflow.impl.WorkflowUtils;
2429
import io.serverlessworkflow.impl.WorkflowValueResolver;
30+
import jakarta.ws.rs.client.Client;
31+
import jakarta.ws.rs.client.ClientBuilder;
32+
import jakarta.ws.rs.client.Entity;
33+
import jakarta.ws.rs.client.Invocation;
34+
import jakarta.ws.rs.client.WebTarget;
35+
import jakarta.ws.rs.core.Form;
36+
import jakarta.ws.rs.core.MediaType;
2537
import java.net.URI;
2638
import java.net.URLEncoder;
27-
import java.net.http.HttpRequest;
2839
import java.nio.charset.StandardCharsets;
29-
import java.time.Duration;
3040
import java.util.HashMap;
3141
import java.util.Map;
32-
import java.util.Objects;
33-
import java.util.stream.Collectors;
3442

35-
public class HttpRequestBuilder {
43+
class HttpRequestBuilder {
3644

3745
private final Map<String, WorkflowValueResolver<String>> headers;
3846

@@ -42,86 +50,99 @@ public class HttpRequestBuilder {
4250

4351
private URI uri;
4452

45-
private String method;
53+
private OAuth2AutenthicationData.OAuth2AutenthicationDataGrant grantType;
54+
55+
private Oauth2TokenRequestEncoding requestContentType = APPLICATION_X_WWW_FORM_URLENCODED;
4656

47-
public HttpRequestBuilder(WorkflowApplication app) {
57+
HttpRequestBuilder(WorkflowApplication app) {
4858
this.app = app;
4959
headers = new HashMap<>();
5060
queryParams = new HashMap<>();
5161
}
5262

53-
public HttpRequestBuilder addHeader(String key, String token) {
63+
HttpRequestBuilder addHeader(String key, String token) {
5464
headers.put(key, WorkflowUtils.buildStringFilter(app, token));
5565
return this;
5666
}
5767

58-
public HttpRequestBuilder addQueryParam(String key, String token) {
68+
HttpRequestBuilder addQueryParam(String key, String token) {
5969
queryParams.put(key, WorkflowUtils.buildStringFilter(app, token));
6070
return this;
6171
}
6272

63-
public HttpRequestBuilder withUri(URI uri) {
73+
HttpRequestBuilder withUri(URI uri) {
6474
this.uri = uri;
6575
return this;
6676
}
6777

68-
public HttpRequestBuilder withMethod(String method) {
69-
this.method = method;
78+
HttpRequestBuilder withRequestContentType(OAuth2TokenRequest oAuth2TokenRequest) {
79+
if (oAuth2TokenRequest != null) {
80+
this.requestContentType = oAuth2TokenRequest.getEncoding();
81+
}
82+
return this;
83+
}
84+
85+
HttpRequestBuilder withGrantType(
86+
OAuth2AutenthicationData.OAuth2AutenthicationDataGrant grantType) {
87+
this.grantType = grantType;
7088
return this;
7189
}
7290

73-
public HttpRequest build(WorkflowContext workflow, TaskContext task, WorkflowModel model) {
74-
HttpRequest.Builder request = HttpRequest.newBuilder();
91+
InvocationHolder build(WorkflowContext workflow, TaskContext task, WorkflowModel model) {
92+
validate();
93+
94+
Client client = ClientBuilder.newClient();
95+
WebTarget target = client.target(uri);
96+
97+
Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON);
98+
99+
builder.header("grant_type", grantType.name().toLowerCase());
100+
builder.header("User-Agent", "OAuth2-Client-Credentials/1.0");
101+
builder.header("Accept", MediaType.APPLICATION_JSON);
102+
builder.header("Cache-Control", "no-cache");
75103

76104
for (var entry : headers.entrySet()) {
77105
String headerValue = entry.getValue().apply(workflow, task, model);
78106
if (headerValue != null) {
79-
request = request.header(entry.getKey(), headerValue);
107+
builder.header(entry.getKey(), headerValue);
80108
}
81109
}
82110

83-
request.header("Accept", "application/json");
111+
Entity<?> entity;
112+
if (requestContentType.equals(APPLICATION_X_WWW_FORM_URLENCODED)) {
113+
Form form = new Form();
114+
form.param("grant_type", grantType.value());
115+
queryParams.forEach(
116+
(key, value) -> {
117+
String v = value.apply(workflow, task, model);
118+
String encodedKey = URLEncoder.encode(key, StandardCharsets.UTF_8);
119+
String encodedValue = URLEncoder.encode(v, StandardCharsets.UTF_8);
120+
form.param(encodedKey, encodedValue);
121+
});
122+
entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED);
123+
} else {
124+
Map<String, Object> jsonData = new HashMap<>();
125+
jsonData.put("grant_type", grantType.value());
126+
queryParams.forEach(
127+
(key, value) -> {
128+
String v = value.apply(workflow, task, model);
129+
String encodedKey = URLEncoder.encode(key, StandardCharsets.UTF_8);
130+
String encodedValue = URLEncoder.encode(v, StandardCharsets.UTF_8);
131+
jsonData.put(encodedKey, encodedValue);
132+
});
133+
entity = Entity.entity(jsonData, MediaType.APPLICATION_JSON);
134+
}
84135

136+
return new InvocationHolder(client, () -> builder.post(entity));
137+
}
138+
139+
private void validate() {
85140
if (uri == null) {
86141
throw new IllegalStateException("URI must be set before building the request");
87142
}
88143

89-
String encoded =
90-
queryParams.entrySet().stream()
91-
.map(
92-
e -> {
93-
String v = e.getValue().apply(workflow, task, model);
94-
if (v == null) return null;
95-
return URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8)
96-
+ "="
97-
+ URLEncoder.encode(v, StandardCharsets.UTF_8);
98-
})
99-
.filter(Objects::nonNull)
100-
.collect(Collectors.joining("&"));
101-
102-
if (method != null) {
103-
switch (method.toUpperCase()) {
104-
case "GET" -> {
105-
if (!encoded.isEmpty()) {
106-
String sep = (uri.getQuery() == null || uri.getQuery().isEmpty()) ? "?" : "&";
107-
uri = URI.create(uri.toString() + sep + encoded);
108-
}
109-
request.uri(uri).GET();
110-
}
111-
case "POST" -> {
112-
request.uri(uri);
113-
HttpRequest.BodyPublisher body =
114-
encoded.isEmpty()
115-
? HttpRequest.BodyPublishers.noBody()
116-
: HttpRequest.BodyPublishers.ofString(encoded);
117-
request.POST(body);
118-
}
119-
default -> throw new IllegalArgumentException("Unsupported HTTP method: " + method);
120-
}
121-
} else {
122-
throw new IllegalStateException("HTTP method must be set before building the request");
144+
if (grantType == null) {
145+
throw new IllegalStateException("Grant type must be set before building the request");
123146
}
124-
request.timeout(Duration.ofSeconds(15));
125-
return request.build();
126147
}
127148
}

0 commit comments

Comments
 (0)