Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e93c4724b |
No files matched your search
+1
-4
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
-----------------
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
|
||||
+26
-47
@@ -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;
|
||||
@@ -29,7 +28,6 @@ import org.jboss.logging.Logger;
|
||||
import org.keycloak.AuthorizationContext;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.HttpFacade.Request;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.ClientAuthorizationContext;
|
||||
@@ -68,16 +66,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);
|
||||
}
|
||||
@@ -156,7 +146,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) {
|
||||
@@ -175,7 +165,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||
policyEnforcer.getPathMatcher().removeFromCache(getPath(request));
|
||||
}
|
||||
|
||||
return hasValidClaims(actualPathConfig, permission, httpFacade, authorization);
|
||||
return hasValidClaims(actualPathConfig, httpFacade, authorization);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -197,22 +187,35 @@ public abstract class AbstractPolicyEnforcer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasValidClaims(PathConfig actualPathConfig, Permission permission, OIDCHttpFacade httpFacade, Authorization authorization) {
|
||||
Map<String, Set<String>> grantedClaims = permission.getClaims();
|
||||
private boolean hasValidClaims(PathConfig actualPathConfig, OIDCHttpFacade httpFacade, Authorization authorization) {
|
||||
Map<String, Map<String, Object>> claimInformationPointConfig = actualPathConfig.getClaimInformationPointConfig();
|
||||
|
||||
if (grantedClaims != null) {
|
||||
Map<String, List<String>> claims = resolveClaims(actualPathConfig, httpFacade);
|
||||
if (claimInformationPointConfig != null) {
|
||||
Map<String, List<String>> claims = new HashMap<>();
|
||||
|
||||
if (claims.isEmpty()) {
|
||||
return false;
|
||||
for (Entry<String, Map<String, Object>> entry : claimInformationPointConfig.entrySet()) {
|
||||
ClaimInformationPointProviderFactory factory = policyEnforcer.getClaimInformationPointProviderFactories().get(entry.getKey());
|
||||
|
||||
if (factory == null) {
|
||||
throw new RuntimeException("Could not find claim information provider with name [" + entry.getKey() + "]");
|
||||
}
|
||||
|
||||
claims.putAll(factory.create(entry.getValue()).resolve(httpFacade));
|
||||
}
|
||||
|
||||
for (Entry<String, Set<String>> entry : grantedClaims.entrySet()) {
|
||||
List<String> requestClaims = claims.get(entry.getKey());
|
||||
Map<String, List<String>> grantedClaims = authorization.getClaims();
|
||||
|
||||
if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
|
||||
if (grantedClaims != null) {
|
||||
if (claims.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (Entry<String, List<String>> entry : grantedClaims.entrySet()) {
|
||||
List<String> requestClaims = claims.get(entry.getKey());
|
||||
|
||||
if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,28 +342,4 @@ public abstract class AbstractPolicyEnforcer {
|
||||
private PathConfig getPathConfig(Request request) {
|
||||
return isDefaultAccessDeniedUri(request) ? null : policyEnforcer.getPathMatcher().matches(getPath(request));
|
||||
}
|
||||
|
||||
protected Map<String, List<String>> resolveClaims(PathConfig pathConfig, OIDCHttpFacade httpFacade) {
|
||||
Map<String, List<String>> claims = getClaims(getEnforcerConfig().getClaimInformationPointConfig(), httpFacade);
|
||||
|
||||
claims.putAll(getClaims(pathConfig.getClaimInformationPointConfig(), httpFacade));
|
||||
|
||||
return claims;
|
||||
}
|
||||
|
||||
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) {
|
||||
claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return claims;
|
||||
}
|
||||
}
|
||||
+65
-30
@@ -18,10 +18,11 @@
|
||||
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;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
@@ -78,14 +79,27 @@ 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)) {
|
||||
grantedPermissions.add(newPermission);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<String>> newClaims = newAuthorization.getClaims();
|
||||
|
||||
if (newClaims != null) {
|
||||
Map<String, List<String>> claims = authorization.getClaims();
|
||||
|
||||
if (claims == null) {
|
||||
claims = new HashMap<>();
|
||||
authorization.setClaims(claims);
|
||||
}
|
||||
|
||||
claims.putAll(newClaims);
|
||||
}
|
||||
}
|
||||
|
||||
original.setAuthorization(authorization);
|
||||
@@ -122,6 +136,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();
|
||||
|
||||
@@ -134,7 +154,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
|
||||
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
|
||||
if (getEnforcerConfig().getUserManagedAccess() != null) {
|
||||
if (getPolicyEnforcer().getDeployment().isBearerOnly() || (isBearerAuthorization(httpFacade) && getEnforcerConfig().getUserManagedAccess() != null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -145,15 +165,20 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
AccessToken accessToken = securityContext.getToken();
|
||||
AuthorizationRequest authzRequest = new AuthorizationRequest();
|
||||
|
||||
if (isBearerAuthorization(httpFacade) || accessToken.getAuthorization() != null) {
|
||||
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
|
||||
}
|
||||
if (getEnforcerConfig().getUserManagedAccess() != null) {
|
||||
String ticket = getPermissionTicket(pathConfig, methodConfig, getAuthzClient(), httpFacade);
|
||||
authzRequest.setTicket(ticket);
|
||||
} else {
|
||||
if (accessToken.getAuthorization() != null) {
|
||||
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
|
||||
}
|
||||
|
||||
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
|
||||
Map<String, List<String>> claims = getClaims(pathConfig, httpFacade);
|
||||
|
||||
if (!claims.isEmpty()) {
|
||||
authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
|
||||
authzRequest.setClaimToken(Base64.encodeBytes(JsonSerialization.writeValueAsBytes(claims)));
|
||||
if (!claims.isEmpty()) {
|
||||
authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
|
||||
authzRequest.setClaimToken(Base64.encodeBytes(JsonSerialization.writeValueAsBytes(claims)));
|
||||
}
|
||||
}
|
||||
|
||||
if (accessToken.getAuthorization() != null) {
|
||||
@@ -161,14 +186,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
|
||||
LOGGER.debug("Obtaining authorization for authenticated user.");
|
||||
AuthorizationResponse authzResponse;
|
||||
|
||||
if (isBearerAuthorization(httpFacade)) {
|
||||
authzRequest.setSubjectToken(accessTokenString);
|
||||
authzResponse = getAuthzClient().authorization().authorize(authzRequest);
|
||||
} else {
|
||||
authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
|
||||
}
|
||||
AuthorizationResponse authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
|
||||
|
||||
if (authzResponse != null) {
|
||||
return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
|
||||
@@ -182,7 +200,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, OIDCHttpFacade httpFacade) {
|
||||
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, HttpFacade httpFacade) {
|
||||
if (getEnforcerConfig().getUserManagedAccess() != null) {
|
||||
ProtectionResource protection = authzClient.protection();
|
||||
PermissionResource permission = protection.permission();
|
||||
@@ -191,7 +209,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
permissionRequest.setResourceId(pathConfig.getId());
|
||||
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
|
||||
|
||||
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
|
||||
Map<String, List<String>> claims = getClaims(pathConfig, httpFacade);
|
||||
|
||||
if (!claims.isEmpty()) {
|
||||
permissionRequest.setClaims(claims);
|
||||
@@ -203,18 +221,35 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isBearerAuthorization(OIDCHttpFacade httpFacade) {
|
||||
List<String> authHeaders = httpFacade.getRequest().getHeaders("Authorization");
|
||||
private Map<String, List<String>> getClaims(PathConfig pathConfig, HttpFacade httpFacade) {
|
||||
Map<String, List<String>> claims = new HashMap<>();
|
||||
Map<String, Map<String, Object>> claimInformationPointConfig = pathConfig.getClaimInformationPointConfig();
|
||||
|
||||
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 (claimInformationPointConfig != null) {
|
||||
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
|
||||
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
|
||||
|
||||
if (factory != null) {
|
||||
claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
|
||||
}
|
||||
}
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
return getPolicyEnforcer().getDeployment().isBearerOnly();
|
||||
private boolean isBearerAuthorization(OIDCHttpFacade httpFacade) {
|
||||
List<String> authHeaders = httpFacade.getRequest().getHeaders("Authorization");
|
||||
if (authHeaders == null || authHeaders.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-17
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,17 +271,7 @@ public class PolicyEnforcer {
|
||||
List<ResourceRepresentation> matchingResources = authzClient.protection().resource().findByMatchingUri(targetUri);
|
||||
|
||||
if (!matchingResources.isEmpty()) {
|
||||
Map<String, Map<String, Object>> cipConfig = null;
|
||||
|
||||
if (pathConfig != null) {
|
||||
cipConfig = pathConfig.getClaimInformationPointConfig();
|
||||
}
|
||||
|
||||
pathConfig = PathConfig.createPathConfigs(matchingResources.get(0)).iterator().next();
|
||||
|
||||
if (cipConfig != null) {
|
||||
pathConfig.setClaimInformationPointConfig(cipConfig);
|
||||
}
|
||||
pathConfig = PathConfig.createPathConfig(matchingResources.get(0));
|
||||
}
|
||||
} catch (Exception cause) {
|
||||
LOGGER.errorf(cause, "Could not lazy load resource with path [" + targetUri + "] from server");
|
||||
@@ -324,13 +313,12 @@ 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());
|
||||
config.setParentConfig(originalConfig);
|
||||
config.setEnforcementMode(originalConfig.getEnforcementMode());
|
||||
config.setClaimInformationPointConfig(originalConfig.getClaimInformationPointConfig());
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
-8
@@ -166,13 +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 List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
@@ -199,7 +192,6 @@ class SharedAttributeDefinitons {
|
||||
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
|
||||
ATTRIBUTES.add(TOKEN_STORE);
|
||||
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
|
||||
ATTRIBUTES.add(PROXY_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-2
@@ -46,7 +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.secure-deployment=A deployment secured by Keycloak
|
||||
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
|
||||
@@ -83,7 +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.proxy-url=The URL for the HTTP proxy if one is used.
|
||||
|
||||
keycloak.secure-deployment.credential=Credential value
|
||||
|
||||
keycloak.credential=Credential
|
||||
|
||||
@@ -65,7 +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:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
@@ -108,7 +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="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -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>
|
||||
|
||||
|
||||
+140
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
|
||||
+1
-6
@@ -86,9 +86,6 @@ public class PaxWebIntegrationService {
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
public void setHttpContext(HttpContext httpContext) {
|
||||
this.httpContext = httpContext;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
ServiceTrackerCustomizer trackerCustomizer = new ServiceTrackerCustomizer() {
|
||||
@@ -118,9 +115,7 @@ public class PaxWebIntegrationService {
|
||||
|
||||
protected WebContainer addingWebContainerCallback(ServiceReference webContainerServiceReference) {
|
||||
WebContainer service = (WebContainer) bundleContext.getService(webContainerServiceReference);
|
||||
if (httpContext == null) {
|
||||
httpContext = service.createDefaultHttpContext();
|
||||
}
|
||||
httpContext = service.createDefaultHttpContext();
|
||||
|
||||
addJettyWebXml(service);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -38,7 +38,7 @@
|
||||
!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)",
|
||||
@@ -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>
|
||||
|
||||
+1
-6
@@ -70,9 +70,6 @@ public class PaxWebIntegrationService {
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
public void setHttpContext(HttpContext httpContext) {
|
||||
this.httpContext = httpContext;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
ServiceTrackerCustomizer trackerCustomizer = new ServiceTrackerCustomizer() {
|
||||
@@ -102,9 +99,7 @@ public class PaxWebIntegrationService {
|
||||
|
||||
protected WebContainer addingWebContainerCallback(ServiceReference webContainerServiceReference) {
|
||||
WebContainer service = (WebContainer) bundleContext.getService(webContainerServiceReference);
|
||||
if (httpContext == null) {
|
||||
httpContext = service.createDefaultHttpContext();
|
||||
}
|
||||
httpContext = service.createDefaultHttpContext();
|
||||
|
||||
if (constraintMappings == null) {
|
||||
throw new IllegalStateException("constraintMappings was null!");
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak Jetty Integration</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+12
-28
@@ -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
|
||||
@@ -167,14 +161,6 @@ declare namespace Keycloak {
|
||||
* the user's profile to a new preferred locale.
|
||||
*/
|
||||
kcLocale?: string;
|
||||
|
||||
/**
|
||||
* Specifies arguments that are passed to the Cordova in-app-browser (if applicable).
|
||||
* Options 'hidden' and 'location' are not affected by these arguments.
|
||||
* All available options are defined at https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/.
|
||||
* Example of use: { zoom: "no", hardwareback: "yes" }
|
||||
*/
|
||||
cordovaOptions?: { [optionName: string]: string };
|
||||
}
|
||||
|
||||
type KeycloakPromiseCallback<T> = (result: T) => void;
|
||||
@@ -216,16 +202,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 +255,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 +273,7 @@ declare namespace Keycloak {
|
||||
/**
|
||||
* The parsed refresh token as a JavaScript object.
|
||||
*/
|
||||
refreshTokenParsed?: KeycloakTokenParsed;
|
||||
refreshTokenParsed?: { nonce?: string };
|
||||
|
||||
/**
|
||||
* The base64 encoded ID token.
|
||||
@@ -299,7 +283,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) {
|
||||
@@ -253,16 +250,7 @@
|
||||
baseUrl = kc.endpoints.authorize();
|
||||
}
|
||||
|
||||
var scope;
|
||||
if (options && options.scope) {
|
||||
if (options.scope.indexOf("openid") != -1) {
|
||||
scope = options.scope;
|
||||
} else {
|
||||
scope = "openid " + options.scope;
|
||||
}
|
||||
} else {
|
||||
scope = "openid";
|
||||
}
|
||||
var scope = (options && options.scope) ? "openid " + options.scope : "openid";
|
||||
|
||||
var url = baseUrl
|
||||
+ '?client_id=' + encodeURIComponent(kc.clientId)
|
||||
@@ -910,7 +898,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 +1330,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
@@ -94,12 +99,6 @@
|
||||
<version>${cxf.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
-89
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+22
-135
@@ -16,169 +16,56 @@
|
||||
*/
|
||||
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;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
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);
|
||||
String path = request.getURI();
|
||||
String[] urlTokens = path.split("/");
|
||||
if (urlTokens.length < 4) {
|
||||
throw new IllegalStateException("Not able to determine the web-context to load the correspondent keycloak.json file");
|
||||
}
|
||||
|
||||
return getOrCreateDeployment(webContext);
|
||||
}
|
||||
String webContext = urlTokens[3];
|
||||
|
||||
/**
|
||||
* {@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);
|
||||
KeycloakDeployment deployment = cache.get(webContext);
|
||||
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.");
|
||||
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;
|
||||
}
|
||||
|
||||
File configuration = new File(keycloakConfigLocation, pathFragment + ("".equals(pathFragment) ? "" : "-")
|
||||
+ "keycloak.json");
|
||||
if (!cacheConfiguration(pathFragment, configuration)) {
|
||||
throw new IllegalStateException("Not able to read the file " + configuration);
|
||||
String absolutePath = keycloakConfig + File.separator + webContext + "-keycloak.json";
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(absolutePath);
|
||||
} catch (FileNotFoundException e){
|
||||
throw new IllegalStateException("Not able to find the file " + absolutePath);
|
||||
}
|
||||
deployment = getCachedDeployment(pathFragment);
|
||||
deployment = KeycloakDeploymentBuilder.build(is);
|
||||
cache.put(webContext, deployment);
|
||||
}
|
||||
|
||||
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;
|
||||
if (relativePath == null || !uri.contains(relativePath)) {
|
||||
String[] urlTokens = uri.split("/");
|
||||
if (urlTokens.length < 4) {
|
||||
throw new IllegalStateException("Not able to determine the web-context to load the correspondent keycloak.json file");
|
||||
}
|
||||
|
||||
webContext = urlTokens[3];
|
||||
} else {
|
||||
URI parsedURI = URI.create(uri);
|
||||
String path = parsedURI.getPath();
|
||||
if (path.contains(relativePath)) {
|
||||
path = path.substring(0, path.indexOf(relativePath));
|
||||
}
|
||||
while (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
webContext = path;
|
||||
if ("".equals(webContext)) {
|
||||
path = relativePath;
|
||||
while (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
if (path.contains("/")) {
|
||||
path = path.substring(0, path.indexOf("/"));
|
||||
}
|
||||
webContext = path;
|
||||
}
|
||||
}
|
||||
|
||||
return webContext;
|
||||
}
|
||||
|
||||
}
|
||||
-162
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-149
@@ -1,149 +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.junit.Assert.assertNotNull;
|
||||
|
||||
public class PathBasedKeycloakConfigResolverTest {
|
||||
|
||||
@Test
|
||||
public void relativeURIsAndContexts() throws Exception {
|
||||
PathBasedKeycloakConfigResolver resolver = new PathBasedKeycloakConfigResolver();
|
||||
|
||||
assertNotNull(populate(resolver, "test")
|
||||
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/a/b/c")));
|
||||
|
||||
assertNotNull(populate(resolver, "test")
|
||||
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/a/b")));
|
||||
|
||||
// means default context and actually we use first segment
|
||||
assertNotNull(populate(resolver, "test")
|
||||
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/test/a/b/c")));
|
||||
|
||||
assertNotNull(populate(resolver, "test/a")
|
||||
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/b/c")));
|
||||
|
||||
assertNotNull(populate(resolver, "")
|
||||
.resolve(new MockRequest("http://localhost/", "/")));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private PathBasedKeycloakConfigResolver populate(PathBasedKeycloakConfigResolver resolver, String context)
|
||||
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(context, new KeycloakDeployment());
|
||||
|
||||
return resolver;
|
||||
}
|
||||
|
||||
private class MockRequest implements OIDCHttpFacade.Request {
|
||||
|
||||
private String uri;
|
||||
private String relativePath;
|
||||
|
||||
public MockRequest(String uri, String relativePath) {
|
||||
this.uri = uri;
|
||||
this.relativePath = relativePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getURI() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativePath() {
|
||||
return this.relativePath;
|
||||
}
|
||||
|
||||
@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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak OIDC Client Adapter Modules</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+17
-24
@@ -25,16 +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.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;
|
||||
@@ -176,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();
|
||||
@@ -185,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 {
|
||||
@@ -227,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()) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-boot-container-bundle</artifactId>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
-19
@@ -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);
|
||||
}
|
||||
-17
@@ -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);
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -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();
|
||||
}
|
||||
}
|
||||
+35
-53
@@ -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();
|
||||
}
|
||||
}
|
||||
+3
-15
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-2
@@ -70,8 +70,6 @@ public class KeycloakSecurityContextRequestFilter extends GenericFilterBean impl
|
||||
clearAuthenticationContext();
|
||||
}
|
||||
}
|
||||
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), keycloakSecurityContext);
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak Tomcat Integration</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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>
|
||||
+9
-3
@@ -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();
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
-14
@@ -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
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+1
-3
@@ -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
|
||||
|
||||
+2
-2
@@ -46,7 +46,7 @@ import org.wildfly.security.http.Scope;
|
||||
*/
|
||||
class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticationMechanism {
|
||||
|
||||
static Logger LOGGER = Logger.getLogger(KeycloakHttpServerAuthenticationMechanism.class);
|
||||
static Logger LOGGER = Logger.getLogger(KeycloakHttpServerAuthenticationMechanismFactory.class);
|
||||
static final String NAME = "KEYCLOAK";
|
||||
|
||||
private final Map<String, ?> properties;
|
||||
@@ -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;
|
||||
|
||||
-42
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
-42
@@ -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;
|
||||
}
|
||||
}
|
||||
-52
@@ -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
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak WildFly Integration</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
-8
@@ -187,13 +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 List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
static {
|
||||
ATTRIBUTES.add(REALM_PUBLIC_KEY);
|
||||
@@ -221,7 +214,6 @@ public class SharedAttributeDefinitons {
|
||||
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
|
||||
ATTRIBUTES.add(AUTODETECT_BEARER_ONLY);
|
||||
ATTRIBUTES.add(IGNORE_OAUTH_QUERY_PARAMETER);
|
||||
ATTRIBUTES.add(PROXY_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-2
@@ -49,7 +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.secure-deployment=A deployment secured by Keycloak
|
||||
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
|
||||
@@ -89,7 +88,7 @@ keycloak.secure-deployment.token-minimum-time-to-live=The adapter will refresh t
|
||||
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.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.credential=Credential value
|
||||
|
||||
keycloak.credential=Credential
|
||||
|
||||
@@ -68,7 +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:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
@@ -114,7 +113,6 @@
|
||||
<xs:element name="min-time-between-jwks-requests" 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:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
|
||||
-1
@@ -28,7 +28,6 @@
|
||||
</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>
|
||||
<credential name="secret">0aa31d98-e0aa-404c-b6e0-e771dba1e798</credential>
|
||||
</secure-deployment>
|
||||
<secure-deployment name="http-endpoint">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+16
-61
@@ -17,6 +17,21 @@
|
||||
|
||||
package org.keycloak.adapters.wildfly;
|
||||
|
||||
import io.undertow.security.api.SecurityContext;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.security.NestableGroup;
|
||||
import org.jboss.security.SecurityConstants;
|
||||
import org.jboss.security.SecurityContextAssociation;
|
||||
import org.jboss.security.SimpleGroup;
|
||||
import org.jboss.security.SimplePrincipal;
|
||||
import org.keycloak.adapters.AdapterTokenStore;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.undertow.KeycloakUndertowAccount;
|
||||
import org.keycloak.adapters.undertow.ServletRequestAuthenticator;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import java.security.Principal;
|
||||
import java.security.acl.Group;
|
||||
import java.util.Collection;
|
||||
@@ -24,33 +39,11 @@ import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.security.NestableGroup;
|
||||
import org.jboss.security.SecurityConstants;
|
||||
import org.jboss.security.SecurityContextAssociation;
|
||||
import org.jboss.security.SimpleGroup;
|
||||
import org.jboss.security.SimplePrincipal;
|
||||
import org.jboss.security.SubjectInfo;
|
||||
import org.jboss.security.identity.RoleGroup;
|
||||
import org.jboss.security.identity.plugins.SimpleRole;
|
||||
import org.jboss.security.identity.plugins.SimpleRoleGroup;
|
||||
import org.keycloak.adapters.AdapterTokenStore;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.undertow.KeycloakUndertowAccount;
|
||||
import org.keycloak.adapters.undertow.ServletRequestAuthenticator;
|
||||
|
||||
import io.undertow.security.api.SecurityContext;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class WildflyRequestAuthenticator extends ServletRequestAuthenticator
|
||||
{
|
||||
public class WildflyRequestAuthenticator extends ServletRequestAuthenticator {
|
||||
protected static Logger log = Logger.getLogger(WildflyRequestAuthenticator.class);
|
||||
|
||||
public WildflyRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort,
|
||||
@@ -94,13 +87,6 @@ public class WildflyRequestAuthenticator extends ServletRequestAuthenticator
|
||||
org.jboss.security.SecurityContext sc = SecurityContextAssociation.getSecurityContext();
|
||||
Principal userPrincipal = getPrincipal(subject);
|
||||
sc.getUtil().createSubjectInfo(userPrincipal, account, subject);
|
||||
|
||||
// Roles of subjectInfo are null, because is was constructed by
|
||||
// org.jboss.security.identity.extensions.CredentialIdentityFactory
|
||||
// .createIdentity(Principal [=userPrincipal], Object [=account], Role [=null]).
|
||||
// Therefore the roles are only contained in the authenticatedSubject (member of subjectInfo)
|
||||
// and subsequent logics do only access subjectInfo#roles instead of authenticatedSubject#roles.
|
||||
mapGroupMembersOfAuthenticatedSubjectIntoSecurityContext(sc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,35 +150,4 @@ public class WildflyRequestAuthenticator extends ServletRequestAuthenticator
|
||||
return roleSets;
|
||||
}
|
||||
|
||||
private static void mapGroupMembersOfAuthenticatedSubjectIntoSecurityContext(org.jboss.security.SecurityContext sc) {
|
||||
SubjectInfo subjectInfo = sc.getSubjectInfo();
|
||||
if (subjectInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Subject authenticatedSubject = subjectInfo.getAuthenticatedSubject();
|
||||
if (authenticatedSubject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get role group of security context in order to add roles of authenticatedSubject.
|
||||
RoleGroup scRoles = sc.getUtil().getRoles();
|
||||
if (scRoles == null) {
|
||||
scRoles = new SimpleRoleGroup("Roles");
|
||||
sc.getUtil().setRoles(scRoles);
|
||||
}
|
||||
|
||||
// Get group roles of authenticatedSubject and add each role of the group into security context
|
||||
Iterator<Principal> principalItr = authenticatedSubject.getPrincipals().iterator();
|
||||
while (principalItr.hasNext()) {
|
||||
Principal principal = principalItr.next();
|
||||
if (principal instanceof Group) {
|
||||
Enumeration<? extends Principal> members = ((Group) principal).members();
|
||||
while (members.hasMoreElements()) {
|
||||
Principal role = members.nextElement();
|
||||
scRoles.addRole(new SimpleRole(role.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
-58
@@ -1,58 +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.subsystem.adapter.extension;
|
||||
|
||||
import org.jboss.as.server.deployment.DeploymentPhaseContext;
|
||||
import org.jboss.as.server.deployment.DeploymentUnit;
|
||||
import org.jboss.as.web.common.WarMetaData;
|
||||
import org.jboss.metadata.web.jboss.JBossWebMetaData;
|
||||
import org.jboss.msc.service.ServiceName;
|
||||
|
||||
/**
|
||||
* Utility class for Elytron integration
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public final class Elytron {
|
||||
|
||||
private static final String DEFAULT_SECURITY_DOMAIN = "other";
|
||||
private static final String UNDERTOW_APPLICATION_SECURITY_DOMAIN = "org.wildfly.undertow.application-security-domain.";
|
||||
|
||||
static boolean isElytronEnabled(DeploymentPhaseContext phaseContext) {
|
||||
String securityDomain = getSecurityDomain(phaseContext.getDeploymentUnit());
|
||||
ServiceName serviceName = ServiceName.parse(new StringBuilder(UNDERTOW_APPLICATION_SECURITY_DOMAIN).append(securityDomain).toString());
|
||||
return phaseContext.getServiceRegistry().getService(serviceName) != null;
|
||||
}
|
||||
|
||||
private static String getSecurityDomain(DeploymentUnit deploymentUnit) {
|
||||
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
|
||||
|
||||
if (warMetaData != null) {
|
||||
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
|
||||
|
||||
if (webMetaData != null) {
|
||||
String configuredSecurityDomain = webMetaData.getSecurityDomain();
|
||||
|
||||
if (configuredSecurityDomain != null) {
|
||||
return configuredSecurityDomain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DEFAULT_SECURITY_DOMAIN;
|
||||
}
|
||||
}
|
||||
+6
-11
@@ -17,8 +17,6 @@
|
||||
|
||||
package org.keycloak.subsystem.adapter.extension;
|
||||
|
||||
import static org.keycloak.subsystem.adapter.extension.Elytron.isElytronEnabled;
|
||||
|
||||
import org.jboss.as.server.deployment.DeploymentPhaseContext;
|
||||
import org.jboss.as.server.deployment.DeploymentUnit;
|
||||
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
|
||||
@@ -75,7 +73,7 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
|
||||
addKeycloakAuthData(phaseContext, service);
|
||||
}
|
||||
|
||||
addConfigurationListener(phaseContext);
|
||||
addConfigurationListener(deploymentUnit);
|
||||
|
||||
// FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
|
||||
|
||||
@@ -127,8 +125,7 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
|
||||
webMetaData.setContextParams(contextParams);
|
||||
}
|
||||
|
||||
private void addConfigurationListener(DeploymentPhaseContext phaseContext) {
|
||||
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
|
||||
private void addConfigurationListener(DeploymentUnit deploymentUnit) {
|
||||
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
|
||||
if (warMetaData == null) {
|
||||
return;
|
||||
@@ -147,18 +144,16 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
|
||||
if (!loginConfig.getAuthMethod().equals("KEYCLOAK")) {
|
||||
return;
|
||||
}
|
||||
ListenerMetaData listenerMetaData = new ListenerMetaData();
|
||||
|
||||
if (isElytronEnabled(phaseContext)) {
|
||||
ListenerMetaData listenerMetaData = new ListenerMetaData();
|
||||
listenerMetaData.setListenerClass(KeycloakConfigurationServletListener.class.getName());
|
||||
|
||||
listenerMetaData.setListenerClass(KeycloakConfigurationServletListener.class.getName());
|
||||
|
||||
webMetaData.getListeners().add(listenerMetaData);
|
||||
}
|
||||
webMetaData.getListeners().add(listenerMetaData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undeploy(DeploymentUnit du) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -63,7 +63,7 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
|
||||
final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
|
||||
final ModuleLoader moduleLoader = Module.getBootModuleLoader();
|
||||
addCommonModules(moduleSpecification, moduleLoader);
|
||||
addPlatformSpecificModules(phaseContext, moduleSpecification, moduleLoader);
|
||||
addPlatformSpecificModules(moduleSpecification, moduleLoader);
|
||||
}
|
||||
|
||||
private void addCommonModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
|
||||
@@ -74,7 +74,7 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
|
||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
|
||||
}
|
||||
|
||||
abstract protected void addPlatformSpecificModules(DeploymentPhaseContext phaseContext, ModuleSpecification moduleSpecification, ModuleLoader moduleLoader);
|
||||
abstract protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader);
|
||||
|
||||
@Override
|
||||
public void undeploy(DeploymentUnit du) {
|
||||
|
||||
+4
-10
@@ -17,9 +17,6 @@
|
||||
|
||||
package org.keycloak.subsystem.adapter.extension;
|
||||
|
||||
import static org.keycloak.subsystem.adapter.extension.Elytron.isElytronEnabled;
|
||||
|
||||
import org.jboss.as.server.deployment.DeploymentPhaseContext;
|
||||
import org.jboss.as.server.deployment.module.ModuleDependency;
|
||||
import org.jboss.as.server.deployment.module.ModuleSpecification;
|
||||
import org.jboss.modules.ModuleIdentifier;
|
||||
@@ -37,12 +34,9 @@ public class KeycloakDependencyProcessorWildFly extends KeycloakDependencyProces
|
||||
private static final ModuleIdentifier KEYCLOAK_UNDERTOW_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-undertow-adapter");
|
||||
|
||||
@Override
|
||||
protected void addPlatformSpecificModules(DeploymentPhaseContext phaseContext, ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
|
||||
if (isElytronEnabled(phaseContext)) {
|
||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_ELYTRON_ADAPTER, true, false, true, false));
|
||||
} else {
|
||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_WILDFLY_ADAPTER, false, false, true, false));
|
||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, false, false));
|
||||
}
|
||||
protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
|
||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_WILDFLY_ADAPTER, false, false, true, false));
|
||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, false, false));
|
||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_ELYTRON_ADAPTER, true, false, false, false));
|
||||
}
|
||||
}
|
||||
-8
@@ -193,13 +193,6 @@ public class SharedAttributeDefinitons {
|
||||
.setDefaultValue(new ModelNode(8443))
|
||||
.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 List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
static {
|
||||
@@ -229,7 +222,6 @@ public class SharedAttributeDefinitons {
|
||||
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
|
||||
ATTRIBUTES.add(AUTODETECT_BEARER_ONLY);
|
||||
ATTRIBUTES.add(IGNORE_OAUTH_QUERY_PARAMETER);
|
||||
ATTRIBUTES.add(PROXY_URL);
|
||||
}
|
||||
|
||||
private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) {
|
||||
|
||||
-3
@@ -52,7 +52,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.secure-deployment=A deployment secured by Keycloak
|
||||
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
|
||||
@@ -94,7 +93,6 @@ keycloak.secure-deployment.turn-off-change-session-id-on-login=The session id is
|
||||
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.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-server=A deployment secured by Keycloak
|
||||
keycloak.secure-server.add=Add a deployment to be secured by Keycloak
|
||||
@@ -136,7 +134,6 @@ keycloak.secure-server.turn-off-change-session-id-on-login=The session id is cha
|
||||
keycloak.secure-server.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-server.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-server.ignore-oauth-query-parameter=disable query parameter parsing for access_token
|
||||
keycloak.secure-server.proxy-url=The URL for the HTTP proxy if one is used.
|
||||
|
||||
keycloak.secure-deployment.credential=Credential value
|
||||
keycloak.secure-server.credential=Credential value
|
||||
|
||||
-2
@@ -70,7 +70,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:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
@@ -118,7 +117,6 @@
|
||||
<xs:element name="min-time-between-jwks-requests" 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:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
|
||||
-3
@@ -40,7 +40,6 @@
|
||||
<register-node-period>60</register-node-period>
|
||||
<token-store>session</token-store>
|
||||
<principal-attribute>sub</principal-attribute>
|
||||
<proxy-url>http://localhost:9000</proxy-url>
|
||||
</realm>
|
||||
<realm name="jboss-infra">
|
||||
<realm-public-key>MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqKoq+a9MgXepmsPJDmo45qswuChW9pWjanX68oIBuI4hGvhQxFHryCow230A+sr7tFdMQMt8f1l/ysmV/fYAuW29WaoY4kI4Ou1yYPuwywKSsxT6PooTs83hKyZ1h4LZMj5DkLGDDDyVRHob2WmPaYg9RGVRw3iGGsD/p+Yb+L/gnBYQnZZ7lYqmN7h36p5CkzzlgXQA1Ha8sQxL+rJNH8+sZm0vBrKsoII3Of7TqHGsm1RwFV3XCuGJ7S61AbjJMXL5DQgJl9Z5scvxGAyoRLKC294UgMnQdzyBTMPw2GybxkRKmiK2KjQKmcopmrJp/Bt6fBR6ZkGSs9qUlxGHgwIDAQAB</realm-public-key>
|
||||
@@ -59,7 +58,6 @@
|
||||
<auth-server-url>http://localhost:8080/auth</auth-server-url>
|
||||
<ssl-required>EXTERNAL</ssl-required>
|
||||
<confidential-port>443</confidential-port>
|
||||
<proxy-url>http://localhost:9000</proxy-url>
|
||||
<credential name="secret">0aa31d98-e0aa-404c-b6e0-e771dba1e798</credential>
|
||||
<redirect-rewrite-rule name="^/wsmaster/api/(.*)$">api/$1/</redirect-rewrite-rule>
|
||||
</secure-deployment>
|
||||
@@ -90,6 +88,5 @@
|
||||
<public-client>true</public-client>
|
||||
<ssl-required>EXTERNAL</ssl-required>
|
||||
<confidential-port>443</confidential-port>
|
||||
<proxy-url>http://localhost:9000</proxy-url>
|
||||
</secure-server>
|
||||
</subsystem>
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak Adapters</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak SAML EAP Integration</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</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.5.0.Final-SNAPSHOT</version>
|
||||
<version>4.0.0.Beta3</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+1
-10
@@ -25,21 +25,12 @@ import org.keycloak.adapters.spi.HttpFacade;
|
||||
*/
|
||||
public class SamlDeploymentContext {
|
||||
private SamlDeployment deployment = null;
|
||||
private SamlConfigResolver resolver = null;
|
||||
|
||||
public SamlDeploymentContext(SamlDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
public SamlDeploymentContext(SamlConfigResolver resolver) {
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
public SamlDeployment resolveDeployment(HttpFacade facade) {
|
||||
if (deployment != null) {
|
||||
return deployment;
|
||||
} else {
|
||||
return resolver.resolve(facade.getRequest());
|
||||
}
|
||||
return deployment;
|
||||
}
|
||||
}
|
||||
+1
-16
@@ -42,11 +42,6 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.keycloak.adapters.cloned.HttpClientBuilder;
|
||||
import java.net.URI;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -210,13 +205,8 @@ public class DeploymentBuilder {
|
||||
Certificate cert = null;
|
||||
try {
|
||||
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
|
||||
if (cert instanceof X509Certificate) {
|
||||
((X509Certificate) cert).checkValidity();
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (CertificateException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
publicKey = cert.getPublicKey();
|
||||
} else {
|
||||
@@ -234,12 +224,7 @@ public class DeploymentBuilder {
|
||||
if (key.getPublicKeyPem() != null) {
|
||||
publicKey = PemUtils.decodePublicKey(key.getPublicKeyPem().trim());
|
||||
} else {
|
||||
X509Certificate cert = PemUtils.decodeCertificate(key.getCertificatePem().trim());
|
||||
try {
|
||||
cert.checkValidity();
|
||||
} catch (CertificateException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
Certificate cert = PemUtils.decodeCertificate(key.getCertificatePem().trim());
|
||||
publicKey = cert.getPublicKey();
|
||||
}
|
||||
return publicKey;
|
||||
|
||||
+3
-5
@@ -84,7 +84,6 @@ import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
|
||||
import org.keycloak.saml.validators.DestinationValidator;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -98,7 +97,6 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||
protected final SamlSessionStore sessionStore;
|
||||
protected final SamlDeployment deployment;
|
||||
protected AuthChallenge challenge;
|
||||
private final DestinationValidator destinationValidator = DestinationValidator.forProtocolMap(null);
|
||||
|
||||
public AbstractSamlAuthenticationHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
|
||||
this.facade = facade;
|
||||
@@ -147,7 +145,7 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||
holder = SAMLRequestParser.parseRequestPostBinding(samlRequest);
|
||||
}
|
||||
RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
|
||||
if (! destinationValidator.validate(requestUri, requestAbstractType.getDestination())) {
|
||||
if (!requestUri.equals(requestAbstractType.getDestination().toString())) {
|
||||
log.error("expected destination '" + requestUri + "' got '" + requestAbstractType.getDestination() + "'");
|
||||
return AuthOutcome.FAILED;
|
||||
}
|
||||
@@ -188,7 +186,7 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||
}
|
||||
final StatusResponseType statusResponse = (StatusResponseType) holder.getSamlObject();
|
||||
// validate destination
|
||||
if (! destinationValidator.validate(requestUri, statusResponse.getDestination())) {
|
||||
if (!requestUri.equals(statusResponse.getDestination())) {
|
||||
log.error("Request URI '" + requestUri + "' does not match SAML request destination '" + statusResponse.getDestination() + "'");
|
||||
return AuthOutcome.FAILED;
|
||||
}
|
||||
@@ -341,7 +339,7 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||
return AuthOutcome.FAILED;
|
||||
}
|
||||
try {
|
||||
assertion = AssertionUtil.getAssertion(responseHolder, responseType, deployment.getDecryptionKey());
|
||||
assertion = AssertionUtil.getAssertion(responseType, deployment.getDecryptionKey());
|
||||
if (AssertionUtil.hasExpired(assertion)) {
|
||||
return initiateLogin();
|
||||
}
|
||||
|
||||
Loaded 100 of 2506 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user