Skip to content

Commit beb676d

Browse files
committed
tests
1 parent 32ad156 commit beb676d

File tree

9 files changed

+297
-29
lines changed

9 files changed

+297
-29
lines changed

impl/http/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,10 @@
5959
<artifactId>logback-classic</artifactId>
6060
<scope>test</scope>
6161
</dependency>
62+
<dependency>
63+
<groupId>com.squareup.okhttp3</groupId>
64+
<artifactId>mockwebserver</artifactId>
65+
<scope>test</scope>
66+
</dependency>
6267
</dependencies>
6368
</project>

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,14 @@ public class OAuth2AuthProvider implements AuthProvider {
3131

3232
private Oauth2 oauth2;
3333

34-
private WorkflowApplication workflowApplication;
34+
private final WorkflowApplication workflowApplication;
3535

3636
private static final String BEARER_TOKEN = "%s %s";
3737

3838
public OAuth2AuthProvider(
3939
WorkflowApplication application, Workflow workflow, OAuth2AuthenticationPolicy authPolicy) {
4040
this.workflowApplication = application;
4141
Oauth2 oauth2 = authPolicy.getOauth2();
42-
4342
if (oauth2.getOAuth2ConnectAuthenticationProperties() != null) {
4443
this.oauth2 = oauth2;
4544
} else if (oauth2.getOAuth2AuthenticationPolicySecret() != null) {
@@ -62,7 +61,6 @@ public void preRequest(
6261
.validateAndGet();
6362

6463
String tokenType = (String) token.getClaim("typ");
65-
6664
builder.header(
6765
AuthProviderFactory.AUTH_HEADER_NAME,
6866
String.format(BEARER_TOKEN, tokenType, token.getToken()));

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,17 @@ public ClientSecretBasic(Oauth2 oauth2) {
3434
public void execute(HttpRequestBuilder requestBuilder) {
3535
OAuth2AutenthicationData authenticationData =
3636
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
37-
3837
if (authenticationData.getGrant().equals(PASSWORD)) {
39-
password(requestBuilder);
38+
password(requestBuilder, authenticationData);
4039
} else if (authenticationData.getGrant().equals(CLIENT_CREDENTIALS)) {
41-
clientCredentials(requestBuilder);
40+
clientCredentials(requestBuilder, authenticationData);
4241
} else {
4342
throw new UnsupportedOperationException(
4443
"Unsupported grant type: " + authenticationData.getGrant());
4544
}
4645
}
4746

48-
private void clientCredentials(HttpRequestBuilder requestBuilder) {
49-
OAuth2AutenthicationData authenticationData =
50-
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
47+
private void clientCredentials(HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
5148
if (authenticationData.getClient() == null
5249
|| authenticationData.getClient().getId() == null
5350
|| authenticationData.getClient().getSecret() == null) {
@@ -65,9 +62,7 @@ private void clientCredentials(HttpRequestBuilder requestBuilder) {
6562
.addQueryParam("grant_type", "client_credentials");
6663
}
6764

68-
private void password(HttpRequestBuilder requestBuilder) {
69-
OAuth2AutenthicationData authenticationData =
70-
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
65+
private void password(HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
7166
if (authenticationData.getUsername() == null || authenticationData.getPassword() == null) {
7267
throw new IllegalArgumentException(
7368
"Username and password must be provided for password grant type");

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,16 @@ public void execute(HttpRequestBuilder requestBuilder) {
3434
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
3535

3636
if (authenticationData.getGrant().equals(PASSWORD)) {
37-
password(requestBuilder);
37+
password(requestBuilder, authenticationData);
3838
} else if (authenticationData.getGrant().equals(CLIENT_CREDENTIALS)) {
39-
clientCredentials(requestBuilder);
39+
clientCredentials(requestBuilder, authenticationData);
4040
} else {
4141
throw new UnsupportedOperationException(
4242
"Unsupported grant type: " + authenticationData.getGrant());
4343
}
4444
}
4545

46-
private void clientCredentials(HttpRequestBuilder requestBuilder) {
47-
OAuth2AutenthicationData authenticationData =
48-
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
46+
private void clientCredentials(HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
4947
if (authenticationData.getClient() == null
5048
|| authenticationData.getClient().getId() == null
5149
|| authenticationData.getClient().getSecret() == null) {
@@ -60,9 +58,7 @@ private void clientCredentials(HttpRequestBuilder requestBuilder) {
6058
.addQueryParam("client_secret", authenticationData.getClient().getSecret());
6159
}
6260

63-
private void password(HttpRequestBuilder requestBuilder) {
64-
OAuth2AutenthicationData authenticationData =
65-
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
61+
private void password(HttpRequestBuilder requestBuilder, OAuth2AutenthicationData authenticationData) {
6662
if (authenticationData.getUsername() == null || authenticationData.getPassword() == null) {
6763
throw new IllegalArgumentException(
6864
"Username and password must be provided for password grant type");

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class OAuthRequestBuilder {
3434

3535
private final Oauth2 oauth2;
3636

37+
private final OAuth2AutenthicationData authenticationData;
38+
3739
private final WorkflowApplication application;
3840

3941
private List<String> issuers;
@@ -46,6 +48,8 @@ public class OAuthRequestBuilder {
4648

4749
public OAuthRequestBuilder(WorkflowApplication application, Oauth2 oauth2) {
4850
this.oauth2 = oauth2;
51+
this.authenticationData =
52+
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
4953
this.application = application;
5054
}
5155

@@ -85,8 +89,6 @@ private void clientSecretPost(HttpRequestBuilder requestBuilder) {
8589
}
8690

8791
private OAuth2AutenthicationDataClient.ClientAuthentication getClientAuthentication() {
88-
OAuth2AutenthicationData authenticationData =
89-
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
9092
if (authenticationData.getClient() == null
9193
|| authenticationData.getClient().getAuthentication() == null) {
9294
return CLIENT_SECRET_POST;
@@ -103,17 +105,13 @@ private void issuers() {
103105
}
104106

105107
public void audience(HttpRequestBuilder requestBuilder) {
106-
OAuth2AutenthicationData authenticationData =
107-
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
108108
if (authenticationData.getAudiences() != null && !authenticationData.getAudiences().isEmpty()) {
109109
String audiences = String.join(" ", authenticationData.getAudiences());
110110
requestBuilder.addQueryParam("audience", audiences);
111111
}
112112
}
113113

114114
private void scope(HttpRequestBuilder requestBuilder) {
115-
OAuth2AutenthicationData authenticationData =
116-
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
117115
if (authenticationData.getScopes() != null && !authenticationData.getScopes().isEmpty()) {
118116
String scopes = String.join(" ", authenticationData.getScopes());
119117
requestBuilder.addQueryParam("scope", scopes);
@@ -143,9 +141,6 @@ private void authenticationURI(HttpRequestBuilder requestBuilder) {
143141
}
144142

145143
public void requestEncoding(HttpRequestBuilder requestBuilder) {
146-
OAuth2AutenthicationData authenticationData =
147-
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AutenthicationData();
148-
149144
if (authenticationData.getRequest() != null
150145
&& authenticationData.getRequest().getEncoding() != null) {
151146
requestBuilder.addHeader(
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.serverlessworkflow.impl;
18+
19+
import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath;
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
23+
import com.fasterxml.jackson.databind.ObjectMapper;
24+
import io.serverlessworkflow.api.types.Workflow;
25+
import java.io.IOException;
26+
import java.nio.charset.StandardCharsets;
27+
import java.time.Instant;
28+
import java.util.Base64;
29+
import java.util.Map;
30+
import okhttp3.OkHttpClient;
31+
import okhttp3.mockwebserver.MockResponse;
32+
import okhttp3.mockwebserver.MockWebServer;
33+
import okhttp3.mockwebserver.RecordedRequest;
34+
import org.junit.jupiter.api.AfterEach;
35+
import org.junit.jupiter.api.BeforeEach;
36+
import org.junit.jupiter.api.Test;
37+
38+
public class OAuthHTTPWorkflowDefinitionTest {
39+
40+
private static final ObjectMapper MAPPER = new ObjectMapper();
41+
42+
private MockWebServer authServer;
43+
private MockWebServer apiServer;
44+
private OkHttpClient httpClient;
45+
private String authBaseUrl;
46+
private String apiBaseUrl;
47+
48+
@BeforeEach
49+
void setUp() throws IOException {
50+
authServer = new MockWebServer();
51+
authServer.start(8888);
52+
authBaseUrl = "http://localhost:8888";
53+
54+
apiServer = new MockWebServer();
55+
apiServer.start(8081);
56+
apiBaseUrl = "http://localhost:8081";
57+
58+
httpClient = new OkHttpClient();
59+
}
60+
61+
@AfterEach
62+
void tearDown() throws IOException {
63+
authServer.shutdown();
64+
apiServer.shutdown();
65+
}
66+
67+
@Test
68+
public void testOAuthClientSecretPostPasswordWorkflowExecution() throws Exception {
69+
String jwt = fakeAccessToken();
70+
71+
String tokenResponse =
72+
"""
73+
{
74+
"access_token": "%s",
75+
"token_type": "Bearer",
76+
"expires_in": 3600,
77+
"scope": "read write"
78+
}
79+
"""
80+
.formatted(jwt);
81+
82+
authServer.enqueue(
83+
new MockResponse()
84+
.setBody(tokenResponse)
85+
.setHeader("Content-Type", "application/json")
86+
.setResponseCode(200));
87+
88+
String response =
89+
"""
90+
{
91+
"message": "Hello World"
92+
}
93+
""";
94+
95+
apiServer.enqueue(
96+
new MockResponse()
97+
.setBody(response)
98+
.setHeader("Content-Type", "application/json")
99+
.setResponseCode(200));
100+
101+
Workflow workflow = readWorkflowFromClasspath("oAuthClientSecretPostPasswordHttpCall.yaml");
102+
Map<String, Object> result;
103+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
104+
result =
105+
app.workflowDefinition(workflow).instance(Map.of()).start().get().asMap().orElseThrow();
106+
} catch (Exception e) {
107+
throw new RuntimeException("Workflow execution failed", e);
108+
}
109+
110+
assertTrue(result.containsKey("message"));
111+
assertTrue(result.get("message").toString().contains("Hello World"));
112+
113+
RecordedRequest tokenRequest = authServer.takeRequest();
114+
assertEquals("POST", tokenRequest.getMethod());
115+
assertEquals("/realms/test-realm/protocol/openid-connect/token", tokenRequest.getPath());
116+
assertEquals(
117+
"application/x-www-form-urlencoded; charset=UTF-8", tokenRequest.getHeader("Content-Type"));
118+
119+
String tokenRequestBody = tokenRequest.getBody().readUtf8();
120+
assertTrue(tokenRequestBody.contains("grant_type=password"));
121+
assertTrue(tokenRequestBody.contains("username=serverless-workflow-test"));
122+
assertTrue(tokenRequestBody.contains("password=serverless-workflow-test"));
123+
124+
RecordedRequest petRequest = apiServer.takeRequest();
125+
assertEquals("GET", petRequest.getMethod());
126+
assertEquals("/hello", petRequest.getPath());
127+
assertEquals("Bearer " + jwt, petRequest.getHeader("Authorization"));
128+
}
129+
130+
@Test
131+
public void testOAuthClientSecretPostClientCredentialsWorkflowExecution() throws Exception {
132+
String jwt = fakeAccessToken();
133+
134+
String tokenResponse =
135+
"""
136+
{
137+
"access_token": "%s",
138+
"token_type": "Bearer",
139+
"expires_in": 3600,
140+
"scope": "read write"
141+
}
142+
"""
143+
.formatted(jwt);
144+
145+
authServer.enqueue(
146+
new MockResponse()
147+
.setBody(tokenResponse)
148+
.setHeader("Content-Type", "application/json")
149+
.setResponseCode(200));
150+
151+
String response =
152+
"""
153+
{
154+
"message": "Hello World"
155+
}
156+
""";
157+
158+
apiServer.enqueue(
159+
new MockResponse()
160+
.setBody(response)
161+
.setHeader("Content-Type", "application/json")
162+
.setResponseCode(200));
163+
164+
Workflow workflow =
165+
readWorkflowFromClasspath("oAuthClientSecretPostClientCredentialsHttpCall.yaml");
166+
Map<String, Object> result;
167+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
168+
result =
169+
app.workflowDefinition(workflow).instance(Map.of()).start().get().asMap().orElseThrow();
170+
} catch (Exception e) {
171+
throw new RuntimeException("Workflow execution failed", e);
172+
}
173+
174+
assertTrue(result.containsKey("message"));
175+
assertTrue(result.get("message").toString().contains("Hello World"));
176+
177+
RecordedRequest tokenRequest = authServer.takeRequest();
178+
assertEquals("POST", tokenRequest.getMethod());
179+
assertEquals("/realms/test-realm/protocol/openid-connect/token", tokenRequest.getPath());
180+
assertEquals(
181+
"application/x-www-form-urlencoded; charset=UTF-8", tokenRequest.getHeader("Content-Type"));
182+
183+
String tokenRequestBody = tokenRequest.getBody().readUtf8();
184+
assertTrue(tokenRequestBody.contains("grant_type=client_credentials"));
185+
assertTrue(tokenRequestBody.contains("client_id=serverless-workflow"));
186+
assertTrue(tokenRequestBody.contains("secret=D0ACXCUKOUrL5YL7j6RQWplMaSjPB8MT"));
187+
188+
RecordedRequest petRequest = apiServer.takeRequest();
189+
assertEquals("GET", petRequest.getMethod());
190+
assertEquals("/hello", petRequest.getPath());
191+
assertEquals("Bearer " + jwt, petRequest.getHeader("Authorization"));
192+
}
193+
194+
public static String fakeJwt(Map<String, Object> payload) throws Exception {
195+
String headerJson =
196+
MAPPER.writeValueAsString(
197+
Map.of(
198+
"alg", "RS256",
199+
"typ", "JWT",
200+
"kid", "test"));
201+
String payloadJson = MAPPER.writeValueAsString(payload);
202+
return b64Url(headerJson) + "." + b64Url(payloadJson) + ".sig";
203+
}
204+
205+
private static String b64Url(String s) {
206+
return Base64.getUrlEncoder()
207+
.withoutPadding()
208+
.encodeToString(s.getBytes(StandardCharsets.UTF_8));
209+
}
210+
211+
public static String fakeAccessToken() throws Exception {
212+
long now = Instant.now().getEpochSecond();
213+
return fakeJwt(
214+
Map.of(
215+
"iss", "http://localhost:8888/realms/test-realm",
216+
"aud", "account",
217+
"sub", "test-subject",
218+
"azp", "serverless-workflow",
219+
"typ", "Bearer",
220+
"scope", "profile email",
221+
"exp", now + 3600,
222+
"iat", now));
223+
}
224+
}

0 commit comments

Comments
 (0)