diff --git a/bundled-war-example/README.md b/bundled-war-example/README.md new file mode 100755 index 0000000000..269f1596ae --- /dev/null +++ b/bundled-war-example/README.md @@ -0,0 +1,16 @@ +Self Bootstrapping Keycloak Server and Bundled Application +========================================================== + +This is an example of bundling the Keycloak server with an app within the same WAR in an EAP 6.x environment. + +* On bootup, a default realm is imported from WEB-INF/testrealm.json if it doesn't exist yet. +* On bootup, the adapter config is created on the fly and configured with the testrealm imported. +* The application is secured with keycloak (see jboss-web.xml) +* web.xml security constraints are set for the secured URLs that are secured by keycloak +* Because of weirdness with Resteasy 2.3.x, any secured JAX-RS urls from the application must have a security +constraint that denies all as they will be reachable in two places. Under the Keycloak REST url "/rest" and under the +application's REST url "/database". +* Adapter config can be modified on the fly by getting the AdapterDeploymentContext from a servlet context attribute. +* You must specify a host-port context param so that the auth url for AdapterConfig can be set correctly. + +* Run this demo by going to http://localhost:8080/app-bundle. Then click on the url. \ No newline at end of file diff --git a/bundled-war-example/pom.xml b/bundled-war-example/pom.xml new file mode 100755 index 0000000000..751e7abd11 --- /dev/null +++ b/bundled-war-example/pom.xml @@ -0,0 +1,285 @@ + + + + keycloak-parent + org.keycloak + 1.0-beta-1-SNAPSHOT + + + 4.0.0 + app-bundle + war + Keycloak Server and App Bundle EAP 6.x + + + + + org.bouncycastle + bcprov-jdk16 + + + org.keycloak + keycloak-core + ${project.version} + + + org.keycloak + keycloak-adapter-core + ${project.version} + + + org.keycloak + keycloak-jboss-adapter-core + ${project.version} + + + org.keycloak + keycloak-as7-adapter + ${project.version} + + + net.iharder + base64 + + + org.keycloak + keycloak-core-jaxrs + ${project.version} + + + org.keycloak + keycloak-services + ${project.version} + + + com.google.zxing + javase + + + org.keycloak + keycloak-model-api + ${project.version} + + + org.keycloak + keycloak-model-jpa + ${project.version} + + + org.keycloak + keycloak-audit-api + ${project.version} + + + org.keycloak + keycloak-audit-jpa + ${project.version} + + + org.keycloak + keycloak-audit-jboss-logging + ${project.version} + + + + org.keycloak + keycloak-social-core + ${project.version} + + + org.json + json + + + org.keycloak + keycloak-social-github + ${project.version} + + + org.keycloak + keycloak-social-google + ${project.version} + + + org.keycloak + keycloak-social-twitter + ${project.version} + + + org.twitter4j + twitter4j-core + + + org.keycloak + keycloak-social-facebook + ${project.version} + + + + org.keycloak + keycloak-forms-common-freemarker + ${project.version} + + + org.freemarker + freemarker + + + org.keycloak + keycloak-forms-common-themes + ${project.version} + + + org.keycloak + keycloak-account-api + ${project.version} + + + org.keycloak + keycloak-account-freemarker + ${project.version} + + + org.keycloak + keycloak-login-api + ${project.version} + + + org.keycloak + keycloak-login-freemarker + ${project.version} + + + org.keycloak + keycloak-admin-ui + ${project.version} + + + org.keycloak + keycloak-js-adapter + ${project.version} + + + + org.keycloak + keycloak-authentication-api + ${project.version} + + + org.keycloak + keycloak-authentication-model + ${project.version} + + + org.keycloak + keycloak-authentication-picketlink + ${project.version} + + + org.picketlink + picketlink-common + + + org.picketlink + picketlink-idm-api + + + org.picketlink + picketlink-idm-impl + + + org.picketlink + picketlink-idm-simple-schema + + + + + org.keycloak + keycloak-timer-api + ${project.version} + + + org.keycloak + keycloak-timer-basic + ${project.version} + + + + + org.keycloak + keycloak-picketlink-api + ${project.version} + + + org.keycloak + keycloak-picketlink-realm + ${project.version} + + + org.jboss.spec.javax.servlet + jboss-servlet-api_3.0_spec + provided + + + + org.jboss.resteasy + resteasy-jaxrs + ${resteasy.version} + provided + + + org.jboss.resteasy + resteasy-multipart-provider + ${resteasy.version} + provided + + + org.jboss.resteasy + async-http-servlet-3.0 + ${resteasy.version} + provided + + + org.jboss.resteasy + jaxrs-api + ${resteasy.version} + provided + + + org.jboss.resteasy + resteasy-jackson-provider + ${resteasy.version} + provided + + + + + + + app-bundle + + + org.jboss.as.plugins + jboss-as-maven-plugin + 7.5.Final + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + diff --git a/bundled-war-example/src/main/java/org/keycloak/example/CustomerDatabaseClient.java b/bundled-war-example/src/main/java/org/keycloak/example/CustomerDatabaseClient.java new file mode 100755 index 0000000000..79bbeb8204 --- /dev/null +++ b/bundled-war-example/src/main/java/org/keycloak/example/CustomerDatabaseClient.java @@ -0,0 +1,72 @@ +package org.keycloak.example; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.keycloak.KeycloakSecurityContext; +import org.keycloak.adapters.HttpClientBuilder; +import org.keycloak.representations.IDToken; +import org.keycloak.util.JsonSerialization; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class CustomerDatabaseClient { + + static class TypedList extends ArrayList { + } + + public static class Failure extends Exception { + private int status; + + public Failure(int status) { + this.status = status; + } + + public int getStatus() { + return status; + } + } + + public static IDToken getIDToken(HttpServletRequest req) { + KeycloakSecurityContext session = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName()); + return session.getIdToken(); + + } + + public static List getCustomers(HttpServletRequest req) throws Failure { + KeycloakSecurityContext session = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName()); + + HttpClient client = new HttpClientBuilder() + .disableTrustManager().build(); + try { + HttpGet get = new HttpGet("http://localhost:8080/app-bundle/database/customers"); + get.addHeader("Authorization", "Bearer " + session.getTokenString()); + try { + HttpResponse response = client.execute(get); + if (response.getStatusLine().getStatusCode() != 200) { + throw new Failure(response.getStatusLine().getStatusCode()); + } + HttpEntity entity = response.getEntity(); + InputStream is = entity.getContent(); + try { + return JsonSerialization.readValue(is, TypedList.class); + } finally { + is.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } finally { + client.getConnectionManager().shutdown(); + } + } +} diff --git a/bundled-war-example/src/main/java/org/keycloak/example/oauth/CustomerService.java b/bundled-war-example/src/main/java/org/keycloak/example/oauth/CustomerService.java new file mode 100755 index 0000000000..8f5f5b1c57 --- /dev/null +++ b/bundled-war-example/src/main/java/org/keycloak/example/oauth/CustomerService.java @@ -0,0 +1,31 @@ +package org.keycloak.example.oauth; + +import org.jboss.resteasy.annotations.cache.NoCache; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@Path("customers") +public class CustomerService { + @GET + @Produces("application/json") + @NoCache + public List getCustomers() { + ArrayList rtn = new ArrayList(); + rtn.add("Bill Burke"); + rtn.add("Stian Thorgersen"); + rtn.add("Stan Silvert"); + rtn.add("Gabriel Cardoso"); + rtn.add("Viliam Rockai"); + rtn.add("Marek Posolda"); + rtn.add("Boleslaw Dawidowicz"); + return rtn; + } +} diff --git a/bundled-war-example/src/main/java/org/keycloak/example/oauth/DataApplication.java b/bundled-war-example/src/main/java/org/keycloak/example/oauth/DataApplication.java new file mode 100755 index 0000000000..10590acbf3 --- /dev/null +++ b/bundled-war-example/src/main/java/org/keycloak/example/oauth/DataApplication.java @@ -0,0 +1,25 @@ +package org.keycloak.example.oauth; + +import javax.ws.rs.core.Application; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class DataApplication extends Application +{ + @Override + public Set> getClasses() { + HashSet> set = new HashSet>(); + set.add(CustomerService.class); + set.add(ProductService.class); + return set; + } + + @Override + public Set getSingletons() { + return super.getSingletons(); //To change body of overridden methods use File | Settings | File Templates. + } +} diff --git a/bundled-war-example/src/main/java/org/keycloak/example/oauth/ProductService.java b/bundled-war-example/src/main/java/org/keycloak/example/oauth/ProductService.java new file mode 100755 index 0000000000..10fd5d765f --- /dev/null +++ b/bundled-war-example/src/main/java/org/keycloak/example/oauth/ProductService.java @@ -0,0 +1,27 @@ +package org.keycloak.example.oauth; + +import org.jboss.resteasy.annotations.cache.NoCache; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@Path("products") +public class ProductService { + @GET + @Produces("application/json") + @NoCache + public List getProducts() { + ArrayList rtn = new ArrayList(); + rtn.add("iphone"); + rtn.add("ipad"); + rtn.add("ipod"); + return rtn; + } +} diff --git a/bundled-war-example/src/main/java/org/keycloak/server/KeycloakServerApplication.java b/bundled-war-example/src/main/java/org/keycloak/server/KeycloakServerApplication.java new file mode 100755 index 0000000000..1b14058ae6 --- /dev/null +++ b/bundled-war-example/src/main/java/org/keycloak/server/KeycloakServerApplication.java @@ -0,0 +1,84 @@ +package org.keycloak.server; + +import org.jboss.resteasy.core.Dispatcher; +import org.jboss.resteasy.logging.Logger; +import org.keycloak.adapters.AdapterDeploymentContext; +import org.keycloak.models.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.resources.KeycloakApplication; +import org.keycloak.util.JsonSerialization; +import org.keycloak.util.KeycloakUriBuilder; + +import javax.servlet.ServletContext; +import javax.ws.rs.core.Context; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +public class KeycloakServerApplication extends KeycloakApplication { + + private static final Logger log = Logger.getLogger(KeycloakServerApplication.class); + + public KeycloakServerApplication(@Context ServletContext servletContext,@Context Dispatcher dispatcher) throws FileNotFoundException { + super(servletContext, dispatcher); + KeycloakSession session = factory.createSession(); + session.getTransaction().begin(); + try { + InputStream is = servletContext.getResourceAsStream("/WEB-INF/testrealm.json"); + RealmRepresentation rep = loadJson(is, RealmRepresentation.class); + RealmModel realm = importRealm(session, rep); + AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext)servletContext.getAttribute(AdapterDeploymentContext.class.getName()); + AdapterConfig adapterConfig = new AdapterConfig(); + String host = (String)servletContext.getInitParameter("host-port"); + String uri = KeycloakUriBuilder.fromUri("http://" + host).path(servletContext.getContextPath()).build().toString(); + log.info("**** auth server url: " + uri); + adapterConfig.setRealm("demo"); + adapterConfig.setResource("customer-portal"); + adapterConfig.setRealmKey(realm.getPublicKeyPem()); + Map creds = new HashMap(); + creds.put(CredentialRepresentation.SECRET, "password"); + adapterConfig.setCredentials(creds); + adapterConfig.setAuthServerUrl(uri); + adapterConfig.setSslNotRequired(true); + deploymentContext.updateDeployment(adapterConfig); + session.getTransaction().commit(); + } finally { + session.close(); + } + + } + + public RealmModel importRealm(KeycloakSession session, RealmRepresentation rep) { + RealmManager manager = new RealmManager(session); + + RealmModel realm = manager.getRealmByName(rep.getRealm()); + if (realm != null) { + log.info("Not importing realm " + rep.getRealm() + " realm already exists"); + return realm; + } + + realm = manager.createRealm(rep.getId(), rep.getRealm()); + manager.importRealm(rep, realm); + + log.info("Imported realm " + realm.getName()); + return realm; + } + + private static T loadJson(InputStream is, Class type) { + try { + return JsonSerialization.readValue(is, type); + } catch (IOException e) { + throw new RuntimeException("Failed to parse json", e); + } + } + +} diff --git a/bundled-war-example/src/main/resources/META-INF/persistence.xml b/bundled-war-example/src/main/resources/META-INF/persistence.xml new file mode 100755 index 0000000000..b9dbe7bec8 --- /dev/null +++ b/bundled-war-example/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,39 @@ + + + java:jboss/datasources/ExampleDS + org.keycloak.models.jpa.entities.ApplicationEntity + org.keycloak.models.jpa.entities.CredentialEntity + org.keycloak.models.jpa.entities.OAuthClientEntity + org.keycloak.models.jpa.entities.RealmEntity + org.keycloak.models.jpa.entities.RequiredCredentialEntity + org.keycloak.models.jpa.entities.AuthenticationProviderEntity + org.keycloak.models.jpa.entities.ApplicationRoleEntity + org.keycloak.models.jpa.entities.RealmRoleEntity + org.keycloak.models.jpa.entities.SocialLinkEntity + org.keycloak.models.jpa.entities.AuthenticationLinkEntity + org.keycloak.models.jpa.entities.UserEntity + org.keycloak.models.jpa.entities.UserRoleMappingEntity + org.keycloak.models.jpa.entities.ScopeMappingEntity + + true + + + + + + + + java:jboss/datasources/ExampleDS + org.keycloak.audit.jpa.EventEntity + + true + + + + + + + diff --git a/bundled-war-example/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/bundled-war-example/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100755 index 0000000000..5457fd5b27 --- /dev/null +++ b/bundled-war-example/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundled-war-example/src/main/webapp/WEB-INF/jboss-web.xml b/bundled-war-example/src/main/webapp/WEB-INF/jboss-web.xml new file mode 100755 index 0000000000..2f94ba4bca --- /dev/null +++ b/bundled-war-example/src/main/webapp/WEB-INF/jboss-web.xml @@ -0,0 +1,5 @@ + + + org.keycloak.adapters.as7.KeycloakAuthenticatorValve + + \ No newline at end of file diff --git a/bundled-war-example/src/main/webapp/WEB-INF/testrealm.json b/bundled-war-example/src/main/webapp/WEB-INF/testrealm.json new file mode 100755 index 0000000000..35ae9ad219 --- /dev/null +++ b/bundled-war-example/src/main/webapp/WEB-INF/testrealm.json @@ -0,0 +1,70 @@ +{ + "realm": "demo", + "enabled": true, + "accessTokenLifespan": 3000, + "accessCodeLifespan": 10, + "accessCodeLifespanUserAction": 6000, + "sslNotRequired": true, + "registrationAllowed": false, + "social": false, + "updateProfileOnInitialSocialLogin": false, + "requiredCredentials": [ "password" ], + "users" : [ + { + "username" : "bburke@redhat.com", + "enabled": true, + "email" : "bburke@redhat.com", + "firstName": "Bill", + "lastName": "Burke", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ] + } + ], + "roles" : { + "realm" : [ + { + "name": "user", + "description": "User privileges" + }, + { + "name": "admin", + "description": "Administrator privileges" + } + ] + }, + "roleMappings": [ + { + "username": "bburke@redhat.com", + "roles": ["user"] + } + ], + "scopeMappings": [ + { + "client": "customer-portal", + "roles": ["user"] + } + ], + "applications": [ + { + "name": "customer-portal", + "enabled": true, + "adminUrl": "http://localhost:8080/app-bundle", + "baseUrl": "http://localhost:8080/app-bundle", + "redirectUris": [ + "http://localhost:8080/app-bundle/*" + ], + "secret": "password" + } + ], + "applicationRoleMappings": { + "account": [ + { + "username": "bburke@redhat.com", + "roles": ["manage-account"] + } + ] + } + +} diff --git a/bundled-war-example/src/main/webapp/WEB-INF/web.xml b/bundled-war-example/src/main/webapp/WEB-INF/web.xml new file mode 100755 index 0000000000..5d17437d11 --- /dev/null +++ b/bundled-war-example/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,144 @@ + + + + app-bundle + + host-port + localhost:8080 + + + + Keycloak REST Interface + org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher + + javax.ws.rs.Application + org.keycloak.server.KeycloakServerApplication + + + resteasy.servlet.mapping.prefix + /rest + + 1 + true + + + + Customer REST Interface + org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher + + javax.ws.rs.Application + org.keycloak.example.oauth.DataApplication + + + resteasy.servlet.mapping.prefix + /database + + 2 + true + + + + TmpAdminRedirectServlet + org.keycloak.services.tmp.TmpAdminRedirectServlet + + + + org.keycloak.services.listeners.KeycloakSessionDestroyListener + + + + index.html + + + + Keycloak Client Connection Filter + org.keycloak.services.filters.ClientConnectionFilter + + + + Keycloak Session Management + org.keycloak.services.filters.KeycloakSessionServletFilter + + + + Keycloak Session Management + /rest/* + + + + Keycloak Client Connection Filter + /rest/* + + + + Keycloak REST Interface + /rest/* + + + + Customer REST Interface + /database/* + + + + TmpAdminRedirectServlet + /admin + /admin/ + + + + + + + Customers + /customers/* + + + user + + + + + + Database + /database/* + + + user + + + + + deny + /rest/customers/* + + + + + + BASIC + demo + + + + admin + + + user + + + + diff --git a/bundled-war-example/src/main/webapp/customers/view.jsp b/bundled-war-example/src/main/webapp/customers/view.jsp new file mode 100755 index 0000000000..5b7aba6ea7 --- /dev/null +++ b/bundled-war-example/src/main/webapp/customers/view.jsp @@ -0,0 +1,47 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> +<%@ page import="org.keycloak.example.CustomerDatabaseClient" %> +<%@ page import="org.keycloak.util.KeycloakUriBuilder" %> +<%@ page import="org.keycloak.representations.IDToken" %> + + + Customer View Page + + +<% + String logoutUri = KeycloakUriBuilder.fromUri("http://localhost:8080/app-bundle/rest/realms/demo/tokens/logout") + .queryParam("redirect_uri", "http://localhost:8080/app-bundle").build().toString(); + String acctUri = "http://localhost:8080/app-bundle/rest/realms/demo/account?referrer=customer-portal"; + IDToken idToken = CustomerDatabaseClient.getIDToken(request); +%> +

logout | manage acct

+Servlet User Principal <%=request.getUserPrincipal().getName()%> + made this request. +

Caller IDToken values (You can specify what is returned in IDToken in the customer-portal claims page in the admin console:

+

Username: <%=idToken.getPreferredUsername()%>

+

Email: <%=idToken.getEmail()%>

+

Full Name: <%=idToken.getName()%>

+

First: <%=idToken.getGivenName()%>

+

Last: <%=idToken.getFamilyName()%>

+

Customer Listing

+<% + java.util.List list = null; + try { + list = CustomerDatabaseClient.getCustomers(request); + } catch (CustomerDatabaseClient.Failure failure) { + out.println("There was a failure processing request. You either didn't configure Keycloak properly, or maybe" + + "you just forgot to secure the database service?"); + out.println("Status from database service invocation was: " + failure.getStatus()); + return; + } + for (String cust : list) { + out.print("

"); + out.print(cust); + out.println("

"); + + } +%> +

+ + \ No newline at end of file diff --git a/bundled-war-example/src/main/webapp/index.html b/bundled-war-example/src/main/webapp/index.html new file mode 100755 index 0000000000..681fa08a17 --- /dev/null +++ b/bundled-war-example/src/main/webapp/index.html @@ -0,0 +1,13 @@ + + + + + + +

Customer Portal

+ +

Customer Listing

+ + + \ No newline at end of file diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java new file mode 100755 index 0000000000..3dfce8e05a --- /dev/null +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java @@ -0,0 +1,26 @@ +package org.keycloak.adapters; + +import org.keycloak.representations.adapters.config.AdapterConfig; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class AdapterDeploymentContext { + protected KeycloakDeployment deployment; + + public AdapterDeploymentContext() { + } + + public AdapterDeploymentContext(KeycloakDeployment deployment) { + this.deployment = deployment; + } + + public KeycloakDeployment getDeployment() { + return deployment; + } + + public void updateDeployment(AdapterConfig config) { + deployment = KeycloakDeploymentBuilder.build(config); + } +} diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java index bd2eabce48..722c40f0c1 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java @@ -12,6 +12,7 @@ import java.util.Map; * @version $Revision: 1 $ */ public class KeycloakDeployment { + protected boolean configured; protected String realm; protected PublicKey realmKey; protected KeycloakUriBuilder authUrl; @@ -37,6 +38,14 @@ public class KeycloakDeployment { protected boolean exposeToken; protected volatile int notBefore; + public boolean isConfigured() { + return configured; + } + + public void setConfigured(boolean configured) { + this.configured = configured; + } + public String getResourceName() { return resourceName; } diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java index fc09bbe7c7..04f8c15a87 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java @@ -27,7 +27,7 @@ public class KeycloakDeploymentBuilder { protected KeycloakDeployment internalBuild(AdapterConfig adapterConfig) { - + deployment.setConfigured(true); if (adapterConfig.getRealm() == null) throw new RuntimeException("Must set 'realm' in config"); deployment.setRealm(adapterConfig.getRealm()); String resource = adapterConfig.getResource(); diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java index 370b313927..c937809ceb 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java @@ -35,6 +35,7 @@ public class PreAuthActionsHandler { } public boolean handleRequest() { + if (!deployment.isConfigured()) return false; String requestUri = facade.getRequest().getURI(); log.debugv("adminRequest {0}", requestUri); if (preflightCors()) { diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java index 84f5ab6347..4d78076cc5 100755 --- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java +++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java @@ -9,6 +9,7 @@ import org.apache.catalina.valves.ValveBase; import org.jboss.logging.Logger; import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.AdapterConstants; +import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AuthenticatedActionsHandler; import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.representations.AccessToken; @@ -33,10 +34,10 @@ import java.util.Set; */ public class AuthenticatedActionsValve extends ValveBase { private static final Logger log = Logger.getLogger(AuthenticatedActionsValve.class); - protected KeycloakDeployment deployment; + protected AdapterDeploymentContext deploymentContext; - public AuthenticatedActionsValve(KeycloakDeployment deployment, Valve next, Container container, ObjectName controller) { - this.deployment = deployment; + public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container, ObjectName controller) { + this.deploymentContext = deploymentContext; if (next == null) throw new RuntimeException("WTF is next null?!"); setNext(next); setContainer(container); @@ -47,7 +48,7 @@ public class AuthenticatedActionsValve extends ValveBase { @Override public void invoke(Request request, Response response) throws IOException, ServletException { log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI()); - AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, new CatalinaHttpFacade(request, response)); + AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deploymentContext.getDeployment(), new CatalinaHttpFacade(request, response)); if (handler.handledRequest()) { return; } diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java index 74050be830..291eb5f3eb 100755 --- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java +++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java @@ -13,6 +13,7 @@ import org.apache.catalina.deploy.LoginConfig; import org.jboss.logging.Logger; import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.AdapterConstants; +import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AuthChallenge; import org.keycloak.adapters.AuthOutcome; import org.keycloak.adapters.KeycloakDeployment; @@ -55,7 +56,7 @@ import java.util.Map; public class KeycloakAuthenticatorValve extends FormAuthenticator implements LifecycleListener { private static final Logger log = Logger.getLogger(KeycloakAuthenticatorValve.class); protected CatalinaUserSessionManagement userSessionManagement = new CatalinaUserSessionManagement(); - protected KeycloakDeployment deployment; + protected AdapterDeploymentContext deploymentContext; @Override @@ -100,15 +101,26 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif protected void init() { - this.deployment = KeycloakDeploymentBuilder.build(getConfigInputStream(context)); - AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deployment, getNext(), getContainer(), getController()); + InputStream configInputStream = getConfigInputStream(context); + KeycloakDeployment kd = null; + if (configInputStream == null) { + log.warn("No adapter configuration. Keycloak is unconfigured and will deny all requests."); + kd = new KeycloakDeployment(); + kd.setConfigured(false); + + } else { + kd = KeycloakDeploymentBuilder.build(configInputStream); + } + deploymentContext = new AdapterDeploymentContext(kd); + context.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext); + AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deploymentContext, getNext(), getContainer(), getController()); setNext(actions); } @Override public void invoke(Request request, Response response) throws IOException, ServletException { try { - PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deployment, new CatalinaHttpFacade(request, response)); + PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deploymentContext.getDeployment(), new CatalinaHttpFacade(request, response)); if (handler.handleRequest()) { return; } @@ -120,8 +132,9 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif @Override public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException { + if (!deploymentContext.getDeployment().isConfigured()) return false; CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response); - CatalinaRequestAuthenticator authenticator = new CatalinaRequestAuthenticator(deployment, this, userSessionManagement, facade, request); + CatalinaRequestAuthenticator authenticator = new CatalinaRequestAuthenticator(deploymentContext.getDeployment(), this, userSessionManagement, facade, request); AuthOutcome outcome = authenticator.authenticate(); if (outcome == AuthOutcome.AUTHENTICATED) { if (facade.isEnded()) { @@ -146,7 +159,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext)request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName()); if (session == null) return; // just in case session got serialized - session.setDeployment(deployment); + session.setDeployment(deploymentContext.getDeployment()); if (session.isActive()) return; // FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will diff --git a/pom.xml b/pom.xml index c600c6bc42..31ae93b047 100755 --- a/pom.xml +++ b/pom.xml @@ -102,6 +102,7 @@ testsuite server timer + bundled-war-example diff --git a/server/src/main/java/org/keycloak/server/KeycloakServerApplication.java b/server/src/main/java/org/keycloak/server/KeycloakServerApplication.java index 262bf60268..823b44b395 100755 --- a/server/src/main/java/org/keycloak/server/KeycloakServerApplication.java +++ b/server/src/main/java/org/keycloak/server/KeycloakServerApplication.java @@ -1,5 +1,6 @@ package org.keycloak.server; +import org.jboss.resteasy.core.Dispatcher; import org.jboss.resteasy.logging.Logger; import org.keycloak.models.Config; import org.keycloak.models.KeycloakSession; @@ -21,8 +22,8 @@ public class KeycloakServerApplication extends KeycloakApplication { private static final Logger log = Logger.getLogger(KeycloakServerApplication.class); - public KeycloakServerApplication(@Context ServletContext servletContext) throws FileNotFoundException { - super(servletContext); + public KeycloakServerApplication(@Context ServletContext servletContext, @Context Dispatcher dispatcher) throws FileNotFoundException { + super(servletContext, dispatcher); String importRealm = System.getProperty("keycloak.import"); if (importRealm != null) { diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index e5c48b8265..7b8b3ddfba 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -1,5 +1,6 @@ package org.keycloak.services.resources; +import org.jboss.resteasy.core.Dispatcher; import org.jboss.resteasy.logging.Logger; import org.keycloak.SkeletonKeyContextResolver; import org.keycloak.audit.AuditListener; @@ -52,9 +53,10 @@ public class KeycloakApplication extends Application { protected ProviderSessionFactory providerSessionFactory; protected String contextPath; - public KeycloakApplication(@Context ServletContext context) { - this.factory = createSessionFactory(); + public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) { + dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this); this.contextPath = context.getContextPath(); + this.factory = createSessionFactory(); this.providerSessionFactory = createProviderSessionFactory(); context.setAttribute(KeycloakSessionFactory.class.getName(), factory); //classes.add(KeycloakSessionCleanupFilter.class); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java index e0aedb08a8..0fb20a9493 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java @@ -52,7 +52,7 @@ public class ApplicationResource { protected UriInfo uriInfo; @Context - protected Application keycloak; + protected KeycloakApplication keycloak; protected KeycloakApplication getKeycloakApplication() { return (KeycloakApplication)keycloak; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java index 5481ae8545..eb72e63091 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java @@ -41,7 +41,7 @@ public class OAuthClientResource { protected UriInfo uriInfo; @Context - protected Application application; + protected KeycloakApplication application; protected KeycloakApplication getApplication() { return (KeycloakApplication)application;