keycloak-uncached

Details

diff --git a/adapters/oidc/fuse7/camel-undertow/pom.xml b/adapters/oidc/fuse7/camel-undertow/pom.xml
new file mode 100644
index 0000000..4be87d5
--- /dev/null
+++ b/adapters/oidc/fuse7/camel-undertow/pom.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-fuse7-integration-pom</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>4.0.0.Beta3-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-camel-undertow</artifactId>
+    <name>Keycloak Fuse 7.0 Adapter - Camel + Undertow</name>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <keycloak.osgi.export>
+            org.keycloak.adapters.camel.undertow;version="${project.version}"
+        </keycloak.osgi.export>
+        <keycloak.osgi.import>
+            org.keycloak.*;version="${project.version}",
+            org.apache.camel.*,
+            org.apache.camel.component.undertow,
+            io.undertow.*,
+            *;resolution:=optional
+        </keycloak.osgi.import>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.enterprise</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.web</groupId>
+            <artifactId>pax-web-runtime</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.web</groupId>
+            <artifactId>pax-web-spi</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.web</groupId>
+            <artifactId>pax-web-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-servlet</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-undertow-adapter</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-undertow</artifactId>
+            <version>2.21.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core</artifactId>
+            <version>2.21.0</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+
+            <!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <instructions>
+                        <Bundle-Name>${project.name}</Bundle-Name>
+                        <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+                        <Import-Package>${keycloak.osgi.import}</Import-Package>
+                        <Export-Package>${keycloak.osgi.export}</Export-Package>
+                        <Export-Service>org.apache.camel.spi.ComponentResolver;component=undertow-keycloak</Export-Service>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakComponent.java b/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakComponent.java
new file mode 100644
index 0000000..9b9219b
--- /dev/null
+++ b/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakComponent.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.keycloak.adapters.camel.undertow;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.component.undertow.RestUndertowHttpBinding;
+import org.apache.camel.component.undertow.UndertowComponent;
+import org.apache.camel.component.undertow.UndertowEndpoint;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.HostUtils;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class UndertowKeycloakComponent extends UndertowComponent {
+
+    public UndertowKeycloakComponent() {
+    }
+
+    public UndertowKeycloakComponent(CamelContext context) {
+        super(context);
+    }
+
+    @Override
+    protected UndertowEndpoint createEndpointInstance(URI endpointUri, UndertowComponent component) throws URISyntaxException {
+        return new UndertowKeycloakEndpoint(endpointUri.toString(), component);
+    }
+
+    // TODO: uncomment line below after backport of https://issues.apache.org/jira/browse/CAMEL-12514 into fuse
+//    @Override
+    protected String getComponentName() {
+        return "undertow-keycloak";
+    }
+
+    // TODO: remove all below this line after backport of https://issues.apache.org/jira/browse/CAMEL-12514 into fuse
+    @Override
+    public Consumer createConsumer(CamelContext camelContext, Processor processor, String verb, String basePath, String uriTemplate,
+                                   String consumes, String produces, RestConfiguration configuration, Map<String, Object> parameters) throws Exception {
+        return doCreateConsumer(camelContext, processor, verb, basePath, uriTemplate, consumes, produces, configuration, parameters, false);
+    }
+
+    @Override
+    public Consumer createApiConsumer(CamelContext camelContext, Processor processor, String contextPath,
+                                      RestConfiguration configuration, Map<String, Object> parameters) throws Exception {
+        // reuse the createConsumer method we already have. The api need to use GET and match on uri prefix
+        return doCreateConsumer(camelContext, processor, "GET", contextPath, null, null, null, configuration, parameters, true);
+    }
+
+    Consumer doCreateConsumer(CamelContext camelContext, Processor processor, String verb, String basePath, String uriTemplate,
+                              String consumes, String produces, RestConfiguration configuration, Map<String, Object> parameters, boolean api) throws Exception {
+        String path = basePath;
+        if (uriTemplate != null) {
+            // make sure to avoid double slashes
+            if (uriTemplate.startsWith("/")) {
+                path = path + uriTemplate;
+            } else {
+                path = path + "/" + uriTemplate;
+            }
+        }
+        path = FileUtil.stripLeadingSeparator(path);
+        String scheme = "http";
+        String host = "";
+        int port = 0;
+
+        RestConfiguration config = configuration;
+        if (config == null) {
+            config = camelContext.getRestConfiguration(getComponentName(), true);
+        }
+        if (config.getScheme() != null) {
+            scheme = config.getScheme();
+        }
+        if (config.getHost() != null) {
+            host = config.getHost();
+        }
+        int num = config.getPort();
+        if (num > 0) {
+            port = num;
+        }
+
+        // prefix path with context-path if configured in rest-dsl configuration
+        String contextPath = config.getContextPath();
+        if (ObjectHelper.isNotEmpty(contextPath)) {
+            contextPath = FileUtil.stripTrailingSeparator(contextPath);
+            contextPath = FileUtil.stripLeadingSeparator(contextPath);
+            if (ObjectHelper.isNotEmpty(contextPath)) {
+                path = contextPath + "/" + path;
+            }
+        }
+
+        // if no explicit hostname set then resolve the hostname
+        if (ObjectHelper.isEmpty(host)) {
+            if (config.getRestHostNameResolver() == RestConfiguration.RestHostNameResolver.allLocalIp) {
+                host = "0.0.0.0";
+            } else if (config.getRestHostNameResolver() == RestConfiguration.RestHostNameResolver.localHostName) {
+                host = HostUtils.getLocalHostName();
+            } else if (config.getRestHostNameResolver() == RestConfiguration.RestHostNameResolver.localIp) {
+                host = HostUtils.getLocalIp();
+            }
+        }
+
+        Map<String, Object> map = new HashMap<String, Object>();
+        // build query string, and append any endpoint configuration properties
+        if (config.getComponent() == null || config.getComponent().equals(getComponentName())) {
+            // setup endpoint options
+            if (config.getEndpointProperties() != null && !config.getEndpointProperties().isEmpty()) {
+                map.putAll(config.getEndpointProperties());
+            }
+        }
+
+        boolean explicitOptions = true;
+        // must use upper case for restrict
+        String restrict = verb.toUpperCase(Locale.US);
+        // allow OPTIONS in rest-dsl to allow clients to call the API and have responses with ALLOW headers
+        if (!restrict.contains("OPTIONS")) {
+            restrict += ",OPTIONS";
+            // this is not an explicit OPTIONS path in the rest-dsl
+            explicitOptions = false;
+        }
+
+        boolean cors = config.isEnableCORS();
+        if (cors) {
+            // allow HTTP Options as we want to handle CORS in rest-dsl
+            map.put("optionsEnabled", "true");
+        } else if (explicitOptions) {
+            // the rest-dsl is using OPTIONS
+            map.put("optionsEnabled", "true");
+        }
+
+        String query = URISupport.createQueryString(map);
+
+        String url;
+        if (api) {
+            url = getComponentName() + ":%s://%s:%s/%s?matchOnUriPrefix=true&httpMethodRestrict=%s";
+        } else {
+            url = getComponentName() + ":%s://%s:%s/%s?matchOnUriPrefix=false&httpMethodRestrict=%s";
+        }
+
+        // get the endpoint
+        url = String.format(url, scheme, host, port, path, restrict);
+
+        if (!query.isEmpty()) {
+            url = url + "&" + query;
+        }
+
+        UndertowEndpoint endpoint = camelContext.getEndpoint(url, UndertowEndpoint.class);
+        setProperties(camelContext, endpoint, parameters);
+
+        if (!map.containsKey("undertowHttpBinding")) {
+            // use the rest binding, if not using a custom http binding
+            endpoint.setUndertowHttpBinding(new RestUndertowHttpBinding());
+        }
+
+        // configure consumer properties
+        Consumer consumer = endpoint.createConsumer(processor);
+        if (config.getConsumerProperties() != null && !config.getConsumerProperties().isEmpty()) {
+            setProperties(camelContext, consumer, config.getConsumerProperties());
+        }
+
+        return consumer;
+    }
+}
diff --git a/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakConsumer.java b/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakConsumer.java
new file mode 100644
index 0000000..8ce2330
--- /dev/null
+++ b/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakConsumer.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.keycloak.adapters.camel.undertow;
+
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.AuthenticatedActionsHandler;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.NodesRegistrationManagement;
+import org.keycloak.adapters.PreAuthActionsHandler;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.keycloak.adapters.spi.AuthChallenge;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.adapters.spi.InMemorySessionIdMapper;
+import org.keycloak.adapters.spi.SessionIdMapper;
+import org.keycloak.adapters.undertow.KeycloakUndertowAccount;
+import org.keycloak.adapters.undertow.OIDCUndertowHttpFacade;
+import org.keycloak.adapters.undertow.SessionManagementBridge;
+import org.keycloak.adapters.undertow.UndertowCookieTokenStore;
+import org.keycloak.adapters.undertow.UndertowRequestAuthenticator;
+import org.keycloak.adapters.undertow.UndertowSessionTokenStore;
+import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
+import org.keycloak.enums.TokenStore;
+import io.undertow.security.api.SecurityContext;
+import io.undertow.security.idm.Account;
+import io.undertow.security.idm.Credential;
+import io.undertow.security.idm.IdentityManager;
+import io.undertow.security.impl.SecurityContextImpl;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.session.InMemorySessionManager;
+import io.undertow.server.session.SessionManager;
+import io.undertow.util.AttachmentKey;
+import io.undertow.util.StatusCodes;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.apache.camel.Processor;
+import org.apache.camel.component.undertow.UndertowConsumer;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class UndertowKeycloakConsumer extends UndertowConsumer {
+
+    private static final Logger LOG = Logger.getLogger(UndertowKeycloakConsumer.class.getName());
+
+    public static final AttachmentKey<KeycloakPrincipal> KEYCLOAK_PRINCIPAL_KEY = AttachmentKey.create(KeycloakPrincipal.class);
+
+    private static final IdentityManager IDENTITY_MANAGER = new IdentityManager() {
+        @Override
+        public Account verify(Account account) {
+            return account;
+        }
+
+        @Override
+        public Account verify(String id, Credential credential) {
+            throw new IllegalStateException("Should never be called in Keycloak flow");
+        }
+
+        @Override
+        public Account verify(Credential credential) {
+            throw new IllegalStateException("Should never be called in Keycloak flow");
+        }
+    };
+
+    protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
+
+    protected final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
+
+    private final UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
+
+    protected final AdapterDeploymentContext deploymentContext;
+
+    protected final SessionManager sessionManager;
+
+    protected final List<String> allowedRoles;
+
+    private final int confidentialPort;
+
+    private final Pattern skipPattern;
+
+    public UndertowKeycloakConsumer(UndertowKeycloakEndpoint endpoint, Processor processor, 
+      AdapterDeploymentContext deploymentContext, Pattern skipPattern, List<String> allowedRoles, int confidentialPort) {
+        super(endpoint, processor);
+        this.sessionManager = new InMemorySessionManager(endpoint.getEndpointUri());
+        this.deploymentContext = deploymentContext;
+        this.skipPattern = skipPattern;
+        this.confidentialPort = confidentialPort;
+        this.allowedRoles = allowedRoles == null ? Collections.<String>emptyList() : allowedRoles;
+    }
+
+    public int getConfidentialPort() {
+        return confidentialPort;
+    }
+
+    @Override
+    public void handleRequest(HttpServerExchange httpExchange) throws Exception {
+        if (shouldSkip(httpExchange.getRequestPath())) {
+            super.handleRequest(httpExchange);
+            return;
+        }
+
+        //perform only non-blocking operation on exchange
+        if (httpExchange.isInIoThread()) {
+            httpExchange.dispatch(this);
+            return;
+        }
+
+        OIDCUndertowHttpFacade facade = new OIDCUndertowHttpFacade(httpExchange);
+        KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
+
+        if (deployment == null || !deployment.isConfigured()) {
+            httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
+            LOG.fine("deployment not configured");
+            return;
+        }
+
+        LOG.fine("executing PreAuthActionsHandler");
+        SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager);
+        PreAuthActionsHandler preAuth = new PreAuthActionsHandler(bridge, deploymentContext, facade);
+        if (preAuth.handleRequest()) return;
+
+        SecurityContext securityContext = httpExchange.getSecurityContext();
+        if (securityContext == null) {
+            securityContext = new SecurityContextImpl(httpExchange, IDENTITY_MANAGER);
+        }
+        AdapterTokenStore tokenStore = getTokenStore(httpExchange, facade, deployment, securityContext);
+        tokenStore.checkCurrentToken();
+
+        LOG.fine("executing AuthenticatedActionsHandler");
+        RequestAuthenticator authenticator = new UndertowRequestAuthenticator(facade, deployment, confidentialPort, securityContext, httpExchange, tokenStore);
+        AuthOutcome outcome = authenticator.authenticate();
+
+        if (outcome == AuthOutcome.AUTHENTICATED) {
+            LOG.fine("AUTHENTICATED");
+            if (httpExchange.isResponseComplete()) {
+                return;
+            }
+            AuthenticatedActionsHandler actions = new AuthenticatedActionsHandler(deployment, facade);
+            if (actions.handledRequest()) {
+                return;
+            } else {
+                final Account authenticatedAccount = securityContext.getAuthenticatedAccount();
+                if (authenticatedAccount instanceof KeycloakUndertowAccount) {
+                    final KeycloakUndertowAccount kua = (KeycloakUndertowAccount) authenticatedAccount;
+                    httpExchange.putAttachment(KEYCLOAK_PRINCIPAL_KEY, (KeycloakPrincipal) kua.getPrincipal());
+                }
+
+                Set<String> roles = Optional
+                  .ofNullable(authenticatedAccount.getRoles())
+                  .orElse((Set<String>) Collections.EMPTY_SET);
+
+                LOG.log(Level.FINE, "Allowed roles: {0}, current roles: {1}", new Object[] {allowedRoles, roles});
+
+                if (isRoleAllowed(roles, httpExchange)) {
+                    super.handleRequest(httpExchange);
+                } else {
+                    httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
+                }
+
+                return;
+            }
+        }
+
+        AuthChallenge challenge = authenticator.getChallenge();
+        if (challenge != null) {
+            LOG.fine("challenge");
+            challenge.challenge(facade);
+            return;
+        }
+
+        httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
+    }
+
+    public boolean isRoleAllowed(Set<String> roles, HttpServerExchange httpExchange) throws Exception {
+        for (String role : allowedRoles) {
+            if (roles.contains(role)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    protected AdapterTokenStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, KeycloakDeployment deployment, SecurityContext securityContext) {
+        if (deployment.getTokenStore() == TokenStore.SESSION) {
+            return new UndertowSessionTokenStore(exchange, deployment, userSessionManagement, securityContext);
+        } else {
+            return new UndertowCookieTokenStore(facade, deployment, securityContext);
+        }
+    }
+
+    private boolean shouldSkip(String requestPath) {
+        return skipPattern != null && skipPattern.matcher(requestPath).matches();
+    }
+
+}
diff --git a/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakEndpoint.java b/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakEndpoint.java
new file mode 100644
index 0000000..a98d5ab
--- /dev/null
+++ b/adapters/oidc/fuse7/camel-undertow/src/main/java/org/keycloak/adapters/camel/undertow/UndertowKeycloakEndpoint.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.keycloak.adapters.camel.undertow;
+
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.representations.adapters.config.AdapterConfig;
+import io.undertow.server.HttpServerExchange;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.apache.camel.Consumer;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.component.undertow.UndertowComponent;
+import org.apache.camel.component.undertow.UndertowEndpoint;
+import static org.keycloak.adapters.camel.undertow.UndertowKeycloakConsumer.KEYCLOAK_PRINCIPAL_KEY;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class UndertowKeycloakEndpoint extends UndertowEndpoint {
+
+    private static final Logger LOG = Logger.getLogger(UndertowKeycloakEndpoint.class.getName());
+
+    private KeycloakConfigResolver configResolver;
+
+    private AdapterConfig adapterConfig;
+
+    private String skipPattern;
+
+    private List<String> allowedRoles = Collections.emptyList();
+
+    private int confidentialPort = 8443;
+
+    public UndertowKeycloakEndpoint(String uri, UndertowComponent component) {
+        super(uri, component);
+    }
+
+    public AdapterConfig getAdapterConfig() {
+        return adapterConfig;
+    }
+
+    public void setAdapterConfig(AdapterConfig adapterConfig) {
+        LOG.info("adapterConfig");
+        this.adapterConfig = adapterConfig;
+    }
+
+    public String getSkipPattern() {
+        return skipPattern;
+    }
+
+    public void setSkipPattern(String skipPattern) {
+        this.skipPattern = skipPattern;
+    }
+
+    public List<String> getAllowedRoles() {
+        return allowedRoles;
+    }
+
+    public void setAllowedRoles(List<String> allowedRoles) {
+        this.allowedRoles = allowedRoles;
+    }
+
+    public void setAllowedRoles(String allowedRoles) {
+        this.allowedRoles = allowedRoles == null ? null : Arrays.asList(allowedRoles.split("\\s*,\\s*"));
+    }
+
+    public int getConfidentialPort() {
+        return confidentialPort;
+    }
+
+    public void setConfidentialPort(int confidentialPort) {
+        this.confidentialPort = confidentialPort;
+    }
+
+    public KeycloakConfigResolver getConfigResolver() {
+        return configResolver;
+    }
+
+    public void setConfigResolver(KeycloakConfigResolver configResolver) {
+        this.configResolver = configResolver;
+    }
+
+    @Override
+    public Consumer createConsumer(Processor processor) throws Exception {
+        return new UndertowKeycloakConsumer(this, processor, getDeploymentContext(), getSkipPatternAsPattern(), computeAllowedRoles(), this.confidentialPort);
+    }
+
+    public List<String> computeAllowedRoles() {
+        List<String> res = this.allowedRoles == null ? Collections.<String>emptyList() : this.allowedRoles;
+        if (res.isEmpty()) {
+            LOG.warning("No roles were configured, Keycloak will deny every request");
+        }
+        LOG.log(Level.FINE, "Allowed roles: {0}", res);
+        return res;
+    }
+
+    @Override
+    public Exchange createExchange(HttpServerExchange httpExchange) throws Exception {
+        final Exchange res = super.createExchange(httpExchange);
+
+        KeycloakPrincipal principal = httpExchange.getAttachment(KEYCLOAK_PRINCIPAL_KEY);
+        LOG.log(Level.FINE, "principal: {0}", principal);
+        if (principal != null) {
+            res.setProperty(KeycloakPrincipal.class.getName(), principal);
+        }
+
+        return res;
+    }
+
+    private AdapterDeploymentContext getDeploymentContext() {
+        if (configResolver != null) {
+            LOG.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolver.getClass());
+            return new AdapterDeploymentContext(configResolver);
+        } else if (adapterConfig != null) {
+            KeycloakDeployment kd = KeycloakDeploymentBuilder.build(adapterConfig);
+            return new AdapterDeploymentContext(kd);
+        }
+
+        LOG.warning("Adapter is unconfigured, Keycloak will deny every request");
+        return new AdapterDeploymentContext();
+    }
+
+    private Pattern getSkipPatternAsPattern() {
+        return skipPattern == null
+          ? null
+          : Pattern.compile(skipPattern, Pattern.DOTALL);
+    }
+}
diff --git a/adapters/oidc/fuse7/camel-undertow/src/main/resources/META-INF/services/org/apache/camel/component/undertow-keycloak b/adapters/oidc/fuse7/camel-undertow/src/main/resources/META-INF/services/org/apache/camel/component/undertow-keycloak
new file mode 100644
index 0000000..145c1f8
--- /dev/null
+++ b/adapters/oidc/fuse7/camel-undertow/src/main/resources/META-INF/services/org/apache/camel/component/undertow-keycloak
@@ -0,0 +1 @@
+class=org.keycloak.adapters.camel.undertow.UndertowKeycloakComponent
\ No newline at end of file
diff --git a/adapters/oidc/fuse7/pom.xml b/adapters/oidc/fuse7/pom.xml
index d289c1f..fad9726 100644
--- a/adapters/oidc/fuse7/pom.xml
+++ b/adapters/oidc/fuse7/pom.xml
@@ -37,6 +37,7 @@
     </properties>
 
     <modules>
+        <module>camel-undertow</module>
         <module>undertow</module>
     </modules>
 </project>
diff --git a/distribution/adapters/osgi/features/src/main/resources/features.xml b/distribution/adapters/osgi/features/src/main/resources/features.xml
index 00207f3..c510990 100755
--- a/distribution/adapters/osgi/features/src/main/resources/features.xml
+++ b/distribution/adapters/osgi/features/src/main/resources/features.xml
@@ -70,6 +70,7 @@
         <bundle>mvn:org.keycloak/keycloak-undertow-adapter/${project.version}</bundle>
         <bundle>mvn:org.keycloak/keycloak-undertow-adapter-spi/${project.version}</bundle>
         <bundle>mvn:org.keycloak/keycloak-pax-web-undertow/${project.version}</bundle>
+        <bundle>mvn:org.keycloak/keycloak-camel-undertow/${project.version}</bundle>
     </feature>
 
     <feature name="keycloak-jaas" version="${project.version}" resolver="(obr)">