diff --git a/adapters/oidc/osgi-adapter/pom.xml b/adapters/oidc/osgi-adapter/pom.xml index f453ffd5f6..59ca85558b 100755 --- a/adapters/oidc/osgi-adapter/pom.xml +++ b/adapters/oidc/osgi-adapter/pom.xml @@ -36,15 +36,32 @@ org.keycloak.adapters.osgi.* - org.ops4j.pax.web.*;version="[3.0,4)", + org.ops4j.pax.web.*;version="[3.0,5)", javax.servlet.*;version="[2.5,4)";resolution:=optional, org.eclipse.jetty.*;version="[8.1,10)";resolution:=optional, org.keycloak.*;version="${project.version}", + org.apache.cxf.transport.http;resolution:=optional;version="[3,4)", + org.apache.cxf.transport.servlet;resolution:=optional;version="[3,4)", *;resolution:=optional + + org.keycloak + keycloak-core + provided + + + org.keycloak + keycloak-adapter-core + provided + + + org.keycloak + keycloak-adapter-spi + provided + org.jboss.logging jboss-logging @@ -65,12 +82,23 @@ pax-web-runtime provided + + org.ops4j.pax.web + pax-web-api + provided + org.eclipse.jetty jetty-security ${jetty9.version} provided + + org.apache.cxf + cxf-rt-transports-http + ${cxf.version} + provided + diff --git a/adapters/oidc/osgi-adapter/src/main/java/org/keycloak/adapters/osgi/PathBasedKeycloakConfigResolver.java b/adapters/oidc/osgi-adapter/src/main/java/org/keycloak/adapters/osgi/PathBasedKeycloakConfigResolver.java new file mode 100644 index 0000000000..93d5cb6f40 --- /dev/null +++ b/adapters/oidc/osgi-adapter/src/main/java/org/keycloak/adapters/osgi/PathBasedKeycloakConfigResolver.java @@ -0,0 +1,71 @@ +/* + * 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. + */ +package org.keycloak.adapters.osgi; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.keycloak.adapters.KeycloakConfigResolver; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.KeycloakDeploymentBuilder; +import org.keycloak.adapters.OIDCHttpFacade; + +public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver { + + private final Map cache = new ConcurrentHashMap(); + + @Override + public KeycloakDeployment resolve(OIDCHttpFacade.Request request) { + String path = request.getURI(); + String[] urlTokens = path.split("/"); + if (urlTokens.length < 4) { + throw new IllegalStateException("Not able to determine the web-context to load the correspondent keycloak.json file"); + } + + String webContext = urlTokens[3]; + + KeycloakDeployment deployment = cache.get(webContext); + if (null == deployment) { + // not found on the simple cache, try to load it from the file system + String keycloakConfig = (String) System.getProperties().get("keycloak.config"); + if(keycloakConfig == null || "".equals(keycloakConfig.trim())){ + String karafEtc = (String) System.getProperties().get("karaf.etc"); + if(karafEtc == null || "".equals(karafEtc.trim())){ + throw new IllegalStateException("Neither \"keycloak.config\" nor \"karaf.etc\" java properties are set. Please set one of them."); + } + keycloakConfig = karafEtc; + } + + String absolutePath = keycloakConfig + File.separator + webContext + "-keycloak.json"; + InputStream is = null; + try { + is = new FileInputStream(absolutePath); + } catch (FileNotFoundException e){ + throw new IllegalStateException("Not able to find the file " + absolutePath); + } + deployment = KeycloakDeploymentBuilder.build(is); + cache.put(webContext, deployment); + } + + return deployment; + } + +} diff --git a/adapters/oidc/osgi-adapter/src/main/java/org/keycloak/adapters/osgi/ServletReregistrationService.java b/adapters/oidc/osgi-adapter/src/main/java/org/keycloak/adapters/osgi/ServletReregistrationService.java index 3bff5a0530..1aace71011 100644 --- a/adapters/oidc/osgi-adapter/src/main/java/org/keycloak/adapters/osgi/ServletReregistrationService.java +++ b/adapters/oidc/osgi-adapter/src/main/java/org/keycloak/adapters/osgi/ServletReregistrationService.java @@ -18,15 +18,22 @@ package org.keycloak.adapters.osgi; import java.util.Arrays; +import java.util.Dictionary; +import java.util.Enumeration; import java.util.Hashtable; import java.util.List; +import java.util.Properties; import javax.servlet.Servlet; +import org.apache.cxf.transport.http.DestinationRegistry; +import org.apache.cxf.transport.servlet.CXFNonSpringServlet; import org.jboss.logging.Logger; import org.ops4j.pax.web.service.WebContainer; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.http.HttpContext; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; @@ -42,13 +49,15 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer; */ public class ServletReregistrationService { + private static final String CXF_SERVLET_PREFIX = "org.apache.cxf.servlet."; protected static final Logger log = Logger.getLogger(ServletReregistrationService.class); private static final List FILTERED_PROPERTIES = Arrays.asList("objectClass", "service.id"); private BundleContext bundleContext; - private ServiceReference servletReference; + private ServiceReference managedServiceReference; private ServiceTracker webContainerTracker; + private String alias; public BundleContext getBundleContext() { return bundleContext; @@ -58,24 +67,18 @@ public class ServletReregistrationService { this.bundleContext = bundleContext; } - public ServiceReference getServletReference() { - return servletReference; - } - - public void setServletReference(ServiceReference servletReference) { - this.servletReference = servletReference; - } - - protected ServiceTracker getWebContainerTracker() { - return webContainerTracker; - } public void start() { - if (servletReference == null) { - throw new IllegalStateException("No servlet reference provided"); + if ( managedServiceReference == null) { + return; + } + + Dictionary properties = obtainProperties(); + alias = (String)getProp(properties, CXF_SERVLET_PREFIX + "context", "/cxf"); + if(alias == null){ + alias = "/cxf"; } - final Servlet servlet = (Servlet) bundleContext.getService(servletReference); WebContainer externalWebContainer = findExternalWebContainer(); if (externalWebContainer == null) { return; @@ -83,18 +86,19 @@ public class ServletReregistrationService { // Unregister servlet from external container now try { - externalWebContainer.unregisterServlet(servlet); + externalWebContainer.unregister(alias); log.debug("Original servlet with alias " + getAlias() + " unregistered successfully from external web container."); } catch (IllegalStateException e) { log.warn("Can't unregister servlet due to: " + e.getMessage()); } + final Dictionary finalProperties = properties; ServiceTrackerCustomizer trackerCustomizer = new ServiceTrackerCustomizer() { @Override public Object addingService(ServiceReference webContainerServiceReference) { WebContainer ourWebContainer = (WebContainer) bundleContext.getService(webContainerServiceReference); - registerServlet(ourWebContainer, servlet); + registerServlet(ourWebContainer, finalProperties); log.debugv("Servlet with alias " + getAlias() + " registered to secured web container"); return ourWebContainer; } @@ -122,28 +126,82 @@ public class ServletReregistrationService { // Re-register servlet back to original context WebContainer externalWebContainer = findExternalWebContainer(); - Servlet servlet = (Servlet) bundleContext.getService(servletReference); - registerServlet(externalWebContainer, servlet); + registerServlet(externalWebContainer, obtainProperties()); log.debug("Servlet with alias " + getAlias() + " registered back to external web container"); } private String getAlias() { - return (String) servletReference.getProperty("alias"); + return alias; } - protected void registerServlet(WebContainer webContainer, Servlet servlet) { + /** + * Code comes from org.apache.cxf.transport.http.osgi.ServletExporter#updated(java.util.Dictionary) + * @param webContainer + * @param properties + */ + protected void registerServlet(WebContainer webContainer, Dictionary properties) { + HttpContext httpContext = webContainer.createDefaultHttpContext(); + + ServiceReference destinationServiceServiceReference = bundleContext.getServiceReference("org.apache.cxf.transport.http.DestinationRegistry"); + DestinationRegistry destinationRegistry = (DestinationRegistry) bundleContext.getService(destinationServiceServiceReference); + + Servlet servlet = new CXFNonSpringServlet(destinationRegistry, false); try { + if (properties == null) { + properties = new Properties(); + } + Properties sprops = new Properties(); + sprops.put("init-prefix", + getProp(properties, CXF_SERVLET_PREFIX + "init-prefix", "")); + sprops.put("servlet-name", + getProp(properties, CXF_SERVLET_PREFIX + "name", "cxf-osgi-transport-servlet")); + sprops.put("hide-service-list-page", + getProp(properties, CXF_SERVLET_PREFIX + "hide-service-list-page", "false")); + sprops.put("disable-address-updates", + getProp(properties, CXF_SERVLET_PREFIX + "disable-address-updates", "true")); + sprops.put("base-address", + getProp(properties, CXF_SERVLET_PREFIX + "base-address", "")); + sprops.put("service-list-path", + getProp(properties, CXF_SERVLET_PREFIX + "service-list-path", "")); + sprops.put("static-resources-list", + getProp(properties, CXF_SERVLET_PREFIX + "static-resources-list", "")); + sprops.put("redirects-list", + getProp(properties, CXF_SERVLET_PREFIX + "redirects-list", "")); + sprops.put("redirect-servlet-name", + getProp(properties, CXF_SERVLET_PREFIX + "redirect-servlet-name", "")); + sprops.put("redirect-servlet-path", + getProp(properties, CXF_SERVLET_PREFIX + "redirect-servlet-path", "")); + sprops.put("service-list-all-contexts", + getProp(properties, CXF_SERVLET_PREFIX + "service-list-all-contexts", "")); + sprops.put("service-list-page-authenticate", + getProp(properties, CXF_SERVLET_PREFIX + "service-list-page-authenticate", "false")); + sprops.put("service-list-page-authenticate-realm", + getProp(properties, CXF_SERVLET_PREFIX + "service-list-page-authenticate-realm", "karaf")); + sprops.put("use-x-forwarded-headers", + getProp(properties, CXF_SERVLET_PREFIX + "use-x-forwarded-headers", "false")); + + // Accept extra properties by default, can be disabled if it is really needed + if (Boolean.valueOf(getProp(properties, CXF_SERVLET_PREFIX + "support.extra.properties", "true").toString())) { + Enumeration keys = properties.keys(); + while (keys.hasMoreElements()) { + String nextKey = keys.nextElement().toString(); + if (!nextKey.startsWith(CXF_SERVLET_PREFIX)) { + sprops.put(nextKey, properties.get(nextKey)); + } + } + } + Hashtable servletInitParams = new Hashtable(); - String[] propNames = servletReference.getPropertyKeys(); - for (String propName : propNames) { + Enumeration keys = sprops.keys(); + + while(keys.hasMoreElements()){ + String propName = (String) keys.nextElement(); if (!FILTERED_PROPERTIES.contains(propName)) { - servletInitParams.put(propName, servletReference.getProperty(propName)); + servletInitParams.put(propName, sprops.getProperty(propName)); } } // Try to register servlet in given web container now - HttpContext httpContext = webContainer.createDefaultHttpContext(); - String alias = (String) servletReference.getProperty("alias"); webContainer.registerServlet(alias, servlet, servletInitParams, httpContext); } catch (Exception e) { log.error("Can't register servlet in web container", e); @@ -156,7 +214,7 @@ public class ServletReregistrationService { * @return web container or null */ protected WebContainer findExternalWebContainer() { - BundleContext servletBundleContext = servletReference.getBundle().getBundleContext(); + BundleContext servletBundleContext = managedServiceReference.getBundle().getBundleContext(); ServiceReference webContainerReference = servletBundleContext.getServiceReference(WebContainer.class.getName()); if (webContainerReference == null) { log.warn("Not found webContainer reference for bundle " + servletBundleContext); @@ -166,4 +224,32 @@ public class ServletReregistrationService { } } + private Dictionary obtainProperties(){ + Dictionary properties = null; + ServiceReference reference = bundleContext.getServiceReference(ConfigurationAdmin.class.getName()); + ConfigurationAdmin admin = (ConfigurationAdmin) bundleContext.getService(reference); + try { + Configuration configuration = admin.getConfiguration("org.apache.cxf.osgi"); + properties = configuration.getProperties(); + } catch (Exception e){ + log.warn("Unable to obtain cxf osgi configadmin reference.", e); + } + return properties; + } + + private Object getProp(Dictionary properties, String key, Object defaultValue) { + Object value = null; + if(properties != null){ + value = properties.get(key); + } + return value == null ? defaultValue : value; + } + + public ServiceReference getManagedServiceReference() { + return managedServiceReference; + } + + public void setManagedServiceReference(ServiceReference managedServiceReference) { + this.managedServiceReference = managedServiceReference; + } } diff --git a/distribution/adapters/osgi/features/src/main/resources/features.xml b/distribution/adapters/osgi/features/src/main/resources/features.xml index 6ead274f5a..f2e825a2b9 100755 --- a/distribution/adapters/osgi/features/src/main/resources/features.xml +++ b/distribution/adapters/osgi/features/src/main/resources/features.xml @@ -46,12 +46,21 @@
The keycloak Jetty8 adapter
keycloak-adapter-core - jetty + jetty mvn:org.keycloak/keycloak-jetty-adapter-spi/${project.version} mvn:org.keycloak/keycloak-jetty-core/${project.version} mvn:org.keycloak/keycloak-jetty81-adapter/${project.version}
+ +
The keycloak Jetty9 adapter
+ keycloak-adapter-core + jetty + mvn:org.keycloak/keycloak-jetty-adapter-spi/${project.version} + mvn:org.keycloak/keycloak-jetty-core/${project.version} + mvn:org.keycloak/keycloak-jetty92-adapter/${project.version} +
+
The keycloak JAAS configuration
keycloak-adapter-core @@ -61,17 +70,7 @@
The keycloak adapter core stuff
keycloak-osgi-adapter - keycloak-jetty8-adapter keycloak-jaas
- - -
This is just simplification to upgrade paxweb to 3.1.2 in JBoss Fuse 6.1 or Karaf 2.3.X environment
- mvn:org.ow2.asm/asm-all/5.0 - mvn:org.apache.xbean/xbean-bundleutils/3.18 - mvn:org.apache.xbean/xbean-reflect/3.18 - mvn:org.apache.xbean/xbean-finder/3.18 -
- - \ No newline at end of file + diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/fuse-adapter.xml b/docbook/auth-server-docs/reference/en/en-US/modules/fuse-adapter.xml index e2750fa9ab..ea2bf81a6a 100644 --- a/docbook/auth-server-docs/reference/en/en-US/modules/fuse-adapter.xml +++ b/docbook/auth-server-docs/reference/en/en-US/modules/fuse-adapter.xml @@ -19,8 +19,9 @@ JBoss Fuse and Apache Karaf Adapter Currently Keycloak supports securing your web applications running inside JBoss Fuse - or Apache Karaf . It leverages Jetty 8 adapter as both JBoss Fuse 6.1 + or Apache Karaf . It leverages Jetty 8 adapter as all of JBoss Fuse 6.2.1, JBoss Fuse 6.2.0 and Apache Karaf 3 are bundled with Jetty 8.1 server under the covers and Jetty is used for running various kinds of web applications. + For JBoss Fuse 6.3 and Karaf 4, bundled with Jetty 9 server, it leverages Jetty 9 adapter. What is supported for Fuse/Karaf is: diff --git a/examples/fuse/README.md b/examples/fuse/README.md index 874214730b..10232b9f42 100644 --- a/examples/fuse/README.md +++ b/examples/fuse/README.md @@ -1,8 +1,9 @@ Keycloak Fuse demo ================== -Currently Keycloak supports securing your web applications running inside [JBoss Fuse](http://www.jboss.org/products/fuse/overview/) or [Apache Karaf](http://karaf.apache.org/). It leverages Jetty8 adapter -as both JBoss Fuse 6 and Apache Karaf 3 are bundled with [Jetty8](http://eclipse.org/jetty/) server under the covers and Jetty is used for running various kinds of web applications. +Currently Keycloak supports securing your web applications running inside [JBoss Fuse](http://www.jboss.org/products/fuse/overview/) or [Apache Karaf](http://karaf.apache.org/). It leverages: +- Jetty8 adapter for both JBoss Fuse 6.2 and Apache Karaf 3, that include [Jetty8](http://eclipse.org/jetty/) server under the covers and Jetty is used for running various kinds of web applications +- Jetty9 adapter for both JBoss Fuse 6.3 and Apache Karaf 4, that include [Jetty9](http://eclipse.org/jetty/) server under the covers and Jetty is used for running various kinds of web applications The Fuse example is slightly modified version of Keycloak base demo applications. The main difference among base demo is that for Fuse demo are applications running on separate Fuse/Karaf server. Keycloak server is supposed to run separately on Wildfly. @@ -19,12 +20,12 @@ more secure to use the separate Jetty Engine instead of the default one. The def Fuse demo contains those basic applications: * **customer-app-fuse** A WAR application that is deployed with [pax-war extender](https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+War) * **product-app-fuse** A servlet application deployed with [pax-whiteboard extender](https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+Whiteboard) -* **cxf-jaxws** [Apache CXF](http://cxf.apache.org/) JAX-WS endpoint running on separate Jetty engine on [http://localhost:8282/PersonServiceCF](http://localhost:8282/PersonServiceCF). -The product-app-fuse invokes the endpoint to get data. * **camel** [Apache Camel](http://camel.apache.org/) endpoint running on separate Jetty engine on [http://localhost:8383/admin-camel-endpoint](http://localhost:8383/admin-camel-endpoint). The customer-app-fuse invokes the endpoint to get data. * **cxf-jaxrs** [Apache CXF](http://cxf.apache.org/) JAX-RS endpoint running on default Jetty on [http://localhost:8181/cxf/customerservice](http://localhost:8181/cxf/customerservice). The customer-app-fuse invokes the endpoint to get data +* **cxf-jaxws** [Apache CXF](http://cxf.apache.org/) JAX-WS endpoint running on separate Jetty engine on [http://localhost:8282/PersonServiceCF](http://localhost:8282/PersonServiceCF). +The product-app-fuse invokes the endpoint to get data. Running of demo consists of 2 steps. First you need to run separate Keycloak server and then Fuse/Karaf server with the applications @@ -42,14 +43,14 @@ cd keycloak-examples-/fuse mvn clean install ``` -Running demo on JBoss Fuse 6.2 ------------------------------- +Running demo on JBoss Fuse 6.2.1 or JBoss Fuse 6.2.0 +---------------------------------------------------- You just need to download and run JBoss Fuse and then run those commands from the karaf terminal to install the needed features and Keycloak fuse demo (Replace Keycloak versions with the current Keycloak version number): ``` -features:addurl mvn:org.keycloak/keycloak-osgi-features/1.2.0.Beta1/xml/features -features:addurl mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.2.0.Beta1/xml/features -features:install keycloak-fuse-example +features:addurl mvn:org.keycloak/keycloak-osgi-features/1.9.4.Final/xml/features +features:addurl mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.9.4.Final/xml/features +features:install keycloak-fuse-6.2-example ``` After that you can test running on [http://localhost:8181/customer-portal](http://localhost:8181/customer-portal) and login as "bburke@redhat.com" with password "password". Customer-portal is able to @@ -61,6 +62,18 @@ From [http://localhost:8181/product-portal](http://localhost:8181/product-portal Note that this demo also secures whole default CXF endpoint on [http://localhost:8181/cxf](http://localhost:8181/cxf) hence every application running under it is secured too. +Running demo on JBoss Fuse 6.3 +------------------------------ +Similar steps to the instructions for JBoss Fuse 6.2.1 but you need to install a different feature (due to the usage of Jetty9 instead of Jetty8): `keycloak-fuse-6.3-example` + +You just need to download and run JBoss Fuse and then run those commands from the karaf terminal to install the needed features and Keycloak fuse demo (Replace Keycloak versions with the current Keycloak version number): + +``` +features:addurl mvn:org.keycloak/keycloak-osgi-features/1.9.4.Final/xml/features +features:addurl mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.9.4.Final/xml/features +features:install keycloak-fuse-6.3-example +``` + Running demo on Apache Karaf 3.0.3 ---------------------------------- @@ -73,54 +86,14 @@ Once you run Apache Karaf, you need to run these commands from Karaf console (Ma ``` feature:repo-add mvn:org.apache.camel.karaf/apache-camel/2.15.1/xml/features feature:repo-add mvn:org.apache.cxf.karaf/apache-cxf/3.0.4/xml/features -feature:repo-add mvn:org.keycloak/keycloak-osgi-features/1.2.0.Beta1/xml/features -feature:repo-add mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.2.0.Beta1/xml/features +feature:repo-add mvn:org.keycloak/keycloak-osgi-features/1.9.4.Final/xml/features +feature:repo-add mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.9.4.Final/xml/features feature:install keycloak-fuse-example ``` Now you can test example applications similarly like described for "JBoss Fuse 6.2" section. -Running demo on JBoss Fuse 6.1.0.redhat-379 -------------------------------------------- - -Securing your applications on JBoss Fuse 6.1 is a bit more tricky. There is bug [https://ops4j1.jira.com/browse/PAXWEB-666](https://ops4j1.jira.com/browse/PAXWEB-666) -, which doesn't easily allow to secure default Jetty engine on [http://localhost:8181](http://localhost:8181) as it's not possible to inject -custom Jetty authenticator provided by Keycloak Jetty adapter into underlying Jetty server. Hence first step is to upgrade pax-web -version from default 3.0.6 to newer 3.1.2 . Then you need to "refresh" cxf feature too. Final step is to install "keycloak-fuse-example" feature. - -All the steps could be performed with these commands in Fuse console (Replace 1.2.0.Beta1 with the actual version number of Keycloak you are using): - -``` -features:uninstall pax-war -features:uninstall pax-http-whiteboard -features:uninstall pax-http -features:uninstall pax-jetty -features:removeurl mvn:org.ops4j.pax.web/pax-web-features/3.0.6/xml/features -``` - -Now restart fuse (use `osgi:shutdown` command and start it again from command line. You can ignore startup messages after restart -as pax-web is not installed at the moment. Then run those commands (replace 1.2.0.Beta1 with the actual version number of Keycloak you are using): - -``` -features:addurl mvn:org.ops4j.pax.web/pax-web-features/3.1.2/xml/features -features:addurl mvn:org.keycloak/keycloak-osgi-features/1.2.0.Beta1/xml/features -features:addurl mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.2.0.Beta1/xml/features -features:install keycloak-pax-web-upgrade -features:install pax-http-whiteboard/3.1.2 -features:install pax-war/3.1.2 -``` - -Now it's recommended to restart again. After the start, you shouldn't see any error messages, which indicates that upgrade to pax-web 3.1.2 went fine. -So last step is to install the demo now: - -``` -features:install keycloak-fuse-example -``` - -Now you can test example applications similarly like described in "JBoss Fuse 6.2" section. - - How to secure your own applications ----------------------------------- Most of the steps should be understandable from testing and understanding the demo. Basically all mentioned applications require to @@ -154,6 +127,13 @@ original unsecured servlet on `/cxf` context is deployed back and hence context It's recommended to use your own Jetty engine for your apps (similarly like `cxf-jaxws` application is doing). + +How to secure WAR application with external keycloak.json configuration +----------------------------------------------------------------------- +It's possible to secure your WAR application with the `keycloak.json` configuration provided outside of the WAR bundle itself. +See [external-config](external-config/README.md) for more details. This is supported on JBoss Fuse 6.3. + + How to secure Fuse admin services --------------------------------- It's possible to secure fuse admin services with Keycloak too. See [fuse-admin](fuse-admin/README.md) for info on how to secure diff --git a/examples/fuse/camel/pom.xml b/examples/fuse/camel/pom.xml index e70fb23fed..47d475c56f 100755 --- a/examples/fuse/camel/pom.xml +++ b/examples/fuse/camel/pom.xml @@ -32,15 +32,21 @@ - 2.15.1 + - org.eclipse.jetty.security;version="[8.1,10)", - org.eclipse.jetty.util.security;version="[8.1,10)", - org.apache.camel;version="[2.12,3)", + javax.servlet;version="[3,4)", + javax.servlet.http;version="[3,4)", + org.apache.camel.*, + org.apache.camel;version="[2.13,3)", + org.eclipse.jetty.security;version="[8,10)", + org.eclipse.jetty.server.nio;version="[8,10)", + org.eclipse.jetty.util.security;version="[8,10)", org.keycloak.*;version="${project.version}", - *;resolution:=optional + org.osgi.service.blueprint, + org.osgi.service.blueprint.container, + org.osgi.service.event, org.keycloak.example.* @@ -63,7 +69,7 @@ ${camel.version} org.apache.camel - camel-jetty + camel-jetty9 ${camel.version} diff --git a/examples/fuse/cxf-jaxrs/pom.xml b/examples/fuse/cxf-jaxrs/pom.xml index 4600f11cb5..a07a78b990 100755 --- a/examples/fuse/cxf-jaxrs/pom.xml +++ b/examples/fuse/cxf-jaxrs/pom.xml @@ -31,7 +31,6 @@ CXF JAXRS Example - Secured in Karaf/Fuse - 3.0.4 @@ -40,9 +39,11 @@ org.apache.cxf.transport.http;version="[2.7,3.2)", org.apache.cxf.*;version="[2.7,3.2)", com.fasterxml.jackson.jaxrs.json;version="${jackson.version}", + org.eclipse.jetty.security;version="[8,10)", + org.eclipse.jetty.util.security;version="[8,10)", + org.keycloak.*;version="${project.version}", org.keycloak.adapters.jetty;version="${project.version}", - org.keycloak.adapters;version="${project.version}", - * + *;resolution:=optional org.keycloak.example.rs.* diff --git a/examples/fuse/cxf-jaxrs/src/main/resources/META-INF/cxf/bus-extensions.txt b/examples/fuse/cxf-jaxrs/src/main/resources/META-INF/cxf/bus-extensions.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/fuse/cxf-jaxrs/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/examples/fuse/cxf-jaxrs/src/main/resources/OSGI-INF/blueprint/blueprint.xml index 619b047ef3..74b5fabf41 100644 --- a/examples/fuse/cxf-jaxrs/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/examples/fuse/cxf-jaxrs/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -69,9 +69,9 @@ - - + + - \ No newline at end of file + diff --git a/examples/fuse/cxf-jaxws/pom.xml b/examples/fuse/cxf-jaxws/pom.xml index 494d7ef4cd..8923a0d2e8 100755 --- a/examples/fuse/cxf-jaxws/pom.xml +++ b/examples/fuse/cxf-jaxws/pom.xml @@ -32,7 +32,6 @@ - 3.0.4 @@ -50,7 +49,9 @@ org.apache.cxf.transport.http;version="[2.7,3.2)", org.apache.cxf.*;version="[2.7,3.2)", org.springframework.beans.factory.config, - *;resolution:=optional + org.eclipse.jetty.security;version="[8,10)", + org.eclipse.jetty.util.security;version="[8,10)", + org.keycloak.*;version="${project.version}" org.keycloak.example.ws.* diff --git a/examples/fuse/external-config/README.md b/examples/fuse/external-config/README.md new file mode 100644 index 0000000000..07004349b1 --- /dev/null +++ b/examples/fuse/external-config/README.md @@ -0,0 +1,22 @@ +Keycloak Example - Externalized keycloak.json +======================================= + +The following example was tested on JBoss Fuse 6.3 and shows a way to package an OSGi compatible .war file that does not + include keycloak.json file in the .war archive but that automatically loads it based on a naming convention. + +To enable the functionality you need to add this section to your `web.xml`: + +``` + + keycloak.config.resolver + org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver + +``` + +That component will use `keycloak.config` or `karaf.etc` java properties to look for a base folder to look for the configuration. + +Inside one of those folders it will look for a file called `-keycloak.json`. + +For this example you need to copy the file `external-config-keycloak.json` to your JBoss Fuse `etc/` folder. + +Once you have done that and once you added feature `keycloak-fuse-6.3-example` (See [here](../README.md) for more details), you can try to access the endpoint: http://localhost:8181/external-config/index.html \ No newline at end of file diff --git a/examples/fuse/external-config/external-config-keycloak.json b/examples/fuse/external-config/external-config-keycloak.json new file mode 100644 index 0000000000..920e99a677 --- /dev/null +++ b/examples/fuse/external-config/external-config-keycloak.json @@ -0,0 +1,10 @@ +{ + "realm": "demo", + "resource": "external-config", + "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "auth-server-url": "http://localhost:8080/auth", + "ssl-required" : "external", + "credentials": { + "secret": "password" + } +} diff --git a/examples/fuse/external-config/pom.xml b/examples/fuse/external-config/pom.xml new file mode 100755 index 0000000000..89768e9b0f --- /dev/null +++ b/examples/fuse/external-config/pom.xml @@ -0,0 +1,123 @@ + + + + 4.0.0 + + + keycloak-examples-fuse-parent + org.keycloak + 2.0.0.CR1-SNAPSHOT + + + Keycloak Examples - External Config + external-config + war + org.keycloak.example.demo + + Keycloak External Config Example + + + + + + + org.apache.http.*;version=${apache.httpcomponents.version}, + javax.servlet.*;version="[2.5,4)", + org.keycloak.adapters.jetty;version="${project.version}", + org.keycloak.adapters;version="${project.version}", + org.keycloak.constants;version="${project.version}", + org.keycloak.adapters.osgi;version="${project.version}", + org.keycloak.util;version="${project.version}", + org.keycloak.*;version="${project.version}", + *;resolution:=optional + + + org.keycloak.example.* + + + + + + org.jboss.spec.javax.servlet + jboss-servlet-api_3.0_spec + provided + + + + + org.keycloak + keycloak-adapter-core + provided + + + org.keycloak + keycloak-adapter-spi + provided + + + + + org.keycloak + keycloak-core + provided + + + + external-config + + + org.apache.maven.plugins + maven-war-plugin + + + ${basedir}/target/classes/META-INF/MANIFEST.MF + + + + + org.apache.felix + maven-bundle-plugin + true + + + bundle-manifest + process-classes + + manifest + + + + + + war + + + external-config + external-config + WEB-INF/lib + ${project.name} + ${project.groupId}.${project.artifactId} + ${keycloak.osgi.import} + ${keycloak.osgi.private} + ${keycloak.osgi.export} + + + + + + diff --git a/examples/fuse/external-config/src/main/java/org/keycloak/examples/ProtectedServlet.java b/examples/fuse/external-config/src/main/java/org/keycloak/examples/ProtectedServlet.java new file mode 100755 index 0000000000..ec30cccb3b --- /dev/null +++ b/examples/fuse/external-config/src/main/java/org/keycloak/examples/ProtectedServlet.java @@ -0,0 +1,58 @@ +package org.keycloak.examples;/* + * 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. + */ + +import org.keycloak.KeycloakPrincipal; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + + +@WebServlet(urlPatterns = "/servlet") +public class ProtectedServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String realm = req.getPathInfo().split("/")[1]; + if (realm.contains("?")) { + realm = realm.split("\\?")[0]; + } + + if (req.getPathInfo().contains("logout")) { + req.logout(); + resp.sendRedirect(req.getContextPath() + "/" + realm); + return; + } + + KeycloakPrincipal principal = (KeycloakPrincipal) req.getUserPrincipal(); + + resp.setContentType("text/html"); + PrintWriter writer = resp.getWriter(); + + writer.write("Realm: "); + writer.write(principal.getKeycloakSecurityContext().getRealm()); + + writer.write("
User: "); + writer.write(principal.getKeycloakSecurityContext().getIdToken().getPreferredUsername()); + + writer.write(String.format("
Logout", realm)); + } + } diff --git a/examples/fuse/external-config/src/main/webapp/WEB-INF/jetty-web.xml b/examples/fuse/external-config/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 0000000000..6b42d3f00c --- /dev/null +++ b/examples/fuse/external-config/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/fuse/external-config/src/main/webapp/WEB-INF/web.xml b/examples/fuse/external-config/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..84aaa7470f --- /dev/null +++ b/examples/fuse/external-config/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,55 @@ + + + + External Config Example + + + keycloak.config.resolver + org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver + + + + index.html + + + + + + REST endpoints + /* + + + user + + + + + BASIC + does-not-matter + + + + admin + + + user + + \ No newline at end of file diff --git a/examples/fuse/external-config/src/main/webapp/index.html b/examples/fuse/external-config/src/main/webapp/index.html new file mode 100755 index 0000000000..600c959181 --- /dev/null +++ b/examples/fuse/external-config/src/main/webapp/index.html @@ -0,0 +1,30 @@ + + + + + + External Config Example Karaf/Fuse + + +

External configuration worked.

+ +

Log out

+ + + \ No newline at end of file diff --git a/examples/fuse/features/src/main/resources/features.xml b/examples/fuse/features/src/main/resources/features.xml index c838b02bfd..5dbb2ca613 100644 --- a/examples/fuse/features/src/main/resources/features.xml +++ b/examples/fuse/features/src/main/resources/features.xml @@ -18,13 +18,33 @@ - + +
The keycloak fuse example
+ war + camel + camel-jetty9 + cxf + keycloak + keycloak-jetty9-adapter + mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/${jackson.version} + mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/${jackson.version} + mvn:org.keycloak.example.demo/product-portal-fuse-example/${project.version} + mvn:org.keycloak.example.demo/customer-portal-fuse-example/${project.version}/war + mvn:org.keycloak.example.demo/external-config/${project.version}/war + mvn:org.keycloak.example.demo/camel-endpoint-example/${project.version} + mvn:org.keycloak.example.demo/cxf-jaxws-example/${project.version} + mvn:org.keycloak.example.demo/cxf-jaxrs-example/${project.version} +
+ + +
The keycloak fuse example
war camel camel-jetty cxf keycloak + keycloak-jetty8-adapter mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/${jackson.version} mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/${jackson.version} mvn:org.keycloak.example.demo/product-portal-fuse-example/${project.version} @@ -34,4 +54,4 @@ mvn:org.keycloak.example.demo/cxf-jaxrs-example/${project.version}
-
\ No newline at end of file + diff --git a/examples/fuse/pom.xml b/examples/fuse/pom.xml index 1d89e0431e..be948677aa 100755 --- a/examples/fuse/pom.xml +++ b/examples/fuse/pom.xml @@ -29,7 +29,9 @@ keycloak-examples-fuse-parent pom - + + 2.16.1 + customer-app-fuse product-app-fuse @@ -37,6 +39,7 @@ cxf-jaxws camel features + external-config - \ No newline at end of file + diff --git a/examples/fuse/testrealm.json b/examples/fuse/testrealm.json index 88afec8315..a27543aaaa 100644 --- a/examples/fuse/testrealm.json +++ b/examples/fuse/testrealm.json @@ -186,7 +186,19 @@ "standardFlowEnabled": false, "directAccessGrantsEnabled": true, "secret": "password" + }, + { + "clientId": "external-config", + "enabled": true, + "adminUrl": "http://localhost:8181/external-config", + "baseUrl": "http://localhost:8181/external-config", + "redirectUris": [ + "http://localhost:8181/external-config", + "http://localhost:8181/external-config/*" + ], + "secret": "password" } + ], "scopeMappings": [ { diff --git a/pom.xml b/pom.xml index ff731e98c0..38efdcffa9 100755 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ 4.4.1 0.6 1.52 + 3.1.5 1.6.1 2011.1 1.3.173 @@ -84,7 +85,7 @@ 3.2.0 5.1.29 4.2.0 - 3.1.2 + 4.2.4 9.3-1100-jdbc41 1.3.7 1.0.2.Final @@ -621,6 +622,11 @@ pax-web-runtime ${pax.web.version} + + org.ops4j.pax.web + pax-web-api + ${pax.web.version} + org.jboss.aesh aesh