diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/DefaultAuthProviderFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/DefaultAuthProviderFactory.java index 59d8f193f..3d68e4b0e 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/DefaultAuthProviderFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/DefaultAuthProviderFactory.java @@ -18,8 +18,6 @@ import io.serverlessworkflow.api.types.AuthenticationPolicyUnion; import io.serverlessworkflow.api.types.EndpointConfiguration; import io.serverlessworkflow.api.types.ReferenceableAuthenticationPolicy; -import io.serverlessworkflow.api.types.Use; -import io.serverlessworkflow.api.types.UseAuthentications; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowDefinition; @@ -46,35 +44,10 @@ public Optional getAuth( @Override public Optional getAuth( WorkflowDefinition definition, ReferenceableAuthenticationPolicy auth, String method) { - if (auth == null) { - return Optional.empty(); - } - if (auth.getAuthenticationPolicyReference() != null) { - return buildFromReference( - definition.application(), - definition.workflow(), - auth.getAuthenticationPolicyReference().getUse(), - method); - } else if (auth.getAuthenticationPolicy() != null) { - return buildFromPolicy( - definition.application(), definition.workflow(), auth.getAuthenticationPolicy(), method); - } - return Optional.empty(); - } - - private Optional buildFromReference( - WorkflowApplication app, Workflow workflow, String use, String method) { - Use useInfo = workflow.getUse(); - if (useInfo == null) { - return Optional.empty(); - } - UseAuthentications authInfo = useInfo.getAuthentications(); - return authInfo == null - ? Optional.empty() - : authInfo.getAdditionalProperties().entrySet().stream() - .filter(s -> s.getKey().equals(use)) - .findAny() - .flatMap(e -> buildFromPolicy(app, workflow, e.getValue(), method)); + return OAuthUtils.resolvePolicy(definition.workflow(), auth) + .flatMap( + policy -> + buildFromPolicy(definition.application(), definition.workflow(), policy, method)); } private Optional buildFromPolicy( @@ -94,16 +67,12 @@ private Optional buildFromPolicy( return Optional.of( new DigestAuthProvider( app, workflow, authenticationPolicy.getDigestAuthenticationPolicy(), method)); - } else if (authenticationPolicy.getOAuth2AuthenticationPolicy() != null) { - return Optional.of( - new OAuth2AuthProvider( - app, workflow, authenticationPolicy.getOAuth2AuthenticationPolicy())); - } else if (authenticationPolicy.getOpenIdConnectAuthenticationPolicy() != null) { - return Optional.of( - new OpenIdAuthProvider( - app, workflow, authenticationPolicy.getOpenIdConnectAuthenticationPolicy())); } - - return Optional.empty(); + return OAuthUtils.from(authenticationPolicy) + .map( + policyData -> + policyData.scheme() == OAuthScheme.OPENID_CONNECT + ? new OpenIdAuthProvider(app, workflow, policyData) + : new OAuth2AuthProvider(app, workflow, policyData)); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuth2AuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuth2AuthProvider.java index 9831051df..424fc9aca 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuth2AuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuth2AuthProvider.java @@ -15,19 +15,18 @@ */ package io.serverlessworkflow.impl.auth; -import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.WorkflowApplication; class OAuth2AuthProvider extends CommonOAuthProvider { public OAuth2AuthProvider( - WorkflowApplication application, Workflow workflow, OAuth2AuthenticationPolicy authPolicy) { + WorkflowApplication application, Workflow workflow, OAuthPolicyData policyData) { super( accessToken( workflow, - authPolicy.getOauth2().getOAuth2ConnectAuthenticationProperties(), - authPolicy.getOauth2().getOAuth2AuthenticationPolicySecret(), + policyData.data(), + policyData.secret(), new OAuthRequestBuilder(application))); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthPolicyData.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthPolicyData.java new file mode 100644 index 000000000..2ea2080fb --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthPolicyData.java @@ -0,0 +1,29 @@ +/* + * 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.auth; + +import io.serverlessworkflow.api.types.AuthenticationPolicyUnion; +import io.serverlessworkflow.api.types.OAuth2AuthenticationData; +import io.serverlessworkflow.api.types.SecretBasedAuthenticationPolicy; +import java.util.Optional; + +public record OAuthPolicyData( + OAuth2AuthenticationData data, SecretBasedAuthenticationPolicy secret, OAuthScheme scheme) { + + public static Optional from(AuthenticationPolicyUnion policy) { + return OAuthUtils.from(policy); + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthScheme.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthScheme.java new file mode 100644 index 000000000..eaa353f2f --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthScheme.java @@ -0,0 +1,21 @@ +/* + * 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.auth; + +public enum OAuthScheme { + OAUTH2, + OPENID_CONNECT +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthUtils.java new file mode 100644 index 000000000..c73ce4b23 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthUtils.java @@ -0,0 +1,74 @@ +/* + * 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.auth; + +import io.serverlessworkflow.api.types.AuthenticationPolicyUnion; +import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy; +import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicy; +import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.ReferenceableAuthenticationPolicy; +import io.serverlessworkflow.api.types.Use; +import io.serverlessworkflow.api.types.Workflow; +import java.util.Optional; + +public class OAuthUtils { + + private OAuthUtils() {} + + public static Optional from(AuthenticationPolicyUnion policy) { + if (policy == null) { + return Optional.empty(); + } + OAuth2AuthenticationPolicy oauth2 = policy.getOAuth2AuthenticationPolicy(); + if (oauth2 != null) { + OAuth2AuthenticationPolicyConfiguration config = oauth2.getOauth2(); + if (config != null) { + return Optional.of( + new OAuthPolicyData( + config.getOAuth2ConnectAuthenticationProperties(), + config.getOAuth2AuthenticationPolicySecret(), + OAuthScheme.OAUTH2)); + } + } + OpenIdConnectAuthenticationPolicy oidc = policy.getOpenIdConnectAuthenticationPolicy(); + if (oidc != null) { + OpenIdConnectAuthenticationPolicyConfiguration config = oidc.getOidc(); + if (config != null) { + return Optional.of( + new OAuthPolicyData( + config.getOpenIdConnectAuthenticationProperties(), + config.getOpenIdConnectAuthenticationPolicySecret(), + OAuthScheme.OPENID_CONNECT)); + } + } + return Optional.empty(); + } + + public static Optional resolvePolicy( + Workflow workflow, ReferenceableAuthenticationPolicy auth) { + if (auth == null) { + return Optional.empty(); + } + if (auth.getAuthenticationPolicyReference() != null) { + String use = auth.getAuthenticationPolicyReference().getUse(); + return Optional.ofNullable(workflow.getUse()) + .map(Use::getAuthentications) + .map(a -> a.getAdditionalProperties().get(use)); + } + return Optional.ofNullable(auth.getAuthenticationPolicy()); + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OpenIdAuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OpenIdAuthProvider.java index dc7db0548..197b23c49 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OpenIdAuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OpenIdAuthProvider.java @@ -15,21 +15,18 @@ */ package io.serverlessworkflow.impl.auth; -import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicy; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.WorkflowApplication; class OpenIdAuthProvider extends CommonOAuthProvider { public OpenIdAuthProvider( - WorkflowApplication application, - Workflow workflow, - OpenIdConnectAuthenticationPolicy authPolicy) { + WorkflowApplication application, Workflow workflow, OAuthPolicyData policyData) { super( accessToken( workflow, - authPolicy.getOidc().getOpenIdConnectAuthenticationProperties(), - authPolicy.getOidc().getOpenIdConnectAuthenticationPolicySecret(), + policyData.data(), + policyData.secret(), new OpenIdRequestBuilder(application))); } } diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/OAuthUtilsTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/OAuthUtilsTest.java new file mode 100644 index 000000000..69dbf7f20 --- /dev/null +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/OAuthUtilsTest.java @@ -0,0 +1,123 @@ +/* + * 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.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.serverlessworkflow.api.types.AuthenticationPolicyUnion; +import io.serverlessworkflow.api.types.BasicAuthenticationPolicy; +import io.serverlessworkflow.api.types.OAuth2AuthenticationData; +import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy; +import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.OAuth2ConnectAuthenticationProperties; +import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicy; +import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.SecretBasedAuthenticationPolicy; +import io.serverlessworkflow.impl.auth.OAuthPolicyData; +import io.serverlessworkflow.impl.auth.OAuthScheme; +import io.serverlessworkflow.impl.auth.OAuthUtils; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +public class OAuthUtilsTest { + + @Test + void fromNullReturnsEmpty() { + assertEquals(Optional.empty(), OAuthUtils.from(null)); + } + + @Test + void fromNonOAuthPolicyReturnsEmpty() { + AuthenticationPolicyUnion union = + new AuthenticationPolicyUnion() + .withBasicAuthenticationPolicy(new BasicAuthenticationPolicy()); + assertTrue(OAuthUtils.from(union).isEmpty()); + } + + @Test + void fromOAuth2InlineData() { + OAuth2ConnectAuthenticationProperties props = new OAuth2ConnectAuthenticationProperties(); + AuthenticationPolicyUnion union = + new AuthenticationPolicyUnion() + .withOAuth2AuthenticationPolicy( + new OAuth2AuthenticationPolicy() + .withOauth2( + new OAuth2AuthenticationPolicyConfiguration() + .withOAuth2ConnectAuthenticationProperties(props))); + Optional result = OAuthUtils.from(union); + assertTrue(result.isPresent()); + OAuthPolicyData data = result.get(); + assertEquals(OAuthScheme.OAUTH2, data.scheme()); + assertEquals(props, data.data()); + assertNull(data.secret()); + } + + @Test + void fromOAuth2Secret() { + SecretBasedAuthenticationPolicy secret = new SecretBasedAuthenticationPolicy("mySecret"); + AuthenticationPolicyUnion union = + new AuthenticationPolicyUnion() + .withOAuth2AuthenticationPolicy( + new OAuth2AuthenticationPolicy() + .withOauth2( + new OAuth2AuthenticationPolicyConfiguration() + .withOAuth2AuthenticationPolicySecret(secret))); + Optional result = OAuthUtils.from(union); + assertTrue(result.isPresent()); + OAuthPolicyData data = result.get(); + assertEquals(OAuthScheme.OAUTH2, data.scheme()); + assertNull(data.data()); + assertEquals(secret, data.secret()); + } + + @Test + void fromOidcInlineData() { + OAuth2AuthenticationData oidcData = new OAuth2AuthenticationData(); + AuthenticationPolicyUnion union = + new AuthenticationPolicyUnion() + .withOpenIdConnectAuthenticationPolicy( + new OpenIdConnectAuthenticationPolicy() + .withOidc( + new OpenIdConnectAuthenticationPolicyConfiguration() + .withOpenIdConnectAuthenticationProperties(oidcData))); + Optional result = OAuthUtils.from(union); + assertTrue(result.isPresent()); + OAuthPolicyData data = result.get(); + assertEquals(OAuthScheme.OPENID_CONNECT, data.scheme()); + assertEquals(oidcData, data.data()); + assertNull(data.secret()); + } + + @Test + void fromOidcSecret() { + SecretBasedAuthenticationPolicy secret = new SecretBasedAuthenticationPolicy("oidcSecret"); + AuthenticationPolicyUnion union = + new AuthenticationPolicyUnion() + .withOpenIdConnectAuthenticationPolicy( + new OpenIdConnectAuthenticationPolicy() + .withOidc( + new OpenIdConnectAuthenticationPolicyConfiguration() + .withOpenIdConnectAuthenticationPolicySecret(secret))); + Optional result = OAuthUtils.from(union); + assertTrue(result.isPresent()); + OAuthPolicyData data = result.get(); + assertEquals(OAuthScheme.OPENID_CONNECT, data.scheme()); + assertNull(data.data()); + assertEquals(secret, data.secret()); + } +} diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/ResolvePolicyTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/ResolvePolicyTest.java new file mode 100644 index 000000000..7bd45b303 --- /dev/null +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/ResolvePolicyTest.java @@ -0,0 +1,101 @@ +/* + * 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.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.serverlessworkflow.api.types.AuthenticationPolicyReference; +import io.serverlessworkflow.api.types.AuthenticationPolicyUnion; +import io.serverlessworkflow.api.types.BearerAuthenticationPolicy; +import io.serverlessworkflow.api.types.BearerAuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.BearerAuthenticationProperties; +import io.serverlessworkflow.api.types.ReferenceableAuthenticationPolicy; +import io.serverlessworkflow.api.types.Use; +import io.serverlessworkflow.api.types.UseAuthentications; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.auth.OAuthUtils; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +public class ResolvePolicyTest { + + private static final AuthenticationPolicyUnion BEARER_POLICY = + new AuthenticationPolicyUnion() + .withBearerAuthenticationPolicy( + new BearerAuthenticationPolicy( + new BearerAuthenticationPolicyConfiguration() + .withBearerAuthenticationProperties( + new BearerAuthenticationProperties("test-token")))); + + @Test + void nullAuthReturnsNull() { + assertTrue(OAuthUtils.resolvePolicy(new Workflow(), null).isEmpty()); + } + + @Test + void inlinePolicyReturnsPolicyDirectly() { + ReferenceableAuthenticationPolicy auth = + new ReferenceableAuthenticationPolicy().withAuthenticationPolicy(BEARER_POLICY); + assertEquals(Optional.of(BEARER_POLICY), OAuthUtils.resolvePolicy(new Workflow(), auth)); + } + + @Test + void referenceResolvesFromWorkflowUseAuthentications() { + Workflow workflow = + new Workflow() + .withUse( + new Use() + .withAuthentications( + new UseAuthentications().withAdditionalProperty("myAuth", BEARER_POLICY))); + ReferenceableAuthenticationPolicy auth = + new ReferenceableAuthenticationPolicy() + .withAuthenticationPolicyReference(new AuthenticationPolicyReference("myAuth")); + assertEquals(Optional.of(BEARER_POLICY), OAuthUtils.resolvePolicy(workflow, auth)); + } + + @Test + void referenceWithNullUseReturnsEmpty() { + ReferenceableAuthenticationPolicy auth = + new ReferenceableAuthenticationPolicy() + .withAuthenticationPolicyReference(new AuthenticationPolicyReference("myAuth")); + assertTrue(OAuthUtils.resolvePolicy(new Workflow(), auth).isEmpty()); + } + + @Test + void referenceWithNullAuthenticationsReturnsEmpty() { + Workflow workflow = new Workflow().withUse(new Use()); + ReferenceableAuthenticationPolicy auth = + new ReferenceableAuthenticationPolicy() + .withAuthenticationPolicyReference(new AuthenticationPolicyReference("myAuth")); + assertTrue(OAuthUtils.resolvePolicy(workflow, auth).isEmpty()); + } + + @Test + void referenceToNonExistentKeyReturnsEmpty() { + Workflow workflow = + new Workflow() + .withUse( + new Use() + .withAuthentications( + new UseAuthentications() + .withAdditionalProperty("otherAuth", BEARER_POLICY))); + ReferenceableAuthenticationPolicy auth = + new ReferenceableAuthenticationPolicy() + .withAuthenticationPolicyReference(new AuthenticationPolicyReference("myAuth")); + assertTrue(OAuthUtils.resolvePolicy(workflow, auth).isEmpty()); + } +}