Compare commits

..
Author SHA1 Message Date
keycloak-bot 5a48072e2a Set version to 1.9.0.CR1 2016-02-04 14:17:43 +00:00
3131 changed files with 45000 additions and 150573 deletions

No files matched your search

+1 -1
View File
@@ -12,7 +12,7 @@ before_script:
- export MAVEN_SKIP_RC=true
install:
- ( mvn install -Pdistribution -DskipTests=true -B -V -q ) & ( MYPID=$!; while [ $(ps -ef | grep $MYPID | grep -v grep | wc -l) -gt 0 ]; do sleep 10; date; done; )
- mvn install -Pdistribution -DskipTests=true -B -V -q
script:
- mvn test -B
+1 -6
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -65,11 +65,6 @@
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-authz-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
@@ -169,7 +169,11 @@ public class AdapterDeploymentContext {
public void setAuthServerBaseUrl(String authServerBaseUrl) {
this.authServerBaseUrl = authServerBaseUrl;
KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(authServerBaseUrl);
resolveUrls(serverBuilder);
resolveBrowserUrls(serverBuilder);
if (delegate.getRelativeUrls() == RelativeUrlsUsed.ALL_REQUESTS) {
resolveNonBrowserUrls(serverBuilder);
}
}
@Override
@@ -456,16 +460,6 @@ public class AdapterDeploymentContext {
public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) {
delegate.setTurnOffChangeSessionIdOnLogin(turnOffChangeSessionIdOnLogin);
}
@Override
public int getTokenMinimumTimeToLive() {
return delegate.getTokenMinimumTimeToLive();
}
@Override
public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
delegate.setTokenMinimumTimeToLive(tokenMinimumTimeToLive);
}
}
protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {
@@ -38,6 +38,37 @@ public class AdapterUtils {
return UUID.randomUUID().toString();
}
/**
* Best effort to find origin for REST request calls from web UI application to REST application. In case of relative or absolute
* "auth-server-url" is returned the URL from request. In case of "auth-server-url-for-backend-request" used in configuration, it returns
* the origin of auth server.
*
* This may be the optimization in cluster, so if you have keycloak and applications on same host, the REST request doesn't need to
* go through loadbalancer, but can be sent directly to same host.
*
* @param browserRequestURL
* @param session
* @return
*/
public static String getOriginForRestCalls(String browserRequestURL, KeycloakSecurityContext session) {
if (session instanceof RefreshableKeycloakSecurityContext) {
KeycloakDeployment deployment = ((RefreshableKeycloakSecurityContext)session).getDeployment();
switch (deployment.getRelativeUrls()) {
case ALL_REQUESTS:
case NEVER:
// Resolve baseURI from the request
return UriUtils.getOrigin(browserRequestURL);
case BROWSER_ONLY:
// Resolve baseURI from the codeURL (This is already non-relative and based on our hostname)
return UriUtils.getOrigin(deployment.getTokenUrl());
default:
return "";
}
} else {
return UriUtils.getOrigin(browserRequestURL);
}
}
public static Set<String> getRolesFromSecurityContext(RefreshableKeycloakSecurityContext session) {
Set<String> roles = null;
AccessToken accessToken = session.getToken();
@@ -18,12 +18,10 @@
package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.common.util.UriUtils;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.representations.AccessToken;
import org.keycloak.common.util.UriUtils;
import java.io.IOException;
import java.util.Set;
@@ -57,9 +55,6 @@ public class AuthenticatedActionsHandler {
queryBearerToken();
return true;
}
if (!isAuthorized()) {
return true;
}
return false;
}
@@ -129,24 +124,4 @@ public class AuthenticatedActionsHandler {
}
return false;
}
private boolean isAuthorized() {
PolicyEnforcer policyEnforcer = this.deployment.getPolicyEnforcer();
if (policyEnforcer == null) {
log.debugv("Policy enforcement is disabled.");
return true;
}
try {
OIDCHttpFacade facade = (OIDCHttpFacade) this.facade;
AuthorizationContext authorizationContext = policyEnforcer.enforce(facade);
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) facade.getSecurityContext();
session.setAuthorizationContext(authorizationContext);
return authorizationContext.isGranted();
} catch (Exception e) {
throw new RuntimeException("Failed to enforce policy decisions.", e);
}
}
}
@@ -24,7 +24,6 @@ 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.apache.http.util.EntityUtils;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
@@ -86,39 +85,42 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
private AccessTokenResponse getToken(String username, String password) throws Exception {
AccessTokenResponse tokenResponse=null;
HttpClient client = deployment.getClient();
HttpClient client = new HttpClientBuilder().disableTrustManager().build();
HttpPost post = new HttpPost(
KeycloakUriBuilder.fromUri(deployment.getAuthServerBaseUrl())
.path(ServiceUrlConstants.TOKEN_PATH).build(deployment.getRealm()));
java.util.List <NameValuePair> formparams = new java.util.ArrayList <NameValuePair>();
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD));
formparams.add(new BasicNameValuePair("username", username));
formparams.add(new BasicNameValuePair("password", password));
try {
HttpPost post = new HttpPost(
KeycloakUriBuilder.fromUri(deployment.getAuthServerBaseUrl())
.path(ServiceUrlConstants.TOKEN_PATH).build(deployment.getRealm()));
java.util.List <NameValuePair> formparams = new java.util.ArrayList <NameValuePair>();
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD));
formparams.add(new BasicNameValuePair("username", username));
formparams.add(new BasicNameValuePair("password", password));
ClientCredentialsProviderUtils.setClientCredentials(deployment, post, formparams);
ClientCredentialsProviderUtils.setClientCredentials(deployment, post, formparams);
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
post.setEntity(form);
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
post.setEntity(form);
HttpResponse response = client.execute(post);
int status = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
if (status != 200) {
EntityUtils.consumeQuietly(entity);
throw new java.io.IOException("Bad status: " + status);
}
if (entity == null) {
throw new java.io.IOException("No Entity");
}
java.io.InputStream is = entity.getContent();
try {
tokenResponse = JsonSerialization.readValue(is, AccessTokenResponse.class);
} finally {
try {
is.close();
} catch (java.io.IOException ignored) { }
}
HttpResponse response = client.execute(post);
int status = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
if (status != 200) {
throw new java.io.IOException("Bad status: " + status);
}
if (entity == null) {
throw new java.io.IOException("No Entity");
}
java.io.InputStream is = entity.getContent();
try {
tokenResponse = JsonSerialization.readValue(is, AccessTokenResponse.class);
} finally {
try {
is.close();
} catch (java.io.IOException ignored) { }
}
} finally {
client.getConnectionManager().shutdown();
}
return (tokenResponse);
}
@@ -17,11 +17,9 @@
package org.keycloak.adapters;
import org.apache.http.HttpHost;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
@@ -36,9 +34,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.common.util.EnvUtil;
import org.keycloak.common.util.KeystoreUtil;
import org.keycloak.representations.adapters.config.AdapterConfig;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@@ -48,7 +46,6 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.URI;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
@@ -115,7 +112,6 @@ public class HttpClientBuilder {
protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
protected long establishConnectionTimeout = -1;
protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
protected HttpHost proxyHost;
/**
@@ -125,7 +121,8 @@ public class HttpClientBuilder {
* @param unit
* @return
*/
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit) {
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit)
{
this.socketTimeout = timeout;
this.socketTimeoutUnits = unit;
return this;
@@ -138,7 +135,8 @@ public class HttpClientBuilder {
* @param unit
* @return
*/
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit) {
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit)
{
this.establishConnectionTimeout = timeout;
this.establishConnectionTimeoutUnits = unit;
return this;
@@ -289,20 +287,16 @@ public class HttpClientBuilder {
cm = new SingleClientConnManager(registry);
}
BasicHttpParams params = new BasicHttpParams();
if (proxyHost != null) {
params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
}
if (socketTimeout > -1) {
if (socketTimeout > -1)
{
HttpConnectionParams.setSoTimeout(params, (int) socketTimeoutUnits.toMillis(socketTimeout));
}
if (establishConnectionTimeout > -1) {
HttpConnectionParams.setConnectionTimeout(params, (int) establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
if (establishConnectionTimeout > -1)
{
HttpConnectionParams.setConnectionTimeout(params, (int)establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
}
DefaultHttpClient client = new DefaultHttpClient(cm, params);
if (disableCookieCache) {
client.setCookieStore(new CookieStore() {
@Override
@@ -370,28 +364,6 @@ public class HttpClientBuilder {
} else {
trustStore(truststore);
}
configureProxyForAuthServerIfProvided(adapterConfig);
return build();
}
/**
* Configures a the proxy to use for auth-server requests if provided.
* <p>
* If the given {@link AdapterConfig} contains the attribute {@code proxy-url} we use the
* given URL as a proxy server, otherwise the proxy configuration is ignored.
* </p>
*
* @param adapterConfig
*/
private void configureProxyForAuthServerIfProvided(AdapterConfig adapterConfig) {
if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) {
return;
}
URI uri = URI.create(adapterConfig.getProxyUrl());
this.proxyHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
}
}
@@ -20,7 +20,6 @@ package org.keycloak.adapters;
import org.apache.http.client.HttpClient;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProvider;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.constants.ServiceUrlConstants;
import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired;
@@ -78,8 +77,6 @@ public class KeycloakDeployment {
protected boolean turnOffChangeSessionIdOnLogin;
protected volatile int notBefore;
protected int tokenMinimumTimeToLive;
private PolicyEnforcer policyEnforcer;
public KeycloakDeployment() {
}
@@ -114,17 +111,39 @@ public class KeycloakDeployment {
public void setAuthServerBaseUrl(AdapterConfig config) {
this.authServerBaseUrl = config.getAuthServerUrl();
if (authServerBaseUrl == null) return;
String authServerURLForBackendReqs = config.getAuthServerUrlForBackendRequests();
if (authServerBaseUrl == null && authServerURLForBackendReqs == null) return;
URI authServerUri = URI.create(authServerBaseUrl);
URI authServerUri = null;
if (authServerBaseUrl != null) {
authServerUri = URI.create(authServerBaseUrl);
}
if (authServerUri.getHost() == null) {
relativeUrls = RelativeUrlsUsed.ALWAYS;
if (authServerUri == null || authServerUri.getHost() == null) {
if (authServerURLForBackendReqs != null) {
relativeUrls = RelativeUrlsUsed.BROWSER_ONLY;
KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(authServerURLForBackendReqs);
if (serverBuilder.getHost() == null || serverBuilder.getScheme() == null) {
throw new IllegalStateException("Relative URL not supported for auth-server-url-for-backend-requests option. URL used: "
+ authServerURLForBackendReqs + ", Client: " + config.getResource());
}
resolveNonBrowserUrls(serverBuilder);
} else {
relativeUrls = RelativeUrlsUsed.ALL_REQUESTS;
}
} else {
// We have absolute URI in config
relativeUrls = RelativeUrlsUsed.NEVER;
KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(authServerBaseUrl);
resolveUrls(serverBuilder);
resolveBrowserUrls(serverBuilder);
if (authServerURLForBackendReqs == null) {
resolveNonBrowserUrls(serverBuilder);
} else {
serverBuilder = KeycloakUriBuilder.fromUri(authServerURLForBackendReqs);
resolveNonBrowserUrls(serverBuilder);
}
}
}
@@ -133,14 +152,23 @@ public class KeycloakDeployment {
/**
* @param authUrlBuilder absolute URI
*/
protected void resolveUrls(KeycloakUriBuilder authUrlBuilder) {
protected void resolveBrowserUrls(KeycloakUriBuilder authUrlBuilder) {
if (log.isDebugEnabled()) {
log.debug("resolveUrls");
log.debug("resolveBrowserUrls");
}
String login = authUrlBuilder.clone().path(ServiceUrlConstants.AUTH_PATH).build(getRealm()).toString();
authUrl = KeycloakUriBuilder.fromUri(login);
realmInfoUrl = authUrlBuilder.clone().path(ServiceUrlConstants.REALM_INFO_PATH).build(getRealm()).toString();
}
/**
* @param authUrlBuilder absolute URI
*/
protected void resolveNonBrowserUrls(KeycloakUriBuilder authUrlBuilder) {
if (log.isDebugEnabled()) {
log.debug("resolveNonBrowserUrls");
}
tokenUrl = authUrlBuilder.clone().path(ServiceUrlConstants.TOKEN_PATH).build(getRealm()).toString();
logoutUrl = KeycloakUriBuilder.fromUri(authUrlBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH).build(getRealm()).toString());
@@ -360,20 +388,4 @@ public class KeycloakDeployment {
public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) {
this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin;
}
public int getTokenMinimumTimeToLive() {
return tokenMinimumTimeToLive;
}
public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
this.tokenMinimumTimeToLive = tokenMinimumTimeToLive;
}
public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) {
this.policyEnforcer = policyEnforcer;
}
public PolicyEnforcer getPolicyEnforcer() {
return policyEnforcer;
}
}
@@ -21,12 +21,10 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.PemUtils;
import org.keycloak.enums.TokenStore;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.common.util.PemUtils;
import org.keycloak.util.SystemPropertiesJsonParserFactory;
import java.io.IOException;
@@ -96,12 +94,11 @@ public class KeycloakDeploymentBuilder {
deployment.setAlwaysRefreshToken(adapterConfig.isAlwaysRefreshToken());
deployment.setRegisterNodeAtStartup(adapterConfig.isRegisterNodeAtStartup());
deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod());
deployment.setTokenMinimumTimeToLive(adapterConfig.getTokenMinimumTimeToLive());
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
}
if (realmKeyPem == null || !deployment.isBearerOnly() || deployment.isEnableBasicAuth() || deployment.isRegisterNodeAtStartup() || deployment.getRegisterNodePeriod() != -1) {
if (realmKeyPem == null || !deployment.isBearerOnly() || deployment.isRegisterNodeAtStartup() || deployment.getRegisterNodePeriod() != -1) {
deployment.setClient(new HttpClientBuilder().build(adapterConfig));
}
if (adapterConfig.getAuthServerUrl() == null && (!deployment.isBearerOnly() || realmKeyPem == null)) {
@@ -112,12 +109,6 @@ public class KeycloakDeploymentBuilder {
deployment.setTurnOffChangeSessionIdOnLogin(adapterConfig.getTurnOffChangeSessionIdOnLogin());
}
PolicyEnforcerConfig policyEnforcerConfig = adapterConfig.getPolicyEnforcerConfig();
if (policyEnforcerConfig != null) {
deployment.setPolicyEnforcer(new PolicyEnforcer(deployment, adapterConfig));
}
log.debug("Use authServerUrl: " + deployment.getAuthServerBaseUrl() + ", tokenUrl: " + deployment.getTokenUrl() + ", relativeUrls: " + deployment.getRelativeUrls());
return deployment;
}
@@ -34,7 +34,6 @@ import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.UriUtils;
import org.keycloak.util.TokenUtil;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicLong;
@@ -160,12 +159,6 @@ public class OAuthRequestAuthenticator {
String scope = getQueryParamValue(OAuth2Constants.SCOPE);
url = UriUtils.stripQueryParam(url, OAuth2Constants.SCOPE);
String prompt = getQueryParamValue(OAuth2Constants.PROMPT);
url = UriUtils.stripQueryParam(url, OAuth2Constants.PROMPT);
String maxAge = getQueryParamValue(OAuth2Constants.MAX_AGE);
url = UriUtils.stripQueryParam(url, OAuth2Constants.MAX_AGE);
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
@@ -178,15 +171,9 @@ public class OAuthRequestAuthenticator {
if (idpHint != null && idpHint.length() > 0) {
redirectUriBuilder.queryParam(AdapterConstants.KC_IDP_HINT,idpHint);
}
if (prompt != null && prompt.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.PROMPT, prompt);
if (scope != null && scope.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
}
if (maxAge != null && maxAge.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.MAX_AGE, maxAge);
}
scope = TokenUtil.attachOIDCScope(scope);
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
return redirectUriBuilder.build().toString();
}
@@ -219,7 +206,7 @@ public class OAuthRequestAuthenticator {
tokenStore.saveRequest();
log.debug("Sending redirect to login page: " + redirect);
exchange.getResponse().setStatus(302);
exchange.getResponse().setCookie(deployment.getStateCookieName(), state, /* need to set path? */ null, null, -1, deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr()), true);
exchange.getResponse().setCookie(deployment.getStateCookieName(), state, /* need to set path? */ null, null, -1, deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr()), false);
exchange.getResponse().setHeader("Location", redirect);
return true;
}
@@ -35,8 +35,7 @@ public class OIDCAuthenticationError implements AuthenticationError {
CODE_TO_TOKEN_FAILURE,
INVALID_TOKEN,
STALE_TOKEN,
NO_AUTHORIZATION_HEADER,
NO_QUERY_PARAMETER_ACCESS_TOKEN
NO_AUTHORIZATION_HEADER
}
private Reason reason;
@@ -1,54 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
/**
* @author <a href="mailto:froehlich.ch@gmail.com">Christian Froehlich</a>
* @version $Revision: 1 $
*/
public class QueryParamterTokenRequestAuthenticator extends BearerTokenRequestAuthenticator {
public static final String ACCESS_TOKEN = "access_token";
protected Logger log = Logger.getLogger(QueryParamterTokenRequestAuthenticator.class);
public QueryParamterTokenRequestAuthenticator(KeycloakDeployment deployment) {
super(deployment);
}
public AuthOutcome authenticate(HttpFacade exchange) {
tokenString = null;
tokenString = getAccessTokenFromQueryParamter(exchange);
if (tokenString == null || tokenString.trim().isEmpty()) {
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.NO_QUERY_PARAMETER_ACCESS_TOKEN, null, null);
return AuthOutcome.NOT_ATTEMPTED;
}
return (authenticateToken(exchange, tokenString));
}
String getAccessTokenFromQueryParamter(HttpFacade exchange) {
try {
if (exchange != null && exchange.getRequest() != null) {
return exchange.getRequest().getQueryParamValue(ACCESS_TOKEN);
}
} catch (Exception ignore) {
}
return null;
}
}
@@ -18,11 +18,9 @@
package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.RSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
@@ -76,11 +74,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
}
public boolean isActive() {
return token != null && this.token.isActive() && deployment!=null && this.token.getIssuedAt() > deployment.getNotBefore();
}
public boolean isTokenTimeToLiveSufficient(AccessToken token) {
return token != null && (token.getExpiration() - this.deployment.getTokenMinimumTimeToLive()) > Time.currentTime();
return token != null && this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
}
public KeycloakDeployment getDeployment() {
@@ -101,7 +95,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
if (log.isTraceEnabled()) {
log.trace("checking whether to refresh.");
}
if (isActive() && isTokenTimeToLiveSufficient(this.token)) return true;
if (isActive()) return true;
}
if (this.deployment == null || refreshToken == null) return false; // Might be serialized in HttpSession?
@@ -136,13 +130,6 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
log.error("failed verification of token");
return false;
}
// If the TTL is greater-or-equal to the expire time on the refreshed token, have to abort or go into an infinite refresh loop
if (!isTokenTimeToLiveSufficient(token)) {
log.error("failed to refresh the token with a longer time-to-live than the minimum");
return false;
}
if (response.getNotBeforePolicy() > deployment.getNotBefore()) {
deployment.setNotBefore(response.getNotBeforePolicy());
}
@@ -158,8 +145,4 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
tokenStore.refreshCallback(this);
return true;
}
public void setAuthorizationContext(AuthorizationContext authorizationContext) {
this.authorizationContext = authorizationContext;
}
}
@@ -61,7 +61,7 @@ public abstract class RequestAuthenticator {
if (log.isTraceEnabled()) {
log.trace("try bearer");
}
AuthOutcome outcome = bearer.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = bearer.getChallenge();
@@ -74,36 +74,18 @@ public abstract class RequestAuthenticator {
return AuthOutcome.AUTHENTICATED;
}
QueryParamterTokenRequestAuthenticator queryParamAuth = createQueryParamterTokenRequestAuthenticator();
if (log.isTraceEnabled()) {
log.trace("try query paramter auth");
}
outcome = queryParamAuth.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = queryParamAuth.getChallenge();
log.debug("QueryParamAuth auth FAILED");
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.AUTHENTICATED) {
if (verifySSL()) return AuthOutcome.FAILED;
log.debug("QueryParamAuth AUTHENTICATED");
completeAuthentication(queryParamAuth, "KEYCLOAK");
return AuthOutcome.AUTHENTICATED;
}
if (deployment.isEnableBasicAuth()) {
BasicAuthRequestAuthenticator basicAuth = createBasicAuthAuthenticator();
if (log.isTraceEnabled()) {
log.trace("try basic auth");
}
outcome = basicAuth.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = basicAuth.getChallenge();
log.debug("BasicAuth FAILED");
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.AUTHENTICATED) {
if (verifySSL()) return AuthOutcome.FAILED;
log.debug("BasicAuth AUTHENTICATED");
completeAuthentication(basicAuth, "BASIC");
return AuthOutcome.AUTHENTICATED;
@@ -168,24 +150,17 @@ public abstract class RequestAuthenticator {
return new BasicAuthRequestAuthenticator(deployment);
}
protected QueryParamterTokenRequestAuthenticator createQueryParamterTokenRequestAuthenticator() {
return new QueryParamterTokenRequestAuthenticator(deployment);
}
protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, tokenStore, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, oauth.getToken()), session);
completeOAuthAuthentication(principal);
log.debugv("User ''{0}'' invoking ''{1}'' on client ''{2}''", principal.getName(), facade.getRequest().getURI(), deployment.getResourceName());
}
protected abstract void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
protected abstract void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method);
/**
* After code is received, we change the session id if possible to guard against https://www.owasp.org/index.php/Session_Fixation
*
* @param create
* @return
*/
@@ -54,10 +54,6 @@ public class JWTClientCredentialsProvider implements ClientCredentialsProvider {
this.tokenTimeout = tokenTimeout;
}
protected int getTokenTimeout() {
return tokenTimeout;
}
@Override
public void init(KeycloakDeployment deployment, Object config) {
if (config == null || !(config instanceof Map)) {
@@ -1,265 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.adapters.authorization;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade.Request;
import org.keycloak.adapters.spi.HttpFacade.Response;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.resource.ProtectedResource;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.Permission;
import java.net.URI;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public abstract class AbstractPolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(AbstractPolicyEnforcer.class);
private final PolicyEnforcerConfig enforcerConfig;
private final PolicyEnforcer policyEnforcer;
private List<PathConfig> paths;
private AuthzClient authzClient;
private PathMatcher pathMatcher;
public AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
this.policyEnforcer = policyEnforcer;
this.enforcerConfig = policyEnforcer.getEnforcerConfig();
this.authzClient = policyEnforcer.getClient();
this.pathMatcher = new PathMatcher();
this.paths = policyEnforcer.getPaths();
}
public AuthorizationContext authorize(OIDCHttpFacade httpFacade) {
EnforcementMode enforcementMode = this.enforcerConfig.getEnforcementMode();
if (EnforcementMode.DISABLED.equals(enforcementMode)) {
return createEmptyAuthorizationContext(true);
}
AccessToken accessToken = httpFacade.getSecurityContext().getToken();
Request request = httpFacade.getRequest();
Response response = httpFacade.getResponse();
String pathInfo = URI.create(request.getURI()).getPath().substring(1);
String path = pathInfo.substring(pathInfo.indexOf('/'), pathInfo.length());
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
if (pathConfig == null) {
if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
return createAuthorizationContext(accessToken);
}
LOGGER.debugf("Could not find a configuration for path [%s]", path);
response.sendError(403, "Could not find a configuration for path [" + path + "].");
return createEmptyAuthorizationContext(false);
}
PathConfig actualPathConfig = resolvePathConfig(pathConfig, request);
Set<String> requiredScopes = getRequiredScopes(actualPathConfig, request);
if (isAuthorized(actualPathConfig, requiredScopes, accessToken, httpFacade)) {
try {
return createAuthorizationContext(accessToken);
} catch (Exception e) {
throw new RuntimeException("Error processing path [" + actualPathConfig.getPath() + "].", e);
}
}
if (!challenge(actualPathConfig, requiredScopes, httpFacade)) {
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
response.sendError(403, "Authorization failed.");
}
return createEmptyAuthorizationContext(false);
}
protected abstract boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade);
protected boolean isAuthorized(PathConfig actualPathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
Request request = httpFacade.getRequest();
PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
String accessDeniedPath = enforcerConfig.getAccessDeniedPath();
if (accessDeniedPath != null) {
if (request.getURI().contains(accessDeniedPath)) {
return true;
}
}
AccessToken.Authorization authorization = accessToken.getAuthorization();
if (authorization == null) {
return false;
}
List<Permission> permissions = authorization.getPermissions();
for (Permission permission : permissions) {
if (permission.getResourceSetId() != null) {
if (isResourcePermission(actualPathConfig, permission)) {
if (actualPathConfig.isInstance() && !matchResourcePermission(actualPathConfig, permission)) {
continue;
}
if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
this.paths.remove(actualPathConfig);
}
return true;
}
}
} else {
if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
return true;
}
}
}
LOGGER.debugf("Authorization FAILED for path [%s]. No enough permissions [%s].", actualPathConfig, permissions);
return false;
}
private boolean hasResourceScopePermission(Set<String> requiredScopes, Permission permission, PathConfig actualPathConfig) {
Set<String> allowedScopes = permission.getScopes();
return (allowedScopes.containsAll(requiredScopes) || allowedScopes.isEmpty());
}
protected AuthzClient getAuthzClient() {
return this.authzClient;
}
protected PolicyEnforcerConfig getEnforcerConfig() {
return enforcerConfig;
}
protected PolicyEnforcer getPolicyEnforcer() {
return policyEnforcer;
}
private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
return new AuthorizationContext() {
@Override
public boolean hasPermission(String resourceName, String scopeName) {
return granted;
}
@Override
public boolean hasResourcePermission(String resourceName) {
return granted;
}
@Override
public boolean hasScopePermission(String scopeName) {
return granted;
}
@Override
public List<Permission> getPermissions() {
return Collections.EMPTY_LIST;
}
@Override
public boolean isGranted() {
return granted;
}
};
}
private PathConfig resolvePathConfig(PathConfig originalConfig, Request request) {
if (originalConfig.hasPattern()) {
String pathInfo = URI.create(request.getURI()).getPath().substring(1);
String path = pathInfo.substring(pathInfo.indexOf('/'), pathInfo.length());
ProtectedResource resource = this.authzClient.protection().resource();
Set<String> search = resource.findByFilter("uri=" + path);
if (!search.isEmpty()) {
// resource does exist on the server, cache it
ResourceRepresentation targetResource = resource.findById(search.iterator().next()).getResourceDescription();
PathConfig config = new PathConfig();
config.setId(targetResource.getId());
config.setName(targetResource.getName());
config.setType(targetResource.getType());
config.setPath(targetResource.getUri());
config.setScopes(originalConfig.getScopes());
config.setMethods(originalConfig.getMethods());
config.setParentConfig(originalConfig);
this.paths.add(config);
return config;
}
}
return originalConfig;
}
private Set<String> getRequiredScopes(PathConfig pathConfig, Request request) {
Set<String> requiredScopes = new HashSet<>();
requiredScopes.addAll(pathConfig.getScopes());
String method = request.getMethod();
for (PolicyEnforcerConfig.MethodConfig methodConfig : pathConfig.getMethods()) {
if (methodConfig.getMethod().equals(method)) {
requiredScopes.addAll(methodConfig.getScopes());
}
}
return requiredScopes;
}
private AuthorizationContext createAuthorizationContext(AccessToken accessToken) {
return new AuthorizationContext(accessToken, this.paths);
}
private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
// first we try a match using resource id
boolean resourceMatch = matchResourcePermission(actualPathConfig, permission);
// as a fallback, check if the current path is an instance and if so, check if parent's id matches the permission
if (!resourceMatch && actualPathConfig.isInstance()) {
resourceMatch = matchResourcePermission(actualPathConfig.getParentConfig(), permission);
}
return resourceMatch;
}
private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
return permission.getResourceSetId().equals(actualPathConfig.getId());
}
}
@@ -1,80 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.adapters.authorization;
import org.jboss.logging.Logger;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.resource.PermissionResource;
import org.keycloak.authorization.client.resource.ProtectionResource;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(BearerTokenPolicyEnforcer.class);
public BearerTokenPolicyEnforcer(PolicyEnforcer enforcer) {
super(enforcer);
}
@Override
protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
if (getEnforcerConfig().getUmaProtocolConfig() != null) {
challengeUmaAuthentication(pathConfig, requiredScopes, facade);
} else {
challengeEntitlementAuthentication(facade);
}
return true;
}
private void challengeEntitlementAuthentication(OIDCHttpFacade facade) {
HttpFacade.Response response = facade.getResponse();
AuthzClient authzClient = getAuthzClient();
String clientId = authzClient.getConfiguration().getClientId();
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/entitlement";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "KC_ETT realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\"");
}
private void challengeUmaAuthentication(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
HttpFacade.Response response = facade.getResponse();
AuthzClient authzClient = getAuthzClient();
String ticket = getPermissionTicket(pathConfig, requiredScopes, authzClient);
String clientId = authzClient.getConfiguration().getClientId();
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "UMA realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\",ticket=\"" + ticket + "\"");
}
private String getPermissionTicket(PathConfig pathConfig, Set<String> requiredScopes, AuthzClient authzClient) {
ProtectionResource protection = authzClient.protection();
PermissionResource permission = protection.permission();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setScopes(requiredScopes);
return permission.forResource(permissionRequest).getTicket();
}
}
@@ -1,151 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.adapters.authorization;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthorizationDeniedException;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.AuthorizationRequest;
import org.keycloak.authorization.client.representation.AuthorizationResponse;
import org.keycloak.authorization.client.representation.EntitlementRequest;
import org.keycloak.authorization.client.representation.EntitlementResponse;
import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.representation.PermissionResponse;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.util.JsonSerialization;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(KeycloakAdapterPolicyEnforcer.class);
public KeycloakAdapterPolicyEnforcer(PolicyEnforcer policyEnforcer) {
super(policyEnforcer);
}
@Override
protected boolean isAuthorized(PathConfig pathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
int retry = 2;
AccessToken original = accessToken;
while (retry > 0) {
if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
return true;
}
accessToken = requestAuthorizationToken(pathConfig, requiredScopes, httpFacade);
if (accessToken == null) {
return false;
}
AccessToken.Authorization authorization = original.getAuthorization();
if (authorization == null) {
authorization = new AccessToken.Authorization();
authorization.setPermissions(new ArrayList<Permission>());
}
AccessToken.Authorization newAuthorization = accessToken.getAuthorization();
if (newAuthorization != null) {
authorization.getPermissions().addAll(newAuthorization.getPermissions());
}
original.setAuthorization(authorization);
retry--;
}
return false;
}
@Override
protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
String accessDeniedPath = getEnforcerConfig().getAccessDeniedPath();
HttpFacade.Response response = facade.getResponse();
if (accessDeniedPath != null) {
response.setStatus(302);
response.setHeader("Location", accessDeniedPath);
} else {
response.sendError(403);
}
return true;
}
private AccessToken requestAuthorizationToken(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade httpFacade) {
try {
String accessToken = httpFacade.getSecurityContext().getTokenString();
AuthzClient authzClient = getAuthzClient();
KeycloakDeployment deployment = getPolicyEnforcer().getDeployment();
if (getEnforcerConfig().getUmaProtocolConfig() != null) {
LOGGER.debug("Obtaining authorization for authenticated user.");
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setScopes(requiredScopes);
PermissionResponse permissionResponse = authzClient.protection().permission().forResource(permissionRequest);
AuthorizationRequest authzRequest = new AuthorizationRequest(permissionResponse.getTicket());
AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest);
if (authzResponse != null) {
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
}
return null;
} else {
LOGGER.debug("Obtaining entitlements for authenticated user.");
AccessToken token = httpFacade.getSecurityContext().getToken();
if (token.getAuthorization() == null) {
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getClientId());
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
} else {
EntitlementRequest request = new EntitlementRequest();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setResourceSetName(pathConfig.getName());
permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes()));
request.addPermission(permissionRequest);
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getClientId(), request);
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
}
}
} catch (AuthorizationDeniedException e) {
return null;
} catch (Exception e) {
throw new RuntimeException("Unexpected error during authorization request.", e);
}
}
}
@@ -1,103 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.adapters.authorization;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import java.util.List;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
class PathMatcher {
private static final String ANY_RESOURCE_PATTERN = "/*";
PathConfig matches(final String requestedUri, List<PathConfig> paths) {
PathConfig actualConfig = null;
for (PathConfig entry : paths) {
String protectedUri = entry.getPath();
String selectedUri = null;
if (protectedUri.equals(ANY_RESOURCE_PATTERN) && actualConfig == null) {
selectedUri = protectedUri;
}
int suffixIndex = protectedUri.indexOf(ANY_RESOURCE_PATTERN + ".");
if (suffixIndex != -1) {
String protectedSuffix = protectedUri.substring(suffixIndex + ANY_RESOURCE_PATTERN.length());
if (requestedUri.endsWith(protectedSuffix)) {
selectedUri = protectedUri;
}
}
if (protectedUri.equals(requestedUri)) {
selectedUri = protectedUri;
}
if (protectedUri.endsWith(ANY_RESOURCE_PATTERN)) {
String formattedPattern = removeWildCardsFromUri(protectedUri);
if (!formattedPattern.equals("/") && requestedUri.startsWith(formattedPattern)) {
selectedUri = protectedUri;
}
if (!formattedPattern.equals("/") && formattedPattern.endsWith("/") && formattedPattern.substring(0, formattedPattern.length() - 1).equals(requestedUri)) {
selectedUri = protectedUri;
}
}
int startRegex = protectedUri.indexOf('{');
if (startRegex != -1) {
String prefix = protectedUri.substring(0, startRegex);
if (requestedUri.startsWith(prefix)) {
selectedUri = protectedUri;
}
}
if (selectedUri != null) {
selectedUri = protectedUri;
}
if (selectedUri != null) {
if (actualConfig == null) {
actualConfig = entry;
} else {
if (actualConfig.equals(ANY_RESOURCE_PATTERN)) {
actualConfig = entry;
}
if (protectedUri.startsWith(removeWildCardsFromUri(actualConfig.getPath()))) {
actualConfig = entry;
}
}
}
}
return actualConfig;
}
private String removeWildCardsFromUri(String protectedUri) {
return protectedUri.replaceAll("/[*]", "/");
}
}
@@ -1,203 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.adapters.authorization;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.representation.RegistrationResponse;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.representation.ScopeRepresentation;
import org.keycloak.authorization.client.resource.ProtectedResource;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.Permission;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(PolicyEnforcer.class);
private final KeycloakDeployment deployment;
private final AuthzClient authzClient;
private final PolicyEnforcerConfig enforcerConfig;
private final List<PathConfig> paths;
public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) {
this.deployment = deployment;
this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig();
this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()));
this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Initialization complete. Path configurations:");
for (PathConfig pathConfig : this.paths) {
LOGGER.debug(pathConfig);
}
}
}
public AuthorizationContext enforce(OIDCHttpFacade facade) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Policy enforcement is enable. Enforcing policy decisions for path [{0}].", facade.getRequest().getURI());
}
AuthorizationContext context;
if (deployment.isBearerOnly()) {
context = new BearerTokenPolicyEnforcer(this).authorize(facade);
} else {
context = new KeycloakAdapterPolicyEnforcer(this).authorize(facade);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Policy enforcement result for path [{0}] is : {1}", facade.getRequest().getURI(), context.isGranted() ? "GRANTED" : "DENIED");
LOGGER.debugv("Returning authorization context with permissions:");
for (Permission permission : context.getPermissions()) {
LOGGER.debug(permission);
}
}
return context;
}
PolicyEnforcerConfig getEnforcerConfig() {
return enforcerConfig;
}
AuthzClient getClient() {
return authzClient;
}
List<PathConfig> getPaths() {
return paths;
}
KeycloakDeployment getDeployment() {
return deployment;
}
private List<PathConfig> configurePaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
if (enforcerConfig.getPaths().isEmpty()) {
LOGGER.info("No path provided in configuration.");
return configureAllPathsForResourceServer(protectedResource);
} else {
LOGGER.info("Paths provided in configuration.");
return configureDefinedPaths(protectedResource, enforcerConfig);
}
}
private List<PathConfig> configureDefinedPaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
List<PathConfig> paths = new ArrayList<>();
for (PathConfig pathConfig : enforcerConfig.getPaths()) {
Set<String> search;
String resourceName = pathConfig.getName();
String path = pathConfig.getPath();
if (resourceName != null) {
LOGGER.debugf("Trying to find resource with name [%s] for path [%s].", resourceName, path);
search = protectedResource.findByFilter("name=" + resourceName);
} else {
LOGGER.debugf("Trying to find resource with uri [%s] for path [%s].", path, path);
search = protectedResource.findByFilter("uri=" + path);
}
if (search.isEmpty()) {
if (enforcerConfig.isCreateResources()) {
LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
ResourceRepresentation resource = new ResourceRepresentation();
resource.setName(resourceName);
resource.setType(pathConfig.getType());
resource.setUri(path);
HashSet<ScopeRepresentation> scopes = new HashSet<>();
for (String scopeName : pathConfig.getScopes()) {
ScopeRepresentation scope = new ScopeRepresentation();
scope.setName(scopeName);
scopes.add(scope);
}
resource.setScopes(scopes);
RegistrationResponse registrationResponse = protectedResource.create(resource);
pathConfig.setId(registrationResponse.getId());
} else {
throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + ". Make sure you have created a resource on the server that matches with the path configuration.");
}
} else {
pathConfig.setId(search.iterator().next());
}
paths.add(pathConfig);
}
return paths;
}
private List<PathConfig> configureAllPathsForResourceServer(ProtectedResource protectedResource) {
LOGGER.info("Querying the server for all resources associated with this application.");
List<PathConfig> paths = new ArrayList<>();
for (String id : protectedResource.findAll()) {
RegistrationResponse response = protectedResource.findById(id);
ResourceRepresentation resourceDescription = response.getResourceDescription();
if (resourceDescription.getUri() != null) {
paths.add(createPathConfig(resourceDescription));
}
}
return paths;
}
private PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
PathConfig pathConfig = new PathConfig();
pathConfig.setId(resourceDescription.getId());
pathConfig.setName(resourceDescription.getName());
pathConfig.setPath(resourceDescription.getUri());
List<String> scopeNames = new ArrayList<>();
for (ScopeRepresentation scope : resourceDescription.getScopes()) {
scopeNames.add(scope.getName());
}
pathConfig.setScopes(scopeNames);
pathConfig.setType(resourceDescription.getType());
return pathConfig;
}
}
@@ -38,13 +38,13 @@ import javax.security.auth.spi.LoginModule;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.RSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.common.util.FindFile;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.FindFile;
import org.keycloak.representations.AccessToken;
import org.keycloak.common.util.reflections.Reflections;
File mode changed.
@@ -54,14 +54,13 @@ public class KeycloakDeploymentBuilderTest {
assertEquals("234234-234234-234234", deployment.getResourceCredentials().get("secret"));
assertEquals(ClientIdAndSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
assertEquals(20, ((ThreadSafeClientConnManager) deployment.getClient().getConnectionManager()).getMaxTotal());
assertEquals("https://localhost:8443/auth/realms/demo/protocol/openid-connect/token", deployment.getTokenUrl());
assertEquals("https://backend:8443/auth/realms/demo/protocol/openid-connect/token", deployment.getTokenUrl());
assertEquals(RelativeUrlsUsed.NEVER, deployment.getRelativeUrls());
assertTrue(deployment.isAlwaysRefreshToken());
assertTrue(deployment.isRegisterNodeAtStartup());
assertEquals(1000, deployment.getRegisterNodePeriod());
assertEquals(TokenStore.COOKIE, deployment.getTokenStore());
assertEquals("email", deployment.getPrincipalAttribute());
assertEquals(10, deployment.getTokenMinimumTimeToLive());
}
@Test
@@ -1,23 +0,0 @@
package org.keycloak.adapters;
import static org.junit.Assert.*;
import org.junit.Test;
import org.keycloak.representations.oidc.TokenMetadataRepresentation;
/**
* @author github.com/tubbynl
*
*/
public class RefreshableKeycloakSecurityContextTest {
@Test
public void isActive() {
TokenMetadataRepresentation token = new TokenMetadataRepresentation();
token.setActive(true);
token.issuedNow();
RefreshableKeycloakSecurityContext sut = new RefreshableKeycloakSecurityContext(null,null,null,token,null, null, null);
// verify false if null deployment (KEYCLOAK-3050; yielded a npe)
assertFalse(sut.isActive());
}
}
@@ -24,10 +24,10 @@
"client-keystore": "classpath:/keystore.jks",
"client-keystore-password": "storepass",
"client-key-password": "keypass",
"auth-server-url-for-backend-requests": "https://backend:8443/auth",
"always-refresh-token": true,
"register-node-at-startup": true,
"register-node-period": 1000,
"token-store": "cookie",
"principal-attribute": "email",
"token-minimum-time-to-live": 10
"principal-attribute": "email"
}
@@ -19,10 +19,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-as7-integration-pom</artifactId>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+3 -3
View File
@@ -19,10 +19,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-as7-integration-pom</artifactId>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+7 -7
View File
@@ -20,9 +20,9 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-integration-pom</artifactId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<artifactId>keycloak-parent</artifactId>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<artifactId>keycloak-as7-subsystem</artifactId>
@@ -64,25 +64,25 @@
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-naming</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-server</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-ee</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-web</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
@@ -49,6 +49,7 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName();
// if it's not a web-app there's nothing to secure
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
@@ -66,24 +67,24 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
// otherwise
LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
boolean hasSubsystemConfig = service.isSecureDeployment(deploymentUnit);
boolean hasSubsystemConfig = service.isSecureDeployment(deploymentName);
boolean webRequiresKC = loginConfig != null && "KEYCLOAK".equalsIgnoreCase(loginConfig.getAuthMethod());
if (hasSubsystemConfig || webRequiresKC) {
log.debug("Setting up KEYCLOAK auth method for WAR: " + deploymentUnit.getName());
log.debug("Setting up KEYCLOAK auth method for WAR: " + deploymentName);
// if secure-deployment configuration exists for web app, we force KEYCLOAK auth method on it
if (hasSubsystemConfig) {
addJSONData(service.getJSON(deploymentUnit), warMetaData);
addJSONData(service.getJSON(deploymentName), warMetaData);
if (loginConfig != null) {
loginConfig.setAuthMethod("KEYCLOAK");
loginConfig.setRealmName(service.getRealmName(deploymentUnit));
loginConfig.setRealmName(service.getRealmName(deploymentName));
} else {
log.warn("Failed to set up KEYCLOAK auth method for WAR: " + deploymentUnit.getName() + " (loginConfig == null)");
log.warn("Failed to set up KEYCLOAK auth method for WAR: " + deploymentName + " (loginConfig == null)");
}
}
addValve(webMetaData);
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName);
}
}
@@ -25,9 +25,6 @@ import java.util.HashMap;
import java.util.Map;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.web.deployment.WarMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
/**
* This service keeps track of the entire Keycloak management model so as to provide
@@ -157,15 +154,13 @@ public final class KeycloakAdapterConfigService {
return null;
}
public String getRealmName(DeploymentUnit deploymentUnit) {
String deploymentName = preferredDeploymentName(deploymentUnit);
public String getRealmName(String deploymentName) {
ModelNode deployment = this.secureDeployments.get(deploymentName);
return deployment.get(RealmDefinition.TAG_NAME).asString();
}
public String getJSON(DeploymentUnit deploymentUnit) {
String deploymentName = preferredDeploymentName(deploymentUnit);
public String getJSON(String deploymentName) {
ModelNode deployment = this.secureDeployments.get(deploymentName);
String realmName = deployment.get(RealmDefinition.TAG_NAME).asString();
ModelNode realm = this.realms.get(realmName);
@@ -189,29 +184,9 @@ public final class KeycloakAdapterConfigService {
}
}
public boolean isSecureDeployment(DeploymentUnit deploymentUnit) {
public boolean isSecureDeployment(String deploymentName) {
//log.info("********* CHECK KEYCLOAK DEPLOYMENT: deployments.size()" + deployments.size());
String deploymentName = preferredDeploymentName(deploymentUnit);
return this.secureDeployments.containsKey(deploymentName);
}
// KEYCLOAK-3273: prefer module name if available
private String preferredDeploymentName(DeploymentUnit deploymentUnit) {
String deploymentName = deploymentUnit.getName();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return deploymentName;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return deploymentName;
}
String moduleName = webMetaData.getModuleName();
if (moduleName != null) return moduleName + ".war";
return deploymentName;
}
}
@@ -46,11 +46,9 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
if (!KeycloakAdapterConfigService.getInstance().isSecureDeployment(deploymentUnit)) {
String deploymentName = deploymentUnit.getName();
if (!KeycloakAdapterConfigService.getInstance().isSecureDeployment(deploymentName)) {
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return;
@@ -15,9 +15,9 @@
# limitations under the License.
#
keycloak.subsystem=Keycloak adapter subsystem
keycloak.subsystem.add=Operation Adds Keycloak adapter subsystem
keycloak.subsystem.remove=Operation removes Keycloak adapter subsystem
keycloak.subsystem=Keycloak subsystem
keycloak.subsystem.add=Operation Adds Keycloak subsystem
keycloak.subsystem.remove=Operation removes Keycloak subsystem
keycloak.subsystem.realm=A Keycloak realm.
keycloak.subsystem.secure-deployment=A deployment secured by Keycloak.
@@ -63,7 +63,6 @@ keycloak.secure-deployment.resource=Application name
keycloak.secure-deployment.use-resource-role-mappings=Use resource level permissions from token
keycloak.secure-deployment.credentials=Adapter credentials
keycloak.secure-deployment.bearer-only=Bearer Token Auth only
keycloak.secure-deployment.enable-basic-auth=Enable Basic Authentication
keycloak.secure-deployment.public-client=Public client
keycloak.secure-deployment.enable-cors=Enable Keycloak CORS support
keycloak.secure-deployment.client-keystore=n/a
@@ -79,7 +78,6 @@ keycloak.secure-deployment.register-node-at-startup=Cluster setting
keycloak.secure-deployment.register-node-period=how often to re-register node
keycloak.secure-deployment.token-store=cookie or session storage for auth session data
keycloak.secure-deployment.principal-attribute=token attribute to use to set Principal name
keycloak.secure-deployment.turn-off-change-session-id-on-login=The session id is changed by default on a successful login. Change this to true if you want to turn this off
keycloak.secure-deployment.credential=Credential value
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -119,7 +119,6 @@ public class KeycloakInstalled {
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.STATE, state)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString();
Desktop.getDesktop().browse(new URI(authUrl));
@@ -176,7 +175,6 @@ public class KeycloakInstalled {
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString();
printer.println("Open the following URL in a browser. After login copy/paste the code back and press <enter>");
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -20,7 +20,6 @@ package org.keycloak.jaxrs;
import org.keycloak.AbstractOAuthClient;
import org.keycloak.OAuth2Constants;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.InternalServerErrorException;
@@ -86,13 +85,14 @@ public class JaxrsOAuthClient extends AbstractOAuthClient {
}
public Response redirect(UriInfo uriInfo, String redirectUri) {
String state = getStateCode();
String scopeParam = TokenUtil.attachOIDCScope(scope);
UriBuilder uriBuilder = UriBuilder.fromUri(authUrl)
.queryParam(OAuth2Constants.CLIENT_ID, clientId)
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.STATE, state)
.queryParam(OAuth2Constants.SCOPE, scopeParam);
.queryParam(OAuth2Constants.STATE, state);
if (scope != null) {
uriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
}
URI url = uriBuilder.build();
+2 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -60,7 +60,7 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-adapter-spi</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
@@ -18,8 +18,6 @@
package org.keycloak.adapters.jetty.core;
import org.eclipse.jetty.security.DefaultUserIdentity;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
@@ -137,44 +135,10 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
return new DefaultUserIdentity(theSubject, principal, theRoles);
}
private class DummyLoginService implements LoginService {
@Override
public String getName() {
return null;
}
@Override
public UserIdentity login(String username, Object credentials) {
return null;
}
@Override
public boolean validate(UserIdentity user) {
return false;
}
@Override
public IdentityService getIdentityService() {
return null;
}
@Override
public void setIdentityService(IdentityService service) {
}
@Override
public void logout(UserIdentity user) {
}
}
@Override
public void setConfiguration(AuthConfiguration configuration) {
//super.setConfiguration(configuration);
initializeKeycloak();
// need this so that getUserPrincipal does not throw NPE
_loginService = new DummyLoginService();
String error = configuration.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE);
setErrorPage(error);
}
@@ -340,13 +304,13 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
Authentication authentication = request.getAuthentication();
if (!(authentication instanceof KeycloakAuthentication)) {
UserIdentity userIdentity = createIdentity(principal);
authentication = createAuthentication(userIdentity, request);
authentication = createAuthentication(userIdentity);
request.setAuthentication(authentication);
}
return authentication;
}
protected abstract Authentication createAuthentication(UserIdentity userIdentity, Request request);
protected abstract Authentication createAuthentication(UserIdentity userIdentity);
public static abstract class KeycloakAuthentication extends UserAuthentication {
public KeycloakAuthentication(String method, UserIdentity userIdentity) {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -50,7 +50,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
}
@Override
protected Authentication createAuthentication(UserIdentity userIdentity, Request request) {
protected Authentication createAuthentication(UserIdentity userIdentity) {
return new KeycloakAuthentication(getAuthMethod(), userIdentity) {
@Override
public void logout() {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -52,7 +52,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
}
@Override
protected Authentication createAuthentication(UserIdentity userIdentity, Request request) {
protected Authentication createAuthentication(UserIdentity userIdentity) {
return new KeycloakAuthentication(getAuthMethod(), userIdentity) {
@Override
public void logout() {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -47,7 +47,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
}
@Override
protected Authentication createAuthentication(UserIdentity userIdentity, Request request) {
protected Authentication createAuthentication(UserIdentity userIdentity) {
return new KeycloakAuthentication(getAuthMethod(), userIdentity) {
@Override
public void logout() {
-156
View File
@@ -1,156 +0,0 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-jetty93-adapter</artifactId>
<name>Keycloak Jetty 9.3.x Integration</name>
<properties>
<jetty9.version>9.3.9.v20160517</jetty9.version>
<keycloak.osgi.export>
org.keycloak.adapters.jetty.*
</keycloak.osgi.export>
<keycloak.osgi.import>
org.eclipse.jetty.*;resolution:=optional,
javax.servlet.*;version="[3.0,4)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,47 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.jetty;
import org.eclipse.jetty.server.Request;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.JettyRequestAuthenticator;
import org.keycloak.adapters.spi.HttpFacade;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Jetty93RequestAuthenticator extends JettyRequestAuthenticator {
public Jetty93RequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort, Request request) {
super(facade, deployment, tokenStore, sslRedirectPort, request);
}
@Override
protected String changeHttpSessionId(boolean create) {
Request request = this.request;
HttpSession session = request.getSession(false);
if (session == null) {
return request.getSession(true).getId();
}
if (!deployment.isTurnOffChangeSessionIdOnLogin()) return request.changeSessionId();
else return session.getId();
}
}
@@ -1,112 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.jetty;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.MultiMap;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.common.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JettyAdapterSessionStore implements AdapterSessionStore {
public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
protected Request myRequest;
public JettyAdapterSessionStore(Request request) {
this.myRequest = request; // for IDE/compilation purposes
}
protected MultiMap<String> extractFormParameters(Request base_request) {
MultiMap<String> formParameters = new MultiMap<String>();
base_request.extractFormParameters(formParameters);
return formParameters;
}
protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
base_request.setContentParameters(j_post);
}
public boolean restoreRequest() {
HttpSession session = myRequest.getSession(false);
if (session == null) return false;
synchronized (session) {
String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
if (j_uri != null) {
// check if the request is for the same url as the original and restore
// params if it was a post
StringBuffer buf = myRequest.getRequestURL();
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
if (j_uri.equals(buf.toString())) {
String method = (String)session.getAttribute(JettyHttpFacade.__J_METHOD);
myRequest.setMethod(method);
MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
if (j_post != null) {
myRequest.setContentType("application/x-www-form-urlencoded");
MultiMap<String> map = new MultiMap<String>();
for (String key : j_post.keySet()) {
for (String val : j_post.getList(key)) {
map.add(key, val);
}
}
restoreFormParameters(map, myRequest);
}
session.removeAttribute(FormAuthenticator.__J_URI);
session.removeAttribute(JettyHttpFacade.__J_METHOD);
session.removeAttribute(FormAuthenticator.__J_POST);
}
return true;
}
}
return false;
}
public void saveRequest() {
// remember the current URI
HttpSession session = myRequest.getSession();
synchronized (session) {
// But only if it is not set already, or we save every uri that leads to a login form redirect
if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
StringBuffer buf = myRequest.getRequestURL();
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
session.setAttribute(JettyHttpFacade.__J_METHOD, myRequest.getMethod());
if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
MultiMap<String> formParameters = extractFormParameters(myRequest);
MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
for (String key : formParameters.keySet()) {
for (Object value : formParameters.getValues(key)) {
map.add(key, (String) value);
}
}
session.setAttribute(CACHED_FORM_PARAMETERS, map);
}
}
}
}
}
@@ -1,69 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.jetty;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.UserIdentity;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
import org.keycloak.adapters.jetty.core.JettyRequestAuthenticator;
import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import javax.servlet.ServletRequest;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticator {
public KeycloakJettyAuthenticator() {
super();
}
@Override
protected Request resolveRequest(ServletRequest req) {
return Request.getBaseRequest(req);
}
@Override
protected Authentication createAuthentication(UserIdentity userIdentity, final Request request) {
return new KeycloakAuthentication(getAuthMethod(), userIdentity) {
@Override
public void logout() {
logoutCurrent(request);
}
};
}
@Override
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override
protected JettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade,
KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
return new Jetty93RequestAuthenticator(facade, deployment, tokenStore, -1, request);
}
}
+2 -3
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak Jetty Integration</name>
@@ -33,8 +33,7 @@
<modules>
<module>jetty-core</module>
<module>jetty8.1</module>
<module>jetty9.1</module>
<module>jetty9.2</module>
<module>jetty9.3</module>
<module>jetty9.1</module>
</modules>
</project>
+1 -20
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -58,25 +58,6 @@
<goal>minify</goal>
</goals>
</execution>
<execution>
<id>min-authz-js</id>
<phase>compile</phase>
<configuration>
<charset>utf-8</charset>
<webappSourceDir>${basedir}/src/main/resources</webappSourceDir>
<jsSourceDir>.</jsSourceDir>
<jsSourceFiles>
<jsSourceFile>keycloak-authz.js</jsSourceFile>
</jsSourceFiles>
<webappTargetDir>${project.build.directory}/classes</webappTargetDir>
<jsTargetDir>.</jsTargetDir>
<jsFinalFile>keycloak-authz.js</jsFinalFile>
</configuration>
<goals>
<goal>minify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
@@ -1,170 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
(function( window, undefined ) {
var KeycloakAuthorization = function (keycloak) {
var _instance = this;
this.rpt = null;
this.init = function () {
var request = new XMLHttpRequest();
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma-configuration');
request.onreadystatechange = function () {
if (request.readyState == 4) {
if (request.status == 200) {
_instance.config = JSON.parse(request.responseText);
} else {
console.error('Could not obtain configuration from server.');
}
}
}
request.send(null);
};
/**
* This method enables client applications to better integrate with resource servers protected by a Keycloak
* policy enforcer.
*
* In this case, the resource server will respond with a 401 status code and a WWW-Authenticate header holding the
* necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol,
* depending on how the policy enforcer at the resource server was configured.
*/
this.authorize = function (wwwAuthenticateHeader) {
this.then = function (onGrant, onDeny, onError) {
if (wwwAuthenticateHeader.indexOf('UMA') != -1) {
var params = wwwAuthenticateHeader.split(',');
for (i = 0; i < params.length; i++) {
var param = params[i].split('=');
if (param[0] == 'ticket') {
var request = new XMLHttpRequest();
request.open('POST', _instance.config.rpt_endpoint, true);
request.setRequestHeader('Content-Type', 'application/json')
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
request.onreadystatechange = function () {
if (request.readyState == 4) {
var status = request.status;
if (status >= 200 && status < 300) {
var rpt = JSON.parse(request.responseText).rpt;
_instance.rpt = rpt;
onGrant(rpt);
} else if (status == 403) {
if (onDeny) {
onDeny();
} else {
console.error('Authorization request was denied by the server.');
}
} else {
if (onError) {
onError();
} else {
console.error('Could not obtain authorization data from server.');
}
}
}
};
var ticket = param[1].substring(1, param[1].length - 1).trim();
request.send(JSON.stringify(
{
ticket: ticket,
rpt: _instance.rpt
}
));
}
}
} else if (wwwAuthenticateHeader.indexOf('KC_ETT') != -1) {
var params = wwwAuthenticateHeader.substring('KC_ETT'.length).trim().split(',');
var clientId = null;
for (i = 0; i < params.length; i++) {
var param = params[i].split('=');
if (param[0] == 'realm') {
clientId = param[1].substring(1, param[1].length - 1).trim();
}
}
_instance.entitlement(clientId).then(onGrant, onDeny, onError);
}
};
/**
* Obtains all entitlements from a Keycloak Server based on a give resourceServerId.
*/
this.entitlement = function (resourceSeververId) {
this.then = function (onGrant, onDeny, onError) {
var request = new XMLHttpRequest();
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
request.onreadystatechange = function () {
if (request.readyState == 4) {
var status = request.status;
if (status >= 200 && status < 300) {
var rpt = JSON.parse(request.responseText).rpt;
_instance.rpt = rpt;
onGrant(rpt);
} else if (status == 403) {
if (onDeny) {
onDeny();
} else {
console.error('Authorization request was denied by the server.');
}
} else {
if (onError) {
onError();
} else {
console.error('Could not obtain authorization data from server.');
}
}
}
};
request.send(null);
};
return this;
};
return this;
};
this.init(this);
};
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
module.exports = KeycloakAuthorization;
} else {
window.KeycloakAuthorization = KeycloakAuthorization;
if ( typeof define === "function" && define.amd ) {
define( "keycloak-authorization", [], function () { return KeycloakAuthorization; } );
}
}
})( window );
+41 -186
View File
@@ -25,7 +25,6 @@
var kc = this;
var adapter;
var refreshQueue = [];
var callbackStorage;
var loginIframe = {
enable: true,
@@ -36,18 +35,10 @@
kc.init = function (initOptions) {
kc.authenticated = false;
callbackStorage = createCallbackStorage();
if (initOptions && initOptions.adapter === 'cordova') {
if (window.Cordova) {
adapter = loadAdapter('cordova');
} else if (initOptions && initOptions.adapter === 'default') {
adapter = loadAdapter();
} else {
if (window.Cordova) {
adapter = loadAdapter('cordova');
} else {
adapter = loadAdapter();
}
adapter = loadAdapter();
}
if (initOptions) {
@@ -103,8 +94,8 @@
initPromise.promise.success(function() {
kc.onReady && kc.onReady(kc.authenticated);
promise.setSuccess(kc.authenticated);
}).error(function(errorData) {
promise.setError(errorData);
}).error(function() {
promise.setError();
});
var configPromise = loadConfig(config);
@@ -172,8 +163,6 @@
}
} else if (initOptions.onLoad) {
onLoad();
} else {
initPromise.setSuccess();
}
} else {
initPromise.setSuccess();
@@ -201,15 +190,13 @@
redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'prompt=' + options.prompt;
}
callbackStorage.add({ state: state, nonce: nonce, redirectUri: encodeURIComponent(redirectUri) });
sessionStorage.oauthState = JSON.stringify({ state: state, nonce: nonce, redirectUri: encodeURIComponent(redirectUri) });
var action = 'auth';
if (options && options.action == 'register') {
action = 'registrations';
}
var scope = (options && options.scope) ? "openid " + options.scope : "openid";
var url = getRealmUrl()
+ '/protocol/openid-connect/' + action
+ '?client_id=' + encodeURIComponent(kc.clientId)
@@ -217,17 +204,12 @@
+ '&state=' + encodeURIComponent(state)
+ '&nonce=' + encodeURIComponent(nonce)
+ '&response_mode=' + encodeURIComponent(kc.responseMode)
+ '&response_type=' + encodeURIComponent(kc.responseType)
+ '&scope=' + encodeURIComponent(scope);
+ '&response_type=' + encodeURIComponent(kc.responseType);
if (options && options.prompt) {
url += '&prompt=' + encodeURIComponent(options.prompt);
}
if (options && options.maxAge) {
url += '&max_age=' + encodeURIComponent(options.maxAge);
}
if (options && options.loginHint) {
url += '&login_hint=' + encodeURIComponent(options.loginHint);
}
@@ -236,6 +218,10 @@
url += '&kc_idp_hint=' + encodeURIComponent(options.idpHint);
}
if (options && options.scope) {
url += '&scope=' + encodeURIComponent(options.scope);
}
if (options && options.locale) {
url += '&ui_locales=' + encodeURIComponent(options.locale);
}
@@ -250,7 +236,7 @@
kc.createLogoutUrl = function(options) {
var url = getRealmUrl()
+ '/protocol/openid-connect/logout'
+ '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
+ '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
return url;
}
@@ -466,9 +452,8 @@
if (error) {
if (prompt != 'none') {
var errorData = { error: error, error_description: oauth.error_description };
kc.onAuthError && kc.onAuthError(errorData);
promise && promise.setError(errorData);
kc.onAuthError && kc.onAuthError();
promise && promise.setError();
} else {
promise && promise.setSuccess();
}
@@ -701,11 +686,14 @@
function parseCallback(url) {
var oauth = new CallbackParser(url, kc.responseMode).parseUri();
var oauthState = callbackStorage.get(oauth.state);
if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
oauth.redirectUri = oauthState.redirectUri;
oauth.storedNonce = oauthState.nonce;
var sessionState = sessionStorage.oauthState && JSON.parse(sessionStorage.oauthState);
if (sessionState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token) && oauth.state && oauth.state == sessionState.state) {
delete sessionStorage.oauthState;
oauth.redirectUri = sessionState.redirectUri;
oauth.storedNonce = sessionState.nonce;
if (oauth.fragment) {
oauth.newUrl += '#' + oauth.fragment;
@@ -792,22 +780,8 @@
if (event.origin !== loginIframe.iframeOrigin) {
return;
}
try {
var data = JSON.parse(event.data);
} catch (err) {
return;
}
if (!data.callbackId) {
return;
}
var data = JSON.parse(event.data);
var promise = loginIframe.callbackMap[data.callbackId];
if (!promise) {
return;
}
delete loginIframe.callbackMap[data.callbackId];
if ((!kc.sessionId || kc.sessionId == data.session) && data.loggedIn) {
@@ -868,18 +842,14 @@
return createPromise().promise;
},
redirectUri: function(options, encodeHash) {
if (arguments.length == 1) {
encodeHash = true;
}
redirectUri: function(options) {
if (options && options.redirectUri) {
return options.redirectUri;
} else if (kc.redirectUri) {
return kc.redirectUri;
} else {
var redirectUri = location.href;
if (location.hash && encodeHash) {
if (location.hash) {
redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
}
@@ -904,28 +874,31 @@
var loginUrl = kc.createLoginUrl(options);
var ref = window.open(loginUrl, '_blank', o);
var completed = false;
var callback;
var error;
ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) {
var callback = parseCallback(event.url);
processCallback(callback, promise);
callback = parseCallback(event.url);
ref.close();
completed = true;
}
});
ref.addEventListener('loaderror', function(event) {
if (!completed) {
if (event.url.indexOf('http://localhost') == 0) {
var callback = parseCallback(event.url);
processCallback(callback, promise);
ref.close();
completed = true;
} else {
promise.setError();
ref.close();
}
if (event.url.indexOf('http://localhost') == 0) {
callback = parseCallback(event.url);
ref.close();
} else {
error = true;
ref.close();
}
});
ref.addEventListener('exit', function(event) {
if (error || !callback) {
promise.setError();
} else {
processCallback(callback, promise);
}
});
@@ -996,124 +969,6 @@
throw 'invalid adapter type: ' + type;
}
var LocalStorage = function() {
if (!(this instanceof LocalStorage)) {
return new LocalStorage();
}
localStorage.setItem('kc-test', 'test');
localStorage.removeItem('kc-test');
var cs = this;
function clearExpired() {
var time = new Date().getTime();
for (var i = 1; i <= localStorage.length; i++) {
var key = localStorage.key(i);
if (key && key.indexOf('kc-callback-') == 0) {
var value = localStorage.getItem(key);
if (value) {
try {
var expires = JSON.parse(value).expires;
if (!expires || expires < time) {
localStorage.removeItem(key);
}
} catch (err) {
localStorage.removeItem(key);
}
}
}
}
}
cs.get = function(state) {
if (!state) {
return;
}
var key = 'kc-callback-' + state;
var value = localStorage.getItem(key);
if (value) {
localStorage.removeItem(key);
value = JSON.parse(value);
}
clearExpired();
return value;
};
cs.add = function(state) {
clearExpired();
var key = 'kc-callback-' + state.state;
state.expires = new Date().getTime() + (60 * 60 * 1000);
localStorage.setItem(key, JSON.stringify(state));
};
};
var CookieStorage = function() {
if (!(this instanceof CookieStorage)) {
return new CookieStorage();
}
var cs = this;
cs.get = function(state) {
if (!state) {
return;
}
var value = getCookie('kc-callback-' + state);
setCookie('kc-callback-' + state, '', cookieExpiration(-100));
if (value) {
return JSON.parse(value);
}
};
cs.add = function(state) {
setCookie('kc-callback-' + state.state, JSON.stringify(state), cookieExpiration(60));
};
cs.removeItem = function(key) {
setCookie(key, '', cookieExpiration(-100));
};
var cookieExpiration = function (minutes) {
var exp = new Date();
exp.setTime(exp.getTime() + (minutes*60*1000));
return exp;
};
var getCookie = function (key) {
var name = key + '=';
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return '';
};
var setCookie = function (key, value, expirationDate) {
var cookie = key + '=' + value + '; '
+ 'expires=' + expirationDate.toUTCString() + '; ';
document.cookie = cookie;
}
};
function createCallbackStorage() {
try {
return new LocalStorage();
} catch (err) {
}
return new CookieStorage();
}
var CallbackParser = function(uriToParse, responseMode) {
if (!(this instanceof CallbackParser)) {
@@ -1159,7 +1014,7 @@
}
var handleQueryParam = function(paramName, paramValue, oauth) {
var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
var supportedOAuthParams = [ 'code', 'error', 'state' ];
for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
if (paramName === supportedOAuthParams[i]) {
+2 -30
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -36,32 +36,15 @@
org.keycloak.adapters.osgi.*
</keycloak.osgi.export>
<keycloak.osgi.import>
org.ops4j.pax.web.*;version="[3.0,5)",
org.ops4j.pax.web.*;version="[3.0,4)",
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.eclipse.jetty.*;version="[8.1,10)";resolution:=optional,
org.keycloak.*;version="${project.version}",
org.apache.cxf.transport.http;resolution:=optional;version="[3,4)",
org.apache.cxf.transport.servlet;resolution:=optional;version="[3,4)",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
@@ -82,23 +65,12 @@
<artifactId>pax-web-runtime</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
@@ -1,71 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.osgi;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<String, KeycloakDeployment>();
@Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
String path = request.getURI();
String[] urlTokens = path.split("/");
if (urlTokens.length < 4) {
throw new IllegalStateException("Not able to determine the web-context to load the correspondent keycloak.json file");
}
String webContext = urlTokens[3];
KeycloakDeployment deployment = cache.get(webContext);
if (null == deployment) {
// not found on the simple cache, try to load it from the file system
String keycloakConfig = (String) System.getProperties().get("keycloak.config");
if(keycloakConfig == null || "".equals(keycloakConfig.trim())){
String karafEtc = (String) System.getProperties().get("karaf.etc");
if(karafEtc == null || "".equals(karafEtc.trim())){
throw new IllegalStateException("Neither \"keycloak.config\" nor \"karaf.etc\" java properties are set. Please set one of them.");
}
keycloakConfig = karafEtc;
}
String absolutePath = keycloakConfig + File.separator + webContext + "-keycloak.json";
InputStream is = null;
try {
is = new FileInputStream(absolutePath);
} catch (FileNotFoundException e){
throw new IllegalStateException("Not able to find the file " + absolutePath);
}
deployment = KeycloakDeploymentBuilder.build(is);
cache.put(webContext, deployment);
}
return deployment;
}
}
@@ -18,22 +18,15 @@
package org.keycloak.adapters.osgi;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;
import javax.servlet.Servlet;
import org.apache.cxf.transport.http.DestinationRegistry;
import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
import org.jboss.logging.Logger;
import org.ops4j.pax.web.service.WebContainer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.http.HttpContext;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
@@ -49,15 +42,13 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
*/
public class ServletReregistrationService {
private static final String CXF_SERVLET_PREFIX = "org.apache.cxf.servlet.";
protected static final Logger log = Logger.getLogger(ServletReregistrationService.class);
private static final List<String> FILTERED_PROPERTIES = Arrays.asList("objectClass", "service.id");
private BundleContext bundleContext;
private ServiceReference managedServiceReference;
private ServiceReference servletReference;
private ServiceTracker webContainerTracker;
private String alias;
public BundleContext getBundleContext() {
return bundleContext;
@@ -67,18 +58,24 @@ public class ServletReregistrationService {
this.bundleContext = bundleContext;
}
public ServiceReference getServletReference() {
return servletReference;
}
public void setServletReference(ServiceReference servletReference) {
this.servletReference = servletReference;
}
protected ServiceTracker getWebContainerTracker() {
return webContainerTracker;
}
public void start() {
if ( managedServiceReference == null) {
return;
}
Dictionary properties = obtainProperties();
alias = (String)getProp(properties, CXF_SERVLET_PREFIX + "context", "/cxf");
if(alias == null){
alias = "/cxf";
if (servletReference == null) {
throw new IllegalStateException("No servlet reference provided");
}
final Servlet servlet = (Servlet) bundleContext.getService(servletReference);
WebContainer externalWebContainer = findExternalWebContainer();
if (externalWebContainer == null) {
return;
@@ -86,19 +83,18 @@ public class ServletReregistrationService {
// Unregister servlet from external container now
try {
externalWebContainer.unregister(alias);
externalWebContainer.unregisterServlet(servlet);
log.debug("Original servlet with alias " + getAlias() + " unregistered successfully from external web container.");
} catch (IllegalStateException e) {
log.warn("Can't unregister servlet due to: " + e.getMessage());
}
final Dictionary finalProperties = properties;
ServiceTrackerCustomizer trackerCustomizer = new ServiceTrackerCustomizer() {
@Override
public Object addingService(ServiceReference webContainerServiceReference) {
WebContainer ourWebContainer = (WebContainer) bundleContext.getService(webContainerServiceReference);
registerServlet(ourWebContainer, finalProperties);
registerServlet(ourWebContainer, servlet);
log.debugv("Servlet with alias " + getAlias() + " registered to secured web container");
return ourWebContainer;
}
@@ -126,82 +122,28 @@ public class ServletReregistrationService {
// Re-register servlet back to original context
WebContainer externalWebContainer = findExternalWebContainer();
registerServlet(externalWebContainer, obtainProperties());
Servlet servlet = (Servlet) bundleContext.getService(servletReference);
registerServlet(externalWebContainer, servlet);
log.debug("Servlet with alias " + getAlias() + " registered back to external web container");
}
private String getAlias() {
return alias;
return (String) servletReference.getProperty("alias");
}
/**
* Code comes from org.apache.cxf.transport.http.osgi.ServletExporter#updated(java.util.Dictionary)
* @param webContainer
* @param properties
*/
protected void registerServlet(WebContainer webContainer, Dictionary properties) {
HttpContext httpContext = webContainer.createDefaultHttpContext();
ServiceReference destinationServiceServiceReference = bundleContext.getServiceReference("org.apache.cxf.transport.http.DestinationRegistry");
DestinationRegistry destinationRegistry = (DestinationRegistry) bundleContext.getService(destinationServiceServiceReference);
Servlet servlet = new CXFNonSpringServlet(destinationRegistry, false);
protected void registerServlet(WebContainer webContainer, Servlet servlet) {
try {
if (properties == null) {
properties = new Properties();
}
Properties sprops = new Properties();
sprops.put("init-prefix",
getProp(properties, CXF_SERVLET_PREFIX + "init-prefix", ""));
sprops.put("servlet-name",
getProp(properties, CXF_SERVLET_PREFIX + "name", "cxf-osgi-transport-servlet"));
sprops.put("hide-service-list-page",
getProp(properties, CXF_SERVLET_PREFIX + "hide-service-list-page", "false"));
sprops.put("disable-address-updates",
getProp(properties, CXF_SERVLET_PREFIX + "disable-address-updates", "true"));
sprops.put("base-address",
getProp(properties, CXF_SERVLET_PREFIX + "base-address", ""));
sprops.put("service-list-path",
getProp(properties, CXF_SERVLET_PREFIX + "service-list-path", ""));
sprops.put("static-resources-list",
getProp(properties, CXF_SERVLET_PREFIX + "static-resources-list", ""));
sprops.put("redirects-list",
getProp(properties, CXF_SERVLET_PREFIX + "redirects-list", ""));
sprops.put("redirect-servlet-name",
getProp(properties, CXF_SERVLET_PREFIX + "redirect-servlet-name", ""));
sprops.put("redirect-servlet-path",
getProp(properties, CXF_SERVLET_PREFIX + "redirect-servlet-path", ""));
sprops.put("service-list-all-contexts",
getProp(properties, CXF_SERVLET_PREFIX + "service-list-all-contexts", ""));
sprops.put("service-list-page-authenticate",
getProp(properties, CXF_SERVLET_PREFIX + "service-list-page-authenticate", "false"));
sprops.put("service-list-page-authenticate-realm",
getProp(properties, CXF_SERVLET_PREFIX + "service-list-page-authenticate-realm", "karaf"));
sprops.put("use-x-forwarded-headers",
getProp(properties, CXF_SERVLET_PREFIX + "use-x-forwarded-headers", "false"));
// Accept extra properties by default, can be disabled if it is really needed
if (Boolean.valueOf(getProp(properties, CXF_SERVLET_PREFIX + "support.extra.properties", "true").toString())) {
Enumeration keys = properties.keys();
while (keys.hasMoreElements()) {
String nextKey = keys.nextElement().toString();
if (!nextKey.startsWith(CXF_SERVLET_PREFIX)) {
sprops.put(nextKey, properties.get(nextKey));
}
}
}
Hashtable<String, Object> servletInitParams = new Hashtable<String, Object>();
Enumeration keys = sprops.keys();
while(keys.hasMoreElements()){
String propName = (String) keys.nextElement();
String[] propNames = servletReference.getPropertyKeys();
for (String propName : propNames) {
if (!FILTERED_PROPERTIES.contains(propName)) {
servletInitParams.put(propName, sprops.getProperty(propName));
servletInitParams.put(propName, servletReference.getProperty(propName));
}
}
// Try to register servlet in given web container now
HttpContext httpContext = webContainer.createDefaultHttpContext();
String alias = (String) servletReference.getProperty("alias");
webContainer.registerServlet(alias, servlet, servletInitParams, httpContext);
} catch (Exception e) {
log.error("Can't register servlet in web container", e);
@@ -214,7 +156,7 @@ public class ServletReregistrationService {
* @return web container or null
*/
protected WebContainer findExternalWebContainer() {
BundleContext servletBundleContext = managedServiceReference.getBundle().getBundleContext();
BundleContext servletBundleContext = servletReference.getBundle().getBundleContext();
ServiceReference webContainerReference = servletBundleContext.getServiceReference(WebContainer.class.getName());
if (webContainerReference == null) {
log.warn("Not found webContainer reference for bundle " + servletBundleContext);
@@ -224,32 +166,4 @@ public class ServletReregistrationService {
}
}
private Dictionary obtainProperties(){
Dictionary properties = null;
ServiceReference reference = bundleContext.getServiceReference(ConfigurationAdmin.class.getName());
ConfigurationAdmin admin = (ConfigurationAdmin) bundleContext.getService(reference);
try {
Configuration configuration = admin.getConfiguration("org.apache.cxf.osgi");
properties = configuration.getProperties();
} catch (Exception e){
log.warn("Unable to obtain cxf osgi configadmin reference.", e);
}
return properties;
}
private Object getProp(Dictionary properties, String key, Object defaultValue) {
Object value = null;
if(properties != null){
value = properties.get(key);
}
return value == null ? defaultValue : value;
}
public ServiceReference getManagedServiceReference() {
return managedServiceReference;
}
public void setManagedServiceReference(ServiceReference managedServiceReference) {
this.managedServiceReference = managedServiceReference;
}
}
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<name>Keycloak OIDC Client Adapter Modules</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -19,12 +19,12 @@ package org.keycloak.adapters.servlet;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OidcKeycloakAccount;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.adapters.spi.SessionIdMapper;
import javax.servlet.http.HttpServletRequest;
@@ -51,16 +51,10 @@ public class OIDCFilterSessionStore extends FilterSessionStore implements Adapte
}
public HttpServletRequestWrapper buildWrapper() {
HttpSession session = request.getSession(false);
KeycloakAccount account = null;
if (session != null) {
account = (KeycloakAccount) session.getAttribute(KeycloakAccount.class.getName());
if (account == null) {
account = (KeycloakAccount) request.getAttribute(KeycloakAccount.class.getName());
}
}
HttpSession session = request.getSession();
KeycloakAccount account = (KeycloakAccount)session.getAttribute(KeycloakAccount.class.getName());
if (account == null) {
account = (KeycloakAccount) request.getAttribute(KeycloakAccount.class.getName());
account = (KeycloakAccount)request.getAttribute(KeycloakAccount.class.getName());
}
return buildWrapper(session, account);
}
@@ -95,7 +89,6 @@ public class OIDCFilterSessionStore extends FilterSessionStore implements Adapte
protected void cleanSession(HttpSession session) {
session.removeAttribute(KeycloakAccount.class.getName());
session.removeAttribute(KeycloakSecurityContext.class.getName());
clearSavedRequest(session);
}
@@ -167,7 +160,6 @@ public class OIDCFilterSessionStore extends FilterSessionStore implements Adapte
SerializableKeycloakAccount sAccount = new SerializableKeycloakAccount(roles, account.getPrincipal(), securityContext);
HttpSession httpSession = request.getSession();
httpSession.setAttribute(KeycloakAccount.class.getName(), sAccount);
httpSession.setAttribute(KeycloakSecurityContext.class.getName(), sAccount.getKeycloakSecurityContext());
if (idMapper != null) idMapper.map(account.getKeycloakSecurityContext().getToken().getClientSession(), account.getPrincipal().getName(), httpSession.getId());
//String username = securityContext.getToken().getSubject();
//log.fine("userSessionManagement.login: " + username);
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,7 +30,6 @@ import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.util.TokenUtil;
import javax.security.cert.X509Certificate;
import javax.servlet.http.Cookie;
@@ -92,15 +91,15 @@ public class ServletOAuthClient extends KeycloakDeploymentDelegateOAuthClient {
String state = getStateCode();
KeycloakDeployment resolvedDeployment = resolveDeployment(getDeployment(), request);
String authUrl = resolvedDeployment.getAuthUrl().clone().build().toString();
String scopeParam = TokenUtil.attachOIDCScope(scope);
KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(authUrl)
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, getClientId())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.STATE, state)
.queryParam(OAuth2Constants.SCOPE, scopeParam);
.queryParam(OAuth2Constants.STATE, state);
if (scope != null) {
uriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
}
URI url = uriBuilder.build();
String stateCookiePath = this.stateCookiePath;
@@ -33,7 +33,7 @@ public class ServletOAuthClientBuilderTest {
public void testBuilder() {
ServletOAuthClient oauthClient = ServletOAuthClientBuilder.build(getClass().getResourceAsStream("/keycloak.json"));
Assert.assertEquals("https://localhost:8443/auth/realms/demo/protocol/openid-connect/auth", oauthClient.getDeployment().getAuthUrl().clone().build().toString());
Assert.assertEquals("https://localhost:8443/auth/realms/demo/protocol/openid-connect/token", oauthClient.getDeployment().getTokenUrl());
Assert.assertEquals("https://backend:8443/auth/realms/demo/protocol/openid-connect/token", oauthClient.getDeployment().getTokenUrl());
assertEquals(RelativeUrlsUsed.NEVER, oauthClient.getRelativeUrlsUsed());
Assert.assertEquals("customer-portal", oauthClient.getClientId());
Assert.assertEquals("234234-234234-234234", oauthClient.getCredentials().get(CredentialRepresentation.SECRET));
@@ -19,6 +19,7 @@
"connection-pool-size": 20,
"disable-trust-manager": true,
"allow-any-hostname": true,
"auth-server-url-for-backend-requests": "https://backend:8443/auth",
"always-refresh-token": true,
"register-node-at-startup": true,
"register-node-period": 1000,
+1 -40
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -67,45 +67,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -17,38 +17,24 @@
package org.keycloak.adapters.springboot;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.WebResourceCollection;
import org.apache.catalina.Context;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.webapp.WebAppContext;
import org.keycloak.adapters.jetty.KeycloakJettyAuthenticator;
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
import org.keycloak.adapters.undertow.KeycloakServletExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.undertow.UndertowDeploymentInfoCustomizer;
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
@@ -75,212 +61,80 @@ public class KeycloakSpringBootConfiguration {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
if (configurableEmbeddedServletContainer instanceof TomcatEmbeddedServletContainerFactory) {
TomcatEmbeddedServletContainerFactory container = (TomcatEmbeddedServletContainerFactory) configurableEmbeddedServletContainer;
container.addContextValves(new KeycloakAuthenticatorValve());
container.addContextCustomizers(tomcatKeycloakContextCustomizer());
container.addContextCustomizers(getTomcatKeycloakContextCustomizer());
} else if (configurableEmbeddedServletContainer instanceof UndertowEmbeddedServletContainerFactory) {
UndertowEmbeddedServletContainerFactory container = (UndertowEmbeddedServletContainerFactory) configurableEmbeddedServletContainer;
container.addDeploymentInfoCustomizers(undertowKeycloakContextCustomizer());
throw new IllegalArgumentException("Undertow Keycloak integration is not yet implemented");
} else if (configurableEmbeddedServletContainer instanceof JettyEmbeddedServletContainerFactory) {
JettyEmbeddedServletContainerFactory container = (JettyEmbeddedServletContainerFactory) configurableEmbeddedServletContainer;
container.addServerCustomizers(jettyKeycloakServerCustomizer());
throw new IllegalArgumentException("Jetty Keycloak integration is not yet implemented");
}
}
};
}
@Bean
@ConditionalOnClass(name = {"org.eclipse.jetty.webapp.WebAppContext"})
public JettyServerCustomizer jettyKeycloakServerCustomizer() {
return new KeycloakJettyServerCustomizer(keycloakProperties);
}
public TomcatContextCustomizer getTomcatKeycloakContextCustomizer() {
return new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
LoginConfig loginConfig = new LoginConfig();
loginConfig.setAuthMethod("KEYCLOAK");
context.setLoginConfig(loginConfig);
@Bean
@ConditionalOnClass(name = {"org.apache.catalina.startup.Tomcat"})
public TomcatContextCustomizer tomcatKeycloakContextCustomizer() {
return new KeycloakTomcatContextCustomizer(keycloakProperties);
}
@Bean
@ConditionalOnClass(name = {"io.undertow.Undertow"})
public UndertowDeploymentInfoCustomizer undertowKeycloakContextCustomizer() {
return new KeycloakUndertowDeploymentInfoCustomizer(keycloakProperties);
}
static class KeycloakUndertowDeploymentInfoCustomizer implements UndertowDeploymentInfoCustomizer {
private final KeycloakSpringBootProperties keycloakProperties;
public KeycloakUndertowDeploymentInfoCustomizer(KeycloakSpringBootProperties keycloakProperties) {
this.keycloakProperties = keycloakProperties;
}
@Override
public void customize(DeploymentInfo deploymentInfo) {
io.undertow.servlet.api.LoginConfig loginConfig = new io.undertow.servlet.api.LoginConfig(keycloakProperties.getRealm());
loginConfig.addFirstAuthMethod("KEYCLOAK");
deploymentInfo.setLoginConfig(loginConfig);
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
deploymentInfo.addSecurityConstraints(getSecurityConstraints());
deploymentInfo.addServletExtension(new KeycloakServletExtension());
}
private List<io.undertow.servlet.api.SecurityConstraint> getSecurityConstraints() {
List<io.undertow.servlet.api.SecurityConstraint> undertowSecurityConstraints = new ArrayList<io.undertow.servlet.api.SecurityConstraint>();
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) {
io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint();
undertowSecurityConstraint.addRolesAllowed(collectionDefinition.getAuthRoles());
WebResourceCollection webResourceCollection = new WebResourceCollection();
webResourceCollection.addHttpMethods(collectionDefinition.getMethods());
webResourceCollection.addHttpMethodOmissions(collectionDefinition.getOmittedMethods());
webResourceCollection.addUrlPatterns(collectionDefinition.getPatterns());
undertowSecurityConstraint.addWebResourceCollections(webResourceCollection);
undertowSecurityConstraints.add(undertowSecurityConstraint);
}
}
return undertowSecurityConstraints;
}
}
static class KeycloakJettyServerCustomizer implements JettyServerCustomizer {
private final KeycloakSpringBootProperties keycloakProperties;
public KeycloakJettyServerCustomizer(KeycloakSpringBootProperties keycloakProperties) {
this.keycloakProperties = keycloakProperties;
}
@Override
public void customize(Server server) {
KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator();
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver());
List<ConstraintMapping> jettyConstraintMappings = new ArrayList<ConstraintMapping>();
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
for (KeycloakSpringBootProperties.SecurityCollection securityCollectionDefinition : constraintDefinition
.getSecurityCollections()) {
Constraint jettyConstraint = new Constraint();
jettyConstraint.setName(securityCollectionDefinition.getName());
jettyConstraint.setAuthenticate(true);
if (securityCollectionDefinition.getName() != null) {
jettyConstraint.setName(securityCollectionDefinition.getName());
}
jettyConstraint.setRoles(securityCollectionDefinition.getAuthRoles().toArray(new String[0]));
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
if (securityCollectionDefinition.getPatterns().size() > 0) {
//First pattern wins
jettyConstraintMapping.setPathSpec(securityCollectionDefinition.getPatterns().get(0));
jettyConstraintMapping.setConstraint(jettyConstraint);
}
if (securityCollectionDefinition.getMethods().size() > 0) {
//First method wins
jettyConstraintMapping.setMethod(securityCollectionDefinition.getMethods().get(0));
}
jettyConstraintMapping.setMethodOmissions(
securityCollectionDefinition.getOmittedMethods().toArray(new String[0]));
jettyConstraintMappings.add(jettyConstraintMapping);
}
}
WebAppContext webAppContext = server.getBean(WebAppContext.class);
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
securityHandler.setConstraintMappings(jettyConstraintMappings);
securityHandler.setAuthenticator(keycloakJettyAuthenticator);
webAppContext.setHandler(securityHandler);
}
}
static class KeycloakTomcatContextCustomizer implements TomcatContextCustomizer {
private final KeycloakSpringBootProperties keycloakProperties;
public KeycloakTomcatContextCustomizer(KeycloakSpringBootProperties keycloakProperties) {
this.keycloakProperties = keycloakProperties;
}
@Override
public void customize(Context context) {
LoginConfig loginConfig = new LoginConfig();
loginConfig.setAuthMethod("KEYCLOAK");
context.setLoginConfig(loginConfig);
Set<String> authRoles = new HashSet<String>();
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
for (String authRole : collection.getAuthRoles()) {
if (!authRoles.contains(authRole)) {
context.addSecurityRole(authRole);
authRoles.add(authRole);
Set<String> authRoles = new HashSet<String>();
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
for (String authRole : collection.getAuthRoles()) {
if (!authRoles.contains(authRole)) {
context.addSecurityRole(authRole);
authRoles.add(authRole);
}
}
}
}
}
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
SecurityConstraint tomcatConstraint = new SecurityConstraint();
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
SecurityConstraint tomcatConstraint = new SecurityConstraint();
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
SecurityCollection tomcatSecCollection = new SecurityCollection();
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
SecurityCollection tomcatSecCollection = new SecurityCollection();
if (collection.getName() != null) {
tomcatSecCollection.setName(collection.getName());
}
if (collection.getDescription() != null) {
tomcatSecCollection.setDescription(collection.getDescription());
if (collection.getName() != null) {
tomcatSecCollection.setName(collection.getName());
}
if (collection.getDescription() != null) {
tomcatSecCollection.setDescription(collection.getDescription());
}
for (String authRole : collection.getAuthRoles()) {
tomcatConstraint.addAuthRole(authRole);
}
for (String pattern : collection.getPatterns()) {
tomcatSecCollection.addPattern(pattern);
}
for (String method : collection.getMethods()) {
tomcatSecCollection.addMethod(method);
}
for (String method : collection.getOmittedMethods()) {
tomcatSecCollection.addOmittedMethod(method);
}
tomcatConstraint.addCollection(tomcatSecCollection);
}
for (String authRole : collection.getAuthRoles()) {
tomcatConstraint.addAuthRole(authRole);
}
for (String pattern : collection.getPatterns()) {
tomcatSecCollection.addPattern(pattern);
}
for (String method : collection.getMethods()) {
tomcatSecCollection.addMethod(method);
}
for (String method : collection.getOmittedMethods()) {
tomcatSecCollection.addOmittedMethod(method);
}
tomcatConstraint.addCollection(tomcatSecCollection);
context.addConstraint(tomcatConstraint);
}
context.addConstraint(tomcatConstraint);
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
}
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
}
};
}
}
+10 -8
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -41,6 +41,7 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
@@ -49,6 +50,7 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
@@ -71,13 +73,6 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
@@ -87,6 +82,7 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bouncycastle.crypto.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
@@ -128,6 +124,12 @@
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak Tomcat Integration</name>
+4 -11
View File
@@ -19,10 +19,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -45,7 +45,7 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
@@ -77,13 +77,6 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<!-- Authorization -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-authz-client</artifactId>
</dependency>
<!--
<dependency>
<groupId>org.apache.tomcat</groupId>
@@ -69,22 +69,12 @@ public class CatalinaSessionTokenStore extends CatalinaAdapterSessionStore imple
// just in case session got serialized
if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) {
request.setAttribute(KeycloakSecurityContext.class.getName(), session);
request.setUserPrincipal(account.getPrincipal());
request.setAuthType("KEYCLOAK");
return;
}
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
// FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
// not be updated
boolean success = session.refreshExpiredToken(false);
if (success && session.isActive()) {
request.setAttribute(KeycloakSecurityContext.class.getName(), session);
request.setUserPrincipal(account.getPrincipal());
request.setAuthType("KEYCLOAK");
return;
}
if (success && session.isActive()) return;
// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
log.fine("Cleanup and expire session " + catalinaSession.getId() + " after failed refresh");
@@ -95,8 +85,6 @@ public class CatalinaSessionTokenStore extends CatalinaAdapterSessionStore imple
}
protected void cleanSession(Session catalinaSession) {
catalinaSession.getSession().removeAttribute(KeycloakSecurityContext.class.getName());
catalinaSession.getSession().removeAttribute(SerializableKeycloakAccount.class.getName());
catalinaSession.getSession().removeAttribute(OidcKeycloakAccount.class.getName());
catalinaSession.setPrincipal(null);
catalinaSession.setAuthType(null);
@@ -176,7 +164,6 @@ public class CatalinaSessionTokenStore extends CatalinaAdapterSessionStore imple
session.setPrincipal(principal);
session.setAuthType("KEYCLOAK");
session.getSession().setAttribute(SerializableKeycloakAccount.class.getName(), sAccount);
session.getSession().setAttribute(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
String username = securityContext.getToken().getSubject();
log.fine("userSessionManagement.login: " + username);
this.sessionManagement.login(session);
+4 -4
View File
@@ -19,10 +19,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -43,7 +43,7 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
+4 -4
View File
@@ -19,10 +19,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -44,7 +44,7 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
+4 -4
View File
@@ -19,10 +19,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -43,7 +43,7 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,16 +17,16 @@
package org.keycloak.adapters.undertow;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.AttachmentKey;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import static org.keycloak.adapters.undertow.OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OIDCServletUndertowHttpFacade extends ServletHttpFacade implements OIDCHttpFacade {
public static final AttachmentKey<KeycloakSecurityContext> KEYCLOAK_SECURITY_CONTEXT_KEY = AttachmentKey.create(KeycloakSecurityContext.class);
public OIDCServletUndertowHttpFacade(HttpServerExchange exchange) {
super(exchange);
@@ -92,8 +92,7 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
} else {
log.debug("Refresh failed. Account was not active. Returning null and invalidating Http session");
try {
session.removeAttribute(KeycloakUndertowAccount.class.getName());
session.removeAttribute(KeycloakSecurityContext.class.getName());
session.setAttribute(KeycloakUndertowAccount.class.getName(), null);
session.invalidate();
} catch (Exception e) {
log.debug("Failed to invalidate session, might already be invalidated");
@@ -107,7 +106,6 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
HttpSession session = getSession(true);
session.setAttribute(KeycloakUndertowAccount.class.getName(), account);
session.setAttribute(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
sessionManagement.login(servletRequestContext.getDeployment().getSessionManager());
}
@@ -22,7 +22,6 @@ import io.undertow.server.HttpServerExchange;
import io.undertow.server.session.Session;
import io.undertow.util.Sessions;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OidcKeycloakAccount;
@@ -83,7 +82,6 @@ public class UndertowSessionTokenStore implements AdapterTokenStore {
} else {
log.debug("Account was not active, returning false");
session.removeAttribute(KeycloakUndertowAccount.class.getName());
session.removeAttribute(KeycloakSecurityContext.class.getName());
session.invalidate(exchange);
return false;
}
@@ -103,7 +101,6 @@ public class UndertowSessionTokenStore implements AdapterTokenStore {
public void saveAccountInfo(OidcKeycloakAccount account) {
Session session = Sessions.getOrCreateSession(exchange);
session.setAttribute(KeycloakUndertowAccount.class.getName(), account);
session.setAttribute(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
sessionManagement.login(session.getSessionManager());
}
@@ -114,7 +111,6 @@ public class UndertowSessionTokenStore implements AdapterTokenStore {
KeycloakUndertowAccount account = (KeycloakUndertowAccount)session.getAttribute(KeycloakUndertowAccount.class.getName());
if (account == null) return;
session.removeAttribute(KeycloakUndertowAccount.class.getName());
session.removeAttribute(KeycloakSecurityContext.class.getName());
}
@Override
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak WildFly Integration</name>
+9 -3
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
@@ -79,15 +79,21 @@
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-annotations</artifactId>
<version>${jboss.logging.tools.wf8.version}</version>
<version>${jboss-logging-tools.version}</version>
<!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
projects that depend on this project.-->
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<version>${jboss.logging.tools.wf8.version}</version>
<version>${jboss-logging-tools.version}</version>
<!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
projects that depend on this project.-->
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@@ -46,7 +46,8 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
// not sure if we need this yet, keeping here just in case
protected void addSecurityDomain(DeploymentUnit deploymentUnit, KeycloakAdapterConfigService service) {
if (!service.isSecureDeployment(deploymentUnit)) {
String deploymentName = deploymentUnit.getName();
if (!service.isSecureDeployment(deploymentName)) {
return;
}
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
@@ -66,9 +67,10 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName();
KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance();
if (service.isSecureDeployment(deploymentUnit)) {
addKeycloakAuthData(phaseContext, service);
if (service.isSecureDeployment(deploymentName)) {
addKeycloakAuthData(phaseContext, deploymentName, service);
}
// FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
@@ -77,14 +79,14 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
// addSecurityDomain(deploymentUnit, service);
}
private void addKeycloakAuthData(DeploymentPhaseContext phaseContext, KeycloakAdapterConfigService service) throws DeploymentUnitProcessingException {
private void addKeycloakAuthData(DeploymentPhaseContext phaseContext, String deploymentName, KeycloakAdapterConfigService service) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
throw new DeploymentUnitProcessingException("WarMetaData not found for " + deploymentUnit.getName() + ". Make sure you have specified a WAR as your secure-deployment in the Keycloak subsystem.");
throw new DeploymentUnitProcessingException("WarMetaData not found for " + deploymentName + ". Make sure you have specified a WAR as your secure-deployment in the Keycloak subsystem.");
}
addJSONData(service.getJSON(deploymentUnit), warMetaData);
addJSONData(service.getJSON(deploymentName), warMetaData);
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
webMetaData = new JBossWebMetaData();
@@ -97,8 +99,8 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
webMetaData.setLoginConfig(loginConfig);
}
loginConfig.setAuthMethod("KEYCLOAK");
loginConfig.setRealmName(service.getRealmName(deploymentUnit));
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
loginConfig.setRealmName(service.getRealmName(deploymentName));
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName);
}
private void addJSONData(String json, WarMetaData warMetaData) {
@@ -24,9 +24,6 @@ import java.util.HashMap;
import java.util.Map;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
/**
* This service keeps track of the entire Keycloak management model so as to provide
@@ -156,15 +153,13 @@ public final class KeycloakAdapterConfigService {
return null;
}
public String getRealmName(DeploymentUnit deploymentUnit) {
String deploymentName = preferredDeploymentName(deploymentUnit);
public String getRealmName(String deploymentName) {
ModelNode deployment = this.secureDeployments.get(deploymentName);
return deployment.get(RealmDefinition.TAG_NAME).asString();
}
public String getJSON(DeploymentUnit deploymentUnit) {
String deploymentName = preferredDeploymentName(deploymentUnit);
public String getJSON(String deploymentName) {
ModelNode deployment = this.secureDeployments.get(deploymentName);
String realmName = deployment.get(RealmDefinition.TAG_NAME).asString();
ModelNode realm = this.realms.get(realmName);
@@ -188,29 +183,9 @@ public final class KeycloakAdapterConfigService {
}
}
public boolean isSecureDeployment(DeploymentUnit deploymentUnit) {
public boolean isSecureDeployment(String deploymentName) {
//log.info("********* CHECK KEYCLOAK DEPLOYMENT: deployments.size()" + deployments.size());
String deploymentName = preferredDeploymentName(deploymentUnit);
return this.secureDeployments.containsKey(deploymentName);
}
// KEYCLOAK-3273: prefer module name if available
private String preferredDeploymentName(DeploymentUnit deploymentUnit) {
String deploymentName = deploymentUnit.getName();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return deploymentName;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return deploymentName;
}
String moduleName = webMetaData.getModuleName();
if (moduleName != null) return moduleName + ".war";
return deploymentName;
}
}
@@ -46,11 +46,9 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
if (!KeycloakAdapterConfigService.getInstance().isSecureDeployment(deploymentUnit)) {
String deploymentName = deploymentUnit.getName();
if (!KeycloakAdapterConfigService.getInstance().isSecureDeployment(deploymentName)) {
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return;
@@ -78,6 +78,8 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
addCredential(addr, service, "secret", "secret1");
addCredential(addr, service, "jwt.client-keystore-file", "/tmp/foo.jks");
addCredential(addr, service, "jwt.token-timeout", "10");
System.out.println("Deployment: " + service.getJSON("foo"));
}
private void addCredential(PathAddress parent, KeycloakAdapterConfigService service, String key, String value) {
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -41,7 +41,7 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
@@ -46,7 +46,8 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
// not sure if we need this yet, keeping here just in case
protected void addSecurityDomain(DeploymentUnit deploymentUnit, KeycloakAdapterConfigService service) {
if (!service.isSecureDeployment(deploymentUnit)) {
String deploymentName = deploymentUnit.getName();
if (!service.isSecureDeployment(deploymentName)) {
return;
}
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
@@ -66,9 +67,10 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName();
KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance();
if (service.isSecureDeployment(deploymentUnit)) {
addKeycloakAuthData(phaseContext, service);
if (service.isSecureDeployment(deploymentName)) {
addKeycloakAuthData(phaseContext, deploymentName, service);
}
// FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
@@ -77,14 +79,14 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
// addSecurityDomain(deploymentUnit, service);
}
private void addKeycloakAuthData(DeploymentPhaseContext phaseContext, KeycloakAdapterConfigService service) throws DeploymentUnitProcessingException {
private void addKeycloakAuthData(DeploymentPhaseContext phaseContext, String deploymentName, KeycloakAdapterConfigService service) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
throw new DeploymentUnitProcessingException("WarMetaData not found for " + deploymentUnit.getName() + ". Make sure you have specified a WAR as your secure-deployment in the Keycloak subsystem.");
throw new DeploymentUnitProcessingException("WarMetaData not found for " + deploymentName + ". Make sure you have specified a WAR as your secure-deployment in the Keycloak subsystem.");
}
addJSONData(service.getJSON(deploymentUnit), warMetaData);
addJSONData(service.getJSON(deploymentName), warMetaData);
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
webMetaData = new JBossWebMetaData();
@@ -97,8 +99,8 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
webMetaData.setLoginConfig(loginConfig);
}
loginConfig.setAuthMethod("KEYCLOAK");
loginConfig.setRealmName(service.getRealmName(deploymentUnit));
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
loginConfig.setRealmName(service.getRealmName(deploymentName));
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName);
}
private void addJSONData(String json, WarMetaData warMetaData) {
@@ -24,9 +24,6 @@ import java.util.HashMap;
import java.util.Map;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
/**
* This service keeps track of the entire Keycloak management model so as to provide
@@ -156,15 +153,13 @@ public final class KeycloakAdapterConfigService {
return null;
}
public String getRealmName(DeploymentUnit deploymentUnit) {
String deploymentName = preferredDeploymentName(deploymentUnit);
public String getRealmName(String deploymentName) {
ModelNode deployment = this.secureDeployments.get(deploymentName);
return deployment.get(RealmDefinition.TAG_NAME).asString();
}
public String getJSON(DeploymentUnit deploymentUnit) {
String deploymentName = preferredDeploymentName(deploymentUnit);
public String getJSON(String deploymentName) {
ModelNode deployment = this.secureDeployments.get(deploymentName);
String realmName = deployment.get(RealmDefinition.TAG_NAME).asString();
ModelNode realm = this.realms.get(realmName);
@@ -188,29 +183,9 @@ public final class KeycloakAdapterConfigService {
}
}
public boolean isSecureDeployment(DeploymentUnit deploymentUnit) {
public boolean isSecureDeployment(String deploymentName) {
//log.info("********* CHECK KEYCLOAK DEPLOYMENT: deployments.size()" + deployments.size());
String deploymentName = preferredDeploymentName(deploymentUnit);
return this.secureDeployments.containsKey(deploymentName);
}
// KEYCLOAK-3273: prefer module name if available
private String preferredDeploymentName(DeploymentUnit deploymentUnit) {
String deploymentName = deploymentUnit.getName();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return deploymentName;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return deploymentName;
}
String moduleName = webMetaData.getModuleName();
if (moduleName != null) return moduleName + ".war";
return deploymentName;
}
}
@@ -45,11 +45,10 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
if (!KeycloakAdapterConfigService.getInstance().isSecureDeployment(deploymentUnit)) {
String deploymentName = deploymentUnit.getName();
if (!KeycloakAdapterConfigService.getInstance().isSecureDeployment(deploymentName)) {
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return;
@@ -65,7 +64,7 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
addCommonModules(moduleSpecification, moduleLoader);
addPlatformSpecificModules(moduleSpecification, moduleLoader);
}
private void addCommonModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
// ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
@@ -78,6 +78,8 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
addCredential(addr, service, "secret", "secret1");
addCredential(addr, service, "jwt.client-keystore-file", "/tmp/foo.jks");
addCredential(addr, service, "jwt.token-timeout", "10");
System.out.println("Deployment: " + service.getJSON("foo"));
}
private void addCredential(PathAddress parent, KeycloakAdapterConfigService service, String key, String value) {
@@ -16,30 +16,6 @@
-->
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
<realm name="master">
<realm-public-key>MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4siLKUew0WYxdtq6/rwk4Uj/4amGFFnE/yzIxQVU0PUqz3QBRVkUWpDj0K6ZnS5nzJV/y6DHLEy7hjZTdRDphyF1sq09aDOYnVpzu8o2sIlMM8q5RnUyEfIyUZqwo8pSZDJ90fS0s+IDUJNCSIrAKO3w1lqZDHL6E/YFHXyzkvQIDAQAB</realm-public-key>
<auth-server-url>http://localhost:8080/auth</auth-server-url>
<truststore>truststore.jks</truststore>
<truststore-password>secret</truststore-password>
<ssl-required>EXTERNAL</ssl-required>
<allow-any-hostname>false</allow-any-hostname>
<disable-trust-manager>true</disable-trust-manager>
<connection-pool-size>20</connection-pool-size>
<enable-cors>true</enable-cors>
<client-keystore>keys.jks</client-keystore>
<client-keystore-password>secret</client-keystore-password>
<client-key-password>secret</client-key-password>
<cors-max-age>600</cors-max-age>
<cors-allowed-headers>X-Custom</cors-allowed-headers>
<cors-allowed-methods>PUT,POST,DELETE,GET</cors-allowed-methods>
<expose-token>false</expose-token>
<auth-server-url-for-backend-requests>http://127.0.0.2:8080/auth</auth-server-url-for-backend-requests>
<always-refresh-token>false</always-refresh-token>
<register-node-at-startup>true</register-node-at-startup>
<register-node-period>60</register-node-period>
<token-store>session</token-store>
<principal-attribute>sub</principal-attribute>
</realm>
<secure-deployment name="web-console">
<realm>master</realm>
<resource>web-console</resource>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Keycloak Integration</name>
+3 -7
View File
@@ -19,10 +19,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -43,10 +43,6 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-api-public</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-core</artifactId>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.1.0.Final</version>
<version>1.9.0.CR1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak SAML EAP Integration</name>
+8 -8
View File
@@ -20,9 +20,9 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
<version>2.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
<artifactId>keycloak-parent</artifactId>
<version>1.9.0.CR1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<artifactId>keycloak-saml-as7-subsystem</artifactId>
@@ -64,25 +64,25 @@
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-naming</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-server</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-ee</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-web</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
@@ -104,7 +104,7 @@ projects that depend on this project.-->
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-controller</artifactId>
<version>${jboss.as.version}</version>
<version>${jboss.version}</version>
</dependency>
<dependency>
@@ -16,11 +16,8 @@
*/
package org.keycloak.subsystem.saml.as7;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.web.deployment.WarMetaData;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
@@ -49,8 +46,7 @@ public class Configuration {
return keymodel.get(key);
}
public ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) {
String name = preferredDeploymentName(deploymentUnit);
public ModelNode getSecureDeployment(String name) {
ModelNode secureDeployment = config.get("subsystem").get("keycloak-saml").get(Constants.Model.SECURE_DEPLOYMENT);
if (secureDeployment.hasDefined(name)) {
return secureDeployment.get(name);
@@ -58,26 +54,7 @@ public class Configuration {
return null;
}
public boolean isSecureDeployment(DeploymentUnit deploymentUnit) {
return getSecureDeployment(deploymentUnit) != null;
}
// KEYCLOAK-3273: prefer module name if available
private String preferredDeploymentName(DeploymentUnit deploymentUnit) {
String deploymentName = deploymentUnit.getName();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return deploymentName;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return deploymentName;
}
String moduleName = webMetaData.getModuleName();
if (moduleName != null) return moduleName + ".war";
return deploymentName;
public boolean isSecureDeployment(String name) {
return getSecureDeployment(name) != null;
}
}
@@ -45,7 +45,6 @@ public class Constants {
static final String KEY_STORE = "KeyStore";
static final String SIGN_REQUEST = "signRequest";
static final String VALIDATE_RESPONSE_SIGNATURE = "validateResponseSignature";
static final String VALIDATE_ASSERTION_SIGNATURE = "validateAssertionSignature";
static final String REQUEST_BINDING = "requestBinding";
static final String BINDING_URL = "bindingUrl";
static final String VALIDATE_REQUEST_SIGNATURE = "validateRequestSignature";
@@ -98,7 +97,6 @@ public class Constants {
static final String CERTIFICATE_ALIAS = "alias";
static final String SIGN_REQUEST = "signRequest";
static final String VALIDATE_RESPONSE_SIGNATURE = "validateResponseSignature";
static final String VALIDATE_ASSERTION_SIGNATURE = "validateAssertionSignature";
static final String REQUEST_BINDING = "requestBinding";
static final String BINDING_URL = "bindingUrl";
static final String VALIDATE_REQUEST_SIGNATURE = "validateRequestSignature";
@@ -51,6 +51,7 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
@@ -68,30 +69,30 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
try {
boolean webRequiresKC = loginConfig != null && "KEYCLOAK-SAML".equalsIgnoreCase(loginConfig.getAuthMethod());
boolean hasSubsystemConfig = Configuration.INSTANCE.isSecureDeployment(deploymentUnit);
boolean hasSubsystemConfig = Configuration.INSTANCE.isSecureDeployment(deploymentName);
if (hasSubsystemConfig || webRequiresKC) {
log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentUnit.getName());
log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentName);
// if secure-deployment configuration exists for web app, we force KEYCLOAK-SAML auth method on it
if (hasSubsystemConfig) {
addXMLData(getXML(deploymentUnit), warMetaData);
addXMLData(getXML(deploymentName), warMetaData);
if (loginConfig != null) {
loginConfig.setAuthMethod("KEYCLOAK-SAML");
//loginConfig.setRealmName(service.getRealmName(deploymentName));
} else {
log.warn("Failed to set up KEYCLOAK-SAML auth method for WAR: " + deploymentUnit.getName() + " (loginConfig == null)");
log.warn("Failed to set up KEYCLOAK-SAML auth method for WAR: " + deploymentName + " (loginConfig == null)");
}
}
addValve(webMetaData);
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName);
}
} catch (Exception e) {
throw new DeploymentUnitProcessingException("Failed to configure KeycloakSamlExtension from subsystem model", e);
}
}
private String getXML(DeploymentUnit deploymentUnit) throws XMLStreamException {
ModelNode node = Configuration.INSTANCE.getSecureDeployment(deploymentUnit);
private String getXML(String deploymentName) throws XMLStreamException {
ModelNode node = Configuration.INSTANCE.getSecureDeployment(deploymentName);
if (node != null) {
KeycloakSubsystemParser writer = new KeycloakSubsystemParser();
ByteArrayOutputStream output = new ByteArrayOutputStream();
@@ -39,18 +39,15 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
private static final ModuleIdentifier KEYCLOAK_JBOSS_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-jboss-adapter-core");
private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-saml-adapter-core");
private static final ModuleIdentifier KEYCLOAK_API_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-saml-adapter-api-public");
private static final ModuleIdentifier KEYCLOAK_COMMON = ModuleIdentifier.create("org.keycloak.keycloak-common");
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
if (Configuration.INSTANCE.getSecureDeployment(deploymentUnit) == null) {
String deploymentName = deploymentUnit.getName();
if (Configuration.INSTANCE.getSecureDeployment(deploymentName) == null) {
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return;
@@ -71,7 +68,6 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
// ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_API_ADAPTER, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
}
@@ -37,11 +37,6 @@ abstract class SingleSignOnDefinition {
.setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE)
.build();
static final SimpleAttributeDefinition VALIDATE_ASSERTION_SIGNATURE =
new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_ASSERTION_SIGNATURE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.VALIDATE_ASSERTION_SIGNATURE)
.build();
static final SimpleAttributeDefinition REQUEST_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.REQUEST_BINDING)
@@ -57,7 +52,7 @@ abstract class SingleSignOnDefinition {
.setXmlName(Constants.XML.BINDING_URL)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGN_REQUEST, VALIDATE_RESPONSE_SIGNATURE, VALIDATE_ASSERTION_SIGNATURE, REQUEST_BINDING, RESPONSE_BINDING, BINDING_URL};
static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGN_REQUEST, VALIDATE_RESPONSE_SIGNATURE, REQUEST_BINDING, RESPONSE_BINDING, BINDING_URL};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
@@ -67,7 +67,6 @@ keycloak-saml.IDP.signatureCanonicalizationMethod=Signature canonicalization met
keycloak-saml.IDP.SingleSignOnService=Single sign-on configuration
keycloak-saml.IDP.SingleSignOnService.signRequest=Sign SSO requests
keycloak-saml.IDP.SingleSignOnService.validateResponseSignature=Validate an SSO response signature
keycloak-saml.IDP.SingleSignOnService.validateAssertionSignature=Validate an SSO assertion signature
keycloak-saml.IDP.SingleSignOnService.requestBinding=HTTP method to use for requests
keycloak-saml.IDP.SingleSignOnService.responseBinding=HTTP method to use for responses
keycloak-saml.IDP.SingleSignOnService.bindingUrl=SSO endpoint URL
Loaded 100 of 3131 files, more files were not shown because too many files have changed in this diff. Show more