Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.WorkflowModel;
import java.util.concurrent.CompletableFuture;

public interface AccessTokenProvider {
JWT validateAndGet(WorkflowContext workflow, TaskContext context, WorkflowModel model);
CompletableFuture<JWT> validateAndGet(
WorkflowContext workflow, TaskContext context, WorkflowModel model);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.WorkflowModel;
import java.net.URI;
import java.util.concurrent.CompletableFuture;

public interface AuthProvider {

String scheme();

String content(WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri);
CompletableFuture<String> content(
WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.serverlessworkflow.impl.WorkflowValueResolver;
import java.net.URI;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;

class BasicAuthProvider implements AuthProvider {

Expand Down Expand Up @@ -58,10 +59,11 @@ public BasicAuthProvider(
}

@Override
public String content(WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri) {
return new String(
public CompletableFuture<String> content(
WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri) {
return CompletableFuture.completedFuture(
Base64.getEncoder()
.encode(
.encodeToString(
String.format(
USER_PASSWORD,
userFilter.apply(workflow, task, model),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.serverlessworkflow.impl.WorkflowUtils;
import io.serverlessworkflow.impl.WorkflowValueResolver;
import java.net.URI;
import java.util.concurrent.CompletableFuture;

class BearerAuthProvider implements AuthProvider {

Expand All @@ -49,8 +50,9 @@ public BearerAuthProvider(
}

@Override
public String content(WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri) {
return tokenFilter.apply(workflow, task, model);
public CompletableFuture<String> content(
WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri) {
return CompletableFuture.completedFuture(tokenFilter.apply(workflow, task, model));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
import java.net.URI;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

abstract class CommonOAuthProvider implements AuthProvider {
public abstract class CommonOAuthProvider implements AuthProvider {

private final WorkflowValueResolver<AccessTokenProvider> tokenProvider;

Expand All @@ -47,8 +48,12 @@ protected CommonOAuthProvider(WorkflowValueResolver<AccessTokenProvider> tokenPr
}

@Override
public String content(WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri) {
return tokenProvider.apply(workflow, task, model).validateAndGet(workflow, task, model).token();
public CompletableFuture<String> content(
WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri) {
return tokenProvider
.apply(workflow, task, model)
.validateAndGet(workflow, task, model)
.thenApply(JWT::token);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
package io.serverlessworkflow.impl.auth;

import io.serverlessworkflow.api.types.AuthenticationPolicyUnion;
import io.serverlessworkflow.api.types.BasicAuthenticationPolicy;
import io.serverlessworkflow.api.types.BearerAuthenticationPolicy;
import io.serverlessworkflow.api.types.DigestAuthenticationPolicy;
import io.serverlessworkflow.api.types.EndpointConfiguration;
import io.serverlessworkflow.api.types.ReferenceableAuthenticationPolicy;
import io.serverlessworkflow.api.types.Workflow;
Expand Down Expand Up @@ -56,23 +59,54 @@ private Optional<AuthProvider> buildFromPolicy(
AuthenticationPolicyUnion authenticationPolicy,
String method) {
if (authenticationPolicy.getBasicAuthenticationPolicy() != null) {
return Optional.of(
new BasicAuthProvider(
app, workflow, authenticationPolicy.getBasicAuthenticationPolicy()));
return Optional.ofNullable(
basicAuthProvider(app, workflow, authenticationPolicy.getBasicAuthenticationPolicy()));
} else if (authenticationPolicy.getBearerAuthenticationPolicy() != null) {
return Optional.of(
new BearerAuthProvider(
app, workflow, authenticationPolicy.getBearerAuthenticationPolicy()));
return Optional.ofNullable(
bearerAuthProvider(app, workflow, authenticationPolicy.getBearerAuthenticationPolicy()));
} else if (authenticationPolicy.getDigestAuthenticationPolicy() != null) {
return Optional.of(
new DigestAuthProvider(
return Optional.ofNullable(
digestAuthProvider(
app, workflow, authenticationPolicy.getDigestAuthenticationPolicy(), method));
}
return OAuthUtils.from(authenticationPolicy)
.map(
policyData ->
policyData.scheme() == OAuthScheme.OPENID_CONNECT
? new OpenIdAuthProvider(app, workflow, policyData)
: new OAuth2AuthProvider(app, workflow, policyData));
? openIdAuthProvider(app, workflow, policyData)
: oAuth2AuthProvider(app, workflow, policyData));
}

protected AuthProvider oAuth2AuthProvider(
WorkflowApplication app, Workflow workflow, OAuthPolicyData policyData) {
return new OAuth2AuthProvider(app, workflow, policyData);
}

protected AuthProvider openIdAuthProvider(
WorkflowApplication app, Workflow workflow, OAuthPolicyData policyData) {
return new OpenIdAuthProvider(app, workflow, policyData);
}

protected AuthProvider digestAuthProvider(
WorkflowApplication app,
Workflow workflow,
DigestAuthenticationPolicy digestAuthenticationPolicy,
String method) {

return new DigestAuthProvider(app, workflow, digestAuthenticationPolicy, method);
}

protected AuthProvider bearerAuthProvider(
WorkflowApplication app,
Workflow workflow,
BearerAuthenticationPolicy bearerAuthenticationPolicy) {
return new BearerAuthProvider(app, workflow, bearerAuthenticationPolicy);
}

protected AuthProvider basicAuthProvider(
WorkflowApplication app,
Workflow workflow,
BasicAuthenticationPolicy basicAuthenticationPolicy) {
return new BasicAuthProvider(app, workflow, basicAuthenticationPolicy);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@
import io.serverlessworkflow.impl.WorkflowValueResolver;
import io.serverlessworkflow.impl.utils.RandomFactory;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Optional;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -146,7 +146,8 @@ public String scheme() {
}

@Override
public String content(WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri) {
public CompletableFuture<String> content(
WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri) {
try {
HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
connection.setRequestMethod(method);
Expand Down Expand Up @@ -186,17 +187,19 @@ public String content(WorkflowContext workflow, TaskContext task, WorkflowModel
ha2))
.orElseGet(() -> calculateHash(ha1, serverInfo.nonce, ha2));

return buildResponseInfo(serverInfo, userName, path, clientNonce, nonceCount, response);
return CompletableFuture.completedFuture(
buildResponseInfo(serverInfo, userName, path, clientNonce, nonceCount, response));
} else {
throw new IllegalStateException(
"URI "
+ uri
+ " is not digest protected, it returned code "
+ responseCode
+ " when invoked without authentication header, but it should have returned 401 as per RFC 2617");
return CompletableFuture.failedFuture(
new IllegalStateException(
"URI "
+ uri
+ " is not digest protected, it returned code "
+ responseCode
+ " when invoked without authentication header, but it should have returned 401 as per RFC 2617"));
}
} catch (IOException io) {
throw new UncheckedIOException(io);
return CompletableFuture.failedFuture(io);
}
}

Expand Down
76 changes: 63 additions & 13 deletions impl/core/src/main/java/io/serverlessworkflow/impl/auth/JWT.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,79 @@
package io.serverlessworkflow.impl.auth;

import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public interface JWT {
public record JWT(String token, Map<String, Object> header, Map<String, Object> claims) {

String token();
public Optional<Instant> expiresAt() {
return toInstant(claims.get("exp"));
}

List<String> audience();
public Optional<Instant> issuedAt() {
return toInstant(claims.get("iat"));
}

Map<String, Object> claims();
public Collection<String> audience() {
return toCollection(claims.get("aud"), String.class);
}

<T> Optional<T> claim(String name, Class<T> type);
public Optional<String> issuer() {
return Optional.ofNullable((String) claims.get("iss"));
}

Optional<Instant> expiresAt();
public Optional<String> subject() {
return Optional.ofNullable((String) claims.get("sub"));
}

Map<String, Object> header();
public Optional<String> type() {
return header.containsKey("typ")
? Optional.of((String) header.get("typ"))
: Optional.ofNullable((String) claims.get("typ"));
}

Optional<Instant> issuedAt();
static Optional<Instant> toInstant(Object v) {
if (v == null) {
return Optional.empty();
}
if (v instanceof Instant i) {
return Optional.of(i);
}
if (v instanceof Number n) {
return Optional.of(Instant.ofEpochSecond((n.longValue())));
}
if (v instanceof String s) {
try {
long sec = Long.parseLong(s.trim());
return Optional.of(Instant.ofEpochSecond((sec)));
} catch (NumberFormatException ignored) {
try {
return Optional.of(Instant.parse(s.trim()));
} catch (DateTimeParseException ex) {
}
}
}
return Optional.empty();
}

Optional<String> issuer();

Optional<String> subject();

Optional<String> type();
/* Does not support primitive types intentionally, as they are not used in that context. */
static <T> Collection<T> toCollection(Object v, Class<T> clazz) {
if (v == null) {
return List.of();
}
if (clazz.isInstance(v)) {
return List.of(clazz.cast(v));
}
if (v instanceof Collection col) {
return col;
}
if (v.getClass().isArray() && v.getClass().getComponentType().equals(clazz)) {
return Arrays.asList((T[]) v);
}
return List.of();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.WorkflowApplication;

class OAuth2AuthProvider extends CommonOAuthProvider {
public class OAuth2AuthProvider extends CommonOAuthProvider {

public OAuth2AuthProvider(
WorkflowApplication application, Workflow workflow, OAuthPolicyData policyData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.WorkflowApplication;

class OpenIdAuthProvider extends CommonOAuthProvider {
public class OpenIdAuthProvider extends CommonOAuthProvider {

public OpenIdAuthProvider(
WorkflowApplication application, Workflow workflow, OAuthPolicyData policyData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,32 +56,31 @@ public RunScriptExecutor(
@Override
public CompletableFuture<WorkflowModel> apply(
WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input) {
ScriptContext scriptContext =
new ScriptContext(
argumentExpr.map(m -> m.apply(workflowContext, taskContext, input)).orElse(Map.of()),
environmentExpr.map(m -> m.apply(workflowContext, taskContext, input)).orElse(Map.of()),
codeSupplier.apply(workflowContext, taskContext, input),
returnType);
if (isAwait) {
return CompletableFuture.supplyAsync(
() -> runScript(scriptContext, workflowContext, taskContext, input),
() -> runScript(workflowContext, taskContext, input),
workflowContext.definition().application().executorService());
} else {
workflowContext
.definition()
.application()
.executorService()
.submit(() -> runScript(scriptContext, workflowContext, taskContext, input));
.submit(() -> runScript(workflowContext, taskContext, input));
return CompletableFuture.completedFuture(input);
}
}

private WorkflowModel runScript(
ScriptContext scriptContext,
WorkflowContext workflowContext,
TaskContext taskContext,
WorkflowModel input) {
WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input) {
try {
ScriptContext scriptContext =
new ScriptContext(
argumentExpr.map(m -> m.apply(workflowContext, taskContext, input)).orElse(Map.of()),
environmentExpr
.map(m -> m.apply(workflowContext, taskContext, input))
.orElse(Map.of()),
codeSupplier.apply(workflowContext, taskContext, input),
returnType);
return taskRunner.runScript(scriptContext, workflowContext, taskContext, input);
} catch (Exception ex) {
throw new WorkflowException(WorkflowError.runtime(taskContext, ex).build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ public <T> T load(
.map(
auth ->
AuthUtils.authHeaderValue(
auth.scheme(), auth.content(workflowContext, taskContext, model, uri))));
auth.scheme(),
auth.content(workflowContext, taskContext, model, uri).join())));
Comment thread
fjtirado marked this conversation as resolved.
}

public <T> T loadURI(URI uri, Function<ExternalResourceHandler, T> function) {
Expand Down
Loading
Loading