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 @@ -35,6 +35,7 @@
import io.serverlessworkflow.impl.resources.DefaultResourceLoaderFactory;
import io.serverlessworkflow.impl.resources.ExternalResourceHandler;
import io.serverlessworkflow.impl.resources.ResourceLoaderFactory;
import io.serverlessworkflow.impl.resources.URITemplateResolver;
import io.serverlessworkflow.impl.scheduler.DefaultWorkflowScheduler;
import io.serverlessworkflow.impl.scheduler.WorkflowScheduler;
import io.serverlessworkflow.impl.schema.SchemaValidator;
Expand Down Expand Up @@ -73,6 +74,7 @@ public class WorkflowApplication implements AutoCloseable {
private final ConfigManager configManager;
private final SecretManager secretManager;
private final SchedulerListener schedulerListener;
private final Optional<URITemplateResolver> templateResolver;

private WorkflowApplication(Builder builder) {
this.taskFactory = builder.taskFactory;
Expand All @@ -94,6 +96,7 @@ private WorkflowApplication(Builder builder) {
this.additionalObjects = builder.additionalObjects;
this.configManager = builder.configManager;
this.secretManager = builder.secretManager;
this.templateResolver = builder.templateResolver;
}

public TaskExecutorFactory taskFactory() {
Expand Down Expand Up @@ -173,6 +176,7 @@ public SchemaValidator getValidator(SchemaInline inline) {
private SecretManager secretManager;
private ConfigManager configManager;
private SchedulerListener schedulerListener;
private Optional<URITemplateResolver> templateResolver;

private Builder() {}

Expand Down Expand Up @@ -325,6 +329,7 @@ public WorkflowApplication build() {
.findFirst()
.orElseGet(() -> new ConfigSecretManager(configManager));
}
templateResolver = ServiceLoader.load(URITemplateResolver.class).findFirst();
return new WorkflowApplication(this);
}
}
Expand Down Expand Up @@ -398,6 +403,10 @@ SchedulerListener schedulerListener() {
return schedulerListener;
}

public Optional<URITemplateResolver> templateResolver() {
return templateResolver;
}

public <T> Optional<T> additionalObject(
String name, WorkflowContext workflowContext, TaskContext taskContext) {
return Optional.ofNullable(additionalObjects.get(name))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import io.serverlessworkflow.api.types.InputFrom;
import io.serverlessworkflow.api.types.OutputAs;
import io.serverlessworkflow.api.types.SchemaUnion;
import io.serverlessworkflow.api.types.SecretBasedAuthenticationPolicy;
import io.serverlessworkflow.api.types.TimeoutAfter;
import io.serverlessworkflow.api.types.UriTemplate;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.expressions.ExpressionDescriptor;
import io.serverlessworkflow.impl.expressions.ExpressionUtils;
import io.serverlessworkflow.impl.resources.ResourceLoader;
Expand Down Expand Up @@ -200,4 +202,45 @@ public static WorkflowValueResolver<Duration> fromTimeoutAfter(
return (w, t, f) -> Duration.ZERO;
}
}

public static final String secretProp(WorkflowContext context, String secretName, String prop) {
return (String) secret(context, secretName).get(prop);
}

public static final Map<String, Object> secret(WorkflowContext context, String secretName) {
return context.definition().application().secretManager().secret(secretName);
}

public static final String checkSecret(
Workflow workflow, SecretBasedAuthenticationPolicy secretPolicy) {
String secretName = secretPolicy.getUse();
return workflow.getUse().getSecrets().stream()
.filter(s -> s.equals(secretName))
.findAny()
.orElseThrow(() -> new IllegalStateException("Secret " + secretName + " does not exist"));
}

public static URI concatURI(URI uri, String pathToAppend) {
return uri.getPath().endsWith("/")
? uri.resolve(pathToAppend)
: URI.create(
uri.toString() + (pathToAppend.startsWith("/") ? pathToAppend : "/" + pathToAppend));
}

public static WorkflowValueResolver<URI> getURISupplier(
WorkflowApplication application, UriTemplate template) {
if (template.getLiteralUri() != null) {
return (w, t, n) -> template.getLiteralUri();
} else if (template.getLiteralUriTemplate() != null) {
return (w, t, n) ->
application
.templateResolver()
.orElseThrow(
() ->
new IllegalStateException(
"Need an uri template resolver to resolve uri template"))
.resolveTemplates(template.getLiteralUriTemplate(), w, t, n);
}
throw new IllegalArgumentException("Invalid uritemplate definition " + template);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ public class ConfigSecretManager implements SecretManager {

private final ConfigManager configManager;

private Map<String, Map<String, String>> secretMap = new ConcurrentHashMap<>();
private Map<String, Map<String, Object>> secretMap = new ConcurrentHashMap<>();

public ConfigSecretManager(ConfigManager configManager) {
this.configManager = configManager;
}

@Override
public Map<String, String> secret(String secretName) {
public Map<String, Object> secret(String secretName) {
return secretMap.computeIfAbsent(secretName, this::buildMap);
}

private Map<String, String> buildMap(String secretName) {
Map<String, String> map = new HashMap<String, String>();
private Map<String, Object> buildMap(String secretName) {
Map<String, Object> map = new HashMap<>();
final String prefix = secretName + ".";
for (String name : configManager.names()) {
if (name.startsWith(prefix)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@

@FunctionalInterface
public interface SecretManager extends ServicePriority {
Map<String, String> secret(String secretName);
Map<String, Object> secret(String secretName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
*/
package io.serverlessworkflow.impl.resources;

import static io.serverlessworkflow.impl.WorkflowUtils.getURISupplier;

import io.serverlessworkflow.api.types.Endpoint;
import io.serverlessworkflow.api.types.EndpointUri;
import io.serverlessworkflow.api.types.ExternalResource;
import io.serverlessworkflow.api.types.UriTemplate;
import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowApplication;
import io.serverlessworkflow.impl.WorkflowContext;
Expand All @@ -31,41 +32,21 @@
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

public class DefaultResourceLoader implements ResourceLoader {

private final Optional<Path> workflowPath;
private final WorkflowApplication application;

private final AtomicReference<URITemplateResolver> templateResolver =
new AtomicReference<URITemplateResolver>();

private Map<ExternalResourceHandler, CachedResource> resourceCache = new ConcurrentHashMap<>();

protected DefaultResourceLoader(WorkflowApplication application, Path workflowPath) {
this.application = application;
this.workflowPath = Optional.ofNullable(workflowPath);
}

private URITemplateResolver templateResolver() {
URITemplateResolver result = templateResolver.get();
if (result == null) {
result =
ServiceLoader.load(URITemplateResolver.class)
.findFirst()
.orElseThrow(
() ->
new IllegalStateException(
"Need an uri template resolver to resolve uri template"));
templateResolver.set(result);
}
return result;
}

private ExternalResourceHandler fileResource(String pathStr) {
Path path = Path.of(pathStr);
if (path.isAbsolute()) {
Expand Down Expand Up @@ -122,7 +103,7 @@ public WorkflowValueResolver<URI> uriSupplier(Endpoint endpoint) {
if (endpoint.getEndpointConfiguration() != null) {
EndpointUri uri = endpoint.getEndpointConfiguration().getUri();
if (uri.getLiteralEndpointURI() != null) {
return getURISupplier(uri.getLiteralEndpointURI());
return getURISupplier(application, uri.getLiteralEndpointURI());
} else if (uri.getExpressionEndpointURI() != null) {
return new ExpressionURISupplier(
application
Expand All @@ -135,21 +116,11 @@ public WorkflowValueResolver<URI> uriSupplier(Endpoint endpoint) {
.expressionFactory()
.resolveString(ExpressionDescriptor.from(endpoint.getRuntimeExpression())));
} else if (endpoint.getUriTemplate() != null) {
return getURISupplier(endpoint.getUriTemplate());
return getURISupplier(application, endpoint.getUriTemplate());
}
throw new IllegalArgumentException("Invalid endpoint definition " + endpoint);
}

private WorkflowValueResolver<URI> getURISupplier(UriTemplate template) {
if (template.getLiteralUri() != null) {
return (w, t, n) -> template.getLiteralUri();
} else if (template.getLiteralUriTemplate() != null) {
return (w, t, n) ->
templateResolver().resolveTemplates(template.getLiteralUriTemplate(), w, t, n);
}
throw new IllegalArgumentException("Invalid uritemplate definition " + template);
}

private class ExpressionURISupplier implements WorkflowValueResolver<URI> {
private WorkflowValueResolver<String> expr;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@
*/
package io.serverlessworkflow.impl.executors.http;

import io.serverlessworkflow.api.types.SecretBasedAuthenticationPolicy;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.WorkflowModel;
import jakarta.ws.rs.client.Invocation.Builder;

public abstract class AbstractAuthProvider implements AuthProvider {
abstract class AbstractAuthProvider implements AuthProvider {

private static final String AUTH_HEADER_FORMAT = "%s %s";

Expand All @@ -37,19 +35,6 @@ public Builder build(
return builder;
}

protected final String checkSecret(
Workflow workflow, SecretBasedAuthenticationPolicy secretPolicy) {
String secretName = secretPolicy.getUse();
return workflow.getUse().getSecrets().stream()
.filter(s -> s.equals(secretName))
.findAny()
.orElseThrow(() -> new IllegalStateException("Secret " + secretName + " does not exist"));
}

protected final String find(WorkflowContext context, String secretName, String prop) {
return context.definition().application().secretManager().secret(secretName).get(prop);
}

protected abstract String authScheme();

protected abstract String authParameter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
*/
package io.serverlessworkflow.impl.executors.http;

import static io.serverlessworkflow.impl.WorkflowUtils.checkSecret;
import static io.serverlessworkflow.impl.WorkflowUtils.secretProp;
import static io.serverlessworkflow.impl.executors.http.SecretKeys.PASSWORD;
import static io.serverlessworkflow.impl.executors.http.SecretKeys.USER;

import io.serverlessworkflow.api.types.BasicAuthenticationPolicy;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.TaskContext;
Expand Down Expand Up @@ -44,8 +49,8 @@ public BasicAuthProvider(
} else if (authPolicy.getBasic().getBasicAuthenticationPolicySecret() != null) {
String secretName =
checkSecret(workflow, authPolicy.getBasic().getBasicAuthenticationPolicySecret());
userFilter = (w, t, m) -> find(w, secretName, "username");
passwordFilter = (w, t, m) -> find(w, secretName, "password");
userFilter = (w, t, m) -> secretProp(w, secretName, USER);
passwordFilter = (w, t, m) -> secretProp(w, secretName, PASSWORD);
} else {
throw new IllegalStateException("Both secret and properties are null for authorization");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
*/
package io.serverlessworkflow.impl.executors.http;

import static io.serverlessworkflow.impl.WorkflowUtils.checkSecret;
import static io.serverlessworkflow.impl.WorkflowUtils.secretProp;
import static io.serverlessworkflow.impl.executors.http.SecretKeys.TOKEN;

import io.serverlessworkflow.api.types.BearerAuthenticationPolicy;
import io.serverlessworkflow.api.types.BearerAuthenticationPolicyConfiguration;
import io.serverlessworkflow.api.types.Workflow;
Expand All @@ -39,7 +43,7 @@ public BearerAuthProvider(
tokenFilter = WorkflowUtils.buildStringFilter(app, token);
} else if (config.getBearerAuthenticationPolicySecret() != null) {
String secretName = checkSecret(workflow, config.getBearerAuthenticationPolicySecret());
tokenFilter = (w, t, m) -> find(w, secretName, "bearer");
tokenFilter = (w, t, m) -> secretProp(w, secretName, TOKEN);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2020-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.serverlessworkflow.impl.executors.http;

import static io.serverlessworkflow.impl.WorkflowUtils.checkSecret;

import io.serverlessworkflow.api.types.OAuth2AuthenticationData;
import io.serverlessworkflow.api.types.SecretBasedAuthenticationPolicy;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.WorkflowModel;
import io.serverlessworkflow.impl.WorkflowValueResolver;
import io.serverlessworkflow.impl.executors.http.auth.requestbuilder.AccessTokenProvider;
import io.serverlessworkflow.impl.executors.http.auth.requestbuilder.AccessTokenProviderFactory;
import io.serverlessworkflow.impl.executors.http.auth.requestbuilder.AuthRequestBuilder;
import java.util.Map;

abstract class CommonOAuthProvider extends AbstractAuthProvider {

private final WorkflowValueResolver<AccessTokenProvider> tokenProvider;

protected CommonOAuthProvider(WorkflowValueResolver<AccessTokenProvider> tokenProvider) {
this.tokenProvider = tokenProvider;
}

@Override
protected String authParameter(WorkflowContext workflow, TaskContext task, WorkflowModel model) {
return tokenProvider.apply(workflow, task, model).validateAndGet(workflow, task, model).token();
}

@Override
protected String authScheme() {
return "Bearer";
}

protected static OAuth2AuthenticationData fillFromMap(
OAuth2AuthenticationData data, Map<String, Object> secretMap) {
return data;
}

protected static WorkflowValueResolver<AccessTokenProvider> accessToken(
Workflow workflow,
OAuth2AuthenticationData authenticationData,
SecretBasedAuthenticationPolicy secret,
AuthRequestBuilder<?> builder) {
if (authenticationData != null) {
return AccessTokenProviderFactory.build(authenticationData, builder);
} else if (secret != null) {
return AccessTokenProviderFactory.build(checkSecret(workflow, secret), builder);
}
throw new IllegalStateException("Both policy and secret are null");
}
}
Loading