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
@@ -0,0 +1,18 @@
/*
* 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;

public record AuthorizationDescriptor(String scheme, String parameter) {}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class TaskContext implements TaskContextData {
private Instant completedAt;
private TransitionInfo transition;
private short retryAttempt;
private AuthorizationDescriptor authorization;

public TaskContext(
WorkflowModel input,
Expand Down Expand Up @@ -93,6 +94,14 @@ public WorkflowModel rawInput() {
return rawInput;
}

public AuthorizationDescriptor authorization() {
return authorization;
}

public void authorization(String scheme, String parameter) {
this.authorization = new AuthorizationDescriptor(scheme, parameter);
}

@Override
public TaskBase task() {
return task;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.additional.WorkflowAdditionalObject;
import io.serverlessworkflow.impl.config.ConfigManager;
import io.serverlessworkflow.impl.config.ConfigSecretManager;
import io.serverlessworkflow.impl.config.SecretManager;
import io.serverlessworkflow.impl.config.SystemPropertyConfigManager;
import io.serverlessworkflow.impl.events.EventConsumer;
Expand Down Expand Up @@ -316,7 +317,7 @@ public WorkflowApplication build() {
secretManager =
ServiceLoader.load(SecretManager.class)
.findFirst()
.orElseGet(() -> s -> configManager.config(s, String.class));
.orElseGet(() -> new ConfigSecretManager(configManager));
}
return new WorkflowApplication(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public static Builder authorization() {
return error(Errors.AUTHORIZATION.toString(), Errors.AUTHORIZATION.status());
}

public static Builder expression() {
return error("https://serverlessworkflow.io/spec/1.0.0/errors/expression", 400);
}

public static Builder communication(int status, TaskContext context, Exception ex) {
return communication(status, context, ex.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@ public interface WorkflowInstanceData {

WorkflowModel output();

WorkflowModel context();

<T> T outputAs(Class<T> clazz);
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ public WorkflowStatus status() {
return status.get();
}

@Override
public WorkflowModel context() {
return workflowContext.context();
}

@Override
public WorkflowModel output() {
CompletableFuture<WorkflowModel> future = futureRef.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
*/
package io.serverlessworkflow.impl.additional;

import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.TaskContextData;
import io.serverlessworkflow.impl.WorkflowContextData;

public class ConstantAdditionalObject<T> implements WorkflowAdditionalObject<T> {

Expand All @@ -27,7 +27,7 @@ public ConstantAdditionalObject(T object) {
}

@Override
public T apply(WorkflowContext t, TaskContext u) {
public T apply(WorkflowContextData t, TaskContextData u) {
return object;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
*/
package io.serverlessworkflow.impl.additional;

import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.TaskContextData;
import io.serverlessworkflow.impl.WorkflowContextData;
import java.util.function.Supplier;

public class SuppliedAdditionalObject<T> implements WorkflowAdditionalObject<T> {
Expand All @@ -28,7 +28,7 @@ public SuppliedAdditionalObject(Supplier<T> supplier) {
}

@Override
public T apply(WorkflowContext t, TaskContext u) {
public T apply(WorkflowContextData t, TaskContextData u) {
return supplier.get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
*/
package io.serverlessworkflow.impl.additional;

import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.TaskContextData;
import io.serverlessworkflow.impl.WorkflowContextData;
import java.util.function.BiFunction;

public interface WorkflowAdditionalObject<T> extends BiFunction<WorkflowContext, TaskContext, T> {}
public interface WorkflowAdditionalObject<T>
extends BiFunction<WorkflowContextData, TaskContextData, T> {}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@
import java.util.Optional;

public interface ConfigManager extends ServicePriority {

<T> Optional<T> config(String propName, Class<T> propClass);

Iterable<String> names();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.config;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConfigSecretManager implements SecretManager {

private final ConfigManager configManager;

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

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

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

private Map<String, String> buildMap(String secretName) {
Map<String, String> map = new HashMap<String, String>();
final String prefix = secretName + ".";
for (String name : configManager.names()) {
if (name.startsWith(prefix)) {
configManager
.config(name, String.class)
.ifPresent(v -> map.put(name.substring(prefix.length()), v));
}
}
return map;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
package io.serverlessworkflow.impl.config;

import io.serverlessworkflow.impl.ServicePriority;
import java.util.Optional;
import java.util.Map;

@FunctionalInterface
public interface SecretManager extends ServicePriority {
Optional<String> secret(String secretName);
Map<String, String> secret(String secretName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ protected <T> T convertComplex(String value, Class<T> propClass) {
throw new UnsupportedOperationException(
"Conversion of property " + value + " to class " + propClass + " is not supported");
}

@Override
public Iterable<String> names() {
return System.getProperties().keySet().stream().map(Object::toString).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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 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 {

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

@Override
public Builder build(
Builder builder, WorkflowContext workflow, TaskContext task, WorkflowModel model) {
String scheme = authScheme();
String parameter = authParameter(workflow, task, model);
task.authorization(scheme, parameter);
builder.header(
AuthProviderFactory.AUTH_HEADER_NAME, String.format(AUTH_HEADER_FORMAT, scheme, parameter));
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(
WorkflowContext workflow, TaskContext task, WorkflowModel model);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,6 @@
import jakarta.ws.rs.client.Invocation;

interface AuthProvider {

default void preRequest(
Invocation.Builder builder, WorkflowContext workflow, TaskContext task, WorkflowModel model) {
// Default implementation does nothing
}

default void postRequest(WorkflowContext workflow, TaskContext task, WorkflowModel model) {
// Default implementation does nothing
}

Invocation.Builder build(
Invocation.Builder builder, WorkflowContext workflow, TaskContext task, WorkflowModel model);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@
import io.serverlessworkflow.impl.WorkflowModel;
import io.serverlessworkflow.impl.WorkflowUtils;
import io.serverlessworkflow.impl.WorkflowValueResolver;
import jakarta.ws.rs.client.Invocation.Builder;
import java.util.Base64;

class BasicAuthProvider implements AuthProvider {
class BasicAuthProvider extends AbstractAuthProvider {

private static final String BASIC_TOKEN = "Basic %s";
private static final String USER_PASSWORD = "%s:%s";

private WorkflowValueResolver<String> userFilter;
private WorkflowValueResolver<String> passwordFilter;
private final WorkflowValueResolver<String> userFilter;
private final WorkflowValueResolver<String> passwordFilter;

public BasicAuthProvider(
WorkflowApplication app, Workflow workflow, BasicAuthenticationPolicy authPolicy) {
Expand All @@ -44,24 +42,29 @@ public BasicAuthProvider(
WorkflowUtils.buildStringFilter(
app, authPolicy.getBasic().getBasicAuthenticationProperties().getPassword());
} else if (authPolicy.getBasic().getBasicAuthenticationPolicySecret() != null) {
throw new UnsupportedOperationException("Secrets are still not supported");
String secretName =
checkSecret(workflow, authPolicy.getBasic().getBasicAuthenticationPolicySecret());
userFilter = (w, t, m) -> find(w, secretName, "username");
passwordFilter = (w, t, m) -> find(w, secretName, "password");
} else {
throw new IllegalStateException("Both secret and properties are null for authorization");
}
}

@Override
public Builder build(
Builder builder, WorkflowContext workflow, TaskContext task, WorkflowModel model) {
builder.header(
AuthProviderFactory.AUTH_HEADER_NAME,
String.format(
BASIC_TOKEN,
Base64.getEncoder()
.encode(
String.format(
USER_PASSWORD,
userFilter.apply(workflow, task, model),
passwordFilter.apply(workflow, task, model))
.getBytes())));
return builder;
protected String authParameter(WorkflowContext workflow, TaskContext task, WorkflowModel model) {
return new String(
Base64.getEncoder()
.encode(
String.format(
USER_PASSWORD,
userFilter.apply(workflow, task, model),
passwordFilter.apply(workflow, task, model))
.getBytes()));
}

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