Compare commits

..
Author SHA1 Message Date
keycloak-bot 5f0ff23332 Set version to 4.0.0.Final 2018-06-13 18:53:04 +00:00
2355 changed files with 43224 additions and 67678 deletions

No files matched your search

+1 -4
View File
@@ -8,9 +8,6 @@
.project
.settings
.classpath
bin/
.factorypath
# NetBeans #
############
@@ -57,4 +54,4 @@ target
##########
# KEYCLOAK-5391: We will re-exclude node_modules when node_modules handling is worked out.
# For now, we keep our js libraries checked into GitHub, so we don't ignore.
#node_modules
#node_modules
+2 -6
View File
@@ -1,4 +1,5 @@
language: java
dist: precise
cache:
cache: false
@@ -14,9 +15,7 @@ env:
- TESTS=server-group3
- TESTS=server-group4
- TESTS=old
- TESTS=crossdc-server
- TESTS=crossdc-adapter
- TESTS=ssl
- TESTS=crossdc
jdk:
- oraclejdk8
@@ -36,7 +35,4 @@ before_install:
script:
- ./travis-run-tests.sh $TESTS
after_success:
- .travis/docker-hub-master-trigger.sh
sudo: false
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
if [ "$DOCKER_HUB_TOKEN" != "" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_REPO_SLUG" == "keycloak/keycloak" ] && [ "$TRAVIS_BRANCH" == "master" ]; then
curl -H "Content-Type: application/json" --data '{"docker_tag": "master"}' -X POST https://registry.hub.docker.com/u/jboss/keycloak/trigger/$DOCKER_HUB_TOKEN/
echo "Triggered Docker hub build"
elif [ "$DOCKER_HUB_TOKEN" != "" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_REPO_SLUG" == "keycloak/keycloak" ] && [ "$TRAVIS_BRANCH" == "openshift-integration2" ]; then
curl -H "Content-Type: application/json" --data '{"docker_tag": "nightly-openshift-integration2"}' -X POST https://registry.hub.docker.com/u/jboss/keycloak/trigger/$DOCKER_HUB_TOKEN/
echo "Triggered Docker hub build"
fi
-5
View File
@@ -32,10 +32,6 @@ To build the distribution run:
Once completed you will find distribution archives in `distribution`.
To build only the server run:
mvn -Pdistribution -pl distribution/server-dist -am -Dmaven.test.skip clean install
Starting Keycloak
-----------------
@@ -44,7 +40,6 @@ To start Keycloak during development first build as specified above, then run:
mvn -f testsuite/utils/pom.xml exec:java -Pkeycloak-server
When running testsuite, by default an account with username `admin` and password `admin` will be created within the master realm at start.
To start Keycloak from the server distribution first build the distribution it as specified above, then run:
+2 -11
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -36,16 +36,7 @@
</keycloak.osgi.export>
<keycloak.osgi.import>
org.keycloak.*;version="${project.version}",
org.apache.http.auth.*;version=${apache.httpcomponents.version},
org.apache.http.client.*;version=${apache.httpcomponents.version},
org.apache.http.conn.*;version=${apache.httpcomponents.version},
org.apache.http.cookie.*;version=${apache.httpcomponents.version},
org.apache.http.impl.auth.*;version=${apache.httpcomponents.version},
org.apache.http.impl.client.*;version=${apache.httpcomponents.version},
org.apache.http.impl.conn.*;version=${apache.httpcomponents.version},
org.apache.http.impl.cookie.*;version=${apache.httpcomponents.version},
org.apache.http.impl.execchain.*;version=${apache.httpcomponents.version},
org.apache.http.*;version=${apache.httpcomponents.httpcore.version},
org.apache.http.*;version=${apache.httpcomponents.version},
org.apache.karaf.jaas.boot.principal;resolution:=optional,
org.apache.karaf.jaas.modules;resolution:=optional,
*;resolution:=optional
@@ -482,16 +482,6 @@ public class AdapterDeploymentContext {
public void setPublicKeyCacheTtl(int publicKeyCacheTtl) {
delegate.setPublicKeyCacheTtl(publicKeyCacheTtl);
}
@Override
public boolean isVerifyTokenAudience() {
return delegate.isVerifyTokenAudience();
}
@Override
public void setVerifyTokenAudience(boolean verifyTokenAudience) {
delegate.setVerifyTokenAudience(verifyTokenAudience);
}
}
protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {
@@ -106,10 +106,7 @@ public class AuthenticatedActionsHandler {
if (deployment.getPolicyEnforcer() != null) {
if (exposeHeaders != null) {
exposeHeaders += ",";
} else {
exposeHeaders = "";
}
exposeHeaders += "WWW-Authenticate";
}
@@ -85,7 +85,7 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
return authenticateToken(exchange, atr.getToken());
}
protected AccessTokenResponse getToken(String username, String password) throws Exception {
private AccessTokenResponse getToken(String username, String password) throws Exception {
AccessTokenResponse tokenResponse=null;
HttpClient client = deployment.getClient();
@@ -18,7 +18,7 @@
package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
@@ -72,13 +72,8 @@ public class BearerTokenRequestAuthenticator {
for (String authHeader : authHeaders) {
String[] split = authHeader.trim().split("\\s+");
if (split == null || split.length != 2) continue;
if (split[0].equalsIgnoreCase("Bearer")) {
tokenString = split[1];
if (log.isDebugEnabled()) {
log.debugf("Found [%s] values in authorization header, selecting the first value for Bearer.", authHeaders.size());
}
break;
};
if (!split[0].equalsIgnoreCase("Bearer")) continue;
tokenString = split[1];
}
if (tokenString == null) {
@@ -101,7 +96,7 @@ public class BearerTokenRequestAuthenticator {
}
}
try {
token = AdapterTokenVerifier.verifyToken(tokenString, deployment);
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
} catch (VerificationException e) {
log.error("Failed to verify token", e);
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage());
@@ -19,8 +19,7 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.TokenVerifier;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.KeycloakUriBuilder;
@@ -72,11 +71,7 @@ public class CookieTokenStore {
try {
// Skip check if token is active now. It's supposed to be done later by the caller
TokenVerifier<AccessToken> tokenVerifier = AdapterTokenVerifier.createVerifier(accessTokenString, deployment, true, AccessToken.class)
.checkActive(false)
.verify();
AccessToken accessToken = tokenVerifier.getToken();
AccessToken accessToken = AdapterRSATokenVerifier.verifyToken(accessTokenString, deployment, false, true);
IDToken idToken;
if (idTokenString != null && idTokenString.length() > 0) {
try {
@@ -92,11 +92,10 @@ public class KeycloakDeployment {
// https://tools.ietf.org/html/rfc7636
protected boolean pkce = false;
protected boolean ignoreOAuthQueryParameter;
protected Map<String, String> redirectRewriteRules;
protected boolean delegateBearerErrorResponseSending = false;
protected boolean verifyTokenAudience = false;
public KeycloakDeployment() {
}
@@ -478,12 +477,4 @@ public class KeycloakDeployment {
public void setDelegateBearerErrorResponseSending(boolean delegateBearerErrorResponseSending) {
this.delegateBearerErrorResponseSending = delegateBearerErrorResponseSending;
}
public boolean isVerifyTokenAudience() {
return verifyTokenAudience;
}
public void setVerifyTokenAudience(boolean verifyTokenAudience) {
this.verifyTokenAudience = verifyTokenAudience;
}
}
@@ -122,7 +122,6 @@ public class KeycloakDeploymentBuilder {
deployment.setPublicKeyCacheTtl(adapterConfig.getPublicKeyCacheTtl());
deployment.setIgnoreOAuthQueryParameter(adapterConfig.isIgnoreOAuthQueryParameter());
deployment.setRewriteRedirectRules(adapterConfig.getRedirectRewriteRules());
deployment.setVerifyTokenAudience(adapterConfig.isVerifyTokenAudience());
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");
@@ -19,7 +19,7 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
@@ -40,6 +40,7 @@ import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.logging.Level;
/**
@@ -358,9 +359,15 @@ public class OAuthRequestAuthenticator {
}
try {
AdapterTokenVerifier.VerifiedTokens tokens = AdapterTokenVerifier.verifyTokens(tokenString, idTokenString, deployment);
token = tokens.getAccessToken();
idToken = tokens.getIdToken();
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
if (idTokenString != null) {
try {
JWSInput input = new JWSInput(idTokenString);
idToken = input.readJsonContent(IDToken.class);
} catch (JWSInputException e) {
throw new VerificationException();
}
}
log.debug("Token Verification succeeded!");
} catch (VerificationException e) {
log.error("failed verification of token: " + e.getMessage());
@@ -20,27 +20,30 @@ package org.keycloak.adapters;
import java.security.PublicKey;
import org.jboss.logging.Logger;
import org.keycloak.TokenVerifier;
import org.keycloak.adapters.authentication.ClientCredentialsProvider;
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.UserSessionManagement;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.StreamUtil;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.VersionRepresentation;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.VersionRepresentation;
import org.keycloak.representations.adapters.action.AdminAction;
import org.keycloak.representations.adapters.action.LogoutAction;
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
import org.keycloak.representations.adapters.action.TestAvailabilityAction;
import org.keycloak.util.JsonSerialization;
import java.security.PublicKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@@ -210,19 +213,17 @@ public class PreAuthActionsHandler {
}
try {
// Check just signature. Other things checked in validateAction
TokenVerifier tokenVerifier = AdapterTokenVerifier.createVerifier(token, deployment, false, JsonWebToken.class);
tokenVerifier.verify();
return new JWSInput(token);
} catch (VerificationException ignore) {
log.warn("admin request failed, unable to verify token: " + ignore.getMessage());
if (log.isDebugEnabled()) {
log.debug(ignore.getMessage(), ignore);
JWSInput input = new JWSInput(token);
PublicKey publicKey = AdapterRSATokenVerifier.getPublicKey(input.getHeader().getKeyId(), deployment);
if (RSAProvider.verify(input, publicKey)) {
return input;
}
facade.getResponse().sendError(403, "token failed verification");
return null;
} catch (JWSInputException ignore) {
}
log.warn("admin request failed, unable to verify token");
facade.getResponse().sendError(403, "no token");
return null;
}
@@ -249,15 +250,6 @@ public class PreAuthActionsHandler {
protected void handleVersion() {
try {
facade.getResponse().setStatus(200);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment.isCors()) {
String origin = facade.getRequest().getHeader(CorsHeaders.ORIGIN);
if (origin == null) {
log.debug("no origin header set in request");
} else {
facade.getResponse().setHeader(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
}
}
facade.getResponse().setHeader("Content-Type", "application/json");
JsonSerialization.writeValueToStream(facade.getResponse().getOutputStream(), VersionRepresentation.SINGLETON);
} catch (Exception e) {
@@ -20,7 +20,7 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessToken;
@@ -130,8 +130,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
String tokenString = response.getToken();
AccessToken token = null;
try {
AdapterTokenVerifier.VerifiedTokens tokens = AdapterTokenVerifier.verifyTokens(tokenString, response.getIdToken(), deployment);
token = tokens.getAccessToken();
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
log.debug("Token Verification succeeded!");
} catch (VerificationException e) {
log.error("failed verification of token");
@@ -17,7 +17,6 @@
*/
package org.keycloak.adapters.authorization;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -68,16 +67,8 @@ public abstract class AbstractPolicyEnforcer {
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
if (securityContext == null) {
if (!isDefaultAccessDeniedUri(request)) {
if (pathConfig != null) {
if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
return createEmptyAuthorizationContext(true);
} else {
challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
}
} else {
handleAccessDenied(httpFacade);
}
if (pathConfig != null) {
challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
}
return createEmptyAuthorizationContext(false);
}
@@ -112,9 +103,8 @@ public abstract class AbstractPolicyEnforcer {
}
MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims)) {
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
try {
return createAuthorizationContext(accessToken, pathConfig);
} catch (Exception e) {
@@ -143,7 +133,7 @@ public abstract class AbstractPolicyEnforcer {
protected abstract boolean challenge(PathConfig pathConfig, MethodConfig methodConfig, OIDCHttpFacade facade);
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
Request request = httpFacade.getRequest();
if (isDefaultAccessDeniedUri(request)) {
@@ -157,7 +147,7 @@ public abstract class AbstractPolicyEnforcer {
}
boolean hasPermission = false;
Collection<Permission> grantedPermissions = authorization.getPermissions();
List<Permission> grantedPermissions = authorization.getPermissions();
for (Permission permission : grantedPermissions) {
if (permission.getResourceId() != null) {
@@ -176,7 +166,7 @@ public abstract class AbstractPolicyEnforcer {
policyEnforcer.getPathMatcher().removeFromCache(getPath(request));
}
return hasValidClaims(permission, claims);
return hasValidClaims(actualPathConfig, permission, httpFacade, authorization);
}
}
} else {
@@ -198,10 +188,12 @@ public abstract class AbstractPolicyEnforcer {
return false;
}
private boolean hasValidClaims(Permission permission, Map<String, List<String>> claims) {
private boolean hasValidClaims(PathConfig actualPathConfig, Permission permission, OIDCHttpFacade httpFacade, Authorization authorization) {
Map<String, Set<String>> grantedClaims = permission.getClaims();
if (grantedClaims != null) {
Map<String, List<String>> claims = resolveClaims(actualPathConfig, httpFacade);
if (claims.isEmpty()) {
return false;
}
@@ -348,16 +340,18 @@ public abstract class AbstractPolicyEnforcer {
}
private Map<String, List<String>> getClaims(Map<String, Map<String, Object>>claimInformationPointConfig, HttpFacade httpFacade) {
Map<String, List<String>> claims = new HashMap<>();
if (claimInformationPointConfig != null) {
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
if (factory != null) {
return factory.create(claimDef.getValue()).resolve(httpFacade);
claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
}
}
}
return new HashMap<>();
return claims;
}
}
@@ -18,7 +18,7 @@
package org.keycloak.adapters.authorization;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -27,7 +27,7 @@ import org.jboss.logging.Logger;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthorizationDeniedException;
import org.keycloak.authorization.client.AuthzClient;
@@ -55,14 +55,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
}
@Override
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
AccessToken original = accessToken;
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims)) {
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
return true;
}
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade, claims);
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade);
if (accessToken == null) {
return false;
@@ -78,8 +78,8 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
AccessToken.Authorization newAuthorization = accessToken.getAuthorization();
if (newAuthorization != null) {
Collection<Permission> grantedPermissions = authorization.getPermissions();
Collection<Permission> newPermissions = newAuthorization.getPermissions();
List<Permission> grantedPermissions = authorization.getPermissions();
List<Permission> newPermissions = newAuthorization.getPermissions();
for (Permission newPermission : newPermissions) {
if (!grantedPermissions.contains(newPermission)) {
@@ -90,7 +90,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
original.setAuthorization(authorization);
return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims);
return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade);
}
@Override
@@ -122,6 +122,12 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
@Override
protected void handleAccessDenied(OIDCHttpFacade facade) {
KeycloakSecurityContext securityContext = facade.getSecurityContext();
if (securityContext == null) {
return;
}
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
HttpFacade.Response response = facade.getResponse();
@@ -133,7 +139,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
}
}
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
if (getEnforcerConfig().getUserManagedAccess() != null) {
return null;
}
@@ -149,6 +155,8 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
}
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
if (!claims.isEmpty()) {
authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
authzRequest.setClaimToken(Base64.encodeBytes(JsonSerialization.writeValueAsBytes(claims)));
@@ -169,7 +177,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
}
if (authzResponse != null) {
return AdapterTokenVerifier.verifyToken(authzResponse.getToken(), deployment);
return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
}
} catch (AuthorizationDeniedException ignore) {
LOGGER.debug("Authorization denied", ignore);
@@ -203,16 +211,17 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
private boolean isBearerAuthorization(OIDCHttpFacade httpFacade) {
List<String> authHeaders = httpFacade.getRequest().getHeaders("Authorization");
if (authHeaders != null) {
for (String authHeader : authHeaders) {
String[] split = authHeader.trim().split("\\s+");
if (split == null || split.length != 2) continue;
if (!split[0].equalsIgnoreCase("Bearer")) continue;
return true;
}
if (authHeaders == null || authHeaders.size() == 0) {
return false;
}
return getPolicyEnforcer().getDeployment().isBearerOnly();
for (String authHeader : authHeaders) {
String[] split = authHeader.trim().split("\\s+");
if (split == null || split.length != 2) continue;
if (!split[0].equalsIgnoreCase("Bearer")) continue;
return true;
}
return false;
}
}
@@ -21,7 +21,6 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import org.keycloak.common.util.Time;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
/**
@@ -155,7 +154,7 @@ public class PathCache {
if(maxAge == -1) {
expiration = -1;
} else {
expiration = Time.currentTimeMillis() + maxAge;
expiration = System.currentTimeMillis() + maxAge;
}
}
@@ -168,7 +167,7 @@ public class PathCache {
}
boolean isExpired() {
return expiration != -1 ? Time.currentTimeMillis() > expiration : false;
return expiration != -1 ? System.currentTimeMillis() > expiration : false;
}
}
}
@@ -225,10 +225,9 @@ public class PolicyEnforcer {
for (String id : protectedResource.findAll()) {
ResourceRepresentation resourceDescription = protectedResource.findById(id);
if (resourceDescription.getUris() != null && !resourceDescription.getUris().isEmpty()) {
for(PathConfig pathConfig : PathConfig.createPathConfigs(resourceDescription)) {
paths.put(pathConfig.getPath(), pathConfig);
}
if (resourceDescription.getUri() != null) {
PathConfig pathConfig = PathConfig.createPathConfig(resourceDescription);
paths.put(pathConfig.getPath(), pathConfig);
}
}
}
@@ -278,7 +277,7 @@ public class PolicyEnforcer {
cipConfig = pathConfig.getClaimInformationPointConfig();
}
pathConfig = PathConfig.createPathConfigs(matchingResources.get(0)).iterator().next();
pathConfig = PathConfig.createPathConfig(matchingResources.get(0));
if (cipConfig != null) {
pathConfig.setClaimInformationPointConfig(cipConfig);
@@ -324,7 +323,7 @@ public class PolicyEnforcer {
if (!search.isEmpty()) {
ResourceRepresentation targetResource = search.get(0);
PathConfig config = PathConfig.createPathConfigs(targetResource).iterator().next();
PathConfig config = PathConfig.createPathConfig(targetResource);
config.setScopes(originalConfig.getScopes());
config.setMethods(originalConfig.getMethods());
@@ -23,7 +23,7 @@ import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.FindFile;
import org.keycloak.common.util.reflections.Reflections;
@@ -202,16 +202,8 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
protected Auth bearerAuth(String tokenString) throws VerificationException {
AccessToken token = AdapterTokenVerifier.verifyToken(tokenString, deployment);
return postTokenVerification(tokenString, token);
}
AccessToken token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
/**
* Called after accessToken was verified (including signature, expiration etc)
*
*/
protected Auth postTokenVerification(String tokenString, AccessToken token) {
boolean verifyCaller;
if (deployment.isUseResourceRoleMappings()) {
verifyCaller = token.isVerifyCaller(deployment.getResourceName());
@@ -27,7 +27,6 @@ import org.apache.http.message.BasicNameValuePair;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.constants.ServiceUrlConstants;
@@ -57,15 +56,11 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
private static final Logger log = Logger.getLogger(DirectAccessGrantsLoginModule.class);
public static final String SCOPE_OPTION = "scope";
private String refreshToken;
private String scope;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
super.initialize(subject, callbackHandler, sharedState, options);
this.scope = (String)options.get(SCOPE_OPTION);
// This is used just for logout
Iterator<RefreshTokenHolder> iterator = subject.getPrivateCredentials(RefreshTokenHolder.class).iterator();
@@ -94,10 +89,6 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
formparams.add(new BasicNameValuePair("username", username));
formparams.add(new BasicNameValuePair("password", password));
if (scope != null) {
formparams.add(new BasicNameValuePair(OAuth2Constants.SCOPE, scope));
}
ClientCredentialsProviderUtils.setClientCredentials(deployment, post, formparams);
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
@@ -130,8 +121,7 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
// refreshToken will be saved to privateCreds of Subject for now
refreshToken = tokenResponse.getRefreshToken();
AdapterTokenVerifier.VerifiedTokens tokens = AdapterTokenVerifier.verifyTokens(tokenResponse.getToken(), tokenResponse.getIdToken(), deployment);
return postTokenVerification(tokenResponse.getToken(), tokens.getAccessToken());
return bearerAuth(tokenResponse.getToken());
}
@Override
@@ -0,0 +1,58 @@
/*
* 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.rotation;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.common.VerificationException;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.AccessToken;
import java.security.PublicKey;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AdapterRSATokenVerifier {
private static final Logger log = Logger.getLogger(AdapterRSATokenVerifier.class);
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment) throws VerificationException {
return verifyToken(tokenString, deployment, true, true);
}
public static PublicKey getPublicKey(String kid, KeycloakDeployment deployment) throws VerificationException {
PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
PublicKey publicKey = pkLocator.getPublicKey(kid, deployment);
if (publicKey == null) {
log.errorf("Didn't find publicKey for kid: %s", kid);
throw new VerificationException("Didn't find publicKey for specified kid");
}
return publicKey;
}
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment, boolean checkActive, boolean checkTokenType) throws VerificationException {
RSATokenVerifier verifier = RSATokenVerifier.create(tokenString).realmUrl(deployment.getRealmInfoUrl()).checkActive(checkActive).checkTokenType(checkTokenType);
PublicKey publicKey = getPublicKey(verifier.getHeader().getKeyId(), deployment);
return verifier.publicKey(publicKey).verify().getToken();
}
}
@@ -1,149 +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.rotation;
import org.jboss.logging.Logger;
import org.keycloak.TokenVerifier;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.common.VerificationException;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.JsonWebToken;
import java.security.PublicKey;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AdapterTokenVerifier {
private static final Logger log = Logger.getLogger(AdapterTokenVerifier.class);
/**
* Verifies bearer token. Typically called when bearer token (access token) is sent to the service, which wants to verify it. Hence it also checks the audience in the token.
*
* @param tokenString
* @param deployment
* @return
* @throws VerificationException
*/
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment) throws VerificationException {
TokenVerifier<AccessToken> tokenVerifier = createVerifier(tokenString, deployment, true, AccessToken.class);
// Verify audience of bearer-token
if (deployment.isVerifyTokenAudience()) {
tokenVerifier.audience(deployment.getResourceName());
}
return tokenVerifier.verify().getToken();
}
/**
* Verify access token and ID token. Typically called after successful tokenResponse is received from Keycloak
*
* @param accessTokenString
* @param idTokenString
* @param deployment
* @return verified and parsed accessToken and idToken
* @throws VerificationException
*/
public static VerifiedTokens verifyTokens(String accessTokenString, String idTokenString, KeycloakDeployment deployment) throws VerificationException {
// Adapters currently do most of the checks including signature etc on the access token
TokenVerifier<AccessToken> tokenVerifier = createVerifier(accessTokenString, deployment, true, AccessToken.class);
AccessToken accessToken = tokenVerifier.verify().getToken();
if (idTokenString != null) {
// Don't verify signature again on IDToken
IDToken idToken = TokenVerifier.create(idTokenString, IDToken.class).getToken();
TokenVerifier<IDToken> idTokenVerifier = TokenVerifier.createWithoutSignature(idToken);
// Always verify audience on IDToken
idTokenVerifier.audience(deployment.getResourceName());
idTokenVerifier.verify();
return new VerifiedTokens(accessToken, idToken);
} else {
return new VerifiedTokens(accessToken, null);
}
}
/**
* Creates verifier, initializes it from the KeycloakDeployment and adds the publicKey and some default basic checks (activeness and tokenType). Useful if caller wants to add/remove/update
* some checks
*
* @param tokenString
* @param deployment
* @param withDefaultChecks
* @param tokenClass
* @param <T>
* @return tokenVerifier
* @throws VerificationException
*/
public static <T extends JsonWebToken> TokenVerifier<T> createVerifier(String tokenString, KeycloakDeployment deployment, boolean withDefaultChecks, Class<T> tokenClass) throws VerificationException {
TokenVerifier<T> tokenVerifier = TokenVerifier.create(tokenString, tokenClass);
if (withDefaultChecks) {
tokenVerifier
.withDefaultChecks()
.realmUrl(deployment.getRealmInfoUrl());
}
String kid = tokenVerifier.getHeader().getKeyId();
PublicKey publicKey = getPublicKey(kid, deployment);
tokenVerifier.publicKey(publicKey);
return tokenVerifier;
}
private static PublicKey getPublicKey(String kid, KeycloakDeployment deployment) throws VerificationException {
PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
PublicKey publicKey = pkLocator.getPublicKey(kid, deployment);
if (publicKey == null) {
log.errorf("Didn't find publicKey for kid: %s", kid);
throw new VerificationException("Didn't find publicKey for specified kid");
}
return publicKey;
}
public static class VerifiedTokens {
private final AccessToken accessToken;
private final IDToken idToken;
public VerifiedTokens(AccessToken accessToken, IDToken idToken) {
this.accessToken = accessToken;
this.idToken = idToken;
}
public AccessToken getAccessToken() {
return accessToken;
}
public IDToken getIdToken() {
return idToken;
}
}
}
@@ -77,7 +77,6 @@ public class KeycloakDeploymentBuilderTest {
assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
assertEquals(120, deployment.getPublicKeyCacheTtl());
assertEquals("/api/$1", deployment.getRedirectRewriteRules().get("^/wsmaster/api/(.*)$"));
assertTrue(deployment.isVerifyTokenAudience());
}
@Test
@@ -34,7 +34,6 @@
"min-time-between-jwks-requests": 20,
"public-key-cache-ttl": 120,
"ignore-oauth-query-parameter": true,
"verify-token-audience": true,
"redirect-rewrite-rules" : {
"^/wsmaster/api/(.*)$" : "/api/$1"
}
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-as7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-as7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-integration-pom</artifactId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -98,12 +98,6 @@ class SecureDeploymentDefinition extends SimpleResourceDefinition {
.setValidator(new IntRangeValidator(-1, true))
.setAllowExpression(true)
.build();
protected static final SimpleAttributeDefinition PUBLIC_KEY_CACHE_TTL =
new SimpleAttributeDefinitionBuilder("public-key-cache-ttl", ModelType.INT, true)
.setXmlName("public-key-cache-ttl")
.setAllowExpression(true)
.setValidator(new IntRangeValidator(-1, true))
.build();
protected static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
@@ -116,7 +110,6 @@ class SecureDeploymentDefinition extends SimpleResourceDefinition {
DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION);
DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_KEY_CACHE_TTL);
}
protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@@ -166,20 +166,6 @@ class SharedAttributeDefinitons {
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition PROXY_URL =
new SimpleAttributeDefinitionBuilder("proxy-url", ModelType.STRING, true)
.setXmlName("proxy-url")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition VERIFY_TOKEN_AUDIENCE =
new SimpleAttributeDefinitionBuilder("verify-token-audience", ModelType.BOOLEAN, true)
.setXmlName("verify-token-audience")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@@ -206,8 +192,6 @@ class SharedAttributeDefinitons {
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
ATTRIBUTES.add(TOKEN_STORE);
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
ATTRIBUTES.add(PROXY_URL);
ATTRIBUTES.add(VERIFY_TOKEN_AUDIENCE);
}
/**
@@ -46,8 +46,6 @@ keycloak.realm.register-node-at-startup=Cluster setting
keycloak.realm.register-node-period=how often to re-register node
keycloak.realm.token-store=cookie or session storage for auth session data
keycloak.realm.principal-attribute=token attribute to use to set Principal name
keycloak.realm.proxy-url=The URL for the HTTP proxy if one is used.
keycloak.realm.verify-token-audience=If true, then during bearer-only authentication, the adapter will verify if token contains this client name (resource) as an audience
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
@@ -84,9 +82,7 @@ keycloak.secure-deployment.principal-attribute=token attribute to use to set Pri
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.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less
keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
keycloak.secure-deployment.public-key-cache-ttl=Maximum time the downloaded public keys are considered valid. When this time reach, the adapter is forced to download public keys from keycloak server
keycloak.secure-deployment.proxy-url=The URL for the HTTP proxy if one is used.
keycloak.secure-deployment.verify-token-audience=If true, then during bearer-only authentication, the adapter will verify if token contains this client name (resource) as an audience
keycloak.secure-deployment.credential=Credential value
keycloak.credential=Credential
@@ -65,8 +65,6 @@
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="verify-token-audience" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
@@ -109,9 +107,6 @@
<xs:element name="turn-off-change-session-id-on-login" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="token-minimum-time-to-live" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="min-time-between-jwks-requests" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="public-key-cache-ttl" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="verify-token-audience" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
+4 -4
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -38,7 +38,7 @@
org.keycloak.*;version="${project.version}",
org.apache.camel.*,
org.apache.camel.component.undertow,
io.undertow.*;version="[1.4,3)",
io.undertow.*,
*;resolution:=optional
</keycloak.osgi.import>
</properties>
@@ -87,12 +87,12 @@
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-undertow</artifactId>
<version>2.21.2</version>
<version>2.21.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.21.2</version>
<version>2.21.0</version>
</dependency>
</dependencies>
@@ -19,9 +19,20 @@ package org.keycloak.adapters.camel.undertow;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.camel.CamelContext;
import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.component.undertow.RestUndertowHttpBinding;
import org.apache.camel.component.undertow.UndertowComponent;
import org.apache.camel.component.undertow.UndertowEndpoint;
import org.apache.camel.spi.RestConfiguration;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.HostUtils;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.URISupport;
/**
*
@@ -41,8 +52,136 @@ public class UndertowKeycloakComponent extends UndertowComponent {
return new UndertowKeycloakEndpoint(endpointUri.toString(), component);
}
@Override
// TODO: uncomment line below after backport of https://issues.apache.org/jira/browse/CAMEL-12514 into fuse
// @Override
protected String getComponentName() {
return "undertow-keycloak";
}
// TODO: remove all below this line after backport of https://issues.apache.org/jira/browse/CAMEL-12514 into fuse
@Override
public Consumer createConsumer(CamelContext camelContext, Processor processor, String verb, String basePath, String uriTemplate,
String consumes, String produces, RestConfiguration configuration, Map<String, Object> parameters) throws Exception {
return doCreateConsumer(camelContext, processor, verb, basePath, uriTemplate, consumes, produces, configuration, parameters, false);
}
@Override
public Consumer createApiConsumer(CamelContext camelContext, Processor processor, String contextPath,
RestConfiguration configuration, Map<String, Object> parameters) throws Exception {
// reuse the createConsumer method we already have. The api need to use GET and match on uri prefix
return doCreateConsumer(camelContext, processor, "GET", contextPath, null, null, null, configuration, parameters, true);
}
Consumer doCreateConsumer(CamelContext camelContext, Processor processor, String verb, String basePath, String uriTemplate,
String consumes, String produces, RestConfiguration configuration, Map<String, Object> parameters, boolean api) throws Exception {
String path = basePath;
if (uriTemplate != null) {
// make sure to avoid double slashes
if (uriTemplate.startsWith("/")) {
path = path + uriTemplate;
} else {
path = path + "/" + uriTemplate;
}
}
path = FileUtil.stripLeadingSeparator(path);
String scheme = "http";
String host = "";
int port = 0;
RestConfiguration config = configuration;
if (config == null) {
config = camelContext.getRestConfiguration(getComponentName(), true);
}
if (config.getScheme() != null) {
scheme = config.getScheme();
}
if (config.getHost() != null) {
host = config.getHost();
}
int num = config.getPort();
if (num > 0) {
port = num;
}
// prefix path with context-path if configured in rest-dsl configuration
String contextPath = config.getContextPath();
if (ObjectHelper.isNotEmpty(contextPath)) {
contextPath = FileUtil.stripTrailingSeparator(contextPath);
contextPath = FileUtil.stripLeadingSeparator(contextPath);
if (ObjectHelper.isNotEmpty(contextPath)) {
path = contextPath + "/" + path;
}
}
// if no explicit hostname set then resolve the hostname
if (ObjectHelper.isEmpty(host)) {
if (config.getRestHostNameResolver() == RestConfiguration.RestHostNameResolver.allLocalIp) {
host = "0.0.0.0";
} else if (config.getRestHostNameResolver() == RestConfiguration.RestHostNameResolver.localHostName) {
host = HostUtils.getLocalHostName();
} else if (config.getRestHostNameResolver() == RestConfiguration.RestHostNameResolver.localIp) {
host = HostUtils.getLocalIp();
}
}
Map<String, Object> map = new HashMap<String, Object>();
// build query string, and append any endpoint configuration properties
if (config.getComponent() == null || config.getComponent().equals(getComponentName())) {
// setup endpoint options
if (config.getEndpointProperties() != null && !config.getEndpointProperties().isEmpty()) {
map.putAll(config.getEndpointProperties());
}
}
boolean explicitOptions = true;
// must use upper case for restrict
String restrict = verb.toUpperCase(Locale.US);
// allow OPTIONS in rest-dsl to allow clients to call the API and have responses with ALLOW headers
if (!restrict.contains("OPTIONS")) {
restrict += ",OPTIONS";
// this is not an explicit OPTIONS path in the rest-dsl
explicitOptions = false;
}
boolean cors = config.isEnableCORS();
if (cors) {
// allow HTTP Options as we want to handle CORS in rest-dsl
map.put("optionsEnabled", "true");
} else if (explicitOptions) {
// the rest-dsl is using OPTIONS
map.put("optionsEnabled", "true");
}
String query = URISupport.createQueryString(map);
String url;
if (api) {
url = getComponentName() + ":%s://%s:%s/%s?matchOnUriPrefix=true&httpMethodRestrict=%s";
} else {
url = getComponentName() + ":%s://%s:%s/%s?matchOnUriPrefix=false&httpMethodRestrict=%s";
}
// get the endpoint
url = String.format(url, scheme, host, port, path, restrict);
if (!query.isEmpty()) {
url = url + "&" + query;
}
UndertowEndpoint endpoint = camelContext.getEndpoint(url, UndertowEndpoint.class);
setProperties(camelContext, endpoint, parameters);
if (!map.containsKey("undertowHttpBinding")) {
// use the rest binding, if not using a custom http binding
endpoint.setUndertowHttpBinding(new RestUndertowHttpBinding());
}
// configure consumer properties
Consumer consumer = endpoint.createConsumer(processor);
if (config.getConsumerProperties() != null && !config.getConsumerProperties().isEmpty()) {
setProperties(camelContext, consumer, config.getConsumerProperties());
}
return consumer;
}
}
+1 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -36,7 +36,6 @@
</keycloak.osgi.export>
<keycloak.osgi.import>
!org.keycloak.adapters.osgi.jetty94,
org.ops4j.pax.web.*;version="[7.1,9)",
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
+3 -16
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -38,21 +38,8 @@
<modules>
<module>camel-undertow</module>
<module>jetty94</module>
<module>tomcat8</module>
<module>undertow</module>
</modules>
<profiles>
<profile>
<id>community</id>
<activation>
<property>
<name>!product</name>
</property>
</activation>
<modules>
<module>jetty94</module>
<module>tomcat8</module>
</modules>
</profile>
</profiles>
</project>
+1 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -36,7 +36,6 @@
</keycloak.osgi.export>
<keycloak.osgi.import>
!org.keycloak.adapters.osgi.tomcat,
org.ops4j.pax.web.*;version="[7.1,9)",
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
+8 -3
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -38,12 +38,12 @@
!org.keycloak.adapters.osgi.undertow,
org.keycloak.*;version="${project.version}",
org.keycloak.adapters.osgi.*;version="${project.version}",
org.ops4j.pax.web.*;version="[7.1,9)",
org.ops4j.pax.web.*;version="[3.0,8)",
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.apache.cxf.transport.http;resolution:=optional;version="[3,4)",
org.apache.cxf.transport.http_undertow;resolution:=optional;version="[3,4)",
org.apache.cxf.transport.servlet;resolution:=optional;version="[3,4)",
io.undertow.*;version="[1.4,3)",
io.undertow.*,
*;resolution:=optional
</keycloak.osgi.import>
<keycloak.osgi.fragment>org.ops4j.pax.web.pax-web-undertow</keycloak.osgi.fragment>
@@ -65,6 +65,11 @@
<artifactId>org.osgi.enterprise</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-runtime</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-spi</artifactId>
+2 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -69,7 +69,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
</dependency>
</dependencies>
@@ -24,13 +24,16 @@ import org.keycloak.OAuthErrorException;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.ServerRequest;
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
@@ -534,9 +537,15 @@ public class KeycloakInstalled {
refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken();
AdapterTokenVerifier.VerifiedTokens tokens = AdapterTokenVerifier.verifyTokens(tokenString, idTokenString, deployment);
token = tokens.getAccessToken();
idToken = tokens.getIdToken();
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
if (idTokenString != null) {
try {
JWSInput input = new JWSInput(idTokenString);
idToken = input.readJsonContent(IDToken.class);
} catch (JWSInputException e) {
throw new VerificationException();
}
}
}
public AccessToken getToken() {
+2 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -33,7 +33,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak Jetty Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+12 -20
View File
@@ -29,7 +29,7 @@ export = Keycloak;
declare function Keycloak(config?: string|{}): Keycloak.KeycloakInstance;
declare namespace Keycloak {
type KeycloakAdapterName = 'cordova' | 'cordova-native' |'default' | any;
type KeycloakAdapterName = 'cordova'|'default' | any;
type KeycloakOnLoad = 'login-required'|'check-sso';
type KeycloakResponseMode = 'query'|'fragment';
type KeycloakResponseType = 'code'|'id_token token'|'code id_token token';
@@ -98,12 +98,6 @@ declare namespace Keycloak {
*/
responseMode?: KeycloakResponseMode;
/**
* Specifies a default uri to redirect to after login or logout.
* This is currently supported for adapter 'cordova-native' and 'default'
*/
redirectUri?: string;
/**
* Set the OpenID Connect flow.
* @default standard
@@ -216,16 +210,6 @@ declare namespace Keycloak {
createdTimestamp?: number;
}
interface KeycloakTokenParsed {
exp?: number;
iat?: number;
nonce?: string;
sub?: string;
session_state?: string;
realm_access?: { roles: string[] };
resource_access?: string[];
}
// export interface KeycloakUserInfo {}
/**
@@ -279,7 +263,15 @@ declare namespace Keycloak {
/**
* The parsed token as a JavaScript object.
*/
tokenParsed?: KeycloakTokenParsed;
tokenParsed?: {
exp?: number;
iat?: number;
nonce?: string;
sub?: string;
session_state?: string;
realm_access?: { roles: string[] };
resource_access?: string[];
};
/**
* The base64 encoded refresh token that can be used to retrieve a new token.
@@ -289,7 +281,7 @@ declare namespace Keycloak {
/**
* The parsed refresh token as a JavaScript object.
*/
refreshTokenParsed?: KeycloakTokenParsed;
refreshTokenParsed?: { nonce?: string };
/**
* The base64 encoded ID token.
@@ -299,7 +291,7 @@ declare namespace Keycloak {
/**
* The parsed id token as a JavaScript object.
*/
idTokenParsed?: KeycloakTokenParsed;
idTokenParsed?: { nonce?: string };
/**
* The estimated time difference between the browser time and the Keycloak
@@ -46,10 +46,11 @@
kc.authenticated = false;
callbackStorage = createCallbackStorage();
var adapters = ['default', 'cordova', 'cordova-native'];
if (initOptions && adapters.indexOf(initOptions.adapter) > -1) {
adapter = loadAdapter(initOptions.adapter);
if (initOptions && initOptions.adapter === 'cordova') {
adapter = loadAdapter('cordova');
} else if (initOptions && initOptions.adapter === 'default') {
adapter = loadAdapter();
} else if (initOptions && typeof initOptions.adapter === "object") {
adapter = initOptions.adapter;
} else {
@@ -105,10 +106,6 @@
if (initOptions.timeSkew != null) {
kc.timeSkew = initOptions.timeSkew;
}
if(initOptions.redirectUri) {
kc.redirectUri = initOptions.redirectUri;
}
}
if (!kc.responseMode) {
@@ -910,7 +907,7 @@
supportedParams = ['code', 'state', 'session_state'];
break;
case 'implicit':
supportedParams = ['access_token', 'token_type', 'id_token', 'state', 'session_state', 'expires_in'];
supportedParams = ['access_token', 'id_token', 'state', 'session_state'];
break;
case 'hybrid':
supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state'];
@@ -1342,75 +1339,6 @@
}
}
if (type == 'cordova-native') {
loginIframe.enable = false;
return {
login: function(options) {
var promise = createPromise();
var loginUrl = kc.createLoginUrl(options);
universalLinks.subscribe('keycloak', function(event) {
universalLinks.unsubscribe('keycloak');
window.cordova.plugins.browsertab.close();
var oauth = parseCallback(event.url);
processCallback(oauth, promise);
});
window.cordova.plugins.browsertab.openUrl(loginUrl);
return promise.promise;
},
logout: function(options) {
var promise = createPromise();
var logoutUrl = kc.createLogoutUrl(options);
universalLinks.subscribe('keycloak', function(event) {
universalLinks.unsubscribe('keycloak');
window.cordova.plugins.browsertab.close();
kc.clearToken();
promise.setSuccess();
});
window.cordova.plugins.browsertab.openUrl(logoutUrl);
return promise.promise;
},
register : function(options) {
var promise = createPromise();
var registerUrl = kc.createRegisterUrl(options);
universalLinks.subscribe('keycloak' , function(event) {
universalLinks.unsubscribe('keycloak');
window.cordova.plugins.browsertab.close();
var oauth = parseCallback(event.url);
processCallback(oauth, promise);
});
window.cordova.plugins.browsertab.openUrl(registerUrl);
return promise.promise;
},
accountManagement : function() {
var accountUrl = kc.createAccountUrl();
if (typeof accountUrl !== 'undefined') {
window.cordova.plugins.browsertab.openUrl(accountUrl);
} else {
throw "Not supported by the OIDC server";
}
},
redirectUri: function(options) {
if (options && options.redirectUri) {
return options.redirectUri;
} else if (kc.redirectUri) {
return kc.redirectUri;
} else {
return "http://localhost";
}
}
}
}
throw 'invalid adapter type: ' + type;
}
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+6 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -77,6 +77,11 @@
<artifactId>org.osgi.enterprise</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-runtime</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-api</artifactId>
@@ -1,89 +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.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
/**
* This {@link KeycloakConfigResolver} tries to resolve most specific configuration for given URI path. If not found,
* <em>parent</em> path is checked up to top-level path.
*/
public class HierarchicalPathBasedKeycloakConfigResolver extends PathBasedKeycloakConfigResolver {
protected static final Logger log = Logger.getLogger(HierarchicalPathBasedKeycloakConfigResolver.class);
public HierarchicalPathBasedKeycloakConfigResolver() {
prepopulateCache();
}
@Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
// we cached all available deployments initially and now we'll try to check them from
// most specific to most general
URI uri = URI.create(request.getURI());
String path = uri.getPath();
if (path != null) {
while (path.startsWith("/")) {
path = path.substring(1);
}
String[] segments = path.split("/");
List<String> paths = collectPaths(segments);
for (String pathFragment: paths) {
KeycloakDeployment cachedDeployment = super.getCachedDeployment(pathFragment);
if (cachedDeployment != null) {
return cachedDeployment;
}
}
}
throw new IllegalStateException("Can't find Keycloak configuration related to URI path " + uri);
}
/**
* <p>For segments like "a, b, c, d", returns:<ul>
* <li>"a-b-c-d"</li>
* <li>"a-b-c"</li>
* <li>"a-b"</li>
* <li>"a"</li>
* <li>""</li>
* </ul></p>
* @param segments
* @return
*/
private List<String> collectPaths(String[] segments) {
List<String> result = new ArrayList<>(segments.length + 1);
for (int idx = segments.length; idx >= 0; idx--) {
StringBuilder sb = null;
for (int i = 0; i < idx; i++) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append("-").append(segments[i]);
}
result.add(sb == null ? "" : sb.toString().substring(1));
}
return result;
}
}
@@ -16,15 +16,12 @@
*/
package org.keycloak.adapters.osgi;
import org.jboss.logging.Logger;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
@@ -34,118 +31,10 @@ import java.util.concurrent.ConcurrentHashMap;
public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
protected static final Logger log = Logger.getLogger(PathBasedKeycloakConfigResolver.class);
private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<String, KeycloakDeployment>();
private File keycloakConfigLocation = null;
public PathBasedKeycloakConfigResolver() {
String location = null;
String keycloakConfig = (String) System.getProperties().get("keycloak.config");
if (keycloakConfig != null && !"".equals(keycloakConfig.trim())) {
location = keycloakConfig;
} else {
String karafEtc = (String) System.getProperties().get("karaf.etc");
if (karafEtc != null && !"".equals(karafEtc.trim())) {
location = karafEtc;
}
}
if (location != null) {
File loc = new File(location);
if (loc.isDirectory()) {
keycloakConfigLocation = loc;
}
}
}
@Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
String webContext = getDeploymentKeyForURI(request);
return getOrCreateDeployment(webContext);
}
/**
* {@code pathFragment} is a key for {@link KeycloakDeployment deployments}. The key is used to construct
* a path relative to {@code keycloak.config} or {@code karaf.etc} system properties.
* For given key, {@code <key>-keycloak.json} file is checked.
* @param pathFragment
* @return
*/
protected synchronized KeycloakDeployment getOrCreateDeployment(String pathFragment) {
KeycloakDeployment deployment = getCachedDeployment(pathFragment);
if (null == deployment) {
// not found on the simple cache, try to load it from the file system
if (keycloakConfigLocation == null) {
throw new IllegalStateException("Neither \"keycloak.config\" nor \"karaf.etc\" java properties are set." +
" Please set one of them.");
}
File configuration = new File(keycloakConfigLocation, pathFragment + ("".equals(pathFragment) ? "" : "-")
+ "keycloak.json");
if (!cacheConfiguration(pathFragment, configuration)) {
throw new IllegalStateException("Not able to read the file " + configuration);
}
deployment = getCachedDeployment(pathFragment);
}
return deployment;
}
protected synchronized KeycloakDeployment getCachedDeployment(String pathFragment) {
return cache.get(pathFragment);
}
/**
* If there's a need, we can pre populate the cache of deployments.
*/
protected void prepopulateCache() {
if (keycloakConfigLocation == null || !keycloakConfigLocation.isDirectory()) {
log.warn("Can't cache Keycloak configurations. No configuration storage is accessible." +
" Please set either \"keycloak.config\" or \"karaf.etc\" system properties");
return;
}
File[] configs = keycloakConfigLocation.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile() && pathname.getName().endsWith("keycloak.json");
}
});
if (configs != null) {
for (File config: configs) {
String pathFragment = null;
if ("keycloak.json".equals(config.getName())) {
pathFragment = "";
} else if (config.getName().endsWith("-keycloak.json")) {
pathFragment = config.getName()
.substring(0, config.getName().length() - "-keycloak.json".length());
}
cacheConfiguration(pathFragment, config);
}
}
}
private boolean cacheConfiguration(String key, File config) {
try {
InputStream is = new FileInputStream(config);
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);
cache.put(key, deployment);
return true;
} catch (FileNotFoundException | RuntimeException e) {
log.warn("Can't cache " + config + ": " + e.getMessage(), e);
return false;
}
}
/**
* Finds a context path from given {@link HttpFacade.Request}. For default context, first path segment
* is returned.
* @param request
* @return
*/
private String getDeploymentKeyForURI(HttpFacade.Request request) {
String uri = request.getURI();
String relativePath = request.getRelativePath();
String webContext = null;
@@ -159,9 +48,7 @@ public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
} else {
URI parsedURI = URI.create(uri);
String path = parsedURI.getPath();
if (path.contains(relativePath)) {
path = path.substring(0, path.indexOf(relativePath));
}
path = path.substring(0, path.indexOf(relativePath));
while (path.startsWith("/")) {
path = path.substring(1);
}
@@ -178,7 +65,31 @@ public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
}
}
return webContext;
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 + ("".equals(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;
}
}
@@ -1,162 +0,0 @@
/*
* Copyright 2018 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.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.AuthenticationError;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.LogoutError;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
public class HierarchicalPathBasedKeycloakConfigResolverTest {
@Test
public void genericAndSpecificConfigurations() throws Exception {
HierarchicalPathBasedKeycloakConfigResolver resolver = new HierarchicalPathBasedKeycloakConfigResolver();
populate(resolver, true);
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/d/e?a=b")).getRealm(), equalTo("a-b-c-d-e"));
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/d/x?a=b")).getRealm(), equalTo("a-b-c-d"));
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/x/x?a=b")).getRealm(), equalTo("a-b-c"));
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/x/x/x?a=b")).getRealm(), equalTo("a-b"));
assertThat(resolver.resolve(new MockRequest("http://localhost/a/x/x/x/x?a=b")).getRealm(), equalTo("a"));
assertThat(resolver.resolve(new MockRequest("http://localhost/x/x/x/x/x?a=b")).getRealm(), equalTo(""));
populate(resolver, false);
try {
resolver.resolve(new MockRequest("http://localhost/x/x/x/x/x?a=b"));
fail("Expected java.lang.IllegalStateException: Can't find Keycloak configuration ...");
} catch (IllegalStateException expected) {
}
}
@SuppressWarnings("unchecked")
private PathBasedKeycloakConfigResolver populate(PathBasedKeycloakConfigResolver resolver, boolean fallback)
throws Exception {
Field f = PathBasedKeycloakConfigResolver.class.getDeclaredField("cache");
f.setAccessible(true);
Map<String, KeycloakDeployment> cache = (Map<String, KeycloakDeployment>) f.get(resolver);
cache.clear();
cache.put("a-b-c-d-e", newKeycloakDeployment("a-b-c-d-e"));
cache.put("a-b-c-d", newKeycloakDeployment("a-b-c-d"));
cache.put("a-b-c", newKeycloakDeployment("a-b-c"));
cache.put("a-b", newKeycloakDeployment("a-b"));
cache.put("a", newKeycloakDeployment("a"));
if (fallback) {
cache.put("", newKeycloakDeployment(""));
}
return resolver;
}
private KeycloakDeployment newKeycloakDeployment(String realm) {
KeycloakDeployment deployment = new KeycloakDeployment();
deployment.setRealm(realm);
return deployment;
}
private class MockRequest implements OIDCHttpFacade.Request {
private String uri;
public MockRequest(String uri) {
this.uri = uri;
}
@Override
public String getMethod() {
return null;
}
@Override
public String getURI() {
return this.uri;
}
@Override
public String getRelativePath() {
return null;
}
@Override
public boolean isSecure() {
return false;
}
@Override
public String getFirstParam(String param) {
return null;
}
@Override
public String getQueryParamValue(String param) {
return null;
}
@Override
public HttpFacade.Cookie getCookie(String cookieName) {
return null;
}
@Override
public String getHeader(String name) {
return null;
}
@Override
public List<String> getHeaders(String name) {
return null;
}
@Override
public InputStream getInputStream() {
return null;
}
@Override
public InputStream getInputStream(boolean buffered) {
return null;
}
@Override
public String getRemoteAddr() {
return null;
}
@Override
public void setError(AuthenticationError error) {
}
@Override
public void setError(LogoutError error) {
}
}
}
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<name>Keycloak OIDC Client Adapter Modules</name>
+1 -45
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,17 +30,6 @@
<name>Keycloak Servlet Filter Adapter Integration</name>
<description/>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.servlet.*
</keycloak.osgi.export>
<keycloak.osgi.import>
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
@@ -105,39 +94,6 @@
<target>${maven.compiler.target}</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>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -25,17 +25,27 @@ 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.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.HandlerWrapper;
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.context.ApplicationContext;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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;
@@ -55,10 +65,6 @@ public class KeycloakBaseSpringBootConfiguration {
KeycloakSpringBootConfigResolver.setAdapterConfig(keycloakProperties);
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
KeycloakSpringBootConfigResolverWrapper.setApplicationContext(context);
}
static class KeycloakBaseUndertowDeploymentInfoCustomizer {
@@ -75,7 +81,7 @@ public class KeycloakBaseSpringBootConfiguration {
deploymentInfo.setLoginConfig(loginConfig);
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolverWrapper.class.getName());
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
deploymentInfo.addSecurityConstraints(getSecurityConstraints());
deploymentInfo.addServletExtension(new KeycloakServletExtension());
@@ -117,7 +123,7 @@ public class KeycloakBaseSpringBootConfiguration {
public void customize(Server server) {
KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator();
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolverWrapper());
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver());
/* see org.eclipse.jetty.webapp.StandardDescriptorProcessor#visitSecurityConstraint for an example
on how to map servlet spec to Constraints */
@@ -181,7 +187,7 @@ public class KeycloakBaseSpringBootConfiguration {
WebAppContext webAppContext = server.getBean(WebAppContext.class);
//if not found as registered bean let's try the handler
if(webAppContext==null){
webAppContext = getWebAppContext(server.getHandlers());
webAppContext = (WebAppContext) server.getHandler();
}
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
@@ -190,21 +196,6 @@ public class KeycloakBaseSpringBootConfiguration {
webAppContext.setSecurityHandler(securityHandler);
}
private WebAppContext getWebAppContext(Handler... handlers) {
for (Handler handler : handlers) {
if (handler instanceof WebAppContext) {
return (WebAppContext) handler;
} else if (handler instanceof HandlerList) {
return getWebAppContext(((HandlerList) handler).getHandlers());
} else if (handler instanceof HandlerCollection) {
return getWebAppContext(((HandlerCollection) handler).getHandlers());
} else if (handler instanceof HandlerWrapper) {
return getWebAppContext(((HandlerWrapper) handler).getHandlers());
}
}
throw new RuntimeException("No WebAppContext found in Jetty server handlers");
}
}
static class KeycloakBaseTomcatContextCustomizer {
@@ -232,12 +223,9 @@ public class KeycloakBaseSpringBootConfiguration {
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
SecurityConstraint tomcatConstraint = new SecurityConstraint();
for (String authRole : constraint.getAuthRoles()) {
tomcatConstraint.addAuthRole(authRole);
if(authRole.equals("*") || authRole.equals("**")) {
// For some reasons embed tomcat don't set the auth constraint on true when wildcard is used
tomcatConstraint.setAuthConstraint(true);
}
}
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
@@ -268,7 +256,7 @@ public class KeycloakBaseSpringBootConfiguration {
context.addConstraint(tomcatConstraint);
}
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolverWrapper.class.getName());
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
}
}
}
@@ -1,48 +0,0 @@
/*
* Copyright 2018 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.springboot;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springsecurity.config.KeycloakSpringConfigResolverWrapper;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
/**
* <p>A specific implementation of {@link KeycloakSpringConfigResolverWrapper} that first tries to register any {@link KeycloakConfigResolver}
* instance provided by the application. if none is provided, {@link KeycloakSpringBootConfigResolver} is set.
*
* <p>This implementation is specially useful when using Spring Boot and Spring Security in the same application where the same {@link KeycloakConfigResolver}
* instance must be used across the different stacks.
*
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class KeycloakSpringBootConfigResolverWrapper extends KeycloakSpringConfigResolverWrapper {
private static ApplicationContext context;
public KeycloakSpringBootConfigResolverWrapper() {
super(new KeycloakSpringBootConfigResolver());
try {
setDelegate(context.getBean(KeycloakConfigResolver.class));
} catch (NoSuchBeanDefinitionException ignore) {
}
}
public static void setApplicationContext(ApplicationContext context) {
KeycloakSpringBootConfigResolverWrapper.context = context;
}
}
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.keycloak.adapters.springboot.KeycloakAutoConfiguration
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>spring-boot-container-bundle</artifactId>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+2 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -31,7 +31,7 @@
<description/>
<properties>
<spring-boot.version>2.0.3.RELEASE</spring-boot.version>
<spring-boot.version>2.0.0.RELEASE</spring-boot.version>
<spring.version>5.0.2.RELEASE</spring.version>
<mockito.version>1.9.5</mockito.version>
</properties>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -1,19 +0,0 @@
package org.keycloak.adapters.springsecurity.authentication;
import javax.servlet.http.HttpServletRequest;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.HttpFacade;
/**
* Creates {@link RequestAuthenticator}s.
*/
public interface RequestAuthenticatorFactory {
/**
* Creates new {@link RequestAuthenticator} instances on a per-request basis.
*/
RequestAuthenticator createRequestAuthenticator(HttpFacade facade, HttpServletRequest request,
KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort);
}
@@ -1,17 +0,0 @@
package org.keycloak.adapters.springsecurity.authentication;
import javax.servlet.http.HttpServletRequest;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.HttpFacade;
public class SpringSecurityRequestAuthenticatorFactory implements RequestAuthenticatorFactory {
@Override
public RequestAuthenticator createRequestAuthenticator(HttpFacade facade,
HttpServletRequest request, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort) {
return new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, sslRedirectPort);
}
}
@@ -1,46 +0,0 @@
/*
* Copyright 2018 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.springsecurity.config;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.spi.HttpFacade;
/**
* Spring applications may use different security stacks in order to enforce access based on the configuration provided
* by a {@code KeycloakDeployment}. This implementation of {@code KeycloakConfigResolver} wraps and avoid calling multiple
* {@code KeycloakConfigResolver} instances but only those defined by applications or set as default by the configuration.
*
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class KeycloakSpringConfigResolverWrapper implements KeycloakConfigResolver {
private KeycloakConfigResolver delegate;
public KeycloakSpringConfigResolverWrapper(KeycloakConfigResolver delegate) {
this.delegate = delegate;
}
@Override
public KeycloakDeployment resolve(HttpFacade.Request facade) {
return delegate.resolve(facade);
}
protected void setDelegate(KeycloakConfigResolver delegate) {
this.delegate = delegate;
}
}
@@ -65,7 +65,7 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
protected AdapterDeploymentContext adapterDeploymentContext() throws Exception {
AdapterDeploymentContextFactoryBean factoryBean;
if (keycloakConfigResolver != null) {
factoryBean = new AdapterDeploymentContextFactoryBean(new KeycloakSpringConfigResolverWrapper(keycloakConfigResolver));
factoryBean = new AdapterDeploymentContextFactoryBean(keycloakConfigResolver);
}
else {
factoryBean = new AdapterDeploymentContextFactoryBean(keycloakConfigFileResource);
@@ -94,6 +94,11 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
return new KeycloakPreAuthActionsFilter(httpSessionManager());
}
@Bean
protected KeycloakAuthenticatedActionsFilter keycloakAuthenticatedActionsFilter() {
return new KeycloakAuthenticatedActionsFilter();
}
protected KeycloakCsrfRequestMatcher keycloakCsrfRequestMatcher() {
return new KeycloakCsrfRequestMatcher();
}
@@ -120,8 +125,8 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
.and()
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
.addFilterBefore(keycloakAuthenticationProcessingFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(keycloakAuthenticatedActionsFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(keycloakSecurityContextRequestFilter(), SecurityContextHolderAwareRequestFilter.class)
.addFilterAfter(keycloakAuthenticatedActionsRequestFilter(), KeycloakSecurityContextRequestFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.logout()
@@ -134,9 +139,4 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
protected KeycloakSecurityContextRequestFilter keycloakSecurityContextRequestFilter() {
return new KeycloakSecurityContextRequestFilter();
}
@Bean
protected KeycloakAuthenticatedActionsFilter keycloakAuthenticatedActionsRequestFilter() {
return new KeycloakAuthenticatedActionsFilter();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* 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");
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.springsecurity.filter;
import java.io.IOException;
@@ -25,84 +26,65 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
* Exposes a Keycloak adapter {@link AuthenticatedActionsHandler} as a Spring Security filter.
*
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
* @version $Revision: 1 $
*/
public class KeycloakAuthenticatedActionsFilter extends GenericFilterBean implements ApplicationContextAware {
private static final String FILTER_APPLIED = KeycloakAuthenticatedActionsFilter.class.getPackage().getName() + ".authenticated-actions";
private static final Logger log = LoggerFactory.getLogger(KeycloakAuthenticatedActionsFilter.class);
private final NodesRegistrationManagement management = new NodesRegistrationManagement();
private ApplicationContext applicationContext;
private AdapterDeploymentContext deploymentContext;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
if (request.getAttribute(FILTER_APPLIED) != null) {
filterChain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
KeycloakSecurityContext keycloakSecurityContext = getKeycloakPrincipal();
if (keycloakSecurityContext instanceof RefreshableKeycloakSecurityContext) {
HttpFacade facade = new SimpleHttpFacade((HttpServletRequest) request, (HttpServletResponse) response);
KeycloakDeployment deployment = resolveDeployment(request, response);
AuthenticatedActionsHandler actions = new AuthenticatedActionsHandler(deployment, OIDCHttpFacade.class.cast(facade));
if (actions.handledRequest()) {
return;
}
}
filterChain.doFilter(request, response);
public KeycloakAuthenticatedActionsFilter() {
super();
}
@Override
protected void initFilterBean() {
protected void initFilterBean() throws ServletException {
deploymentContext = applicationContext.getBean(AdapterDeploymentContext.class);
}
@Override
public void destroy() {
log.debug("Unregistering deployment");
management.stop();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpFacade facade = new SimpleHttpFacade((HttpServletRequest)request, (HttpServletResponse)response);
AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deploymentContext.resolveDeployment(facade), (OIDCHttpFacade)facade);
boolean handled = handler.handledRequest();
if (handled) {
log.debug("Authenticated filter handled request: {}", ((HttpServletRequest) request).getRequestURI());
} else {
chain.doFilter(request, response);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
private KeycloakSecurityContext getKeycloakPrincipal() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
Object principal = authentication.getPrincipal();
if (principal instanceof KeycloakPrincipal) {
return KeycloakPrincipal.class.cast(principal).getKeycloakSecurityContext();
}
}
return null;
}
private KeycloakDeployment resolveDeployment(ServletRequest servletRequest, ServletResponse servletResponse) {
return deploymentContext.resolveDeployment(new SimpleHttpFacade(HttpServletRequest.class.cast(servletRequest), HttpServletResponse.class.cast(servletResponse)));
}
private void clearAuthenticationContext() {
SecurityContextHolder.clearContext();
}
}
@@ -34,8 +34,7 @@ import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.springsecurity.KeycloakAuthenticationException;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationFailureHandler;
import org.keycloak.adapters.springsecurity.authentication.RequestAuthenticatorFactory;
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticatorFactory;
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
import org.keycloak.adapters.springsecurity.token.AdapterTokenStoreFactory;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@@ -84,7 +83,6 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
private AdapterDeploymentContext adapterDeploymentContext;
private AdapterTokenStoreFactory adapterTokenStoreFactory = new SpringSecurityAdapterTokenStoreFactory();
private AuthenticationManager authenticationManager;
private RequestAuthenticatorFactory requestAuthenticatorFactory = new SpringSecurityRequestAuthenticatorFactory();
/**
* Creates a new Keycloak authentication processing filter with given {@link AuthenticationManager} and the
@@ -144,7 +142,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
AdapterTokenStore tokenStore = adapterTokenStoreFactory.createAdapterTokenStore(deployment, request);
RequestAuthenticator authenticator
= requestAuthenticatorFactory.createRequestAuthenticator(facade, request, deployment, tokenStore, -1);
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
AuthOutcome result = authenticator.authenticate();
log.debug("Auth outcome: {}", result);
@@ -251,14 +249,4 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
public final void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
throw new UnsupportedOperationException("This filter does not support explicitly setting a continue chain before success policy");
}
/**
* Sets the request authenticator factory to use when creating per-request authenticators.
*
* @param requestAuthenticatorFactory the <code>RequestAuthenticatorFactory</code> to use
*/
public void setRequestAuthenticatorFactory(RequestAuthenticatorFactory requestAuthenticatorFactory) {
Assert.notNull(requestAuthenticatorFactory, "RequestAuthenticatorFactory cannot be null");
this.requestAuthenticatorFactory = requestAuthenticatorFactory;
}
}
}
@@ -48,11 +48,10 @@ public class KeycloakPreAuthActionsFilter extends GenericFilterBean implements A
private static final Logger log = LoggerFactory.getLogger(KeycloakPreAuthActionsFilter.class);
private NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
private final NodesRegistrationManagement management = new NodesRegistrationManagement();
private ApplicationContext applicationContext;
private AdapterDeploymentContext deploymentContext;
private UserSessionManagement userSessionManagement;
private PreAuthActionsHandlerFactory preAuthActionsHandlerFactory = new PreAuthActionsHandlerFactory();
public KeycloakPreAuthActionsFilter() {
super();
@@ -70,7 +69,7 @@ public class KeycloakPreAuthActionsFilter extends GenericFilterBean implements A
@Override
public void destroy() {
log.debug("Unregistering deployment");
nodesRegistrationManagement.stop();
management.stop();
}
@Override
@@ -78,8 +77,7 @@ public class KeycloakPreAuthActionsFilter extends GenericFilterBean implements A
throws IOException, ServletException {
HttpFacade facade = new SimpleHttpFacade((HttpServletRequest)request, (HttpServletResponse)response);
nodesRegistrationManagement.tryRegister(deploymentContext.resolveDeployment(facade));
PreAuthActionsHandler handler = preAuthActionsHandlerFactory.createPreAuthActionsHandler(facade);
PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
if (handler.handleRequest()) {
log.debug("Pre-auth filter handled request: {}", ((HttpServletRequest) request).getRequestURI());
} else {
@@ -95,23 +93,4 @@ public class KeycloakPreAuthActionsFilter extends GenericFilterBean implements A
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
void setNodesRegistrationManagement(NodesRegistrationManagement nodesRegistrationManagement) {
this.nodesRegistrationManagement = nodesRegistrationManagement;
}
void setPreAuthActionsHandlerFactory(PreAuthActionsHandlerFactory preAuthActionsHandlerFactory) {
this.preAuthActionsHandlerFactory = preAuthActionsHandlerFactory;
}
/**
* Creates {@link PreAuthActionsHandler}s.
*
* Package-private class to enable mocking.
*/
class PreAuthActionsHandlerFactory {
PreAuthActionsHandler createPreAuthActionsHandler(HttpFacade facade) {
return new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
}
}
}
@@ -70,8 +70,6 @@ public class KeycloakSecurityContextRequestFilter extends GenericFilterBean impl
clearAuthenticationContext();
}
}
request.setAttribute(KeycloakSecurityContext.class.getName(), keycloakSecurityContext);
}
filterChain.doFilter(request, response);
@@ -1,90 +0,0 @@
package org.keycloak.adapters.springsecurity.filter;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.PreAuthActionsHandler;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.UserSessionManagement;
import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.PreAuthActionsHandlerFactory;
import org.mockito.Mock;
import org.springframework.context.ApplicationContext;
public class KeycloakPreAuthActionsFilterTest {
private KeycloakPreAuthActionsFilter filter;
@Mock
private NodesRegistrationManagement nodesRegistrationManagement;
@Mock
private ApplicationContext applicationContext;
@Mock
private AdapterDeploymentContext deploymentContext;
@Mock
private PreAuthActionsHandlerFactory preAuthActionsHandlerFactory;
@Mock
private UserSessionManagement userSessionManagement;
@Mock
private PreAuthActionsHandler preAuthActionsHandler;
@Mock
private KeycloakDeployment deployment;
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
@Mock
private FilterChain chain;
@Before
public void setUp() throws Exception {
initMocks(this);
filter = new KeycloakPreAuthActionsFilter(userSessionManagement);
filter.setNodesRegistrationManagement(nodesRegistrationManagement);
filter.setApplicationContext(applicationContext);
filter.setPreAuthActionsHandlerFactory(preAuthActionsHandlerFactory);
when(applicationContext.getBean(AdapterDeploymentContext.class)).thenReturn(deploymentContext);
when(deploymentContext.resolveDeployment(any(HttpFacade.class))).thenReturn(deployment);
when(preAuthActionsHandlerFactory.createPreAuthActionsHandler(any(HttpFacade.class))).thenReturn(preAuthActionsHandler);
filter.initFilterBean();
}
@Test
public void shouldIgnoreChainWhenPreAuthActionHandlerHandled() throws Exception {
when(preAuthActionsHandler.handleRequest()).thenReturn(true);
filter.doFilter(request, response, chain);
verifyZeroInteractions(chain);
verify(nodesRegistrationManagement).tryRegister(deployment);
}
@Test
public void shouldContinueChainWhenPreAuthActionHandlerDidNotHandle() throws Exception {
when(preAuthActionsHandler.handleRequest()).thenReturn(false);
filter.doFilter(request, response, chain);
verify(chain).doFilter(request, response);;
verify(nodesRegistrationManagement).tryRegister(deployment);
}
@After
public void tearDown() {
filter.destroy();
verify(nodesRegistrationManagement).stop();
}
}
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak Tomcat Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -35,7 +35,6 @@
org.keycloak.adapters.undertow.*
</keycloak.osgi.export>
<keycloak.osgi.import>
io.undertow.*;version="[1.4,3)",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
+1 -6
View File
@@ -22,7 +22,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -95,10 +95,5 @@
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -78,7 +78,7 @@ public class ElytronAccount implements OidcKeycloakAccount {
return false;
}
boolean tryRefresh() {
boolean tryRefresh(CallbackHandler callbackHandler) {
log.debug("Trying to refresh");
RefreshableKeycloakSecurityContext securityContext = getKeycloakSecurityContext();
@@ -88,8 +88,14 @@ public class ElytronAccount implements OidcKeycloakAccount {
}
if (securityContext.refreshExpiredToken(false)) {
log.debug("refresh succeeded");
return true;
SecurityIdentity securityIdentity = SecurityIdentityUtil.authorize(callbackHandler, principal);
if (securityIdentity != null) {
log.debug("refresh succeeded");
return true;
}
return false;
}
return checkActive();
@@ -88,7 +88,7 @@ public class ElytronCookieTokenStore implements ElytronTokeStore {
boolean active = account.checkActive();
if (!active) {
active = account.tryRefresh();
active = account.tryRefresh(this.callbackHandler);
}
if (active) {
@@ -18,7 +18,6 @@
package org.keycloak.adapters.elytron;
import io.undertow.server.handlers.CookieImpl;
import org.bouncycastle.asn1.cmp.Challenge;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterDeploymentContext;
@@ -63,8 +62,6 @@ import java.util.function.Consumer;
*/
class ElytronHttpFacade implements OIDCHttpFacade {
static final String UNDERTOW_EXCHANGE = ElytronHttpFacade.class.getName() + ".undertow.exchange";
private final HttpServerRequest request;
private final CallbackHandler callbackHandler;
private final AdapterTokenStore tokenStore;
@@ -315,17 +312,6 @@ class ElytronHttpFacade implements OIDCHttpFacade {
@Override
public void resetCookie(final String name, final String path) {
responseConsumer = responseConsumer.andThen(response -> setCookie(name, "", path, null, 0, false, false, response));
HttpScope exchangeScope = getScope(Scope.EXCHANGE);
ProtectedHttpServerExchange undertowExchange = ProtectedHttpServerExchange.class.cast(exchangeScope.getAttachment(UNDERTOW_EXCHANGE));
if (undertowExchange != null) {
CookieImpl cookie = new CookieImpl(name, "");
cookie.setMaxAge(0);
cookie.setPath(path);
undertowExchange.getExchange().setResponseCookie(cookie);
}
}
@Override
@@ -104,7 +104,7 @@ public class ElytronSessionTokenStore implements ElytronTokeStore {
boolean active = account.checkActive();
if (!active) {
active = account.tryRefresh();
active = account.tryRefresh(this.callbackHandler);
}
if (active) {
@@ -43,8 +43,7 @@ import java.io.InputStream;
*/
public class KeycloakConfigurationServletListener implements ServletContextListener {
static final String ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE = AdapterDeploymentContext.class.getName();
static final String ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE_ELYTRON = AdapterDeploymentContext.class.getName() + ".elytron";
static final String ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE = AdapterDeploymentContext.class.getName() + ".elytron";
@Override
public void contextInitialized(ServletContextEvent sce) {
@@ -77,7 +76,6 @@ public class KeycloakConfigurationServletListener implements ServletContextListe
}
servletContext.setAttribute(ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE, deploymentContext);
servletContext.setAttribute(ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE_ELYTRON, deploymentContext);
}
@Override
@@ -126,7 +126,7 @@ class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticat
private AdapterDeploymentContext getDeploymentContext(HttpServerRequest request) {
if (this.deploymentContext == null) {
return (AdapterDeploymentContext) request.getScope(Scope.APPLICATION).getAttachment(KeycloakConfigurationServletListener.ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE_ELYTRON);
return (AdapterDeploymentContext) request.getScope(Scope.APPLICATION).getAttachment(KeycloakConfigurationServletListener.ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE);
}
return this.deploymentContext;
@@ -1,42 +0,0 @@
/*
* Copyright 2018 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.elytron;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import io.undertow.server.HttpHandler;
import io.undertow.servlet.ServletExtension;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.handlers.ServletRequestContext;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class KeycloakServletExtension implements ServletExtension {
@Override
public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) {
deploymentInfo.addOuterHandlerChainWrapper(handler -> (HttpHandler) exchange -> {
ServletRequest servletRequest = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY).getServletRequest();
servletRequest.setAttribute(ElytronHttpFacade.UNDERTOW_EXCHANGE, new ProtectedHttpServerExchange(exchange));
handler.handleRequest(exchange);
});
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2018 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.elytron;
import io.undertow.server.HttpServerExchange;
/**
* <p>A wrapper for {@code {@link HttpServerExchange}} accessible only from classes in the same package.
*
* <p>This class is used to provide to the elytron mechanism access to the current exchange in order to allow making
* changes to the exchange (e.g. response) during the evaluation of requests. By default, changes to the exchange are only
* propagated after the execution of the mechanism. But in certain situations, such as when making a programmatic logout (HttpServletRequest.logout()) from
* within application code, any change made to the exchange is not propagated.
*
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
class ProtectedHttpServerExchange {
private final HttpServerExchange exchange;
public ProtectedHttpServerExchange(HttpServerExchange exchange) {
this.exchange = exchange;
}
HttpServerExchange getExchange() {
return exchange;
}
}
@@ -1,52 +0,0 @@
#
# * Copyright 2018 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.
#
#
# * Copyright 2018 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.
#
#
# 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.
#
org.keycloak.adapters.elytron.KeycloakServletExtension
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak WildFly Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<version>4.6.0.Tmp</version>
<version>4.0.0.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
@@ -96,12 +96,6 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
.setValidator(new IntRangeValidator(-1, true))
.setAllowExpression(true)
.build();
protected static final SimpleAttributeDefinition PUBLIC_KEY_CACHE_TTL =
new SimpleAttributeDefinitionBuilder("public-key-cache-ttl", ModelType.INT, true)
.setXmlName("public-key-cache-ttl")
.setAllowExpression(true)
.setValidator(new IntRangeValidator(-1, true))
.build();
protected static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
@@ -114,7 +108,6 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION);
DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_KEY_CACHE_TTL);
}
protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@@ -187,20 +187,6 @@ public class SharedAttributeDefinitons {
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition PROXY_URL =
new SimpleAttributeDefinitionBuilder("proxy-url", ModelType.STRING, true)
.setXmlName("proxy-url")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition VERIFY_TOKEN_AUDIENCE =
new SimpleAttributeDefinitionBuilder("verify-token-audience", ModelType.BOOLEAN, true)
.setXmlName("verify-token-audience")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
ATTRIBUTES.add(REALM_PUBLIC_KEY);
@@ -228,8 +214,6 @@ public class SharedAttributeDefinitons {
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
ATTRIBUTES.add(AUTODETECT_BEARER_ONLY);
ATTRIBUTES.add(IGNORE_OAUTH_QUERY_PARAMETER);
ATTRIBUTES.add(PROXY_URL);
ATTRIBUTES.add(VERIFY_TOKEN_AUDIENCE);
}
/**
@@ -49,8 +49,6 @@ keycloak.realm.token-store=cookie or session storage for auth session data
keycloak.realm.principal-attribute=token attribute to use to set Principal name
keycloak.realm.autodetect-bearer-only=autodetect bearer-only requests
keycloak.realm.ignore-oauth-query-parameter=disable query parameter parsing for access_token
keycloak.realm.proxy-url=The URL for the HTTP proxy if one is used.
keycloak.realm.verify-token-audience=If true, then during bearer-only authentication, the adapter will verify if token contains this client name (resource) as an audience
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
@@ -88,11 +86,9 @@ keycloak.secure-deployment.principal-attribute=token attribute to use to set Pri
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.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less
keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
keycloak.secure-deployment.public-key-cache-ttl=Maximum time the downloaded public keys are considered valid. When this time reach, the adapter is forced to download public keys from keycloak server
keycloak.secure-deployment.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment.ignore-oauth-query-parameter=disable query parameter parsing for access_token
keycloak.secure-deployment.proxy-url=The URL for the HTTP proxy if one is used.
keycloak.secure-deployment.verify-token-audience=If true, then during bearer-only authentication, the adapter will verify if token contains this client name (resource) as an audience
keycloak.secure-deployment.credential=Credential value
keycloak.credential=Credential
@@ -68,8 +68,6 @@
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="ignore-oauth-query-parameter" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="verify-token-audience" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
@@ -113,11 +111,8 @@
<xs:element name="turn-off-change-session-id-on-login" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="token-minimum-time-to-live" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="min-time-between-jwks-requests" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="public-key-cache-ttl" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="ignore-oauth-query-parameter" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="verify-token-audience" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
@@ -23,14 +23,11 @@
<turn-off-change-session-id-on-login>false</turn-off-change-session-id-on-login>
<token-minimum-time-to-live>10</token-minimum-time-to-live>
<min-time-between-jwks-requests>20</min-time-between-jwks-requests>
<public-key-cache-ttl>3600</public-key-cache-ttl>
<realm-public-key>
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4siLKUew0WYxdtq6/rwk4Uj/4amGFFnE/yzIxQVU0PUqz3QBRVkUWpDj0K6ZnS5nzJV/y6DHLEy7hjZTdRDphyF1sq09aDOYnVpzu8o2sIlMM8q5RnUyEfIyUZqwo8pSZDJ90fS0s+IDUJNCSIrAKO3w1lqZDHL6E/YFHXyzkvQIDAQAB
</realm-public-key>
<auth-server-url>http://localhost:8080/auth</auth-server-url>
<ssl-required>EXTERNAL</ssl-required>
<proxy-url>http://localhost:9000</proxy-url>
<verify-token-audience>true</verify-token-audience>
<credential name="secret">0aa31d98-e0aa-404c-b6e0-e771dba1e798</credential>
</secure-deployment>
<secure-deployment name="http-endpoint">
Loaded 100 of 2355 files, more files were not shown because too many files have changed in this diff. Show more