refactor core/adapter

This commit is contained in:
Bill Burke 2013-12-13 19:53:02 -05:00
parent 698b5a1a16
commit d28b1ff98b
29 changed files with 485 additions and 308 deletions

View file

@ -2,7 +2,6 @@ package org.keycloak;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.keycloak.adapters.config.ManagedResourceConfig;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.UriBuilder;
@ -23,9 +22,6 @@ public class RealmConfiguration {
public RealmConfiguration() {
}
public RealmConfiguration(ManagedResourceConfig config) {
}
public ResourceMetadata getMetadata() {
return metadata;
}

View file

@ -1,217 +0,0 @@
package org.keycloak.adapters.config;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.EnvUtil;
import org.keycloak.PemUtils;
import org.keycloak.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.representations.idm.PublishedRealmRepresentation;
import javax.ws.rs.core.UriBuilder;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PublicKey;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ManagedResourceConfigLoader {
protected ManagedResourceConfig remoteSkeletonKeyConfig;
protected ResourceMetadata resourceMetadata;
protected KeyStore clientCertKeystore;
protected KeyStore truststore;
protected ResteasyClient client;
protected RealmConfiguration realmConfiguration;
public ManagedResourceConfigLoader() {
}
public ManagedResourceConfigLoader(InputStream is) {
loadConfig(is);
}
public static KeyStore loadKeyStore(String filename, String password) throws Exception {
KeyStore trustStore = KeyStore.getInstance(KeyStore
.getDefaultType());
File truststoreFile = new File(filename);
FileInputStream trustStream = new FileInputStream(truststoreFile);
trustStore.load(trustStream, password.toCharArray());
trustStream.close();
return trustStore;
}
public void init(boolean setupClient) {
String truststorePath = remoteSkeletonKeyConfig.getTruststore();
if (truststorePath != null) {
truststorePath = EnvUtil.replace(truststorePath);
String truststorePassword = remoteSkeletonKeyConfig.getTruststorePassword();
truststorePath = null;
try {
this.truststore = loadKeyStore(truststorePath, truststorePassword);
} catch (Exception e) {
throw new RuntimeException("Failed to load truststore", e);
}
}
String clientKeystore = remoteSkeletonKeyConfig.getClientKeystore();
String clientKeyPassword = null;
if (clientKeystore != null) {
clientKeystore = EnvUtil.replace(clientKeystore);
String clientKeystorePassword = remoteSkeletonKeyConfig.getClientKeystorePassword();
clientCertKeystore = null;
try {
clientCertKeystore = loadKeyStore(clientKeystore, clientKeystorePassword);
} catch (Exception e) {
throw new RuntimeException("Failed to load keystore", e);
}
}
initClient();
if (remoteSkeletonKeyConfig.getRealmUrl() != null) {
PublishedRealmRepresentation rep = null;
try {
rep = client.target(remoteSkeletonKeyConfig.getRealmUrl()).request().get(PublishedRealmRepresentation.class);
} finally {
if (!setupClient) {
client.close();
}
}
remoteSkeletonKeyConfig.setRealm(rep.getRealm());
remoteSkeletonKeyConfig.setAuthUrl(rep.getAuthorizationUrl());
remoteSkeletonKeyConfig.setCodeUrl(rep.getCodeUrl());
remoteSkeletonKeyConfig.setRealmKey(rep.getPublicKeyPem());
remoteSkeletonKeyConfig.setAdminRole(rep.getAdminRole());
}
if (remoteSkeletonKeyConfig.getAdminRole() == null) {
remoteSkeletonKeyConfig.setAdminRole("$REALM-ADMIN$");
}
String realm = remoteSkeletonKeyConfig.getRealm();
if (realm == null) throw new RuntimeException("Must set 'realm' in config");
String resource = remoteSkeletonKeyConfig.getResource();
if (resource == null) throw new RuntimeException("Must set 'resource' in config");
String realmKeyPem = remoteSkeletonKeyConfig.getRealmKey();
if (realmKeyPem == null) {
throw new IllegalArgumentException("You must set the realm-public-key");
}
PublicKey realmKey = null;
try {
realmKey = PemUtils.decodePublicKey(realmKeyPem);
} catch (Exception e) {
throw new RuntimeException(e);
}
resourceMetadata = new ResourceMetadata();
resourceMetadata.setRealm(realm);
resourceMetadata.setResourceName(resource);
resourceMetadata.setRealmKey(realmKey);
resourceMetadata.setClientKeystore(clientCertKeystore);
clientKeyPassword = remoteSkeletonKeyConfig.getClientKeyPassword();
resourceMetadata.setClientKeyPassword(clientKeyPassword);
resourceMetadata.setTruststore(this.truststore);
if (!setupClient || remoteSkeletonKeyConfig.isBearerOnly()) return;
realmConfiguration = new RealmConfiguration();
String authUrl = remoteSkeletonKeyConfig.getAuthUrl();
if (authUrl == null) {
throw new RuntimeException("You must specify auth-url");
}
String tokenUrl = remoteSkeletonKeyConfig.getCodeUrl();
if (tokenUrl == null) {
throw new RuntimeException("You mut specify code-url");
}
realmConfiguration.setMetadata(resourceMetadata);
realmConfiguration.setSslRequired(!remoteSkeletonKeyConfig.isSslNotRequired());
for (Map.Entry<String, String> entry : getRemoteSkeletonKeyConfig().getCredentials().entrySet()) {
realmConfiguration.getResourceCredentials().param(entry.getKey(), entry.getValue());
}
ResteasyClient client = getClient();
realmConfiguration.setClient(client);
realmConfiguration.setAuthUrl(UriBuilder.fromUri(authUrl).queryParam("client_id", resourceMetadata.getResourceName()));
realmConfiguration.setCodeUrl(client.target(tokenUrl));
}
protected void initClient() {
int size = 10;
if (remoteSkeletonKeyConfig.getConnectionPoolSize() > 0)
size = remoteSkeletonKeyConfig.getConnectionPoolSize();
ResteasyClientBuilder.HostnameVerificationPolicy policy = ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD;
if (remoteSkeletonKeyConfig.isAllowAnyHostname())
policy = ResteasyClientBuilder.HostnameVerificationPolicy.ANY;
ResteasyProviderFactory providerFactory = new ResteasyProviderFactory();
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(ManagedResourceConfigLoader.class.getClassLoader());
try {
ResteasyProviderFactory.getInstance(); // initialize builtins
RegisterBuiltin.register(providerFactory);
} finally {
Thread.currentThread().setContextClassLoader(old);
}
ResteasyClientBuilder builder = new ResteasyClientBuilder()
.providerFactory(providerFactory)
.connectionPoolSize(size)
.hostnameVerification(policy)
.keyStore(clientCertKeystore, remoteSkeletonKeyConfig.getClientKeyPassword());
if (remoteSkeletonKeyConfig.isDisableTrustManager()) {
builder.disableTrustManager();
} else {
builder.trustStore(truststore);
}
client = builder.build();
}
public ManagedResourceConfig getRemoteSkeletonKeyConfig() {
return remoteSkeletonKeyConfig;
}
public ResourceMetadata getResourceMetadata() {
return resourceMetadata;
}
public ResteasyClient getClient() {
return client;
}
public KeyStore getClientCertKeystore() {
return clientCertKeystore;
}
public KeyStore getTruststore() {
return truststore;
}
public RealmConfiguration getRealmConfiguration() {
return realmConfiguration;
}
protected void loadConfig(InputStream is) {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
remoteSkeletonKeyConfig = null;
try {
remoteSkeletonKeyConfig = mapper.readValue(is, ManagedResourceConfig.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -38,6 +38,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-adapter</artifactId>

View file

@ -34,6 +34,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-adapter</artifactId>

View file

@ -38,6 +38,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-adapter</artifactId>

View file

@ -38,6 +38,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>

View file

@ -34,6 +34,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>

View file

@ -38,6 +38,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>

View file

@ -31,6 +31,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>

View file

@ -0,0 +1,53 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-adapter-core</artifactId>
<name>Keycloak AS7 Integration</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -11,7 +11,8 @@ import java.util.Map;
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm-url", "realm", "resource", "realm-public-key", "admin-role", "auth-url", "code-url", "allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password", "client-id", "client-credentials"})
public class ManagedResourceConfig {
public class AdapterConfig
{
@JsonProperty("realm-url")
protected String realmUrl;
@JsonProperty("realm")
@ -20,9 +21,7 @@ public class ManagedResourceConfig {
protected String resource;
@JsonProperty("realm-public-key")
protected String realmKey;
@JsonProperty("admin-role")
protected String adminRole;
@JsonProperty("auth-url")
@JsonProperty("auth-url")
protected String authUrl;
@JsonProperty("code-url")
protected String codeUrl;
@ -194,15 +193,7 @@ public class ManagedResourceConfig {
this.connectionPoolSize = connectionPoolSize;
}
public String getAdminRole() {
return adminRole;
}
public void setAdminRole(String adminRole) {
this.adminRole = adminRole;
}
public boolean isCors() {
public boolean isCors() {
return cors;
}

View file

@ -0,0 +1,115 @@
package org.keycloak.adapters.config;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.keycloak.EnvUtil;
import org.keycloak.PemUtils;
import org.keycloak.ResourceMetadata;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PublicKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AdapterConfigLoader {
protected AdapterConfig adapterConfig;
protected ResourceMetadata resourceMetadata;
protected KeyStore clientCertKeystore;
protected KeyStore truststore;
public static KeyStore loadKeyStore(String filename, String password) throws Exception {
KeyStore trustStore = KeyStore.getInstance(KeyStore
.getDefaultType());
File truststoreFile = new File(filename);
FileInputStream trustStream = new FileInputStream(truststoreFile);
trustStore.load(trustStream, password.toCharArray());
trustStream.close();
return trustStore;
}
public void init() {
String truststorePath = adapterConfig.getTruststore();
if (truststorePath != null) {
truststorePath = EnvUtil.replace(truststorePath);
String truststorePassword = adapterConfig.getTruststorePassword();
truststorePath = null;
try {
this.truststore = loadKeyStore(truststorePath, truststorePassword);
} catch (Exception e) {
throw new RuntimeException("Failed to load truststore", e);
}
}
String clientKeystore = adapterConfig.getClientKeystore();
String clientKeyPassword = null;
if (clientKeystore != null) {
clientKeystore = EnvUtil.replace(clientKeystore);
String clientKeystorePassword = adapterConfig.getClientKeystorePassword();
clientCertKeystore = null;
try {
clientCertKeystore = loadKeyStore(clientKeystore, clientKeystorePassword);
} catch (Exception e) {
throw new RuntimeException("Failed to load keystore", e);
}
}
String realm = adapterConfig.getRealm();
if (realm == null) throw new RuntimeException("Must set 'realm' in config");
String resource = adapterConfig.getResource();
if (resource == null) throw new RuntimeException("Must set 'resource' in config");
String realmKeyPem = adapterConfig.getRealmKey();
if (realmKeyPem == null) {
throw new IllegalArgumentException("You must set the realm-public-key");
}
PublicKey realmKey = null;
try {
realmKey = PemUtils.decodePublicKey(realmKeyPem);
} catch (Exception e) {
throw new RuntimeException(e);
}
resourceMetadata = new ResourceMetadata();
resourceMetadata.setRealm(realm);
resourceMetadata.setResourceName(resource);
resourceMetadata.setRealmKey(realmKey);
resourceMetadata.setClientKeystore(clientCertKeystore);
clientKeyPassword = adapterConfig.getClientKeyPassword();
resourceMetadata.setClientKeyPassword(clientKeyPassword);
resourceMetadata.setTruststore(this.truststore);
}
public AdapterConfig getAdapterConfig() {
return adapterConfig;
}
public ResourceMetadata getResourceMetadata() {
return resourceMetadata;
}
public KeyStore getClientCertKeystore() {
return clientCertKeystore;
}
public KeyStore getTruststore() {
return truststore;
}
protected void loadConfig(InputStream is) {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
adapterConfig = null;
try {
adapterConfig = mapper.readValue(is, AdapterConfig.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -25,6 +25,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>

View file

@ -8,7 +8,7 @@ import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.jboss.logging.Logger;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.AdapterConfig;
import org.keycloak.representations.SkeletonKeyToken;
import javax.management.ObjectName;
@ -29,9 +29,9 @@ import java.util.Set;
*/
public class AuthenticatedActionsValve extends ValveBase {
private static final Logger log = Logger.getLogger(AuthenticatedActionsValve.class);
protected ManagedResourceConfig config;
protected AdapterConfig config;
public AuthenticatedActionsValve(ManagedResourceConfig config, Valve next, Container container, ObjectName controller) {
public AuthenticatedActionsValve(AdapterConfig config, Valve next, Container container, ObjectName controller) {
this.config = config;
if (next == null) throw new RuntimeException("WTF is next null?!");
setNext(next);

View file

@ -12,9 +12,9 @@ import org.apache.catalina.deploy.LoginConfig;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.ResourceMetadata;
import org.keycloak.adapters.as7.config.CatalinaManagedResourceConfigLoader;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.ManagedResourceConfigLoader;
import org.keycloak.adapters.as7.config.CatalinaAdapterConfigLoader;
import org.keycloak.adapters.config.AdapterConfig;
import org.keycloak.adapters.config.AdapterConfigLoader;
import javax.security.auth.login.LoginException;
import javax.servlet.ServletException;
@ -30,7 +30,7 @@ import java.io.IOException;
*/
public class BearerTokenAuthenticatorValve extends AuthenticatorBase implements LifecycleListener {
private static final Logger log = Logger.getLogger(BearerTokenAuthenticatorValve.class);
protected ManagedResourceConfig remoteSkeletonKeyConfig;
protected AdapterConfig adapterConfig;
protected ResourceMetadata resourceMetadata;
@Override
@ -46,11 +46,11 @@ public class BearerTokenAuthenticatorValve extends AuthenticatorBase implements
}
protected void init() {
ManagedResourceConfigLoader managedResourceConfigLoader = new CatalinaManagedResourceConfigLoader(context);
remoteSkeletonKeyConfig = managedResourceConfigLoader.getRemoteSkeletonKeyConfig();
managedResourceConfigLoader.init(false);
resourceMetadata = managedResourceConfigLoader.getResourceMetadata();
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(remoteSkeletonKeyConfig, getNext(), getContainer(), getController());
AdapterConfigLoader adapterConfigLoader = new CatalinaAdapterConfigLoader(context);
adapterConfig = adapterConfigLoader.getAdapterConfig();
adapterConfigLoader.init();
resourceMetadata = adapterConfigLoader.getResourceMetadata();
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(adapterConfig, getNext(), getContainer(), getController());
setNext(actions);
}
@ -58,7 +58,7 @@ public class BearerTokenAuthenticatorValve extends AuthenticatorBase implements
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
log.debugv("{0} {1}", request.getMethod(), request.getRequestURI());
if (remoteSkeletonKeyConfig.isCors() && new CorsPreflightChecker(remoteSkeletonKeyConfig).checkCorsPreflight(request, response)) {
if (adapterConfig.isCors() && new CorsPreflightChecker(adapterConfig).checkCorsPreflight(request, response)) {
return;
}
super.invoke(request, response);
@ -70,7 +70,7 @@ public class BearerTokenAuthenticatorValve extends AuthenticatorBase implements
@Override
protected boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException {
try {
CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(resourceMetadata, true, remoteSkeletonKeyConfig.isUseResourceRoleMappings());
CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(resourceMetadata, true, adapterConfig.isUseResourceRoleMappings());
if (bearer.login(request, response)) {
return true;
}

View file

@ -3,7 +3,7 @@ package org.keycloak.adapters.as7;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.jboss.logging.Logger;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.AdapterConfig;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -11,9 +11,9 @@ import org.keycloak.adapters.config.ManagedResourceConfig;
*/
public class CorsPreflightChecker {
private static final Logger log = Logger.getLogger(CorsPreflightChecker.class);
protected ManagedResourceConfig config;
protected AdapterConfig config;
public CorsPreflightChecker(ManagedResourceConfig config) {
public CorsPreflightChecker(AdapterConfig config) {
this.config = config;
}

View file

@ -21,9 +21,9 @@ import org.keycloak.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeyPrincipal;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.as7.config.CatalinaManagedResourceConfigLoader;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.ManagedResourceConfigLoader;
import org.keycloak.adapters.as7.config.CatalinaAdapterConfigLoader;
import org.keycloak.adapters.as7.config.RealmConfigurationLoader;
import org.keycloak.adapters.config.AdapterConfig;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.representations.idm.admin.LogoutAction;
@ -47,7 +47,7 @@ public class OAuthManagedResourceValve extends FormAuthenticator implements Life
protected RealmConfiguration realmConfiguration;
private static final Logger log = Logger.getLogger(OAuthManagedResourceValve.class);
protected UserSessionManagement userSessionManagement = new UserSessionManagement();
protected ManagedResourceConfig remoteSkeletonKeyConfig;
protected AdapterConfig adapterConfig;
protected ResourceMetadata resourceMetadata;
@ -64,20 +64,20 @@ public class OAuthManagedResourceValve extends FormAuthenticator implements Life
}
protected void init() {
ManagedResourceConfigLoader managedResourceConfigLoader = new CatalinaManagedResourceConfigLoader(context);
managedResourceConfigLoader.init(true);
resourceMetadata = managedResourceConfigLoader.getResourceMetadata();
remoteSkeletonKeyConfig = managedResourceConfigLoader.getRemoteSkeletonKeyConfig();
RealmConfigurationLoader configLoader = new CatalinaAdapterConfigLoader(context);
configLoader.init(true);
resourceMetadata = configLoader.getResourceMetadata();
adapterConfig = configLoader.getAdapterConfig();
realmConfiguration = managedResourceConfigLoader.getRealmConfiguration();
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(remoteSkeletonKeyConfig, getNext(), getContainer(), getController());
realmConfiguration = configLoader.getRealmConfiguration();
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(adapterConfig, getNext(), getContainer(), getController());
setNext(actions);
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
if (remoteSkeletonKeyConfig.isCors() && new CorsPreflightChecker(remoteSkeletonKeyConfig).checkCorsPreflight(request, response)) {
if (adapterConfig.isCors() && new CorsPreflightChecker(adapterConfig).checkCorsPreflight(request, response)) {
return;
}
String requestURI = request.getDecodedRequestURI();
@ -181,7 +181,7 @@ public class OAuthManagedResourceValve extends FormAuthenticator implements Life
}
protected boolean bearer(boolean challenge, Request request, HttpServletResponse response) throws LoginException, IOException {
CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(realmConfiguration.getMetadata(), challenge, remoteSkeletonKeyConfig.isUseResourceRoleMappings());
CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(realmConfiguration.getMetadata(), challenge, adapterConfig.isUseResourceRoleMappings());
if (bearer.login(request, response)) {
return true;
}
@ -228,7 +228,7 @@ public class OAuthManagedResourceValve extends FormAuthenticator implements Life
SkeletonKeyToken token = oauth.getToken();
Set<String> roles = new HashSet<String>();
if (remoteSkeletonKeyConfig.isUseResourceRoleMappings()) {
if (adapterConfig.isUseResourceRoleMappings()) {
SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
if (access != null) roles.addAll(access.getRoles());
} else {

View file

@ -1,15 +1,15 @@
package org.keycloak.adapters.as7.config;
import org.apache.catalina.Context;
import org.keycloak.adapters.config.ManagedResourceConfigLoader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class CatalinaManagedResourceConfigLoader extends ManagedResourceConfigLoader {
public class CatalinaAdapterConfigLoader extends RealmConfigurationLoader
{
public CatalinaManagedResourceConfigLoader(Context context) {
public CatalinaAdapterConfigLoader(Context context) {
InputStream is = null;
String path = context.getServletContext().getInitParameter("keycloak.config.file");
if (path == null) {

View file

@ -0,0 +1,97 @@
package org.keycloak.adapters.as7.config;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.config.AdapterConfigLoader;
import javax.ws.rs.core.UriBuilder;
import java.io.InputStream;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmConfigurationLoader extends AdapterConfigLoader {
protected ResteasyClient client;
protected RealmConfiguration realmConfiguration;
public RealmConfigurationLoader() {
}
public RealmConfigurationLoader(InputStream is) {
loadConfig(is);
}
public void init(boolean setupClient) {
init();
initRealmConfiguration(setupClient);
}
protected void initRealmConfiguration(boolean setupClient) {
if (!setupClient || adapterConfig.isBearerOnly()) return;
initClient();
realmConfiguration = new RealmConfiguration();
String authUrl = adapterConfig.getAuthUrl();
if (authUrl == null) {
throw new RuntimeException("You must specify auth-url");
}
String tokenUrl = adapterConfig.getCodeUrl();
if (tokenUrl == null) {
throw new RuntimeException("You mut specify code-url");
}
realmConfiguration.setMetadata(resourceMetadata);
realmConfiguration.setSslRequired(!adapterConfig.isSslNotRequired());
for (Map.Entry<String, String> entry : getAdapterConfig().getCredentials().entrySet()) {
realmConfiguration.getResourceCredentials().param(entry.getKey(), entry.getValue());
}
ResteasyClient client = getClient();
realmConfiguration.setClient(client);
realmConfiguration.setAuthUrl(UriBuilder.fromUri(authUrl).queryParam("client_id", resourceMetadata.getResourceName()));
realmConfiguration.setCodeUrl(client.target(tokenUrl));
}
protected void initClient() {
int size = 10;
if (adapterConfig.getConnectionPoolSize() > 0)
size = adapterConfig.getConnectionPoolSize();
ResteasyClientBuilder.HostnameVerificationPolicy policy = ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD;
if (adapterConfig.isAllowAnyHostname())
policy = ResteasyClientBuilder.HostnameVerificationPolicy.ANY;
ResteasyProviderFactory providerFactory = new ResteasyProviderFactory();
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(RealmConfigurationLoader.class.getClassLoader());
try {
ResteasyProviderFactory.getInstance(); // initialize builtins
RegisterBuiltin.register(providerFactory);
} finally {
Thread.currentThread().setContextClassLoader(old);
}
ResteasyClientBuilder builder = new ResteasyClientBuilder()
.providerFactory(providerFactory)
.connectionPoolSize(size)
.hostnameVerification(policy)
.keyStore(clientCertKeystore, adapterConfig.getClientKeyPassword());
if (adapterConfig.isDisableTrustManager()) {
builder.disableTrustManager();
} else {
builder.trustStore(truststore);
}
client = builder.build();
}
public ResteasyClient getClient() {
return client;
}
public RealmConfiguration getRealmConfiguration() {
return realmConfiguration;
}
}

View file

@ -15,6 +15,7 @@
<packaging>pom</packaging>
<modules>
<module>adapter-core</module>
<module>as7-eap6/adapter</module>
<module>undertow</module>
<!-- <module>as7-eap6/jboss-modules</module> -->

View file

@ -25,6 +25,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>

View file

@ -5,7 +5,7 @@ import io.undertow.server.HttpServerExchange;
import io.undertow.util.Headers;
import org.jboss.logging.Logger;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.AdapterConfig;
import org.keycloak.representations.SkeletonKeyToken;
import javax.servlet.ServletException;
@ -25,11 +25,11 @@ import java.util.Set;
*/
public class AuthenticatedActionsHandler implements HttpHandler {
private static final Logger log = Logger.getLogger(AuthenticatedActionsHandler.class);
protected ManagedResourceConfig config;
protected AdapterConfig adapterConfig;
protected HttpHandler next;
protected AuthenticatedActionsHandler(ManagedResourceConfig config, HttpHandler next) {
this.config = config;
protected AuthenticatedActionsHandler(AdapterConfig config, HttpHandler next) {
this.adapterConfig = config;
this.next = next;
}
@ -68,12 +68,12 @@ public class AuthenticatedActionsHandler implements HttpHandler {
exchange.endExchange();
return true;
}
if (!config.isExposeToken()) {
if (!adapterConfig.isExposeToken()) {
exchange.setResponseCode(200);
exchange.endExchange();
return true;
}
if (!config.isCors() && exchange.getRequestHeaders().getFirst(Headers.ORIGIN) != null) {
if (!adapterConfig.isCors() && exchange.getRequestHeaders().getFirst(Headers.ORIGIN) != null) {
exchange.setResponseCode(200);
exchange.endExchange();
return true;
@ -82,7 +82,7 @@ public class AuthenticatedActionsHandler implements HttpHandler {
}
protected boolean corsRequest(HttpServerExchange exchange, SkeletonKeySession session) throws IOException {
if (!config.isCors()) return false;
if (!adapterConfig.isCors()) return false;
log.debugv("CORS enabled + request.getRequestURI()");
String origin = exchange.getRequestHeaders().getFirst("Origin");
log.debugv("Origin: {0} uri: {1}", origin, exchange.getRequestURI());

View file

@ -10,16 +10,13 @@ import org.keycloak.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeyPrincipal;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.AdapterConfig;
import org.keycloak.representations.SkeletonKeyToken;
import java.security.Principal;
import java.util.Collections;
import java.util.Set;
import static io.undertow.util.Headers.WWW_AUTHENTICATE;
import static io.undertow.util.StatusCodes.UNAUTHORIZED;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@ -31,20 +28,20 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
public static final AttachmentKey<SkeletonKeySession> SKELETON_KEY_SESSION_ATTACHMENT_KEY = AttachmentKey.create(SkeletonKeySession.class);
protected ResourceMetadata resourceMetadata;
protected ManagedResourceConfig config;
protected AdapterConfig adapterConfig;
protected RealmConfiguration realmConfig;
protected int sslRedirectPort;
public KeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, ManagedResourceConfig config, RealmConfiguration realmConfig, int sslRedirectPort) {
public KeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, AdapterConfig config, RealmConfiguration realmConfig, int sslRedirectPort) {
this.resourceMetadata = resourceMetadata;
this.config = config;
this.adapterConfig = config;
this.realmConfig = realmConfig;
this.sslRedirectPort = sslRedirectPort;
}
public KeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, ManagedResourceConfig config, RealmConfiguration realmConfig) {
public KeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, AdapterConfig config, RealmConfiguration realmConfig) {
this.resourceMetadata = resourceMetadata;
this.config = config;
this.adapterConfig = config;
this.realmConfig = realmConfig;
}
@ -64,7 +61,7 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
completeAuthentication(exchange, securityContext, token, surrogate);
return AuthenticationMechanismOutcome.AUTHENTICATED;
}
else if (config.isBearerOnly()) {
else if (adapterConfig.isBearerOnly()) {
exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, bearer.getChallenge());
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
}
@ -92,13 +89,13 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
}
protected BearerTokenAuthenticator createBearerTokenAuthenticator() {
return new BearerTokenAuthenticator(resourceMetadata, config.isUseResourceRoleMappings());
return new BearerTokenAuthenticator(resourceMetadata, adapterConfig.isUseResourceRoleMappings());
}
protected void completeAuthentication(HttpServerExchange exchange, SecurityContext securityContext, SkeletonKeyToken token, String surrogate) {
final SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getPrincipal(), surrogate);
Set<String> roles = null;
if (config.isUseResourceRoleMappings()) {
if (adapterConfig.isUseResourceRoleMappings()) {
SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
if (access != null) roles = access.getRoles();
} else {

View file

@ -7,8 +7,7 @@ import io.undertow.servlet.ServletExtension;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.ServletSessionConfig;
import org.jboss.logging.Logger;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.ManagedResourceConfigLoader;
import org.keycloak.adapters.config.AdapterConfig;
import javax.servlet.ServletContext;
import java.io.InputStream;
@ -30,9 +29,9 @@ public class KeycloakServletExtension implements ServletExtension {
deploymentInfo.setIgnoreStandardAuthenticationMechanism(true);
InputStream is = servletContext.getResourceAsStream("/WEB-INF/keycloak.json");
if (is == null) throw new RuntimeException("Unable to find /WEB-INF/keycloak.json configuration file");
ManagedResourceConfigLoader loader = new ManagedResourceConfigLoader(is);
RealmConfigurationLoader loader = new RealmConfigurationLoader(is);
loader.init(true);
ManagedResourceConfig keycloakConfig = loader.getRemoteSkeletonKeyConfig();
AdapterConfig keycloakConfig = loader.getAdapterConfig();
PreflightCorsHandler.Wrapper preflight = new PreflightCorsHandler.Wrapper(keycloakConfig);
ServletKeycloakAuthenticationMechanism auth = new ServletKeycloakAuthenticationMechanism(loader.getResourceMetadata(),
keycloakConfig,

View file

@ -5,7 +5,7 @@ import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HttpString;
import org.jboss.logging.Logger;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.AdapterConfig;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -13,7 +13,7 @@ import org.keycloak.adapters.config.ManagedResourceConfig;
*/
public class PreflightCorsHandler implements HttpHandler {
private static final Logger log = Logger.getLogger(PreflightCorsHandler.class);
protected ManagedResourceConfig config;
protected AdapterConfig adapterConfig;
protected HttpHandler next;
public static final HttpString ACCESS_CONTROL_ALLOW_ORIGIN = new HttpString("Access-Control-Allow-Origin");
@ -23,9 +23,9 @@ public class PreflightCorsHandler implements HttpHandler {
public static final HttpString ACCESS_CONTROL_MAX_AGE = new HttpString("Access-Control-Max-Age");
public static class Wrapper implements HandlerWrapper {
protected ManagedResourceConfig config;
protected AdapterConfig config;
public Wrapper(ManagedResourceConfig config) {
public Wrapper(AdapterConfig config) {
this.config = config;
}
@ -35,8 +35,8 @@ public class PreflightCorsHandler implements HttpHandler {
}
}
protected PreflightCorsHandler(ManagedResourceConfig config, HttpHandler next) {
this.config = config;
protected PreflightCorsHandler(AdapterConfig config, HttpHandler next) {
this.adapterConfig = config;
this.next = next;
}
@ -60,20 +60,20 @@ public class PreflightCorsHandler implements HttpHandler {
exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
String requestMethods = exchange.getRequestHeaders().getFirst("Access-Control-Request-Method");
if (requestMethods != null) {
if (config.getCorsAllowedMethods() != null) {
requestMethods = config.getCorsAllowedMethods();
if (adapterConfig.getCorsAllowedMethods() != null) {
requestMethods = adapterConfig.getCorsAllowedMethods();
}
exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_METHODS, requestMethods);
}
String allowHeaders = exchange.getRequestHeaders().getFirst("Access-Control-Request-Headers");
if (allowHeaders != null) {
if (config.getCorsAllowedHeaders() != null) {
allowHeaders = config.getCorsAllowedHeaders();
if (adapterConfig.getCorsAllowedHeaders() != null) {
allowHeaders = adapterConfig.getCorsAllowedHeaders();
}
exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_HEADERS, allowHeaders);
}
if (config.getCorsMaxAge() > -1) {
exchange.getResponseHeaders().put(ACCESS_CONTROL_MAX_AGE, Integer.toString(config.getCorsMaxAge()));
if (adapterConfig.getCorsMaxAge() > -1) {
exchange.getResponseHeaders().put(ACCESS_CONTROL_MAX_AGE, Integer.toString(adapterConfig.getCorsMaxAge()));
}
exchange.endExchange();
}

View file

@ -0,0 +1,97 @@
package org.keycloak.adapters.undertow;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.config.AdapterConfigLoader;
import javax.ws.rs.core.UriBuilder;
import java.io.InputStream;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmConfigurationLoader extends AdapterConfigLoader {
protected ResteasyClient client;
protected RealmConfiguration realmConfiguration;
public RealmConfigurationLoader() {
}
public RealmConfigurationLoader(InputStream is) {
loadConfig(is);
}
public void init(boolean setupClient) {
init();
initRealmConfiguration(setupClient);
}
protected void initRealmConfiguration(boolean setupClient) {
if (!setupClient || adapterConfig.isBearerOnly()) return;
initClient();
realmConfiguration = new RealmConfiguration();
String authUrl = adapterConfig.getAuthUrl();
if (authUrl == null) {
throw new RuntimeException("You must specify auth-url");
}
String tokenUrl = adapterConfig.getCodeUrl();
if (tokenUrl == null) {
throw new RuntimeException("You mut specify code-url");
}
realmConfiguration.setMetadata(resourceMetadata);
realmConfiguration.setSslRequired(!adapterConfig.isSslNotRequired());
for (Map.Entry<String, String> entry : getAdapterConfig().getCredentials().entrySet()) {
realmConfiguration.getResourceCredentials().param(entry.getKey(), entry.getValue());
}
ResteasyClient client = getClient();
realmConfiguration.setClient(client);
realmConfiguration.setAuthUrl(UriBuilder.fromUri(authUrl).queryParam("client_id", resourceMetadata.getResourceName()));
realmConfiguration.setCodeUrl(client.target(tokenUrl));
}
protected void initClient() {
int size = 10;
if (adapterConfig.getConnectionPoolSize() > 0)
size = adapterConfig.getConnectionPoolSize();
ResteasyClientBuilder.HostnameVerificationPolicy policy = ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD;
if (adapterConfig.isAllowAnyHostname())
policy = ResteasyClientBuilder.HostnameVerificationPolicy.ANY;
ResteasyProviderFactory providerFactory = new ResteasyProviderFactory();
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(RealmConfigurationLoader.class.getClassLoader());
try {
ResteasyProviderFactory.getInstance(); // initialize builtins
RegisterBuiltin.register(providerFactory);
} finally {
Thread.currentThread().setContextClassLoader(old);
}
ResteasyClientBuilder builder = new ResteasyClientBuilder()
.providerFactory(providerFactory)
.connectionPoolSize(size)
.hostnameVerification(policy)
.keyStore(clientCertKeystore, adapterConfig.getClientKeyPassword());
if (adapterConfig.isDisableTrustManager()) {
builder.disableTrustManager();
} else {
builder.trustStore(truststore);
}
client = builder.build();
}
public ResteasyClient getClient() {
return client;
}
public RealmConfiguration getRealmConfiguration() {
return realmConfiguration;
}
}

View file

@ -5,7 +5,7 @@ import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.handlers.ServletRequestContext;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.AdapterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@ -16,14 +16,14 @@ import javax.servlet.http.HttpSession;
*/
public class ServletAuthenticatedActionsHandler extends AuthenticatedActionsHandler {
protected ServletAuthenticatedActionsHandler(ManagedResourceConfig config, HttpHandler next) {
protected ServletAuthenticatedActionsHandler(AdapterConfig config, HttpHandler next) {
super(config, next);
}
public static class Wrapper implements HandlerWrapper {
protected ManagedResourceConfig config;
protected AdapterConfig config;
public Wrapper(ManagedResourceConfig config) {
public Wrapper(AdapterConfig config) {
this.config = config;
}

View file

@ -6,7 +6,7 @@ import io.undertow.servlet.handlers.ServletRequestContext;
import org.keycloak.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.config.ManagedResourceConfig;
import org.keycloak.adapters.config.AdapterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@ -18,7 +18,7 @@ import javax.servlet.http.HttpSession;
public class ServletKeycloakAuthenticationMechanism extends KeycloakAuthenticationMechanism {
protected ConfidentialPortManager portManager;
public ServletKeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, ManagedResourceConfig config, RealmConfiguration realmConfig, ConfidentialPortManager portManager) {
public ServletKeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, AdapterConfig config, RealmConfiguration realmConfig, ConfidentialPortManager portManager) {
super(resourceMetadata, config, realmConfig);
this.portManager = portManager;
}

View file

@ -134,6 +134,7 @@ public class AccountTest {
}
@Test
@Ignore
public void changePassword() {
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");