refactor adapters
This commit is contained in:
parent
0fd0240be7
commit
a653e5a406
43 changed files with 674 additions and 927 deletions
|
@ -15,7 +15,6 @@ public class AbstractOAuthClient {
|
|||
public static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
|
||||
protected String clientId;
|
||||
protected Map<String, String> credentials;
|
||||
protected KeyStore truststore;
|
||||
protected String authUrl;
|
||||
protected String codeUrl;
|
||||
protected String refreshUrl;
|
||||
|
@ -46,20 +45,10 @@ public class AbstractOAuthClient {
|
|||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
public KeyStore getTruststore() {
|
||||
return truststore;
|
||||
}
|
||||
|
||||
public void setTruststore(KeyStore truststore) {
|
||||
this.truststore = truststore;
|
||||
}
|
||||
|
||||
public String getAuthUrl() {
|
||||
return authUrl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setAuthUrl(String authUrl) {
|
||||
this.authUrl = authUrl;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak;
|
||||
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
|
@ -15,17 +14,15 @@ public class KeycloakSecurityContext implements Serializable {
|
|||
protected AccessToken token;
|
||||
protected IDToken idToken;
|
||||
protected String idTokenString;
|
||||
protected transient ResourceMetadata metadata;
|
||||
|
||||
public KeycloakSecurityContext() {
|
||||
}
|
||||
|
||||
public KeycloakSecurityContext(String tokenString, AccessToken token, String idTokenString, IDToken idToken, ResourceMetadata metadata) {
|
||||
public KeycloakSecurityContext(String tokenString, AccessToken token, String idTokenString, IDToken idToken) {
|
||||
this.tokenString = tokenString;
|
||||
this.token = token;
|
||||
this.idToken = idToken;
|
||||
this.idTokenString = idTokenString;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public AccessToken getToken() {
|
||||
|
@ -36,14 +33,6 @@ public class KeycloakSecurityContext implements Serializable {
|
|||
return tokenString;
|
||||
}
|
||||
|
||||
public ResourceMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public void setMetadata(ResourceMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public IDToken getIdToken() {
|
||||
return idToken;
|
||||
}
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
package org.keycloak.adapters;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ResourceMetadata {
|
||||
protected String realm;
|
||||
protected String resourceName;
|
||||
protected KeyStore clientKeystore;
|
||||
protected String clientKeyPassword;
|
||||
protected KeyStore truststore;
|
||||
protected PublicKey realmKey;
|
||||
protected String scope;
|
||||
|
||||
public String getResourceName() {
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* keystore that contains service's private key and certificate.
|
||||
* Used when making invocations on remote HTTPS endpoints that require client-cert authentication
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public KeyStore getClientKeystore() {
|
||||
return clientKeystore;
|
||||
}
|
||||
|
||||
public String getClientKeyPassword() {
|
||||
return clientKeyPassword;
|
||||
}
|
||||
|
||||
public void setClientKeyPassword(String clientKeyPassword) {
|
||||
this.clientKeyPassword = clientKeyPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truststore to use if this service makes client invocations on remote HTTPS endpoints.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public KeyStore getTruststore() {
|
||||
return truststore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public key of the realm. Used to verify access tokens
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public PublicKey getRealmKey() {
|
||||
return realmKey;
|
||||
}
|
||||
|
||||
public void setResourceName(String resourceName) {
|
||||
this.resourceName = resourceName;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public void setClientKeystore(KeyStore clientKeystore) {
|
||||
this.clientKeystore = clientKeystore;
|
||||
}
|
||||
|
||||
public void setTruststore(KeyStore truststore) {
|
||||
this.truststore = truststore;
|
||||
}
|
||||
|
||||
public void setRealmKey(PublicKey realmKey) {
|
||||
this.realmKey = realmKey;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
}
|
21
core/src/main/java/org/keycloak/util/KeystoreUtil.java
Executable file
21
core/src/main/java/org/keycloak/util/KeystoreUtil.java
Executable file
|
@ -0,0 +1,21 @@
|
|||
package org.keycloak.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.security.KeyStore;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeystoreUtil {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.keycloak.example.demo</groupId>
|
||||
<artifactId>customer-portal-cli-example</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<packaging>jar</packaging>
|
||||
<name>Customer Portal CLI</name>
|
||||
<description/>
|
||||
|
||||
|
@ -62,7 +62,10 @@
|
|||
<plugin>
|
||||
<groupId>org.jboss.as.plugins</groupId>
|
||||
<artifactId>jboss-as-maven-plugin</artifactId>
|
||||
<version>7.4.Final</version>
|
||||
<version>7.1.1.Final</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
|
|
@ -46,8 +46,7 @@ public class CustomerDatabaseClient {
|
|||
KeycloakSecurityContext session = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
|
||||
HttpClient client = new HttpClientBuilder()
|
||||
.trustStore(session.getMetadata().getTruststore())
|
||||
.hostnameVerification(HttpClientBuilder.HostnameVerificationPolicy.ANY).build();
|
||||
.disableTrustManager().build();
|
||||
try {
|
||||
HttpGet get = new HttpGet("http://localhost:8080/database/customers");
|
||||
get.addHeader("Authorization", "Bearer " + session.getTokenString());
|
||||
|
|
|
@ -37,8 +37,7 @@ public class ProductDatabaseClient
|
|||
public static List<String> getProducts(HttpServletRequest req) throws Failure {
|
||||
KeycloakSecurityContext session = (KeycloakSecurityContext)req.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
HttpClient client = new HttpClientBuilder()
|
||||
.trustStore(session.getMetadata().getTruststore())
|
||||
.hostnameVerification(HttpClientBuilder.HostnameVerificationPolicy.ANY).build();
|
||||
.disableTrustManager().build();
|
||||
try {
|
||||
HttpGet get = new HttpGet("http://localhost:8080/database/products");
|
||||
get.addHeader("Authorization", "Bearer " + session.getTokenString());
|
||||
|
|
|
@ -12,7 +12,7 @@ import javax.servlet.annotation.WebListener;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.servlet.ServletOAuthClient;
|
||||
import org.keycloak.servlet.ServletOAuthClientConfigLoader;
|
||||
import org.keycloak.servlet.ServletOAuthClientBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -40,12 +40,7 @@ public class AppContextListener implements ServletContextListener {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
ServletOAuthClientConfigLoader loader = new ServletOAuthClientConfigLoader(is);
|
||||
loader.initOAuthClientConfiguration(true);
|
||||
loader.configureServletOAuthClient(oauthClient);
|
||||
|
||||
oauthClient.start();
|
||||
ServletOAuthClientBuilder.build(is, oauthClient);
|
||||
logger.info("OAuth client configured and started");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.keycloak.example.oauth;
|
||||
|
||||
import org.keycloak.servlet.ServletOAuthClient;
|
||||
import org.keycloak.servlet.ServletOAuthClientConfigLoader;
|
||||
import org.keycloak.servlet.ServletOAuthClientBuilder;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
|
@ -23,16 +23,6 @@ public class Bootstrap implements ServletContextListener {
|
|||
|
||||
private ServletOAuthClient client;
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
client = new ServletOAuthClient();
|
||||
|
@ -62,8 +52,6 @@ public class Bootstrap implements ServletContextListener {
|
|||
}
|
||||
}
|
||||
|
||||
ServletOAuthClientConfigLoader loader = new ServletOAuthClientConfigLoader(is);
|
||||
loader.initOAuthClientConfiguration(true);
|
||||
loader.configureServletOAuthClient(client);
|
||||
client = ServletOAuthClientBuilder.build(is);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@ import org.apache.http.impl.conn.SingleClientConnManager;
|
|||
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
||||
import org.apache.http.params.BasicHttpParams;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.util.EnvUtil;
|
||||
import org.keycloak.util.KeystoreUtil;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
@ -271,4 +274,41 @@ public class HttpClientBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
public HttpClient build(AdapterConfig adapterConfig) {
|
||||
String truststorePath = adapterConfig.getTruststore();
|
||||
if (truststorePath != null) {
|
||||
truststorePath = EnvUtil.replace(truststorePath);
|
||||
String truststorePassword = adapterConfig.getTruststorePassword();
|
||||
try {
|
||||
this.truststore = KeystoreUtil.loadKeyStore(truststorePath, truststorePassword);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load truststore", e);
|
||||
}
|
||||
}
|
||||
String clientKeystore = adapterConfig.getClientKeystore();
|
||||
if (clientKeystore != null) {
|
||||
clientKeystore = EnvUtil.replace(clientKeystore);
|
||||
String clientKeystorePassword = adapterConfig.getClientKeystorePassword();
|
||||
try {
|
||||
KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword);
|
||||
keyStore(clientCertKeystore, clientKeystorePassword);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load keystore", e);
|
||||
}
|
||||
}
|
||||
int size = 10;
|
||||
if (adapterConfig.getConnectionPoolSize() > 0)
|
||||
size = adapterConfig.getConnectionPoolSize();
|
||||
HttpClientBuilder.HostnameVerificationPolicy policy = HttpClientBuilder.HostnameVerificationPolicy.WILDCARD;
|
||||
if (adapterConfig.isAllowAnyHostname())
|
||||
policy = HttpClientBuilder.HostnameVerificationPolicy.ANY;
|
||||
connectionPoolSize(size);
|
||||
hostnameVerification(policy);
|
||||
if (adapterConfig.isDisableTrustManager()) {
|
||||
disableTrustManager();
|
||||
} else {
|
||||
trustStore(truststore);
|
||||
}
|
||||
return build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
package org.keycloak.adapters;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakDeployment {
|
||||
protected String realm;
|
||||
protected PublicKey realmKey;
|
||||
protected KeycloakUriBuilder authUrl;
|
||||
protected String codeUrl;
|
||||
protected String refreshUrl;
|
||||
|
||||
protected String resourceName;
|
||||
protected boolean bearerOnly;
|
||||
protected boolean publicClient;
|
||||
protected Map<String, String> resourceCredentials = new HashMap<String, String>();
|
||||
protected HttpClient client;
|
||||
|
||||
protected String scope;
|
||||
protected boolean sslRequired = true;
|
||||
protected String stateCookieName = "OAuth_Token_Request_State";
|
||||
protected boolean useResourceRoleMappings;
|
||||
protected boolean cors;
|
||||
protected int corsMaxAge = -1;
|
||||
protected String corsAllowedHeaders;
|
||||
protected String corsAllowedMethods;
|
||||
protected boolean exposeToken;
|
||||
protected volatile int notBefore;
|
||||
|
||||
public String getResourceName() {
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public PublicKey getRealmKey() {
|
||||
return realmKey;
|
||||
}
|
||||
|
||||
public void setRealmKey(PublicKey realmKey) {
|
||||
this.realmKey = realmKey;
|
||||
}
|
||||
|
||||
public KeycloakUriBuilder getAuthUrl() {
|
||||
return authUrl;
|
||||
}
|
||||
|
||||
public void setAuthUrl(KeycloakUriBuilder authUrl) {
|
||||
this.authUrl = authUrl;
|
||||
}
|
||||
|
||||
public String getCodeUrl() {
|
||||
return codeUrl;
|
||||
}
|
||||
|
||||
public void setCodeUrl(String codeUrl) {
|
||||
this.codeUrl = codeUrl;
|
||||
}
|
||||
|
||||
public String getRefreshUrl() {
|
||||
return refreshUrl;
|
||||
}
|
||||
|
||||
public void setRefreshUrl(String refreshUrl) {
|
||||
this.refreshUrl = refreshUrl;
|
||||
}
|
||||
|
||||
public void setResourceName(String resourceName) {
|
||||
this.resourceName = resourceName;
|
||||
}
|
||||
|
||||
public boolean isBearerOnly() {
|
||||
return bearerOnly;
|
||||
}
|
||||
|
||||
public void setBearerOnly(boolean bearerOnly) {
|
||||
this.bearerOnly = bearerOnly;
|
||||
}
|
||||
|
||||
public boolean isPublicClient() {
|
||||
return publicClient;
|
||||
}
|
||||
|
||||
public void setPublicClient(boolean publicClient) {
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public Map<String, String> getResourceCredentials() {
|
||||
return resourceCredentials;
|
||||
}
|
||||
|
||||
public void setResourceCredentials(Map<String, String> resourceCredentials) {
|
||||
this.resourceCredentials = resourceCredentials;
|
||||
}
|
||||
|
||||
public HttpClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(HttpClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public boolean isSslRequired() {
|
||||
return sslRequired;
|
||||
}
|
||||
|
||||
public void setSslRequired(boolean sslRequired) {
|
||||
this.sslRequired = sslRequired;
|
||||
}
|
||||
|
||||
public String getStateCookieName() {
|
||||
return stateCookieName;
|
||||
}
|
||||
|
||||
public void setStateCookieName(String stateCookieName) {
|
||||
this.stateCookieName = stateCookieName;
|
||||
}
|
||||
|
||||
public boolean isUseResourceRoleMappings() {
|
||||
return useResourceRoleMappings;
|
||||
}
|
||||
|
||||
public void setUseResourceRoleMappings(boolean useResourceRoleMappings) {
|
||||
this.useResourceRoleMappings = useResourceRoleMappings;
|
||||
}
|
||||
|
||||
public boolean isCors() {
|
||||
return cors;
|
||||
}
|
||||
|
||||
public void setCors(boolean cors) {
|
||||
this.cors = cors;
|
||||
}
|
||||
|
||||
public int getCorsMaxAge() {
|
||||
return corsMaxAge;
|
||||
}
|
||||
|
||||
public void setCorsMaxAge(int corsMaxAge) {
|
||||
this.corsMaxAge = corsMaxAge;
|
||||
}
|
||||
|
||||
public String getCorsAllowedHeaders() {
|
||||
return corsAllowedHeaders;
|
||||
}
|
||||
|
||||
public void setCorsAllowedHeaders(String corsAllowedHeaders) {
|
||||
this.corsAllowedHeaders = corsAllowedHeaders;
|
||||
}
|
||||
|
||||
public String getCorsAllowedMethods() {
|
||||
return corsAllowedMethods;
|
||||
}
|
||||
|
||||
public void setCorsAllowedMethods(String corsAllowedMethods) {
|
||||
this.corsAllowedMethods = corsAllowedMethods;
|
||||
}
|
||||
|
||||
public boolean isExposeToken() {
|
||||
return exposeToken;
|
||||
}
|
||||
|
||||
public void setExposeToken(boolean exposeToken) {
|
||||
this.exposeToken = exposeToken;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package org.keycloak.adapters;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||
import org.keycloak.ServiceUrlConstants;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.util.EnvUtil;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
import org.keycloak.util.KeystoreUtil;
|
||||
import org.keycloak.util.PemUtils;
|
||||
|
||||
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 KeycloakDeploymentBuilder {
|
||||
protected KeycloakDeployment deployment = new KeycloakDeployment();
|
||||
|
||||
protected KeycloakDeploymentBuilder() {}
|
||||
|
||||
|
||||
|
||||
protected KeycloakDeployment internalBuild(AdapterConfig adapterConfig) {
|
||||
|
||||
if (adapterConfig.getRealm() == null) throw new RuntimeException("Must set 'realm' in config");
|
||||
deployment.setRealm(adapterConfig.getRealm());
|
||||
String resource = adapterConfig.getResource();
|
||||
if (resource == null) throw new RuntimeException("Must set 'resource' in config");
|
||||
deployment.setResourceName(resource);
|
||||
|
||||
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);
|
||||
}
|
||||
deployment.setRealmKey(realmKey);
|
||||
deployment.setSslRequired(!adapterConfig.isSslNotRequired());
|
||||
deployment.setResourceCredentials(adapterConfig.getCredentials());
|
||||
deployment.setPublicClient(adapterConfig.isPublicClient());
|
||||
|
||||
if (adapterConfig.isBearerOnly()) {
|
||||
deployment.setBearerOnly(true);
|
||||
return deployment;
|
||||
}
|
||||
|
||||
deployment.setClient(new HttpClientBuilder().build(adapterConfig));
|
||||
if (adapterConfig.getAuthServerUrl() == null) {
|
||||
throw new RuntimeException("You must specify auth-url");
|
||||
}
|
||||
KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(adapterConfig.getAuthServerUrl());
|
||||
String authUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_LOGIN_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String tokenUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_ACCESS_CODE_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String refreshUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_REFRESH_PATH).build(adapterConfig.getRealm()).toString();
|
||||
|
||||
|
||||
|
||||
deployment.setAuthUrl(KeycloakUriBuilder.fromUri(authUrl).queryParam("client_id", deployment.getResourceName()));
|
||||
deployment.setCodeUrl(tokenUrl);
|
||||
deployment.setRefreshUrl(refreshUrl);
|
||||
|
||||
|
||||
|
||||
return deployment;
|
||||
}
|
||||
|
||||
public static KeycloakDeployment build(InputStream is) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
|
||||
AdapterConfig adapterConfig = null;
|
||||
try {
|
||||
adapterConfig = mapper.readValue(is, AdapterConfig.class);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new KeycloakDeploymentBuilder().internalBuild(adapterConfig);
|
||||
}
|
||||
|
||||
|
||||
public static KeycloakDeployment build(AdapterConfig adapterConfig) {
|
||||
return new KeycloakDeploymentBuilder().internalBuild(adapterConfig);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -3,7 +3,6 @@ package org.keycloak.adapters;
|
|||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -19,15 +18,15 @@ public class RefreshableKeycloakSession extends KeycloakSecurityContext {
|
|||
|
||||
protected static Logger log = Logger.getLogger(RefreshableKeycloakSession.class);
|
||||
|
||||
protected transient RealmConfiguration realmConfiguration;
|
||||
protected transient KeycloakDeployment deployment;
|
||||
protected String refreshToken;
|
||||
|
||||
public RefreshableKeycloakSession() {
|
||||
}
|
||||
|
||||
public RefreshableKeycloakSession(String tokenString, AccessToken token, String idTokenString, IDToken idToken, ResourceMetadata metadata, RealmConfiguration realmConfiguration, String refreshToken) {
|
||||
super(tokenString, token, idTokenString, idToken, metadata);
|
||||
this.realmConfiguration = realmConfiguration;
|
||||
public RefreshableKeycloakSession(KeycloakDeployment deployment, String tokenString, AccessToken token, String idTokenString, IDToken idToken, String refreshToken) {
|
||||
super(tokenString, token, idTokenString, idToken);
|
||||
this.deployment = deployment;
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
|
@ -44,22 +43,22 @@ public class RefreshableKeycloakSession extends KeycloakSecurityContext {
|
|||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.token.isActive() && this.token.getIssuedAt() > realmConfiguration.getNotBefore();
|
||||
return this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
|
||||
}
|
||||
|
||||
public void setRealmConfiguration(RealmConfiguration realmConfiguration) {
|
||||
this.realmConfiguration = realmConfiguration;
|
||||
public void setDeployment(KeycloakDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
public void refreshExpiredToken() {
|
||||
log.info("checking whether to refresh.");
|
||||
if (isActive()) return;
|
||||
if (this.realmConfiguration == null || refreshToken == null) return; // Might be serialized in HttpSession?
|
||||
if (this.deployment == null || refreshToken == null) return; // Might be serialized in HttpSession?
|
||||
|
||||
log.info("Doing refresh");
|
||||
AccessTokenResponse response = null;
|
||||
try {
|
||||
response = ServerRequest.invokeRefresh(realmConfiguration, refreshToken);
|
||||
response = ServerRequest.invokeRefresh(deployment, refreshToken);
|
||||
} catch (IOException e) {
|
||||
log.error("Refresh token failure", e);
|
||||
return;
|
||||
|
@ -71,13 +70,13 @@ public class RefreshableKeycloakSession extends KeycloakSecurityContext {
|
|||
String tokenString = response.getToken();
|
||||
AccessToken token = null;
|
||||
try {
|
||||
token = RSATokenVerifier.verifyToken(tokenString, realmConfiguration.getMetadata().getRealmKey(), realmConfiguration.getMetadata().getRealm());
|
||||
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm());
|
||||
log.info("Token Verification succeeded!");
|
||||
} catch (VerificationException e) {
|
||||
log.error("failed verification of token");
|
||||
}
|
||||
if (response.getNotBeforePolicy() > realmConfiguration.getNotBefore()) {
|
||||
realmConfiguration.setNotBefore(response.getNotBeforePolicy());
|
||||
if (response.getNotBeforePolicy() > deployment.getNotBefore()) {
|
||||
deployment.setNotBefore(response.getNotBeforePolicy());
|
||||
}
|
||||
|
||||
this.token = token;
|
||||
|
|
|
@ -7,9 +7,7 @@ import org.apache.http.client.HttpClient;
|
|||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -47,13 +45,13 @@ public class ServerRequest {
|
|||
}
|
||||
}
|
||||
|
||||
public static AccessTokenResponse invokeAccessCodeToToken(RealmConfiguration config, String code, String redirectUri) throws HttpFailure, IOException {
|
||||
String codeUrl = config.getCodeUrl();
|
||||
String client_id = config.getMetadata().getResourceName();
|
||||
Map<String, String> credentials = config.getResourceCredentials();
|
||||
HttpClient client = config.getClient();
|
||||
public static AccessTokenResponse invokeAccessCodeToToken(KeycloakDeployment deployment, String code, String redirectUri) throws HttpFailure, IOException {
|
||||
String codeUrl = deployment.getCodeUrl();
|
||||
String client_id = deployment.getResourceName();
|
||||
Map<String, String> credentials = deployment.getResourceCredentials();
|
||||
HttpClient client = deployment.getClient();
|
||||
|
||||
return invokeAccessCodeToToken(client, config.isPublicClient(), code, codeUrl, redirectUri, client_id, credentials);
|
||||
return invokeAccessCodeToToken(client, deployment.isPublicClient(), code, codeUrl, redirectUri, client_id, credentials);
|
||||
}
|
||||
|
||||
public static AccessTokenResponse invokeAccessCodeToToken(HttpClient client, boolean publicClient, String code, String codeUrl, String redirectUri, String client_id, Map<String, String> credentials) throws IOException, HttpFailure {
|
||||
|
@ -108,12 +106,12 @@ public class ServerRequest {
|
|||
}
|
||||
}
|
||||
|
||||
public static AccessTokenResponse invokeRefresh(RealmConfiguration config, String refreshToken) throws IOException, HttpFailure {
|
||||
String refreshUrl = config.getRefreshUrl();
|
||||
String client_id = config.getMetadata().getResourceName();
|
||||
Map<String, String> credentials = config.getResourceCredentials();
|
||||
HttpClient client = config.getClient();
|
||||
return invokeRefresh(client, config.isPublicClient(), refreshToken, refreshUrl, client_id, credentials);
|
||||
public static AccessTokenResponse invokeRefresh(KeycloakDeployment deployment, String refreshToken) throws IOException, HttpFailure {
|
||||
String refreshUrl = deployment.getRefreshUrl();
|
||||
String client_id = deployment.getResourceName();
|
||||
Map<String, String> credentials = deployment.getResourceCredentials();
|
||||
HttpClient client = deployment.getClient();
|
||||
return invokeRefresh(client, deployment.isPublicClient(), refreshToken, refreshUrl, client_id, credentials);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
package org.keycloak.adapters.config;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||
import org.keycloak.util.EnvUtil;
|
||||
import org.keycloak.util.PemUtils;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
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() {
|
||||
initTruststore();
|
||||
initClientKeystore();
|
||||
|
||||
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);
|
||||
String 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);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initTruststore() {
|
||||
String truststorePath = adapterConfig.getTruststore();
|
||||
if (truststorePath != null) {
|
||||
truststorePath = EnvUtil.replace(truststorePath);
|
||||
String truststorePassword = adapterConfig.getTruststorePassword();
|
||||
try {
|
||||
this.truststore = loadKeyStore(truststorePath, truststorePassword);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load truststore", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void initClientKeystore() {
|
||||
String clientKeystore = adapterConfig.getClientKeystore();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package org.keycloak.adapters.config;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.keycloak.AbstractOAuthClient;
|
||||
import org.keycloak.ServiceUrlConstants;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class OAuthClientConfigLoader extends RealmConfigurationLoader {
|
||||
|
||||
public OAuthClientConfigLoader() {
|
||||
}
|
||||
|
||||
public OAuthClientConfigLoader(InputStream is) {
|
||||
super(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, configure just things supported by AbstractOAuthClient
|
||||
*/
|
||||
public void initOAuthClientConfiguration() {
|
||||
initTruststore();
|
||||
initClientKeystore();
|
||||
}
|
||||
|
||||
public void configureOAuthClient(AbstractOAuthClient oauthClient) {
|
||||
oauthClient.setClientId(adapterConfig.getResource());
|
||||
oauthClient.setPublicClient(adapterConfig.isPublicClient());
|
||||
oauthClient.setCredentials(adapterConfig.getCredentials());
|
||||
if (adapterConfig.getAuthServerUrl() == null) {
|
||||
throw new RuntimeException("You must specify auth-url");
|
||||
}
|
||||
KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(adapterConfig.getAuthServerUrl());
|
||||
String authUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_LOGIN_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String tokenUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_ACCESS_CODE_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String refreshUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_REFRESH_PATH).build(adapterConfig.getRealm()).toString();
|
||||
oauthClient.setAuthUrl(authUrl);
|
||||
oauthClient.setCodeUrl(tokenUrl);
|
||||
oauthClient.setRefreshUrl(refreshUrl);
|
||||
oauthClient.setTruststore(truststore);
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
package org.keycloak.adapters.config;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RealmConfiguration {
|
||||
protected ResourceMetadata metadata;
|
||||
protected HttpClient client;
|
||||
protected KeycloakUriBuilder authUrl;
|
||||
protected String codeUrl;
|
||||
protected String refreshUrl;
|
||||
protected boolean publicClient;
|
||||
protected Map<String, String> resourceCredentials = new HashMap<String, String>();
|
||||
protected boolean sslRequired = true;
|
||||
protected String stateCookieName = "OAuth_Token_Request_State";
|
||||
protected volatile int notBefore;
|
||||
|
||||
public RealmConfiguration() {
|
||||
}
|
||||
|
||||
public ResourceMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public void setMetadata(ResourceMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
|
||||
public boolean isSslRequired() {
|
||||
return sslRequired;
|
||||
}
|
||||
|
||||
public void setSslRequired(boolean sslRequired) {
|
||||
this.sslRequired = sslRequired;
|
||||
}
|
||||
|
||||
public String getStateCookieName() {
|
||||
return stateCookieName;
|
||||
}
|
||||
|
||||
public void setStateCookieName(String stateCookieName) {
|
||||
this.stateCookieName = stateCookieName;
|
||||
}
|
||||
|
||||
public HttpClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(HttpClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public KeycloakUriBuilder getAuthUrl() {
|
||||
return authUrl;
|
||||
}
|
||||
|
||||
public void setAuthUrl(KeycloakUriBuilder authUrl) {
|
||||
this.authUrl = authUrl;
|
||||
}
|
||||
|
||||
public String getCodeUrl() {
|
||||
return codeUrl;
|
||||
}
|
||||
|
||||
public void setCodeUrl(String codeUrl) {
|
||||
this.codeUrl = codeUrl;
|
||||
}
|
||||
|
||||
public String getRefreshUrl() {
|
||||
return refreshUrl;
|
||||
}
|
||||
|
||||
public void setRefreshUrl(String refreshUrl) {
|
||||
this.refreshUrl = refreshUrl;
|
||||
}
|
||||
|
||||
public Map<String, String> getResourceCredentials() {
|
||||
return resourceCredentials;
|
||||
}
|
||||
|
||||
public void setResourceCredentials(Map<String, String> resourceCredentials) {
|
||||
this.resourceCredentials = resourceCredentials;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public boolean isPublicClient() {
|
||||
return publicClient;
|
||||
}
|
||||
|
||||
public void setPublicClient(boolean publicClient) {
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package org.keycloak.adapters.config;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.ServiceUrlConstants;
|
||||
import org.keycloak.adapters.HttpClientBuilder;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RealmConfigurationLoader extends AdapterConfigLoader {
|
||||
protected HttpClient 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) {
|
||||
realmConfiguration = new RealmConfiguration();
|
||||
realmConfiguration.setMetadata(resourceMetadata);
|
||||
realmConfiguration.setSslRequired(!adapterConfig.isSslNotRequired());
|
||||
realmConfiguration.setResourceCredentials(adapterConfig.getCredentials());
|
||||
realmConfiguration.setPublicClient(adapterConfig.isPublicClient());
|
||||
if (!setupClient || adapterConfig.isBearerOnly()) return;
|
||||
initClient();
|
||||
if (adapterConfig.getAuthServerUrl() == null) {
|
||||
throw new RuntimeException("You must specify auth-url");
|
||||
}
|
||||
KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(adapterConfig.getAuthServerUrl());
|
||||
String authUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_LOGIN_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String tokenUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_ACCESS_CODE_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String refreshUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_REFRESH_PATH).build(adapterConfig.getRealm()).toString();
|
||||
|
||||
|
||||
HttpClient client = getClient();
|
||||
|
||||
realmConfiguration.setClient(client);
|
||||
realmConfiguration.setAuthUrl(KeycloakUriBuilder.fromUri(authUrl).queryParam("client_id", resourceMetadata.getResourceName()));
|
||||
realmConfiguration.setCodeUrl(tokenUrl);
|
||||
realmConfiguration.setRefreshUrl(refreshUrl);
|
||||
}
|
||||
|
||||
protected void initClient() {
|
||||
int size = 10;
|
||||
if (adapterConfig.getConnectionPoolSize() > 0)
|
||||
size = adapterConfig.getConnectionPoolSize();
|
||||
HttpClientBuilder.HostnameVerificationPolicy policy = HttpClientBuilder.HostnameVerificationPolicy.WILDCARD;
|
||||
if (adapterConfig.isAllowAnyHostname())
|
||||
policy = HttpClientBuilder.HostnameVerificationPolicy.ANY;
|
||||
HttpClientBuilder builder = new HttpClientBuilder()
|
||||
.connectionPoolSize(size)
|
||||
.hostnameVerification(policy)
|
||||
.keyStore(clientCertKeystore, adapterConfig.getClientKeyPassword());
|
||||
if (adapterConfig.isDisableTrustManager()) {
|
||||
builder.disableTrustManager();
|
||||
} else {
|
||||
builder.trustStore(truststore);
|
||||
}
|
||||
client = builder.build();
|
||||
}
|
||||
|
||||
public HttpClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public RealmConfiguration getRealmConfiguration() {
|
||||
return realmConfiguration;
|
||||
}
|
||||
|
||||
}
|
|
@ -68,8 +68,8 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
|
|||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition PUBLIC_CLIENT =
|
||||
new SimpleAttributeDefinitionBuilder("bearer-only", ModelType.BOOLEAN, true)
|
||||
.setXmlName("bearer-only")
|
||||
new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true)
|
||||
.setXmlName("public-client")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
|
|
|
@ -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.KeycloakDeployment;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
|
@ -31,10 +32,10 @@ import java.util.Set;
|
|||
*/
|
||||
public class AuthenticatedActionsValve extends ValveBase {
|
||||
private static final Logger log = Logger.getLogger(AuthenticatedActionsValve.class);
|
||||
protected AdapterConfig config;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
public AuthenticatedActionsValve(AdapterConfig config, Valve next, Container container, ObjectName controller) {
|
||||
this.config = config;
|
||||
public AuthenticatedActionsValve(KeycloakDeployment deployment, Valve next, Container container, ObjectName controller) {
|
||||
this.deployment = deployment;
|
||||
if (next == null) throw new RuntimeException("WTF is next null?!");
|
||||
setNext(next);
|
||||
setContainer(container);
|
||||
|
@ -81,11 +82,11 @@ public class AuthenticatedActionsValve extends ValveBase {
|
|||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return true;
|
||||
}
|
||||
if (!config.isExposeToken()) {
|
||||
if (!deployment.isExposeToken()) {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
return true;
|
||||
}
|
||||
if (!config.isCors() && request.getHeader("Origin") != null) {
|
||||
if (!deployment.isCors() && request.getHeader("Origin") != null) {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
return true;
|
||||
}
|
||||
|
@ -93,7 +94,7 @@ public class AuthenticatedActionsValve extends ValveBase {
|
|||
}
|
||||
|
||||
protected boolean corsRequest(Request request, Response response, KeycloakSecurityContext session) throws IOException {
|
||||
if (!config.isCors()) return false;
|
||||
if (!deployment.isCors()) return false;
|
||||
log.debugv("CORS enabled + request.getRequestURI()");
|
||||
String origin = request.getHeader("Origin");
|
||||
log.debugv("Origin: {0} uri: {1}", origin, request.getRequestURI());
|
||||
|
|
|
@ -5,7 +5,7 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
||||
|
@ -22,24 +22,16 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CatalinaBearerTokenAuthenticator {
|
||||
protected ResourceMetadata resourceMetadata;
|
||||
protected boolean challenge;
|
||||
protected Logger log = Logger.getLogger(CatalinaBearerTokenAuthenticator.class);
|
||||
protected String tokenString;
|
||||
protected AccessToken token;
|
||||
private Principal principal;
|
||||
protected boolean useResourceRoleMappings;
|
||||
protected int notBefore;
|
||||
protected Principal principal;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
public CatalinaBearerTokenAuthenticator(ResourceMetadata resourceMetadata, int notBefore, boolean challenge, boolean useResourceRoleMappings) {
|
||||
this.resourceMetadata = resourceMetadata;
|
||||
public CatalinaBearerTokenAuthenticator(KeycloakDeployment deployment, boolean challenge) {
|
||||
this.deployment = deployment;
|
||||
this.challenge = challenge;
|
||||
this.useResourceRoleMappings = useResourceRoleMappings;
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public ResourceMetadata getResourceMetadata() {
|
||||
return resourceMetadata;
|
||||
}
|
||||
|
||||
public String getTokenString() {
|
||||
|
@ -73,23 +65,23 @@ public class CatalinaBearerTokenAuthenticator {
|
|||
tokenString = split[1];
|
||||
|
||||
try {
|
||||
token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata.getRealmKey(), resourceMetadata.getRealm());
|
||||
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm());
|
||||
} catch (VerificationException e) {
|
||||
log.error("Failed to verify token", e);
|
||||
challengeResponse(response, "invalid_token", e.getMessage());
|
||||
}
|
||||
|
||||
if (token.getIssuedAt() < notBefore) {
|
||||
if (token.getIssuedAt() < deployment.getNotBefore()) {
|
||||
log.error("Stale token");
|
||||
challengeResponse(response, "invalid_token", "Stale token");
|
||||
}
|
||||
|
||||
boolean verifyCaller = false;
|
||||
Set<String> roles = new HashSet<String>();
|
||||
if (useResourceRoleMappings) {
|
||||
AccessToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
|
||||
if (deployment.isUseResourceRoleMappings()) {
|
||||
AccessToken.Access access = token.getResourceAccess(deployment.getResourceName());
|
||||
if (access != null) roles = access.getRoles();
|
||||
verifyCaller = token.isVerifyCaller(resourceMetadata.getResourceName());
|
||||
verifyCaller = token.isVerifyCaller(deployment.getResourceName());
|
||||
} else {
|
||||
verifyCaller = token.isVerifyCaller();
|
||||
AccessToken.Access access = token.getRealmAccess();
|
||||
|
@ -114,7 +106,7 @@ public class CatalinaBearerTokenAuthenticator {
|
|||
principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skeletonKeyPrincipal, roles);
|
||||
request.setUserPrincipal(principal);
|
||||
request.setAuthType("KEYCLOAK");
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null, resourceMetadata);
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null);
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), skSession);
|
||||
|
||||
return true;
|
||||
|
@ -123,7 +115,7 @@ public class CatalinaBearerTokenAuthenticator {
|
|||
|
||||
protected void challengeResponse(HttpServletResponse response, String error, String description) throws LoginException {
|
||||
StringBuilder header = new StringBuilder("Bearer realm=\"");
|
||||
header.append(resourceMetadata.getRealm()).append("\"");
|
||||
header.append(deployment.getRealm()).append("\"");
|
||||
if (error != null) {
|
||||
header.append(", error=\"").append(error).append("\"");
|
||||
}
|
||||
|
|
|
@ -3,6 +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.KeycloakDeployment;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -13,10 +14,10 @@ import javax.servlet.http.HttpServletResponse;
|
|||
*/
|
||||
public class CorsPreflightChecker {
|
||||
private static final Logger log = Logger.getLogger(CorsPreflightChecker.class);
|
||||
protected AdapterConfig config;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
public CorsPreflightChecker(AdapterConfig config) {
|
||||
this.config = config;
|
||||
public CorsPreflightChecker(KeycloakDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
public boolean checkCorsPreflight(Request request, Response response) {
|
||||
|
@ -37,20 +38,20 @@ public class CorsPreflightChecker {
|
|||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
String requestMethods = request.getHeader("Access-Control-Request-Method");
|
||||
if (requestMethods != null) {
|
||||
if (config.getCorsAllowedMethods() != null) {
|
||||
requestMethods = config.getCorsAllowedMethods();
|
||||
if (deployment.getCorsAllowedMethods() != null) {
|
||||
requestMethods = deployment.getCorsAllowedMethods();
|
||||
}
|
||||
response.setHeader("Access-Control-Allow-Methods", requestMethods);
|
||||
}
|
||||
String allowHeaders = request.getHeader("Access-Control-Request-Headers");
|
||||
if (allowHeaders != null) {
|
||||
if (config.getCorsAllowedHeaders() != null) {
|
||||
allowHeaders = config.getCorsAllowedHeaders();
|
||||
if (deployment.getCorsAllowedHeaders() != null) {
|
||||
allowHeaders = deployment.getCorsAllowedHeaders();
|
||||
}
|
||||
response.setHeader("Access-Control-Allow-Headers", allowHeaders);
|
||||
}
|
||||
if (config.getCorsMaxAge() > -1) {
|
||||
response.setHeader("Access-Control-Max-Age", Integer.toString(config.getCorsMaxAge()));
|
||||
if (deployment.getCorsMaxAge() > -1) {
|
||||
response.setHeader("Access-Control-Max-Age", Integer.toString(deployment.getCorsMaxAge()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.adapters.as7;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.LifecycleEvent;
|
||||
import org.apache.catalina.LifecycleException;
|
||||
|
@ -16,9 +17,9 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.AdapterConstants;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSession;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.adapters.as7.config.CatalinaAdapterConfigLoader;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.adapters.action.AdminAction;
|
||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||
|
@ -26,9 +27,6 @@ import org.keycloak.representations.adapters.action.SessionStats;
|
|||
import org.keycloak.representations.adapters.action.SessionStatsAction;
|
||||
import org.keycloak.representations.adapters.action.UserStats;
|
||||
import org.keycloak.representations.adapters.action.UserStatsAction;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.adapters.config.RealmConfigurationLoader;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.representations.adapters.action.LogoutAction;
|
||||
|
@ -36,10 +34,15 @@ import org.keycloak.util.JsonSerialization;
|
|||
import org.keycloak.util.StreamUtil;
|
||||
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -55,11 +58,9 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakAuthenticatorValve extends FormAuthenticator implements LifecycleListener {
|
||||
protected RealmConfiguration realmConfiguration;
|
||||
private static final Logger log = Logger.getLogger(KeycloakAuthenticatorValve.class);
|
||||
protected UserSessionManagement userSessionManagement = new UserSessionManagement();
|
||||
protected AdapterConfig adapterConfig;
|
||||
protected ResourceMetadata resourceMetadata;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -75,21 +76,44 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
if (event.getType() == Lifecycle.AFTER_START_EVENT) init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
RealmConfigurationLoader configLoader = new CatalinaAdapterConfigLoader(context);
|
||||
configLoader.init(true);
|
||||
resourceMetadata = configLoader.getResourceMetadata();
|
||||
adapterConfig = configLoader.getAdapterConfig();
|
||||
private static InputStream getJSONFromServletContext(ServletContext servletContext) {
|
||||
String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
log.info("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
|
||||
log.info(json);
|
||||
return new ByteArrayInputStream(json.getBytes());
|
||||
}
|
||||
private static InputStream getConfigInputStream(Context context) {
|
||||
InputStream is = getJSONFromServletContext(context.getServletContext());
|
||||
if (is == null) {
|
||||
String path = context.getServletContext().getInitParameter("keycloak.config.file");
|
||||
if (path == null) {
|
||||
log.info("**** using /WEB-INF/keycloak.json");
|
||||
is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json");
|
||||
} else {
|
||||
try {
|
||||
is = new FileInputStream(path);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
realmConfiguration = configLoader.getRealmConfiguration();
|
||||
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(adapterConfig, getNext(), getContainer(), getController());
|
||||
|
||||
protected void init() {
|
||||
this.deployment = KeycloakDeploymentBuilder.build(getConfigInputStream(context));
|
||||
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deployment, getNext(), getContainer(), getController());
|
||||
setNext(actions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(Request request, Response response) throws IOException, ServletException {
|
||||
try {
|
||||
if (adapterConfig.isCors() && new CorsPreflightChecker(adapterConfig).checkCorsPreflight(request, response)) {
|
||||
if (deployment.isCors() && new CorsPreflightChecker(deployment).checkCorsPreflight(request, response)) {
|
||||
return;
|
||||
}
|
||||
String requestURI = request.getDecodedRequestURI();
|
||||
|
@ -148,7 +172,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
}
|
||||
|
||||
// initiate or continue oauth2 protocol
|
||||
if (!adapterConfig.isBearerOnly()) oauth(request, response);
|
||||
if (!deployment.isBearerOnly()) oauth(request, response);
|
||||
} catch (LoginException e) {
|
||||
}
|
||||
return false;
|
||||
|
@ -165,7 +189,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
JWSInput input = new JWSInput(token);
|
||||
boolean verified = false;
|
||||
try {
|
||||
verified = RSAProvider.verify(input, realmConfiguration.getMetadata().getRealmKey());
|
||||
verified = RSAProvider.verify(input, deployment.getRealmKey());
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
if (!verified) {
|
||||
|
@ -188,7 +212,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expired token");
|
||||
return false;
|
||||
}
|
||||
if (!resourceMetadata.getResourceName().equals(action.getResource())) {
|
||||
if (!deployment.getResourceName().equals(action.getResource())) {
|
||||
log.warn("Resource name does not match");
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Resource name does not match");
|
||||
return false;
|
||||
|
@ -203,7 +227,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
if (!validateAction(response, action)) {
|
||||
return;
|
||||
}
|
||||
realmConfiguration.setNotBefore(action.getNotBefore());
|
||||
deployment.setNotBefore(action.getNotBefore());
|
||||
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
|
||||
}
|
||||
|
@ -280,8 +304,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
}
|
||||
|
||||
protected boolean bearer(boolean challenge, Request request, HttpServletResponse response) throws LoginException, IOException {
|
||||
boolean useResourceRoleMappings = adapterConfig.isUseResourceRoleMappings();
|
||||
CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(resourceMetadata, realmConfiguration.getNotBefore(), challenge, useResourceRoleMappings);
|
||||
boolean useResourceRoleMappings = deployment.isUseResourceRoleMappings();
|
||||
CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(deployment, challenge);
|
||||
if (bearer.login(request, response)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -298,8 +322,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
RefreshableKeycloakSession session = (RefreshableKeycloakSession)request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
|
||||
if (session == null) return;
|
||||
// just in case session got serialized
|
||||
session.setRealmConfiguration(realmConfiguration);
|
||||
session.setMetadata(resourceMetadata);
|
||||
session.setDeployment(deployment);
|
||||
if (session.isActive()) return;
|
||||
|
||||
// FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
|
||||
|
@ -335,7 +358,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
* This method always set the HTTP response, so do not continue after invoking
|
||||
*/
|
||||
protected void oauth(Request request, HttpServletResponse response) throws IOException {
|
||||
ServletOAuthLogin oauth = new ServletOAuthLogin(realmConfiguration, request, response, request.getConnector().getRedirectPort());
|
||||
ServletOAuthLogin oauth = new ServletOAuthLogin(deployment, request, response, request.getConnector().getRedirectPort());
|
||||
String code = oauth.getCode();
|
||||
if (code == null) {
|
||||
String error = oauth.getError();
|
||||
|
@ -352,8 +375,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
|
||||
AccessToken token = oauth.getToken();
|
||||
Set<String> roles = new HashSet<String>();
|
||||
if (adapterConfig.isUseResourceRoleMappings()) {
|
||||
AccessToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
|
||||
if (deployment.isUseResourceRoleMappings()) {
|
||||
AccessToken.Access access = token.getResourceAccess(deployment.getResourceName());
|
||||
if (access != null) roles.addAll(access.getRoles());
|
||||
} else {
|
||||
AccessToken.Access access = token.getRealmAccess();
|
||||
|
@ -364,7 +387,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
Session session = request.getSessionInternal(true);
|
||||
session.setPrincipal(principal);
|
||||
session.setAuthType("OAUTH");
|
||||
KeycloakSecurityContext skSession = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), resourceMetadata, realmConfiguration, oauth.getRefreshToken());
|
||||
KeycloakSecurityContext skSession = new RefreshableKeycloakSession(deployment, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
|
||||
session.setNote(KeycloakSecurityContext.class.getName(), skSession);
|
||||
|
||||
String username = token.getSubject();
|
||||
|
|
|
@ -3,8 +3,8 @@ package org.keycloak.adapters.as7;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
|
@ -27,7 +27,7 @@ public class ServletOAuthLogin {
|
|||
protected HttpServletRequest request;
|
||||
protected HttpServletResponse response;
|
||||
protected boolean codePresent;
|
||||
protected RealmConfiguration realmInfo;
|
||||
protected KeycloakDeployment deployment;
|
||||
protected int redirectPort;
|
||||
protected String tokenString;
|
||||
protected String idTokenString;
|
||||
|
@ -35,10 +35,10 @@ public class ServletOAuthLogin {
|
|||
protected AccessToken token;
|
||||
protected String refreshToken;
|
||||
|
||||
public ServletOAuthLogin(RealmConfiguration realmInfo, HttpServletRequest request, HttpServletResponse response, int redirectPort) {
|
||||
public ServletOAuthLogin(KeycloakDeployment deployment, HttpServletRequest request, HttpServletResponse response, int redirectPort) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.realmInfo = realmInfo;
|
||||
this.deployment = deployment;
|
||||
this.redirectPort = redirectPort;
|
||||
}
|
||||
|
||||
|
@ -62,10 +62,6 @@ public class ServletOAuthLogin {
|
|||
return idToken;
|
||||
}
|
||||
|
||||
public RealmConfiguration getRealmInfo() {
|
||||
return realmInfo;
|
||||
}
|
||||
|
||||
protected String getDefaultCookiePath() {
|
||||
String path = request.getContextPath();
|
||||
if ("".equals(path) || path == null) path = "/";
|
||||
|
@ -145,7 +141,7 @@ public class ServletOAuthLogin {
|
|||
|
||||
protected String getRedirectUri(String state) {
|
||||
String url = getRequestUrl();
|
||||
if (!isRequestSecure() && realmInfo.isSslRequired()) {
|
||||
if (!isRequestSecure() && deployment.isSslRequired()) {
|
||||
int port = redirectPort;
|
||||
if (port < 0) {
|
||||
// disabled?
|
||||
|
@ -155,13 +151,13 @@ public class ServletOAuthLogin {
|
|||
if (port != 443) secureUrl.port(port);
|
||||
url = secureUrl.build().toString();
|
||||
}
|
||||
KeycloakUriBuilder uriBuilder = realmInfo.getAuthUrl().clone()
|
||||
.queryParam("client_id", realmInfo.getMetadata().getResourceName())
|
||||
KeycloakUriBuilder uriBuilder = deployment.getAuthUrl().clone()
|
||||
.queryParam("client_id", deployment.getResourceName())
|
||||
.queryParam("redirect_uri", url)
|
||||
.queryParam("state", state)
|
||||
.queryParam("login", "true");
|
||||
if (realmInfo.getMetadata().getScope() != null) {
|
||||
uriBuilder.queryParam("scope", realmInfo.getMetadata().getScope());
|
||||
if (deployment.getScope() != null) {
|
||||
uriBuilder.queryParam("scope", deployment.getScope());
|
||||
}
|
||||
return uriBuilder.build().toString();
|
||||
}
|
||||
|
@ -179,12 +175,12 @@ public class ServletOAuthLogin {
|
|||
sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
setCookie(realmInfo.getStateCookieName(), state, null, getDefaultCookiePath(), realmInfo.isSslRequired());
|
||||
setCookie(deployment.getStateCookieName(), state, null, getDefaultCookiePath(), deployment.isSslRequired());
|
||||
sendRedirect(redirect);
|
||||
}
|
||||
|
||||
public boolean checkStateCookie() {
|
||||
Cookie stateCookie = getCookie(realmInfo.getStateCookieName());
|
||||
Cookie stateCookie = getCookie(deployment.getStateCookieName());
|
||||
|
||||
if (stateCookie == null) {
|
||||
sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||
|
@ -193,12 +189,12 @@ public class ServletOAuthLogin {
|
|||
}
|
||||
// reset the cookie
|
||||
log.debug("** reseting application state cookie");
|
||||
Cookie reset = new Cookie(realmInfo.getStateCookieName(), "");
|
||||
Cookie reset = new Cookie(deployment.getStateCookieName(), "");
|
||||
reset.setPath(getDefaultCookiePath());
|
||||
reset.setMaxAge(0);
|
||||
response.addCookie(reset);
|
||||
|
||||
String stateCookieValue = getCookieValue(realmInfo.getStateCookieName());
|
||||
String stateCookieValue = getCookieValue(deployment.getStateCookieName());
|
||||
// its ok to call request.getParameter() because this should be a redirect
|
||||
String state = request.getParameter("state");
|
||||
if (state == null) {
|
||||
|
@ -231,7 +227,7 @@ public class ServletOAuthLogin {
|
|||
*/
|
||||
public boolean resolveCode(String code) {
|
||||
// abort if not HTTPS
|
||||
if (realmInfo.isSslRequired() && !isRequestSecure()) {
|
||||
if (deployment.isSslRequired() && !isRequestSecure()) {
|
||||
log.error("SSL is required");
|
||||
sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return false;
|
||||
|
@ -242,7 +238,7 @@ public class ServletOAuthLogin {
|
|||
String redirectUri = stripOauthParametersFromRedirect();
|
||||
AccessTokenResponse tokenResponse = null;
|
||||
try {
|
||||
tokenResponse = ServerRequest.invokeAccessCodeToToken(realmInfo, code, redirectUri);
|
||||
tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri);
|
||||
} catch (ServerRequest.HttpFailure failure) {
|
||||
log.error("failed to turn code into token");
|
||||
log.error("status from server: " + failure.getStatus());
|
||||
|
@ -260,7 +256,7 @@ public class ServletOAuthLogin {
|
|||
tokenString = tokenResponse.getToken();
|
||||
idTokenString = tokenResponse.getIdToken();
|
||||
try {
|
||||
token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata().getRealmKey(), realmInfo.getMetadata().getRealm());
|
||||
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm());
|
||||
if (idTokenString != null) {
|
||||
JWSInput input = new JWSInput(idTokenString);
|
||||
try {
|
||||
|
@ -275,10 +271,10 @@ public class ServletOAuthLogin {
|
|||
sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
if (tokenResponse.getNotBeforePolicy() > realmInfo.getNotBefore()) {
|
||||
realmInfo.setNotBefore(tokenResponse.getNotBeforePolicy());
|
||||
if (tokenResponse.getNotBeforePolicy() > deployment.getNotBefore()) {
|
||||
deployment.setNotBefore(tokenResponse.getNotBeforePolicy());
|
||||
}
|
||||
if (token.getIssuedAt() < realmInfo.getNotBefore()) {
|
||||
if (token.getIssuedAt() < deployment.getNotBefore()) {
|
||||
log.error("Stale token");
|
||||
sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return false;
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
package org.keycloak.adapters.as7.config;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.AdapterConstants;
|
||||
import org.keycloak.adapters.config.RealmConfigurationLoader;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class CatalinaAdapterConfigLoader extends RealmConfigurationLoader {
|
||||
private static final Logger log = Logger.getLogger(CatalinaAdapterConfigLoader.class);
|
||||
|
||||
private InputStream getJSONFromServletContext(ServletContext servletContext) {
|
||||
String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
log.info("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
|
||||
log.info(json);
|
||||
return new ByteArrayInputStream(json.getBytes());
|
||||
}
|
||||
|
||||
public CatalinaAdapterConfigLoader(Context context) {
|
||||
log.info("******* Loading adapter config.");
|
||||
InputStream is = getJSONFromServletContext(context.getServletContext());
|
||||
if (is == null) {
|
||||
String path = context.getServletContext().getInitParameter("keycloak.config.file");
|
||||
if (path == null) {
|
||||
log.info("**** using /WEB-INF/keycloak.json");
|
||||
is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json");
|
||||
} else {
|
||||
try {
|
||||
is = new FileInputStream(path);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is == null) throw new RuntimeException("Could not find keycloak config.");
|
||||
loadConfig(is);
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,6 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
|||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
||||
|
@ -19,6 +18,7 @@ import javax.ws.rs.core.Response;
|
|||
import javax.ws.rs.core.SecurityContext;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -26,16 +26,20 @@ import java.security.Principal;
|
|||
*/
|
||||
@Priority(Priorities.AUTHENTICATION)
|
||||
public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
|
||||
protected ResourceMetadata resourceMetadata;
|
||||
private static Logger log = Logger.getLogger(JaxrsBearerTokenFilter.class);
|
||||
protected String realm;
|
||||
protected PublicKey realmPublicKey;
|
||||
protected String resourceName;
|
||||
|
||||
public JaxrsBearerTokenFilter(ResourceMetadata resourceMetadata) {
|
||||
this.resourceMetadata = resourceMetadata;
|
||||
public JaxrsBearerTokenFilter(String realm, PublicKey realmPublicKey, String resourceName) {
|
||||
this.realm = realm;
|
||||
this.realmPublicKey = realmPublicKey;
|
||||
this.resourceName = resourceName;
|
||||
}
|
||||
|
||||
protected void challengeResponse(ContainerRequestContext request, String error, String description) {
|
||||
StringBuilder header = new StringBuilder("Bearer realm=\"");
|
||||
header.append(resourceMetadata.getRealm()).append("\"");
|
||||
header.append(realm).append("\"");
|
||||
if (error != null) {
|
||||
header.append(", error=\"").append(error).append("\"");
|
||||
}
|
||||
|
@ -66,16 +70,16 @@ public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
|
|||
|
||||
|
||||
try {
|
||||
AccessToken token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata.getRealmKey(), resourceMetadata.getRealm());
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null, resourceMetadata);
|
||||
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realmPublicKey, realm);
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null);
|
||||
ResteasyProviderFactory.pushContext(KeycloakSecurityContext.class, skSession);
|
||||
String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;
|
||||
|
||||
final KeycloakPrincipal principal = new KeycloakPrincipal(token.getSubject(), callerPrincipal);
|
||||
final boolean isSecure = securityContext.isSecure();
|
||||
final AccessToken.Access access;
|
||||
if (resourceMetadata.getResourceName() != null) {
|
||||
access = token.getResourceAccess(resourceMetadata.getResourceName());
|
||||
if (resourceName != null) {
|
||||
access = token.getResourceAccess(resourceName);
|
||||
} else {
|
||||
access = token.getRealmAccess();
|
||||
}
|
||||
|
|
|
@ -33,22 +33,10 @@ public class JaxrsOAuthClient extends AbstractOAuthClient {
|
|||
protected Client client;
|
||||
|
||||
/**
|
||||
* Creates a Client for obtaining access token from code
|
||||
*/
|
||||
public void start() {
|
||||
if (client == null) {
|
||||
client = new ResteasyClientBuilder().trustStore(truststore)
|
||||
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY)
|
||||
.connectionPoolSize(10)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* closes cllient
|
||||
* closes client
|
||||
*/
|
||||
public void stop() {
|
||||
client.close();
|
||||
if (client != null) client.close();
|
||||
}
|
||||
public Client getClient() {
|
||||
return client;
|
||||
|
|
|
@ -36,14 +36,6 @@
|
|||
<version>${keycloak.apache.httpcomponents.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>net.iharder</groupId>
|
||||
<artifactId>base64</artifactId>
|
||||
|
|
|
@ -22,20 +22,11 @@ import java.net.URI;
|
|||
public class ServletOAuthClient extends AbstractOAuthClient {
|
||||
protected HttpClient client;
|
||||
|
||||
/**
|
||||
* Creates a Client for obtaining access token from code
|
||||
*/
|
||||
public void start() {
|
||||
if (client == null) {
|
||||
client = new HttpClientBuilder().trustStore(truststore)
|
||||
.hostnameVerification(HttpClientBuilder.HostnameVerificationPolicy.ANY)
|
||||
.connectionPoolSize(10)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* closes cllient
|
||||
* closes client
|
||||
*/
|
||||
public void stop() {
|
||||
client.getConnectionManager().shutdown();
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package org.keycloak.servlet;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.ServiceUrlConstants;
|
||||
import org.keycloak.adapters.HttpClientBuilder;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ServletOAuthClientBuilder {
|
||||
|
||||
public static ServletOAuthClient build(InputStream is) {
|
||||
AdapterConfig adapterConfig = getAdapterConfig(is);
|
||||
return build(adapterConfig);
|
||||
}
|
||||
|
||||
private static AdapterConfig getAdapterConfig(InputStream is) {
|
||||
try {
|
||||
return JsonSerialization.readValue(is, AdapterConfig.class);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ServletOAuthClient build(AdapterConfig adapterConfig) {
|
||||
ServletOAuthClient oauthClient = new ServletOAuthClient();
|
||||
build(adapterConfig, oauthClient);
|
||||
return oauthClient;
|
||||
}
|
||||
|
||||
public static void build(InputStream is, ServletOAuthClient oauthClient) {
|
||||
build(getAdapterConfig(is), oauthClient);
|
||||
}
|
||||
|
||||
|
||||
public static void build(AdapterConfig adapterConfig, ServletOAuthClient oauthClient) {
|
||||
HttpClient client = new HttpClientBuilder().build(adapterConfig);
|
||||
oauthClient.setClient(client);
|
||||
oauthClient.setClientId(adapterConfig.getResource());
|
||||
oauthClient.setPublicClient(adapterConfig.isPublicClient());
|
||||
oauthClient.setCredentials(adapterConfig.getCredentials());
|
||||
if (adapterConfig.getAuthServerUrl() == null) {
|
||||
throw new RuntimeException("You must specify auth-url");
|
||||
}
|
||||
KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(adapterConfig.getAuthServerUrl());
|
||||
String authUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_LOGIN_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String tokenUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_ACCESS_CODE_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String refreshUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_REFRESH_PATH).build(adapterConfig.getRealm()).toString();
|
||||
oauthClient.setAuthUrl(authUrl);
|
||||
oauthClient.setCodeUrl(tokenUrl);
|
||||
oauthClient.setRefreshUrl(refreshUrl);
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package org.keycloak.servlet;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.keycloak.adapters.config.OAuthClientConfigLoader;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ServletOAuthClientConfigLoader extends OAuthClientConfigLoader {
|
||||
|
||||
public ServletOAuthClientConfigLoader() {
|
||||
}
|
||||
|
||||
public ServletOAuthClientConfigLoader(InputStream is) {
|
||||
super(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, configure just things supported by ServletOAuthClient
|
||||
* @param setupClient
|
||||
*/
|
||||
public void initOAuthClientConfiguration(boolean setupClient) {
|
||||
initOAuthClientConfiguration();
|
||||
if (setupClient) {
|
||||
initClient();
|
||||
}
|
||||
}
|
||||
|
||||
public void configureServletOAuthClient(ServletOAuthClient oauthClient) {
|
||||
configureOAuthClient(oauthClient);
|
||||
if (client != null) {
|
||||
oauthClient.setClient(client);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import io.undertow.util.Headers;
|
|||
import io.undertow.util.StatusCodes;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.AdapterConstants;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
|
@ -27,25 +28,25 @@ import java.util.Set;
|
|||
*/
|
||||
public class AuthenticatedActionsHandler implements HttpHandler {
|
||||
private static final Logger log = Logger.getLogger(AuthenticatedActionsHandler.class);
|
||||
protected AdapterConfig adapterConfig;
|
||||
protected KeycloakDeployment deployment;
|
||||
protected HttpHandler next;
|
||||
|
||||
public static class Wrapper implements HandlerWrapper {
|
||||
protected AdapterConfig config;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
public Wrapper(AdapterConfig config) {
|
||||
this.config = config;
|
||||
public Wrapper(KeycloakDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHandler wrap(HttpHandler handler) {
|
||||
return new AuthenticatedActionsHandler(config, handler);
|
||||
return new AuthenticatedActionsHandler(deployment, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected AuthenticatedActionsHandler(AdapterConfig config, HttpHandler next) {
|
||||
this.adapterConfig = config;
|
||||
protected AuthenticatedActionsHandler(KeycloakDeployment deployment, HttpHandler next) {
|
||||
this.deployment = deployment;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
|
@ -82,13 +83,13 @@ public class AuthenticatedActionsHandler implements HttpHandler {
|
|||
exchange.endExchange();
|
||||
return true;
|
||||
}
|
||||
if (!adapterConfig.isExposeToken()) {
|
||||
if (!deployment.isExposeToken()) {
|
||||
exchange.setResponseCode(StatusCodes.OK);
|
||||
exchange.endExchange();
|
||||
return true;
|
||||
}
|
||||
// Don't allow a CORS request if we're not validating CORS requests.
|
||||
if (!adapterConfig.isCors() && exchange.getRequestHeaders().getFirst(Headers.ORIGIN) != null) {
|
||||
if (!deployment.isCors() && exchange.getRequestHeaders().getFirst(Headers.ORIGIN) != null) {
|
||||
exchange.setResponseCode(StatusCodes.OK);
|
||||
exchange.endExchange();
|
||||
return true;
|
||||
|
@ -97,7 +98,7 @@ public class AuthenticatedActionsHandler implements HttpHandler {
|
|||
}
|
||||
|
||||
protected boolean corsRequest(HttpServerExchange exchange, KeycloakUndertowAccount account) throws IOException {
|
||||
if (!adapterConfig.isCors()) return false;
|
||||
if (!deployment.isCors()) return false;
|
||||
log.debugv("CORS enabled + request.getRequestURI()");
|
||||
String origin = exchange.getRequestHeaders().getFirst("Origin");
|
||||
log.debugv("Origin: {0} uri: {1}", origin, exchange.getRequestURI());
|
||||
|
|
|
@ -5,14 +5,12 @@ import io.undertow.security.api.SecurityContext;
|
|||
import io.undertow.server.HttpServerExchange;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
||||
import javax.security.cert.X509Certificate;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.undertow.util.Headers.AUTHORIZATION;
|
||||
import static io.undertow.util.Headers.WWW_AUTHENTICATE;
|
||||
|
@ -23,29 +21,21 @@ import static io.undertow.util.StatusCodes.UNAUTHORIZED;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class BearerTokenAuthenticator {
|
||||
protected ResourceMetadata resourceMetadata;
|
||||
protected Logger log = Logger.getLogger(BearerTokenAuthenticator.class);
|
||||
protected String tokenString;
|
||||
protected AccessToken token;
|
||||
protected boolean useResourceRoleMappings;
|
||||
protected String surrogate;
|
||||
protected KeycloakChallenge challenge;
|
||||
protected int notBefore;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
public BearerTokenAuthenticator(ResourceMetadata resourceMetadata, int notBefore, boolean useResourceRoleMappings) {
|
||||
this.resourceMetadata = resourceMetadata;
|
||||
this.useResourceRoleMappings = useResourceRoleMappings;
|
||||
this.notBefore = notBefore;
|
||||
public BearerTokenAuthenticator(KeycloakDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
public KeycloakChallenge getChallenge() {
|
||||
return challenge;
|
||||
}
|
||||
|
||||
public ResourceMetadata getResourceMetadata() {
|
||||
return resourceMetadata;
|
||||
}
|
||||
|
||||
public String getTokenString() {
|
||||
return tokenString;
|
||||
}
|
||||
|
@ -79,20 +69,20 @@ public class BearerTokenAuthenticator {
|
|||
}
|
||||
|
||||
try {
|
||||
token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata.getRealmKey(), resourceMetadata.getRealm());
|
||||
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm());
|
||||
} catch (VerificationException e) {
|
||||
log.error("Failed to verify token", e);
|
||||
challenge = challengeResponse(exchange, "invalid_token", e.getMessage());
|
||||
return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
}
|
||||
if (token.getIssuedAt() < notBefore) {
|
||||
if (token.getIssuedAt() < deployment.getNotBefore()) {
|
||||
log.error("Stale token");
|
||||
challenge = challengeResponse(exchange, "invalid_token", "Stale token");
|
||||
return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
}
|
||||
boolean verifyCaller = false;
|
||||
if (useResourceRoleMappings) {
|
||||
verifyCaller = token.isVerifyCaller(resourceMetadata.getResourceName());
|
||||
if (deployment.isUseResourceRoleMappings()) {
|
||||
verifyCaller = token.isVerifyCaller(deployment.getResourceName());
|
||||
} else {
|
||||
verifyCaller = token.isVerifyCaller();
|
||||
}
|
||||
|
@ -135,7 +125,7 @@ public class BearerTokenAuthenticator {
|
|||
|
||||
protected KeycloakChallenge challengeResponse(HttpServerExchange exchange, String error, String description) {
|
||||
StringBuilder header = new StringBuilder("Bearer realm=\"");
|
||||
header.append(resourceMetadata.getRealm()).append("\"");
|
||||
header.append(deployment.getRealm()).append("\"");
|
||||
if (error != null) {
|
||||
header.append(", error=\"").append(error).append("\"");
|
||||
}
|
||||
|
|
|
@ -7,10 +7,8 @@ import io.undertow.util.AttachmentKey;
|
|||
import io.undertow.util.Headers;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSession;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -21,23 +19,17 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
|
||||
public static final AttachmentKey<KeycloakChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(KeycloakChallenge.class);
|
||||
|
||||
protected ResourceMetadata resourceMetadata;
|
||||
protected AdapterConfig adapterConfig;
|
||||
protected RealmConfiguration realmConfig;
|
||||
protected KeycloakDeployment deployment;
|
||||
protected int sslRedirectPort;
|
||||
|
||||
public KeycloakAuthenticationMechanism(AdapterConfig config, RealmConfiguration realmConfig, int sslRedirectPort) {
|
||||
this.resourceMetadata = realmConfig.getMetadata();
|
||||
this.adapterConfig = config;
|
||||
this.realmConfig = realmConfig;
|
||||
public KeycloakAuthenticationMechanism(KeycloakDeployment deployment, int sslRedirectPort) {
|
||||
this.deployment = deployment;
|
||||
this.sslRedirectPort = sslRedirectPort;
|
||||
}
|
||||
|
||||
public KeycloakAuthenticationMechanism(AdapterConfig adapterConfig, RealmConfiguration realmConfig) {
|
||||
this.resourceMetadata = realmConfig.getMetadata();
|
||||
this.adapterConfig = adapterConfig;
|
||||
this.realmConfig = realmConfig;
|
||||
}
|
||||
public KeycloakAuthenticationMechanism(KeycloakDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
||||
|
@ -50,7 +42,7 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
} else if (outcome == AuthenticationMechanismOutcome.AUTHENTICATED) {
|
||||
completeAuthentication(exchange, securityContext, bearer);
|
||||
return AuthenticationMechanismOutcome.AUTHENTICATED;
|
||||
} else if (adapterConfig.isBearerOnly()) {
|
||||
} else if (deployment.isBearerOnly()) {
|
||||
exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, bearer.getChallenge());
|
||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||
}
|
||||
|
@ -88,17 +80,17 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
}
|
||||
|
||||
protected OAuthAuthenticator createOAuthAuthenticator(HttpServerExchange exchange) {
|
||||
return new OAuthAuthenticator(exchange, realmConfig, sslRedirectPort);
|
||||
return new OAuthAuthenticator(exchange, deployment, sslRedirectPort);
|
||||
}
|
||||
|
||||
protected BearerTokenAuthenticator createBearerTokenAuthenticator() {
|
||||
return new BearerTokenAuthenticator(resourceMetadata, realmConfig.getNotBefore(), adapterConfig.isUseResourceRoleMappings());
|
||||
return new BearerTokenAuthenticator(deployment);
|
||||
}
|
||||
|
||||
protected void completeAuthentication(HttpServerExchange exchange, SecurityContext securityContext, OAuthAuthenticator oauth) {
|
||||
final KeycloakPrincipal principal = new KeycloakPrincipal(oauth.getToken().getSubject(), null);
|
||||
RefreshableKeycloakSession session = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), resourceMetadata, realmConfig, oauth.getRefreshToken());
|
||||
KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, adapterConfig, resourceMetadata);
|
||||
RefreshableKeycloakSession session = new RefreshableKeycloakSession(deployment, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
|
||||
KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, deployment);
|
||||
|
||||
// We cache account ourselves instead of using the Cache session handler of Undertow because
|
||||
// Undertow will return a 403 from an invalid account when calling IdentityManager.verify(Account) and
|
||||
|
@ -118,8 +110,8 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
|
||||
protected void completeAuthentication(HttpServerExchange exchange, SecurityContext securityContext, BearerTokenAuthenticator bearer) {
|
||||
final KeycloakPrincipal principal = new KeycloakPrincipal(bearer.getToken().getSubject(), bearer.getSurrogate());
|
||||
RefreshableKeycloakSession session = new RefreshableKeycloakSession(bearer.getTokenString(), bearer.getToken(), null, null, resourceMetadata, realmConfig, null);
|
||||
KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, adapterConfig, resourceMetadata);
|
||||
RefreshableKeycloakSession session = new RefreshableKeycloakSession(deployment, bearer.getTokenString(), bearer.getToken(), null, null, null);
|
||||
KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, deployment);
|
||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
||||
propagateKeycloakContext(exchange, account);
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package org.keycloak.adapters.undertow;
|
||||
|
||||
import io.undertow.security.idm.Account;
|
||||
import io.undertow.security.idm.Credential;
|
||||
import io.undertow.security.idm.IdentityManager;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
class KeycloakIdentityManager implements IdentityManager {
|
||||
protected static Logger log = Logger.getLogger(KeycloakIdentityManager.class);
|
||||
protected AdapterConfig adapterConfig;
|
||||
protected RealmConfiguration realmConfiguration;
|
||||
|
||||
KeycloakIdentityManager(AdapterConfig adapterConfig, RealmConfiguration realmConfiguration) {
|
||||
this.adapterConfig = adapterConfig;
|
||||
this.realmConfiguration = realmConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(Account account) {
|
||||
log.info("Verifying account in IdentityManager");
|
||||
KeycloakUndertowAccount keycloakAccount = (KeycloakUndertowAccount)account;
|
||||
if (!keycloakAccount.isActive(realmConfiguration, adapterConfig)) {
|
||||
log.info("account.isActive() returned false, returning null");
|
||||
return null;
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(String id, Credential credential) {
|
||||
throw new IllegalStateException("Unsupported verify method");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(Credential credential) {
|
||||
throw new IllegalStateException("Unsupported verify method");
|
||||
}
|
||||
}
|
|
@ -14,9 +14,8 @@ import io.undertow.servlet.api.ServletSessionConfig;
|
|||
import java.io.ByteArrayInputStream;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.AdapterConstants;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.adapters.config.RealmConfigurationLoader;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.io.InputStream;
|
||||
|
@ -63,24 +62,20 @@ public class KeycloakServletExtension implements ServletExtension {
|
|||
is = servletContext.getResourceAsStream("/WEB-INF/keycloak.json");
|
||||
}
|
||||
if (is == null) throw new RuntimeException("Unable to find realm config in /WEB-INF/keycloak.json or in keycloak subsystem.");
|
||||
RealmConfigurationLoader loader = new RealmConfigurationLoader(is);
|
||||
loader.init(true);
|
||||
AdapterConfig keycloakConfig = loader.getAdapterConfig();
|
||||
RealmConfiguration realmConfiguration = loader.getRealmConfiguration();
|
||||
PreflightCorsHandler.Wrapper preflight = new PreflightCorsHandler.Wrapper(keycloakConfig);
|
||||
UserSessionManagement userSessionManagement = new UserSessionManagement(realmConfiguration);
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);
|
||||
PreflightCorsHandler.Wrapper preflight = new PreflightCorsHandler.Wrapper(deployment);
|
||||
UserSessionManagement userSessionManagement = new UserSessionManagement(deployment);
|
||||
ServletKeycloakAuthenticationMechanism auth = null;
|
||||
auth = new ServletKeycloakAuthenticationMechanism(
|
||||
userSessionManagement,
|
||||
keycloakConfig,
|
||||
realmConfiguration,
|
||||
deployment,
|
||||
deploymentInfo.getConfidentialPortManager());
|
||||
AuthenticatedActionsHandler.Wrapper actions = new AuthenticatedActionsHandler.Wrapper(keycloakConfig);
|
||||
AuthenticatedActionsHandler.Wrapper actions = new AuthenticatedActionsHandler.Wrapper(deployment);
|
||||
|
||||
// setup handlers
|
||||
|
||||
deploymentInfo.addInitialHandlerChainWrapper(preflight); // cors preflight
|
||||
deploymentInfo.addOuterHandlerChainWrapper(new ServletAdminActionsHandler.Wrapper(realmConfiguration, loader.getResourceMetadata(), userSessionManagement));
|
||||
deploymentInfo.addOuterHandlerChainWrapper(new ServletAdminActionsHandler.Wrapper(deployment, userSessionManagement));
|
||||
final ServletKeycloakAuthenticationMechanism theAuth = auth;
|
||||
deploymentInfo.addAuthenticationMechanism("KEYCLOAK", new AuthenticationMechanismFactory() {
|
||||
@Override
|
||||
|
|
|
@ -3,11 +3,9 @@ package org.keycloak.adapters.undertow;
|
|||
import io.undertow.security.idm.Account;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSession;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.Principal;
|
||||
|
@ -24,16 +22,16 @@ public class KeycloakUndertowAccount implements Account, Serializable {
|
|||
protected KeycloakPrincipal principal;
|
||||
protected Set<String> accountRoles;
|
||||
|
||||
public KeycloakUndertowAccount(KeycloakPrincipal principal, RefreshableKeycloakSession session, AdapterConfig config, ResourceMetadata metadata) {
|
||||
public KeycloakUndertowAccount(KeycloakPrincipal principal, RefreshableKeycloakSession session, KeycloakDeployment deployment) {
|
||||
this.principal = principal;
|
||||
this.session = session;
|
||||
setRoles(session.getToken(), config, metadata);
|
||||
setRoles(session.getToken(), deployment);
|
||||
}
|
||||
|
||||
protected void setRoles(AccessToken accessToken, AdapterConfig adapterConfig, ResourceMetadata resourceMetadata) {
|
||||
protected void setRoles(AccessToken accessToken, KeycloakDeployment deployment) {
|
||||
Set<String> roles = null;
|
||||
if (adapterConfig.isUseResourceRoleMappings()) {
|
||||
AccessToken.Access access = accessToken.getResourceAccess(resourceMetadata.getResourceName());
|
||||
if (deployment.isUseResourceRoleMappings()) {
|
||||
AccessToken.Access access = accessToken.getResourceAccess(deployment.getResourceName());
|
||||
if (access != null) roles = access.getRoles();
|
||||
} else {
|
||||
AccessToken.Access access = accessToken.getRealmAccess();
|
||||
|
@ -65,11 +63,10 @@ public class KeycloakUndertowAccount implements Account, Serializable {
|
|||
return session;
|
||||
}
|
||||
|
||||
public boolean isActive(RealmConfiguration realmConfiguration, AdapterConfig config) {
|
||||
public boolean isActive(KeycloakDeployment deployment) {
|
||||
// this object may have been serialized, so we need to reset realm config/metadata
|
||||
session.setRealmConfiguration(realmConfiguration);
|
||||
session.setMetadata(realmConfiguration.getMetadata());
|
||||
log.info("realmConfig notBefore: " + realmConfiguration.getNotBefore());
|
||||
session.setDeployment(deployment);
|
||||
log.info("realmConfig notBefore: " + deployment.getNotBefore());
|
||||
if (session.isActive()) {
|
||||
log.info("session is active");
|
||||
return true;
|
||||
|
@ -84,7 +81,7 @@ public class KeycloakUndertowAccount implements Account, Serializable {
|
|||
}
|
||||
log.info("refresh succeeded");
|
||||
|
||||
setRoles(session.getToken(), config, realmConfiguration.getMetadata());
|
||||
setRoles(session.getToken(), deployment);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ import io.undertow.util.Headers;
|
|||
import io.undertow.util.StatusCodes;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
*/
|
||||
public class OAuthAuthenticator {
|
||||
private static final Logger log = Logger.getLogger(OAuthAuthenticator.class);
|
||||
protected RealmConfiguration realmInfo;
|
||||
protected KeycloakDeployment deployment;
|
||||
protected int sslRedirectPort;
|
||||
protected String tokenString;
|
||||
protected String idTokenString;
|
||||
|
@ -41,9 +41,9 @@ public class OAuthAuthenticator {
|
|||
protected String refreshToken;
|
||||
protected String strippedOauthParametersRequestUri;
|
||||
|
||||
public OAuthAuthenticator(HttpServerExchange exchange, RealmConfiguration realmInfo, int sslRedirectPort) {
|
||||
public OAuthAuthenticator(HttpServerExchange exchange, KeycloakDeployment deployment, int sslRedirectPort) {
|
||||
this.exchange = exchange;
|
||||
this.realmInfo = realmInfo;
|
||||
this.deployment = deployment;
|
||||
this.sslRedirectPort = sslRedirectPort;
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ public class OAuthAuthenticator {
|
|||
protected String getRedirectUri(String state) {
|
||||
String url = getRequestUrl();
|
||||
log.infof("sending redirect uri: %s", url);
|
||||
if (!isRequestSecure() && realmInfo.isSslRequired()) {
|
||||
if (!isRequestSecure() && deployment.isSslRequired()) {
|
||||
int port = sslRedirectPort();
|
||||
if (port < 0) {
|
||||
// disabled?
|
||||
|
@ -139,8 +139,8 @@ public class OAuthAuthenticator {
|
|||
if (port != 443) secureUrl.port(port);
|
||||
url = secureUrl.build().toString();
|
||||
}
|
||||
return realmInfo.getAuthUrl().clone()
|
||||
.queryParam("client_id", realmInfo.getMetadata().getResourceName())
|
||||
return deployment.getAuthUrl().clone()
|
||||
.queryParam("client_id", deployment.getResourceName())
|
||||
.queryParam("redirect_uri", url)
|
||||
.queryParam("state", state)
|
||||
.queryParam("login", "true")
|
||||
|
@ -166,9 +166,9 @@ public class OAuthAuthenticator {
|
|||
if (redirect == null) {
|
||||
return new AuthenticationMechanism.ChallengeResult(true, StatusCodes.FORBIDDEN);
|
||||
}
|
||||
CookieImpl cookie = new CookieImpl(realmInfo.getStateCookieName(), state);
|
||||
CookieImpl cookie = new CookieImpl(deployment.getStateCookieName(), state);
|
||||
//cookie.setPath(getDefaultCookiePath()); todo I don't think we need to set state cookie path as it will be the same redirect
|
||||
cookie.setSecure(realmInfo.isSslRequired());
|
||||
cookie.setSecure(deployment.isSslRequired());
|
||||
exchange.setResponseCookie(cookie);
|
||||
exchange.getResponseHeaders().put(Headers.LOCATION, redirect);
|
||||
return new AuthenticationMechanism.ChallengeResult(true, StatusCodes.FOUND);
|
||||
|
@ -177,7 +177,7 @@ public class OAuthAuthenticator {
|
|||
}
|
||||
|
||||
protected KeycloakChallenge checkStateCookie() {
|
||||
Cookie stateCookie = getCookie(realmInfo.getStateCookieName());
|
||||
Cookie stateCookie = getCookie(deployment.getStateCookieName());
|
||||
|
||||
if (stateCookie == null) {
|
||||
log.warn("No state cookie");
|
||||
|
@ -185,12 +185,12 @@ public class OAuthAuthenticator {
|
|||
}
|
||||
// reset the cookie
|
||||
log.info("** reseting application state cookie");
|
||||
Cookie reset = new CookieImpl(realmInfo.getStateCookieName(), "");
|
||||
Cookie reset = new CookieImpl(deployment.getStateCookieName(), "");
|
||||
reset.setPath(stateCookie.getPath());
|
||||
reset.setMaxAge(0);
|
||||
exchange.setResponseCookie(reset);
|
||||
|
||||
String stateCookieValue = getCookieValue(realmInfo.getStateCookieName());
|
||||
String stateCookieValue = getCookieValue(deployment.getStateCookieName());
|
||||
|
||||
String state = getQueryParamValue("state");
|
||||
if (state == null) {
|
||||
|
@ -256,7 +256,7 @@ public class OAuthAuthenticator {
|
|||
*/
|
||||
protected KeycloakChallenge resolveCode(String code) {
|
||||
// abort if not HTTPS
|
||||
if (realmInfo.isSslRequired() && !isRequestSecure()) {
|
||||
if (deployment.isSslRequired() && !isRequestSecure()) {
|
||||
log.error("SSL is required");
|
||||
return challenge(StatusCodes.FORBIDDEN);
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ public class OAuthAuthenticator {
|
|||
AccessTokenResponse tokenResponse = null;
|
||||
strippedOauthParametersRequestUri = stripOauthParametersFromRedirect();
|
||||
try {
|
||||
tokenResponse = ServerRequest.invokeAccessCodeToToken(realmInfo, code, strippedOauthParametersRequestUri);
|
||||
tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, strippedOauthParametersRequestUri);
|
||||
} catch (ServerRequest.HttpFailure failure) {
|
||||
log.error("failed to turn code into token");
|
||||
log.error("status from server: " + failure.getStatus());
|
||||
|
@ -286,7 +286,7 @@ public class OAuthAuthenticator {
|
|||
refreshToken = tokenResponse.getRefreshToken();
|
||||
idTokenString = tokenResponse.getIdToken();
|
||||
try {
|
||||
token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata().getRealmKey(), realmInfo.getMetadata().getRealm());
|
||||
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm());
|
||||
if (idTokenString != null) {
|
||||
JWSInput input = new JWSInput(idTokenString);
|
||||
try {
|
||||
|
@ -300,10 +300,10 @@ public class OAuthAuthenticator {
|
|||
log.error("failed verification of token");
|
||||
return challenge(StatusCodes.FORBIDDEN);
|
||||
}
|
||||
if (tokenResponse.getNotBeforePolicy() > realmInfo.getNotBefore()) {
|
||||
realmInfo.setNotBefore(tokenResponse.getNotBeforePolicy());
|
||||
if (tokenResponse.getNotBeforePolicy() > deployment.getNotBefore()) {
|
||||
deployment.setNotBefore(tokenResponse.getNotBeforePolicy());
|
||||
}
|
||||
if (token.getIssuedAt() < realmInfo.getNotBefore()) {
|
||||
if (token.getIssuedAt() < deployment.getNotBefore()) {
|
||||
log.error("Stale token");
|
||||
return challenge(StatusCodes.FORBIDDEN);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.undertow.server.HttpServerExchange;
|
|||
import io.undertow.util.HttpString;
|
||||
import io.undertow.util.StatusCodes;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
/**
|
||||
|
@ -14,7 +15,7 @@ import org.keycloak.representations.adapters.config.AdapterConfig;
|
|||
*/
|
||||
public class PreflightCorsHandler implements HttpHandler {
|
||||
private static final Logger log = Logger.getLogger(PreflightCorsHandler.class);
|
||||
protected AdapterConfig adapterConfig;
|
||||
protected KeycloakDeployment deployment;
|
||||
protected HttpHandler next;
|
||||
|
||||
public static final HttpString ACCESS_CONTROL_ALLOW_ORIGIN = new HttpString("Access-Control-Allow-Origin");
|
||||
|
@ -24,20 +25,20 @@ 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 AdapterConfig config;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
public Wrapper(AdapterConfig config) {
|
||||
this.config = config;
|
||||
public Wrapper(KeycloakDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHandler wrap(HttpHandler handler) {
|
||||
return new PreflightCorsHandler(config, handler);
|
||||
return new PreflightCorsHandler(deployment, handler);
|
||||
}
|
||||
}
|
||||
|
||||
protected PreflightCorsHandler(AdapterConfig config, HttpHandler next) {
|
||||
this.adapterConfig = config;
|
||||
protected PreflightCorsHandler(KeycloakDeployment deployment, HttpHandler next) {
|
||||
this.deployment = deployment;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
|
@ -61,20 +62,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 (adapterConfig.getCorsAllowedMethods() != null) {
|
||||
requestMethods = adapterConfig.getCorsAllowedMethods();
|
||||
if (deployment.getCorsAllowedMethods() != null) {
|
||||
requestMethods = deployment.getCorsAllowedMethods();
|
||||
}
|
||||
exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_METHODS, requestMethods);
|
||||
}
|
||||
String allowHeaders = exchange.getRequestHeaders().getFirst("Access-Control-Request-Headers");
|
||||
if (allowHeaders != null) {
|
||||
if (adapterConfig.getCorsAllowedHeaders() != null) {
|
||||
allowHeaders = adapterConfig.getCorsAllowedHeaders();
|
||||
if (deployment.getCorsAllowedHeaders() != null) {
|
||||
allowHeaders = deployment.getCorsAllowedHeaders();
|
||||
}
|
||||
exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_HEADERS, allowHeaders);
|
||||
}
|
||||
if (adapterConfig.getCorsMaxAge() > -1) {
|
||||
exchange.getResponseHeaders().put(ACCESS_CONTROL_MAX_AGE, Integer.toString(adapterConfig.getCorsMaxAge()));
|
||||
if (deployment.getCorsMaxAge() > -1) {
|
||||
exchange.getResponseHeaders().put(ACCESS_CONTROL_MAX_AGE, Integer.toString(deployment.getCorsMaxAge()));
|
||||
}
|
||||
exchange.endExchange();
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@ import io.undertow.servlet.handlers.ServletRequestContext;
|
|||
import io.undertow.util.StatusCodes;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.AdapterConstants;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.representations.adapters.action.AdminAction;
|
||||
|
@ -25,8 +24,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -38,35 +35,30 @@ public class ServletAdminActionsHandler implements HttpHandler {
|
|||
private static final Logger log = Logger.getLogger(ServletAdminActionsHandler.class);
|
||||
protected HttpHandler next;
|
||||
protected UserSessionManagement userSessionManagement;
|
||||
protected RealmConfiguration realmConfig;
|
||||
protected ResourceMetadata resourceMetadata;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
public static class Wrapper implements HandlerWrapper {
|
||||
protected RealmConfiguration realmConfig;
|
||||
protected ResourceMetadata resourceMetadata;
|
||||
protected KeycloakDeployment deployment;
|
||||
protected UserSessionManagement userSessionManagement;
|
||||
|
||||
|
||||
public Wrapper(RealmConfiguration realmConfig, ResourceMetadata resourceMetadata, UserSessionManagement userSessionManagement) {
|
||||
this.realmConfig = realmConfig;
|
||||
this.resourceMetadata = resourceMetadata;
|
||||
public Wrapper(KeycloakDeployment deployment, UserSessionManagement userSessionManagement) {
|
||||
this.deployment = deployment;
|
||||
this.userSessionManagement = userSessionManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHandler wrap(HttpHandler handler) {
|
||||
return new ServletAdminActionsHandler(realmConfig, resourceMetadata, userSessionManagement, handler);
|
||||
return new ServletAdminActionsHandler(deployment, userSessionManagement, handler);
|
||||
}
|
||||
}
|
||||
|
||||
protected ServletAdminActionsHandler(RealmConfiguration realmConfig,
|
||||
ResourceMetadata resourceMetadata,
|
||||
protected ServletAdminActionsHandler(KeycloakDeployment deployment,
|
||||
UserSessionManagement userSessionManagement,
|
||||
HttpHandler next) {
|
||||
this.next = next;
|
||||
this.resourceMetadata = resourceMetadata;
|
||||
this.deployment = deployment;
|
||||
this.userSessionManagement = userSessionManagement;
|
||||
this.realmConfig = realmConfig;
|
||||
}
|
||||
|
||||
|
||||
|
@ -110,7 +102,7 @@ public class ServletAdminActionsHandler implements HttpHandler {
|
|||
}
|
||||
PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
|
||||
if (!validateAction(response, action)) return;
|
||||
realmConfig.setNotBefore(action.getNotBefore());
|
||||
deployment.setNotBefore(action.getNotBefore());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -125,7 +117,7 @@ public class ServletAdminActionsHandler implements HttpHandler {
|
|||
JWSInput input = new JWSInput(token);
|
||||
boolean verified = false;
|
||||
try {
|
||||
verified = RSAProvider.verify(input, realmConfig.getMetadata().getRealmKey());
|
||||
verified = RSAProvider.verify(input, deployment.getRealmKey());
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
if (!verified) {
|
||||
|
@ -148,7 +140,7 @@ public class ServletAdminActionsHandler implements HttpHandler {
|
|||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expired token");
|
||||
return false;
|
||||
}
|
||||
if (!resourceMetadata.getResourceName().equals(action.getResource())) {
|
||||
if (!deployment.getResourceName().equals(action.getResource())) {
|
||||
log.warn("Resource name does not match");
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Resource name does not match");
|
||||
return false;
|
||||
|
|
|
@ -4,8 +4,7 @@ import io.undertow.server.HttpServerExchange;
|
|||
import io.undertow.servlet.api.ConfidentialPortManager;
|
||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
@ -18,15 +17,15 @@ public class ServletKeycloakAuthenticationMechanism extends KeycloakAuthenticati
|
|||
protected ConfidentialPortManager portManager;
|
||||
protected UserSessionManagement userSessionManagement;
|
||||
|
||||
public ServletKeycloakAuthenticationMechanism(UserSessionManagement userSessionManagement, AdapterConfig config, RealmConfiguration realmConfig, ConfidentialPortManager portManager) {
|
||||
super(config, realmConfig);
|
||||
public ServletKeycloakAuthenticationMechanism(UserSessionManagement userSessionManagement, KeycloakDeployment deployment, ConfidentialPortManager portManager) {
|
||||
super(deployment);
|
||||
this.portManager = portManager;
|
||||
this.userSessionManagement = userSessionManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuthAuthenticator createOAuthAuthenticator(HttpServerExchange exchange) {
|
||||
return new ServletOAuthAuthenticator(exchange, realmConfig, portManager);
|
||||
return new ServletOAuthAuthenticator(exchange, deployment, portManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,7 +42,7 @@ public class ServletKeycloakAuthenticationMechanism extends KeycloakAuthenticati
|
|||
log.info("Account was not in session, returning null");
|
||||
return null;
|
||||
}
|
||||
if (account.isActive(realmConfig, adapterConfig)) return account;
|
||||
if (account.isActive(deployment)) return account;
|
||||
log.info("Account was not active, returning null");
|
||||
session.setAttribute(KeycloakUndertowAccount.class.getName(), null);
|
||||
return null;
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.keycloak.adapters.undertow;
|
|||
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.servlet.api.ConfidentialPortManager;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -11,8 +11,8 @@ import org.keycloak.adapters.config.RealmConfiguration;
|
|||
public class ServletOAuthAuthenticator extends OAuthAuthenticator {
|
||||
protected ConfidentialPortManager portManager;
|
||||
|
||||
public ServletOAuthAuthenticator(HttpServerExchange exchange, RealmConfiguration realmInfo, ConfidentialPortManager portManager) {
|
||||
super(exchange, realmInfo, -1);
|
||||
public ServletOAuthAuthenticator(HttpServerExchange exchange, KeycloakDeployment deployment, ConfidentialPortManager portManager) {
|
||||
super(exchange, deployment, -1);
|
||||
this.portManager = portManager;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import io.undertow.server.session.SessionManager;
|
|||
import io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler;
|
||||
import io.undertow.util.StatusCodes;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.representations.adapters.action.LogoutAction;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -33,10 +33,10 @@ public class UserSessionManagement implements SessionListener {
|
|||
private static final String AUTH_SESSION_NAME = CachedAuthenticatedSessionHandler.class.getName() + ".AuthenticatedSession";
|
||||
protected ConcurrentHashMap<String, UserSessions> userSessionMap = new ConcurrentHashMap<String, UserSessions>();
|
||||
|
||||
protected RealmConfiguration realmInfo;
|
||||
protected KeycloakDeployment deployment;
|
||||
|
||||
public UserSessionManagement(RealmConfiguration realmInfo) {
|
||||
this.realmInfo = realmInfo;
|
||||
public UserSessionManagement(KeycloakDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
public static class UserSessions {
|
||||
|
@ -52,10 +52,6 @@ public class UserSessionManagement implements SessionListener {
|
|||
}
|
||||
}
|
||||
|
||||
public int getNumUserLogins() {
|
||||
return userSessionMap.size();
|
||||
}
|
||||
|
||||
public int getActiveSessions() {
|
||||
int active = 0;
|
||||
synchronized (userSessionMap) {
|
||||
|
@ -93,7 +89,7 @@ public class UserSessionManagement implements SessionListener {
|
|||
response.sendError(StatusCodes.BAD_REQUEST, "Expired token");
|
||||
return;
|
||||
}
|
||||
if (!realmInfo.getMetadata().getResourceName().equals(action.getResource())) {
|
||||
if (!deployment.getResourceName().equals(action.getResource())) {
|
||||
log.warn("Resource name does not match");
|
||||
response.sendError(StatusCodes.BAD_REQUEST, "Resource name does not match");
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue