This is an automated email from the ASF dual-hosted git repository.

tdiesler pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new edb78e8fafd CAMEL-21899: camel-oauth - openshift does not maintain 
http session
edb78e8fafd is described below

commit edb78e8fafd67d62c5877cab1053c67cd40da92c
Author: Thomas Diesler <tdies...@redhat.com>
AuthorDate: Tue Apr 15 12:42:31 2025 +0200

    CAMEL-21899: camel-oauth - openshift does not maintain http session
---
 components/camel-oauth/helm/README.md              | 21 ++++++++++++
 components/camel-oauth/helm/etc/camel-realm.json   |  5 +--
 .../apache/camel/oauth/AbstractOAuthProcessor.java |  9 -----
 .../apache/camel/oauth/InMemorySessionStore.java   | 14 ++++++++
 .../apache/camel/oauth/OAuthCodeFlowCallback.java  |  3 +-
 .../apache/camel/oauth/OAuthCodeFlowProcessor.java | 32 +++++++++++++++---
 .../apache/camel/oauth/OAuthLogoutProcessor.java   |  7 ++--
 .../apache/camel/test/oauth/SSLCertTrustTest.java  | 38 +++++++++++++++++++++-
 .../modules/ROOT/pages/camel-jbang-kubernetes.adoc |  7 +---
 .../commands/kubernetes/traits/IngressTrait.java   |  9 +++--
 10 files changed, 115 insertions(+), 30 deletions(-)

diff --git a/components/camel-oauth/helm/README.md 
b/components/camel-oauth/helm/README.md
index 6563035d3c2..c7f7bbedad5 100644
--- a/components/camel-oauth/helm/README.md
+++ b/components/camel-oauth/helm/README.md
@@ -214,3 +214,24 @@ Verify access to the OIDC configuration
 ```
 curl -s 
https://keycloak.${OPENSHIFT_HOSTNAME}/realms/camel/.well-known/openid-configuration
 | jq .
 ```
+
+### Modify Keycloak OIDC Config for OpenShift
+
+```sh
+kcadm config credentials --server https://keycloak.${OPENSHIFT_HOSTNAME} 
--realm master --user admin --password admin
+
+# Show client config
+kcadm get clients -r camel | jq '.[] | select(.clientId=="camel-client")'
+
+CLIENT_ID=$(kcadm get clients -r camel --fields id,clientId | jq -r '.[] | 
select(.clientId=="camel-client").id')
+
+# Update redirect URIs
+kcadm update clients/$CLIENT_ID -r camel -s 
'redirectUris=["https://webapp.'${OPENSHIFT_HOSTNAME}'/auth"]'
+
+# Update post-logout redirect URIs
+kcadm update clients/$CLIENT_ID -r camel -s 
'attributes."post.logout.redirect.uris"="https://webapp.'${OPENSHIFT_HOSTNAME}'/"'
+
+kcadm get realms | jq '.[] | select(.realm=="camel")'
+
+kcadm get keys -r camel
+```
\ No newline at end of file
diff --git a/components/camel-oauth/helm/etc/camel-realm.json 
b/components/camel-oauth/helm/etc/camel-realm.json
index ef300a59d29..281d6960253 100644
--- a/components/camel-oauth/helm/etc/camel-realm.json
+++ b/components/camel-oauth/helm/etc/camel-realm.json
@@ -168,10 +168,11 @@
       "consentRequired" : false,
       "fullScopeAllowed" : false,
       "redirectUris": [
-        "http://127.0.0.1:8080/auth";
+        "https://example.local/auth";,
+        "https://example.k3s/auth";
       ],
       "attributes": {
-        "post.logout.redirect.uris": "http://127.0.0.1:8080/";
+        "post.logout.redirect.uris": 
"https://example.local/##https://example.k3s/";
       }
     },
     {
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/AbstractOAuthProcessor.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/AbstractOAuthProcessor.java
index 25614aee067..a5dac7ad693 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/AbstractOAuthProcessor.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/AbstractOAuthProcessor.java
@@ -26,8 +26,6 @@ import org.apache.camel.Processor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static org.apache.camel.oauth.OAuth.CAMEL_OAUTH_COOKIE;
-
 public abstract class AbstractOAuthProcessor implements Processor {
 
     protected final Logger log = LoggerFactory.getLogger(getClass());
@@ -71,11 +69,4 @@ public abstract class AbstractOAuthProcessor implements 
Processor {
         msg.setHeader("Location", redirectUrl);
         msg.setBody("");
     }
-
-    protected void setSessionCookie(Message msg, OAuthSession session) {
-        var sessionId = session.getSessionId();
-        var cookie = "%s=%s; Path=/; HttpOnly; SameSite=None; 
Secure".formatted(CAMEL_OAUTH_COOKIE, sessionId);
-        msg.setHeader("Set-Cookie", cookie);
-        log.debug("Set-Cookie: {}", cookie);
-    }
 }
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/InMemorySessionStore.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/InMemorySessionStore.java
index 04ad43bc01a..22244eae6c9 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/InMemorySessionStore.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/InMemorySessionStore.java
@@ -24,6 +24,7 @@ import java.util.UUID;
 import java.util.stream.Collectors;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.Message;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,6 +70,8 @@ public class InMemorySessionStore implements 
OAuthSessionStore {
 
         var msg = exchange.getMessage();
         log.info("New OAuthSession: {}", sessionId);
+
+        setSessionCookie(msg, session);
         msg.setHeader(OAuth.CAMEL_OAUTH_SESSION_ID, sessionId);
 
         return session;
@@ -93,4 +96,15 @@ public class InMemorySessionStore implements 
OAuthSessionStore {
 
         return cookieMap;
     }
+
+    private void setSessionCookie(Message msg, OAuthSession session) {
+        var sessionId = session.getSessionId();
+        var cookieId = "%s=%s".formatted(CAMEL_OAUTH_COOKIE, sessionId);
+        if (msg.getHeader("Set-Cookie") != null) {
+            throw new IllegalStateException("Duplicate 'Set-Cookie' header");
+        }
+        var cookie = cookieId + "; Path=/; HttpOnly; SameSite=None; Secure";
+        msg.setHeader("Set-Cookie", cookie);
+        log.debug("Set-Cookie: {}", cookie);
+    }
 }
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
index d18c429d492..5ad86652f14 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
@@ -66,9 +66,8 @@ public class OAuthCodeFlowCallback extends 
AbstractOAuthProcessor {
             postLoginUrl = postLoginUrl.substring(0, lastSlashIdx + 1);
         }
 
-        setSessionCookie(msg, session);
         sendRedirect(msg, postLoginUrl);
 
-        log.info("{} - Done", procName);
+        log.info("{} - Redirect to {}", procName, postLoginUrl);
     }
 }
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
index 6a9cbf13f9e..537ca8188b2 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
@@ -17,6 +17,7 @@
 package org.apache.camel.oauth;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.Message;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,23 +50,46 @@ public class OAuthCodeFlowProcessor extends 
AbstractOAuthProcessor {
         //
         if (session.getUserProfile().isPresent()) {
             authenticateExistingUserProfile(oauth, session);
+            log.info("{} - Done", procName);
         }
 
         // Fallback to the authorization code flow
         //
         if (session.getUserProfile().isEmpty()) {
 
-            var postLoginUrl = msg.getHeader(Exchange.HTTP_URL, String.class);
-            session.putValue("OAuthPostLoginUrl", postLoginUrl);
+            session.putValue("OAuthPostLoginUrl", getPostLoginUrl(msg));
 
             var redirectUri = getRequiredProperty(exchange.getContext(), 
CAMEL_OAUTH_REDIRECT_URI);
             var params = new OAuthCodeFlowParams().setRedirectUri(redirectUri);
             var authRequestUrl = oauth.buildCodeFlowAuthRequestUrl(params);
 
-            setSessionCookie(msg, session);
             sendRedirect(msg, authRequestUrl);
+            log.info("{} - Redirect to {}", procName, authRequestUrl);
         }
+    }
 
-        log.info("{} - Done", procName);
+    private String getPostLoginUrl(Message msg) {
+        String postLoginUrl;
+        var xProto = msg.getHeader("X-Forwarded-Proto", String.class);
+        var xHost = msg.getHeader("X-Forwarded-Host", String.class);
+        var xPort = msg.getHeader("X-Forwarded-Port", Integer.class);
+        if (xProto != null && xHost != null) {
+            postLoginUrl = xProto + "://" + xHost;
+            if (xPort != null) {
+                if (xProto.equals("https") && xPort != 443) {
+                    postLoginUrl += ":" + xPort;
+                }
+                if (xProto.equals("http") && xPort != 80) {
+                    postLoginUrl += ":" + xPort;
+                }
+            }
+            var httpPath = msg.getHeader(Exchange.HTTP_PATH, String.class);
+            if (httpPath != null && !httpPath.isEmpty()) {
+                postLoginUrl += httpPath;
+            }
+        } else {
+            postLoginUrl = msg.getHeader(Exchange.HTTP_URL, String.class);
+        }
+        return postLoginUrl;
     }
 }
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthLogoutProcessor.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthLogoutProcessor.java
index d0aea8626dd..435086dc1e1 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthLogoutProcessor.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthLogoutProcessor.java
@@ -26,6 +26,7 @@ public class OAuthLogoutProcessor extends 
AbstractOAuthProcessor {
     @Override
     public void process(Exchange exchange) {
         var context = exchange.getContext();
+        var msg = exchange.getMessage();
 
         findOAuth(context).ifPresent(oauth -> {
 
@@ -41,10 +42,8 @@ public class OAuthLogoutProcessor extends 
AbstractOAuthProcessor {
 
                 var logoutUrl = oauth.buildLogoutRequestUrl(params);
 
-                log.info("OAuth logout: {}", logoutUrl);
-                exchange.getMessage().setHeader(Exchange.HTTP_RESPONSE_CODE, 
302);
-                exchange.getMessage().setHeader("Location", logoutUrl);
-                exchange.getMessage().setBody("");
+                log.info("{} - Logout, then {}", procName, postLogoutUrl);
+                sendRedirect(msg, logoutUrl);
             });
         });
     }
diff --git 
a/components/camel-oauth/src/test/java/org/apache/camel/test/oauth/SSLCertTrustTest.java
 
b/components/camel-oauth/src/test/java/org/apache/camel/test/oauth/SSLCertTrustTest.java
index 31fbfccdbe5..1f94f1cbab2 100644
--- 
a/components/camel-oauth/src/test/java/org/apache/camel/test/oauth/SSLCertTrustTest.java
+++ 
b/components/camel-oauth/src/test/java/org/apache/camel/test/oauth/SSLCertTrustTest.java
@@ -16,11 +16,17 @@
  */
 package org.apache.camel.test.oauth;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.URL;
+import java.security.KeyStore;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManagerFactory;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Assumptions;
@@ -29,7 +35,37 @@ import org.junit.jupiter.api.Test;
 class SSLCertTrustTest extends AbstractKeycloakTest {
 
     @Test
-    void testTrustedCertificate() {
+    void testCheckClusterCertificateTrust() throws Exception {
+
+        // Load certificate to check
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        FileInputStream fis = new FileInputStream("helm/etc/cluster.crt");
+        X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
+
+        // Load default Java truststore
+        FileInputStream trustStream = new 
FileInputStream(System.getProperty("java.home") + "/lib/security/cacerts");
+        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        trustStore.load(trustStream, "changeit".toCharArray());
+
+        // Initialize TrustManager
+        TrustManagerFactory tmf = 
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init(trustStore);
+
+        try {
+            for (var tm : tmf.getTrustManagers()) {
+                var xtm = (javax.net.ssl.X509TrustManager) tm;
+                xtm.checkServerTrusted(new X509Certificate[] { cert }, "RSA");
+            }
+        } catch (CertificateException ex) {
+            System.err.println("Untrusted, because of: " + ex);
+            return;
+        }
+
+        System.out.println("Trusted");
+    }
+
+    @Test
+    void testCheckKeycloakCertificateTrust() {
         var admin = new KeycloakAdmin(new 
KeycloakAdmin.AdminParams(KEYCLOAK_BASE_URL));
         Assumptions.assumeTrue(admin.isKeycloakRunning(), "Keycloak is not 
running");
         Assertions.assertDoesNotThrow(() -> connectToUrl(KEYCLOAK_BASE_URL), 
"Certificate should be trusted");
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
index cac96c85cec..58aec45a7f7 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
@@ -103,9 +103,6 @@ The Camel JBang Kubernetes export command provides several 
options to customize
 |=======================================================================
 |Option |Description
 
-|--trait-profile
-|The trait profile to use for the deployment.
-
 |--service-account
 |The service account used to run the application.
 
@@ -1135,8 +1132,6 @@ spec:
 
 The Route trait enhances the Kubernetes manifest with a Route resource to 
expose the application to the outside world. This requires the presence in the 
Kubernetes manifest of a Service Resource.
 
-NOTE: You need to enable the OpenShift profile trait with 
`--trait-profile=openshift` option.
-
 The Route trait provides the following configuration options:
 
 [cols="2m,1m,5a"]
@@ -1193,7 +1188,7 @@ You may specify these options with the export command to 
customize the Route Res
 
 [source,bash]
 ----
-camel kubernetes export Sample.java --trait-profile=openshift -t 
route.enabled=true --trait route.host=example.com -t route.tls-termination=edge 
-t route.tls-certificate=file:/tmp/tls.crt -t route.tls-key=file:/tmp/tls.key
+camel kubernetes export Sample.java --cluster-type=openshift -t 
route.enabled=true --trait route.host=example.com -t route.tls-termination=edge 
-t route.tls-certificate=file:/tmp/tls.crt -t route.tls-key=file:/tmp/tls.key
 ----
 
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/IngressTrait.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/IngressTrait.java
index e1eb76f4f20..62881712e38 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/IngressTrait.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/IngressTrait.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.kubernetes.traits;
 
+import java.util.List;
 import java.util.Optional;
 
 import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath;
@@ -86,8 +87,9 @@ public class IngressTrait extends BaseTrait {
                 .endBackend()
                 .build();
 
+        var ingressHost = 
Optional.ofNullable(ingressTrait.getHost()).orElse(DEFAULT_INGRESS_HOST);
         IngressRule rule = new IngressRuleBuilder()
-                
.withHost(Optional.ofNullable(ingressTrait.getHost()).orElse(DEFAULT_INGRESS_HOST))
+                .withHost(ingressHost)
                 .withNewHttp()
                 .withPaths(path)
                 .endHttp()
@@ -99,7 +101,10 @@ public class IngressTrait extends BaseTrait {
                 .withRules(rule)
                 .endSpec();
 
-        if (ingressTrait.getTlsHosts() != null && 
ingressTrait.getTlsSecretName() != null) {
+        if (ingressTrait.getTlsSecretName() != null) {
+            if (ingressTrait.getTlsHosts() == null && !ingressHost.isEmpty()) {
+                ingressTrait.setTlsHosts(List.of(ingressHost));
+            }
             IngressTLS tls = new IngressTLSBuilder()
                     .withHosts(ingressTrait.getTlsHosts())
                     .withSecretName(ingressTrait.getTlsSecretName())

Reply via email to