This is an automated email from the ASF dual-hosted git repository.
pefernan pushed a commit to branch quarkus-3.2LTS
in repository
https://gitbox.apache.org/repos/asf/incubator-kie-kogito-runtimes.git
The following commit(s) were added to refs/heads/quarkus-3.2LTS by this push:
new 419670c3e4 kie-issues#262: Spring-Boot 3.0.5 migration: Fix SVG Addon
(#3303)
419670c3e4 is described below
commit 419670c3e4d0cf731381ef86da8e6bba8799a57e
Author: Pere Fernández <[email protected]>
AuthorDate: Fri Dec 1 14:48:43 2023 +0100
kie-issues#262: Spring-Boot 3.0.5 migration: Fix SVG Addon (#3303)
* kie-issues#262: Spring-Boot 3.0.5 migration: Fix SVG Addon
* `keycloak-spring-boot-starter` removal in favour of springboot ouath2
* IT test fixes
* upgrade `quarkus-embedded-postgresql` version
---
kogito-build/kogito-dependencies-bom/pom.xml | 2 +-
springboot/addons/process-svg/pom.xml | 16 +------
.../kogito/svg/auth/PrincipalAuthTokenReader.java | 26 ++++++++++
.../kie/kogito/svg/auth/SpringBootAuthHelper.java | 56 ++++++++++++++++++++++
.../svg/auth/impl/JwtPrincipalAuthTokenReader.java | 39 +++++++++++++++
.../auth/impl/OIDCPrincipalAuthTokenReader.java | 39 +++++++++++++++
.../svg/dataindex/SpringBootDataIndexClient.java | 38 ++++-----------
.../dataindex/SpringBootDataIndexClientTest.java | 47 +++++++++++++-----
.../springboot/SignalProcessTest.java | 4 +-
9 files changed, 211 insertions(+), 56 deletions(-)
diff --git a/kogito-build/kogito-dependencies-bom/pom.xml
b/kogito-build/kogito-dependencies-bom/pom.xml
index 4b22a07ddf..bff9397dce 100644
--- a/kogito-build/kogito-dependencies-bom/pom.xml
+++ b/kogito-build/kogito-dependencies-bom/pom.xml
@@ -32,7 +32,7 @@
<version.io.quarkiverse.openapi.generator>2.2.9</version.io.quarkiverse.openapi.generator>
<version.io.quarkiverse.asyncapi>0.2.0</version.io.quarkiverse.asyncapi>
<version.io.quarkiverse.reactivemessaging.http>2.0.2</version.io.quarkiverse.reactivemessaging.http>
-
<version.io.quarkiverse.embedded.postgresql>0.0.9</version.io.quarkiverse.embedded.postgresql>
+
<version.io.quarkiverse.embedded.postgresql>0.1.0</version.io.quarkiverse.embedded.postgresql>
<version.com.github.haifengl.smile>1.5.2</version.com.github.haifengl.smile>
<version.com.github.javaparser>3.25.1</version.com.github.javaparser>
<version.com.fasterxml.jackson.datatype>2.15.2</version.com.fasterxml.jackson.datatype>
diff --git a/springboot/addons/process-svg/pom.xml
b/springboot/addons/process-svg/pom.xml
index ff6a94dbdd..6f415f81d4 100644
--- a/springboot/addons/process-svg/pom.xml
+++ b/springboot/addons/process-svg/pom.xml
@@ -31,18 +31,6 @@
<name>Kogito :: Add-Ons :: Process SVG :: Spring Boot Addon</name>
<description>Springboot Addon to interact with Process SVG
Service</description>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.keycloak.bom</groupId>
- <artifactId>keycloak-adapter-bom</artifactId>
- <version>${version.org.keycloak}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
<dependencies>
<dependency>
<groupId>org.kie.kogito</groupId>
@@ -62,8 +50,8 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-spring-boot-starter</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-oauth2-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
diff --git
a/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/PrincipalAuthTokenReader.java
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/PrincipalAuthTokenReader.java
new file mode 100644
index 0000000000..7a24ec72b9
--- /dev/null
+++
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/PrincipalAuthTokenReader.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.kie.kogito.svg.auth;
+
+public interface PrincipalAuthTokenReader<T> {
+
+ boolean acceptsPrincipal(Object principal);
+
+ String readToken(T principal);
+}
diff --git
a/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/SpringBootAuthHelper.java
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/SpringBootAuthHelper.java
new file mode 100644
index 0000000000..ef73f791d2
--- /dev/null
+++
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/SpringBootAuthHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.kie.kogito.svg.auth;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConditionalOnClass({ SecurityContextHolder.class })
+public class SpringBootAuthHelper {
+
+ private List<PrincipalAuthTokenReader> authTokenReaders;
+
+ public SpringBootAuthHelper(@Autowired List<PrincipalAuthTokenReader>
authTokenReaders) {
+ this.authTokenReaders = authTokenReaders;
+ }
+
+ public Optional<String> getAuthToken() {
+ return Optional.ofNullable(getToken());
+ }
+
+ private String getToken() {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+
+ if (securityContext == null || securityContext.getAuthentication() ==
null) {
+ return null;
+ }
+
+ Object principal = securityContext.getAuthentication().getPrincipal();
+
+ return this.authTokenReaders.stream().filter(reader ->
reader.acceptsPrincipal(principal)).findFirst()
+ .map(reader -> "Bearer " +
reader.readToken(principal)).orElse(null);
+ }
+}
diff --git
a/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/impl/JwtPrincipalAuthTokenReader.java
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/impl/JwtPrincipalAuthTokenReader.java
new file mode 100644
index 0000000000..dcb91f44a6
--- /dev/null
+++
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/impl/JwtPrincipalAuthTokenReader.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.kie.kogito.svg.auth.impl;
+
+import org.kie.kogito.svg.auth.PrincipalAuthTokenReader;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConditionalOnClass(Jwt.class)
+public class JwtPrincipalAuthTokenReader implements
PrincipalAuthTokenReader<Jwt> {
+
+ @Override
+ public boolean acceptsPrincipal(Object principal) {
+ return principal instanceof Jwt;
+ }
+
+ @Override
+ public String readToken(Jwt principal) {
+ return principal.getTokenValue();
+ }
+}
diff --git
a/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/impl/OIDCPrincipalAuthTokenReader.java
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/impl/OIDCPrincipalAuthTokenReader.java
new file mode 100644
index 0000000000..1187f44c60
--- /dev/null
+++
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/auth/impl/OIDCPrincipalAuthTokenReader.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.kie.kogito.svg.auth.impl;
+
+import org.kie.kogito.svg.auth.PrincipalAuthTokenReader;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConditionalOnClass({ OidcUser.class })
+public class OIDCPrincipalAuthTokenReader implements
PrincipalAuthTokenReader<OidcUser> {
+
+ @Override
+ public boolean acceptsPrincipal(Object principal) {
+ return principal instanceof OidcUser;
+ }
+
+ @Override
+ public String readToken(OidcUser principal) {
+ return principal.getIdToken().getTokenValue();
+ }
+}
diff --git
a/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/dataindex/SpringBootDataIndexClient.java
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/dataindex/SpringBootDataIndexClient.java
index 69f490e5ce..8f165cb170 100644
---
a/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/dataindex/SpringBootDataIndexClient.java
+++
b/springboot/addons/process-svg/src/main/java/org/kie/kogito/svg/dataindex/SpringBootDataIndexClient.java
@@ -20,10 +20,10 @@ package org.kie.kogito.svg.dataindex;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
-import jakarta.annotation.PostConstruct;
-import org.keycloak.KeycloakPrincipal;
import org.kie.kogito.svg.ProcessSVGException;
+import org.kie.kogito.svg.auth.SpringBootAuthHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,8 +32,6 @@ import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
@@ -41,6 +39,8 @@ import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.annotation.PostConstruct;
+
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonMap;
@@ -53,16 +53,18 @@ public class SpringBootDataIndexClient implements
DataIndexClient {
private RestTemplate restTemplate;
private ObjectMapper objectMapper;
- private boolean isKeycloakAdapterAvailable = false;
+ private Optional<SpringBootAuthHelper> authHelper;
@Autowired
public SpringBootDataIndexClient(
@Value("${kogito.dataindex.http.url:http://localhost:8180}")
String dataIndexHttpURL,
@Autowired(required = false) RestTemplate restTemplate,
- @Autowired ObjectMapper objectMapper) {
+ @Autowired ObjectMapper objectMapper,
+ @Autowired Optional<SpringBootAuthHelper> authHelper) {
this.dataIndexHttpURL = dataIndexHttpURL;
this.restTemplate = restTemplate;
this.objectMapper = objectMapper;
+ this.authHelper = authHelper;
}
@PostConstruct
@@ -71,21 +73,6 @@ public class SpringBootDataIndexClient implements
DataIndexClient {
restTemplate = new RestTemplate();
LOGGER.debug("No RestTemplate found, creating a default one");
}
- try {
-
Class.forName("org.springframework.security.core.context.SecurityContextHolder");
- Class.forName("org.keycloak.KeycloakPrincipal");
- setKeycloakAdapterAvailable(true);
- } catch (ClassNotFoundException exception) {
- LOGGER.debug("No Keycloak Adapter available, continue just
propagating received authorization header");
- }
- }
-
- public boolean isKeycloakAdapterAvailable() {
- return isKeycloakAdapterAvailable;
- }
-
- public void setKeycloakAdapterAvailable(boolean keycloakAdapterAvailable) {
- isKeycloakAdapterAvailable = keycloakAdapterAvailable;
}
@Override
@@ -120,13 +107,8 @@ public class SpringBootDataIndexClient implements
DataIndexClient {
}
protected String getAuthHeader(String authHeader) {
- if (isKeycloakAdapterAvailable()) {
- SecurityContext securityContext =
SecurityContextHolder.getContext();
- if (securityContext != null &&
- securityContext.getAuthentication() != null &&
- securityContext.getAuthentication().getPrincipal()
instanceof KeycloakPrincipal) {
- return "Bearer " + ((KeycloakPrincipal)
securityContext.getAuthentication().getPrincipal()).getKeycloakSecurityContext().getTokenString();
- }
+ if (authHelper.isPresent()) {
+ return authHelper.get().getAuthToken().orElse(authHeader);
}
return authHeader;
}
diff --git
a/springboot/addons/process-svg/src/test/java/org/kie/kogito/svg/dataindex/SpringBootDataIndexClientTest.java
b/springboot/addons/process-svg/src/test/java/org/kie/kogito/svg/dataindex/SpringBootDataIndexClientTest.java
index 6a613d8ae8..ac50a77770 100644
---
a/springboot/addons/process-svg/src/test/java/org/kie/kogito/svg/dataindex/SpringBootDataIndexClientTest.java
+++
b/springboot/addons/process-svg/src/test/java/org/kie/kogito/svg/dataindex/SpringBootDataIndexClientTest.java
@@ -19,13 +19,15 @@
package org.kie.kogito.svg.dataindex;
import java.util.List;
+import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
-import org.keycloak.KeycloakPrincipal;
-import org.keycloak.KeycloakSecurityContext;
import org.kie.kogito.svg.ProcessSVGException;
+import org.kie.kogito.svg.auth.SpringBootAuthHelper;
+import org.kie.kogito.svg.auth.impl.JwtPrincipalAuthTokenReader;
+import org.kie.kogito.svg.auth.impl.OIDCPrincipalAuthTokenReader;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpEntity;
@@ -33,6 +35,9 @@ import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.core.oidc.OidcIdToken;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
@@ -80,7 +85,11 @@ public class SpringBootDataIndexClientTest {
@BeforeEach
void setUp() {
- client = new SpringBootDataIndexClient("data-indexURL", restTemplate,
objectMapper);
+ client = buildClient(Optional.empty());
+ }
+
+ private SpringBootDataIndexClient
buildClient(Optional<SpringBootAuthHelper> authHelper) {
+ return new SpringBootDataIndexClient("data-indexURL", restTemplate,
objectMapper, authHelper);
}
@Test
@@ -117,27 +126,43 @@ public class SpringBootDataIndexClientTest {
}
@Test
- public void testAuthHeaderWithSecurityContext() {
+ public void testAuthHeaderWithSecurityContextOidcUserPrincipal() {
+ String token = "testToken";
+ SecurityContext securityContextMock = mock(SecurityContext.class);
+ Authentication authenticationMock = mock(Authentication.class);
+ OidcUser principalMock = mock(OidcUser.class);
+ OidcIdToken tokenMock = mock(OidcIdToken.class);
+
+
when(securityContextMock.getAuthentication()).thenReturn(authenticationMock);
+ when(authenticationMock.getPrincipal()).thenReturn(principalMock);
+ when(principalMock.getIdToken()).thenReturn(tokenMock);
+ when(tokenMock.getTokenValue()).thenReturn(token);
+
+ SecurityContextHolder.setContext(securityContextMock);
+ client = buildClient(Optional.of(new SpringBootAuthHelper(List.of(new
OIDCPrincipalAuthTokenReader(), new JwtPrincipalAuthTokenReader()))));
+ assertThat(client.getAuthHeader("")).isEqualTo("Bearer " + token);
+ }
+
+ @Test
+ public void testAuthHeaderWithSecurityContextJwtPrincipal() {
String token = "testToken";
SecurityContext securityContextMock = mock(SecurityContext.class);
Authentication authenticationMock = mock(Authentication.class);
- KeycloakPrincipal principalMock = mock(KeycloakPrincipal.class);
- KeycloakSecurityContext keycloakSecurityContextMock =
mock(KeycloakSecurityContext.class);
+ Jwt principalMock = mock(Jwt.class);
when(securityContextMock.getAuthentication()).thenReturn(authenticationMock);
when(authenticationMock.getPrincipal()).thenReturn(principalMock);
-
when(principalMock.getKeycloakSecurityContext()).thenReturn(keycloakSecurityContextMock);
- when(keycloakSecurityContextMock.getTokenString()).thenReturn(token);
+ when(principalMock.getTokenValue()).thenReturn(token);
SecurityContextHolder.setContext(securityContextMock);
- client.setKeycloakAdapterAvailable(true);
+ client = buildClient(Optional.of(new SpringBootAuthHelper(List.of(new
OIDCPrincipalAuthTokenReader(), new JwtPrincipalAuthTokenReader()))));
assertThat(client.getAuthHeader("")).isEqualTo("Bearer " + token);
}
@Test
- public void testAuthHeaderWithoutKeycloakSecurityContext() {
+ public void testAuthHeaderWithoutSecurityContext() {
String authHeader = "Bearer testToken";
- client.setKeycloakAdapterAvailable(false);
+ client = buildClient(Optional.of(new SpringBootAuthHelper(List.of(new
OIDCPrincipalAuthTokenReader(), new JwtPrincipalAuthTokenReader()))));
assertThat(client.getAuthHeader(authHeader)).isEqualTo(authHeader);
}
}
diff --git
a/springboot/integration-tests/src/it/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/SignalProcessTest.java
b/springboot/integration-tests/src/it/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/SignalProcessTest.java
index 8cdb8bb6cb..ba7515c73b 100644
---
a/springboot/integration-tests/src/it/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/SignalProcessTest.java
+++
b/springboot/integration-tests/src/it/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/SignalProcessTest.java
@@ -48,10 +48,10 @@ class SignalProcessTest extends BaseRestTest {
given()
.contentType(ContentType.JSON)
.when()
- .get("/signalStart/")
+ .get("/signalStart")
.then()
.statusCode(200)
- .body("$.size()", is(1))
+ .body("size()", is(1))
.body("[0].message", equalTo("hello world"));
}
@Test
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]