Compare commits

..
Author SHA1 Message Date
keycloak-bot c3fa471223 Set version to 3.3.0.Final 2017-10-26 16:00:14 +00:00
Marek Posolda 302d5a9334 Merge pull request #4615 from vramik/3.3.x
KEYCLOAK-5749 Update migration scripts for WildFly 11 Final
2017-10-26 13:24:39 +02:00
vramik 8b8db265ba KEYCLOAK-5749 remove steps for migration between 3.3.0 and 3.4.0 2017-10-26 11:35:30 +02:00
vramik 1bc75509e3 KEYCLOAK-5749 Update migration scripts for WildFly 11 Final 2017-10-26 11:33:52 +02:00
vramik b896c2eb57 KEYCLOAK-5674 updated migration scripts 2017-10-26 11:33:20 +02:00
Marek Posolda fed39d4953 Merge pull request #4609 from mposolda/3.3.x
KEYCLOAK-4936 Upgrade to WildFly 11.0.0.Final
2017-10-24 22:35:59 +02:00
mposolda 88e77f7b08 KEYCLOAK-4936 Upgrade to WildFly 11.0.0.Final 2017-10-24 21:26:21 +02:00
Stian Thorgersen e201f205d1 KEYCLOAK-5234 (#4595) 2017-10-23 16:13:00 +02:00
Stian Thorgersen 9f38e246df KEYCLOAK-5551 (#4580) 2017-10-19 09:23:34 +02:00
Stian Thorgersen 6ea9ed9be8 KEYCLOAK-5225 (#4578)
KEYCLOAK-5225 fix test

Fix
2017-10-19 08:23:09 +02:00
Stian Thorgersen 96fe460806 KEYCLOAK-5280 2017-10-19 07:04:43 +02:00
Hynek Mlnařík d7edee8a06 KEYCLOAK-5416 Migration from 3.2.1 to 3.3.0 doesn't work on MSSQL due to constraint violation (#4479)
(cherry picked from commit ba0a6976055dc314433ff30464ec59c7d9c0e052)
2017-09-15 09:56:27 +02:00
keycloak-bot 366ba829ab Set version to 3.3.0.Final-SNAPSHOT 2017-09-12 10:44:59 +00:00
keycloak-bot 79cd646ac7 Set version to 3.3.0.CR2 2017-09-12 09:51:02 +00:00
Bill Burke a3a33c2359 Merge pull request #4466 from sebastienblanc/KEYCLOAK-3473-3.3.x
KEYCLOAK-3473 : add new flag to determine if error response must be sent or not
2017-09-11 18:48:52 -04:00
sebastienblanc 9f3babb66c add new flag to determine if error response must be sent or not 2017-09-11 15:59:49 +02:00
Stan Silvert 4dd469933c KEYCLOAK-5391: Add node_modules back to repository for commons theme (#4457) 2017-09-11 06:16:12 +02:00
vramik 570ea94a33 KEYCLOAK-5385 add not null constraint for user_entity.not_before (#4446) 2017-09-01 08:58:49 +02:00
keycloak-bot cc36cadebd Set version to 3.3.0.Final-SNAPSHOT 2017-08-28 15:13:57 +00:00
keycloak-bot f45ef2ccc8 Set version to 3.3.0.CR1 2017-08-28 13:52:06 +00:00
5335 changed files with 35288 additions and 1330465 deletions

No files matched your search

-11
View File
@@ -15,23 +15,12 @@ env:
- TESTS=server-group3 - TESTS=server-group3
- TESTS=server-group4 - TESTS=server-group4
- TESTS=old - TESTS=old
- TESTS=crossdc
jdk: jdk:
- oraclejdk8 - oraclejdk8
install: true install: true
before_install:
- "export PHANTOMJS_VERSION=2.1.1"
- "phantomjs --version"
- "export PATH=$PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64/bin:$PATH"
- "phantomjs --version"
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then rm -rf $PWD/travis_phantomjs; mkdir -p $PWD/travis_phantomjs; fi"
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then wget https://github.com/Medium/phantomjs/releases/download/v$PHANTOMJS_VERSION/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -O $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2; fi"
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then tar -xvf $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi"
- "phantomjs --version"
script: script:
- ./travis-run-tests.sh $TESTS - ./travis-run-tests.sh $TESTS
Regular → Executable
+1 -5
View File
@@ -38,7 +38,7 @@ Starting Keycloak
To start Keycloak during development first build as specified above, then run: To start Keycloak during development first build as specified above, then run:
mvn -f testsuite/utils/pom.xml exec:java -Pkeycloak-server mvn -f testsuite/integration/pom.xml exec:java -Pkeycloak-server
To start Keycloak from the server distribution first build the distribution it as specified above, then run: To start Keycloak from the server distribution first build the distribution it as specified above, then run:
@@ -49,10 +49,6 @@ To start Keycloak from the server distribution first build the distribution it a
To stop the server press `Ctrl + C`. To stop the server press `Ctrl + C`.
Reporting security vulnerabilities
----------------------------------
If you've found a security vulnerability, please look at the [instructions on how to properly report it](http://www.keycloak.org/security.html)
Help and Documentation Help and Documentation
---------------------- ----------------------
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -206,16 +206,6 @@ public class AdapterDeploymentContext {
public void setBearerOnly(boolean bearerOnly) { public void setBearerOnly(boolean bearerOnly) {
delegate.setBearerOnly(bearerOnly); delegate.setBearerOnly(bearerOnly);
} }
@Override
public boolean isAutodetectBearerOnly() {
return delegate.isAutodetectBearerOnly();
}
@Override
public void setAutodetectBearerOnly(boolean autodetectBearerOnly) {
delegate.setAutodetectBearerOnly(autodetectBearerOnly);
}
@Override @Override
public boolean isEnableBasicAuth() { public boolean isEnableBasicAuth() {
@@ -287,16 +277,6 @@ public class AdapterDeploymentContext {
delegate.setSslRequired(sslRequired); delegate.setSslRequired(sslRequired);
} }
@Override
public int getConfidentialPort() {
return delegate.getConfidentialPort();
}
@Override
public void setConfidentialPort(int confidentialPort) {
delegate.setConfidentialPort(confidentialPort);
}
@Override @Override
public TokenStore getTokenStore() { public TokenStore getTokenStore() {
return delegate.getTokenStore(); return delegate.getTokenStore();
@@ -71,10 +71,9 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
AccessTokenResponse atr=null; AccessTokenResponse atr=null;
try { try {
String userpw=new String(Base64.decode(tokenString)); String userpw=new String(Base64.decode(tokenString));
int seperatorIndex = userpw.indexOf(":"); String[] parts=userpw.split(":");
String user = userpw.substring(0, seperatorIndex);
String pw = userpw.substring(seperatorIndex + 1); atr = getToken(parts[0], parts[1]);
atr = getToken(user, pw);
tokenString = atr.getToken(); tokenString = atr.getToken();
} catch (Exception e) { } catch (Exception e) {
log.debug("Failed to obtain token", e); log.debug("Failed to obtain token", e);
@@ -83,8 +82,8 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
} }
return authenticateToken(exchange, atr.getToken()); return authenticateToken(exchange, atr.getToken());
} }
private AccessTokenResponse getToken(String username, String password) throws Exception { private AccessTokenResponse getToken(String username, String password) throws Exception {
AccessTokenResponse tokenResponse=null; AccessTokenResponse tokenResponse=null;
HttpClient client = deployment.getClient(); HttpClient client = deployment.getClient();
@@ -23,8 +23,6 @@ import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome; import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import javax.security.cert.X509Certificate; import javax.security.cert.X509Certificate;
@@ -85,16 +83,6 @@ public class BearerTokenRequestAuthenticator {
} }
protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) { protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
log.debug("Verifying access_token");
if (log.isTraceEnabled()) {
try {
JWSInput jwsInput = new JWSInput(tokenString);
String wireString = jwsInput.getWireString();
log.tracef("\taccess_token: %s", wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
} catch (JWSInputException e) {
log.errorf(e, "Failed to parse access_token: %s", tokenString);
}
}
try { try {
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment); token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
} catch (VerificationException e) { } catch (VerificationException e) {
@@ -136,7 +124,6 @@ public class BearerTokenRequestAuthenticator {
} }
surrogate = chain[0].getSubjectDN().getName(); surrogate = chain[0].getSubjectDN().getName();
} }
log.debug("successful authorized");
return AuthOutcome.AUTHENTICATED; return AuthOutcome.AUTHENTICATED;
} }
@@ -174,10 +161,6 @@ public class BearerTokenRequestAuthenticator {
@Override @Override
public boolean challenge(HttpFacade facade) { public boolean challenge(HttpFacade facade) {
if (deployment.getPolicyEnforcer() != null) {
deployment.getPolicyEnforcer().enforce(OIDCHttpFacade.class.cast(facade));
return true;
}
OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description); OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description);
facade.getRequest().setError(error); facade.getRequest().setError(error);
facade.getResponse().addHeader("WWW-Authenticate", challenge); facade.getResponse().addHeader("WWW-Authenticate", challenge);
@@ -57,8 +57,6 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
/** /**
* Abstraction for creating HttpClients. Allows SSL configuration. * Abstraction for creating HttpClients. Allows SSL configuration.
@@ -291,7 +289,6 @@ public class HttpClientBuilder {
cm = new SingleClientConnManager(registry); cm = new SingleClientConnManager(registry);
} }
BasicHttpParams params = new BasicHttpParams(); BasicHttpParams params = new BasicHttpParams();
params.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
if (proxyHost != null) { if (proxyHost != null) {
params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost); params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
@@ -68,7 +68,6 @@ public class KeycloakDeployment {
protected String scope; protected String scope;
protected SslRequired sslRequired = SslRequired.ALL; protected SslRequired sslRequired = SslRequired.ALL;
protected int confidentialPort = -1;
protected TokenStore tokenStore = TokenStore.SESSION; protected TokenStore tokenStore = TokenStore.SESSION;
protected String stateCookieName = "OAuth_Token_Request_State"; protected String stateCookieName = "OAuth_Token_Request_State";
protected boolean useResourceRoleMappings; protected boolean useResourceRoleMappings;
@@ -154,8 +153,6 @@ public class KeycloakDeployment {
log.debug("resolveUrls"); log.debug("resolveUrls");
} }
authServerBaseUrl = authUrlBuilder.build().toString();
String login = authUrlBuilder.clone().path(ServiceUrlConstants.AUTH_PATH).build(getRealm()).toString(); String login = authUrlBuilder.clone().path(ServiceUrlConstants.AUTH_PATH).build(getRealm()).toString();
authUrl = KeycloakUriBuilder.fromUri(login); authUrl = KeycloakUriBuilder.fromUri(login);
realmInfoUrl = authUrlBuilder.clone().path(ServiceUrlConstants.REALM_INFO_PATH).build(getRealm()).toString(); realmInfoUrl = authUrlBuilder.clone().path(ServiceUrlConstants.REALM_INFO_PATH).build(getRealm()).toString();
@@ -280,14 +277,6 @@ public class KeycloakDeployment {
this.sslRequired = sslRequired; this.sslRequired = sslRequired;
} }
public int getConfidentialPort() {
return confidentialPort;
}
public void setConfidentialPort(int confidentialPort) {
this.confidentialPort = confidentialPort;
}
public TokenStore getTokenStore() { public TokenStore getTokenStore() {
return tokenStore; return tokenStore;
} }
@@ -78,11 +78,6 @@ public class KeycloakDeploymentBuilder {
} else { } else {
deployment.setSslRequired(SslRequired.EXTERNAL); deployment.setSslRequired(SslRequired.EXTERNAL);
} }
if (adapterConfig.getConfidentialPort() != -1) {
deployment.setConfidentialPort(adapterConfig.getConfidentialPort());
}
if (adapterConfig.getTokenStore() != null) { if (adapterConfig.getTokenStore() != null) {
deployment.setTokenStore(TokenStore.valueOf(adapterConfig.getTokenStore().toUpperCase())); deployment.setTokenStore(TokenStore.valueOf(adapterConfig.getTokenStore().toUpperCase()));
} else { } else {
@@ -66,7 +66,7 @@ public class OAuthRequestAuthenticator {
this.reqAuthenticator = requestAuthenticator; this.reqAuthenticator = requestAuthenticator;
this.facade = facade; this.facade = facade;
this.deployment = deployment; this.deployment = deployment;
this.sslRedirectPort = deployment.getConfidentialPort() != -1 ? deployment.getConfidentialPort() : sslRedirectPort; this.sslRedirectPort = sslRedirectPort;
this.tokenStore = tokenStore; this.tokenStore = tokenStore;
} }
@@ -170,9 +170,6 @@ public class OAuthRequestAuthenticator {
String maxAge = getQueryParamValue(OAuth2Constants.MAX_AGE); String maxAge = getQueryParamValue(OAuth2Constants.MAX_AGE);
url = UriUtils.stripQueryParam(url, OAuth2Constants.MAX_AGE); url = UriUtils.stripQueryParam(url, OAuth2Constants.MAX_AGE);
String uiLocales = getQueryParamValue(OAuth2Constants.UI_LOCALES_PARAM);
url = UriUtils.stripQueryParam(url, OAuth2Constants.UI_LOCALES_PARAM);
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone() KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE) .queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName()) .queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
@@ -191,9 +188,6 @@ public class OAuthRequestAuthenticator {
if (maxAge != null && maxAge.length() > 0) { if (maxAge != null && maxAge.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.MAX_AGE, maxAge); redirectUriBuilder.queryParam(OAuth2Constants.MAX_AGE, maxAge);
} }
if (uiLocales != null && uiLocales.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.UI_LOCALES_PARAM, uiLocales);
}
scope = TokenUtil.attachOIDCScope(scope); scope = TokenUtil.attachOIDCScope(scope);
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope); redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
@@ -337,7 +331,7 @@ public class OAuthRequestAuthenticator {
} catch (ServerRequest.HttpFailure failure) { } catch (ServerRequest.HttpFailure failure) {
log.error("failed to turn code into token"); log.error("failed to turn code into token");
log.error("status from server: " + failure.getStatus()); log.error("status from server: " + failure.getStatus());
if (failure.getError() != null) { if (failure.getStatus() == 400 && failure.getError() != null) {
log.error(" " + failure.getError()); log.error(" " + failure.getError());
} }
return challenge(403, OIDCAuthenticationError.Reason.CODE_TO_TOKEN_FAILURE, null); return challenge(403, OIDCAuthenticationError.Reason.CODE_TO_TOKEN_FAILURE, null);
@@ -350,14 +344,6 @@ public class OAuthRequestAuthenticator {
tokenString = tokenResponse.getToken(); tokenString = tokenResponse.getToken();
refreshToken = tokenResponse.getRefreshToken(); refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken(); idTokenString = tokenResponse.getIdToken();
log.debug("Verifying tokens");
if (log.isTraceEnabled()) {
logToken("\taccess_token", tokenString);
logToken("\tid_token", idTokenString);
logToken("\trefresh_token", refreshToken);
}
try { try {
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment); token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
if (idTokenString != null) { if (idTokenString != null) {
@@ -390,8 +376,7 @@ public class OAuthRequestAuthenticator {
protected String stripOauthParametersFromRedirect() { protected String stripOauthParametersFromRedirect() {
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(facade.getRequest().getURI()) KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(facade.getRequest().getURI())
.replaceQueryParam(OAuth2Constants.CODE, null) .replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null) .replaceQueryParam(OAuth2Constants.STATE, null);
.replaceQueryParam(OAuth2Constants.SESSION_STATE, null);
return builder.build().toString(); return builder.build().toString();
} }
@@ -413,13 +398,4 @@ public class OAuthRequestAuthenticator {
return originalUri; return originalUri;
} }
private void logToken(String name, String token) {
try {
JWSInput jwsInput = new JWSInput(token);
String wireString = jwsInput.getWireString();
log.tracef("\t%s: %s", name, wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
} catch (JWSInputException e) {
log.errorf(e, "Failed to parse %s: %s", name, token);
}
}
} }
@@ -32,7 +32,7 @@ import java.util.Map;
*/ */
public class ClientIdAndSecretCredentialsProvider implements ClientCredentialsProvider { public class ClientIdAndSecretCredentialsProvider implements ClientCredentialsProvider {
private static Logger logger = Logger.getLogger(ClientIdAndSecretCredentialsProvider.class); private static Logger logger = Logger.getLogger(ClientCredentialsProviderUtils.class);
public static final String PROVIDER_ID = CredentialRepresentation.SECRET; public static final String PROVIDER_ID = CredentialRepresentation.SECRET;
@@ -1,97 +0,0 @@
package org.keycloak.adapters.authentication;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.JsonWebToken;
/**
* Client authentication based on JWT signed by client secret instead of private key .
* See <a href="http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">specs</a> for more details.
*
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/
public class JWTClientSecretCredentialsProvider implements ClientCredentialsProvider {
private static final Logger logger = Logger.getLogger(JWTClientSecretCredentialsProvider.class);
public static final String PROVIDER_ID = "secret-jwt";
private SecretKey clientSecret;
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public void init(KeycloakDeployment deployment, Object config) {
if (config == null || !(config instanceof Map)) {
throw new RuntimeException("Configuration of jwt credentials by client secret is missing or incorrect for client '" + deployment.getResourceName() + "'. Check your adapter configuration");
}
Map<String, Object> cfg = (Map<String, Object>) config;
String clientSecretString = (String) cfg.get("secret");
if (clientSecretString == null) {
throw new RuntimeException("Missing parameter secret-jwt in configuration of jwt for client " + deployment.getResourceName());
}
setClientSecret(clientSecretString);
}
@Override
public void setClientCredentials(KeycloakDeployment deployment, Map<String, String> requestHeaders, Map<String, String> formParams) {
String signedToken = createSignedRequestToken(deployment.getResourceName(), deployment.getRealmInfoUrl());
formParams.put(OAuth2Constants.CLIENT_ASSERTION_TYPE, OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT);
formParams.put(OAuth2Constants.CLIENT_ASSERTION, signedToken);
}
public void setClientSecret(String clientSecretString) {
// Get client secret and validate signature
// According to <a href="http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">OIDC's client authentication spec</a>,
// The HMAC (Hash-based Message Authentication Code) is calculated using the octets of the UTF-8 representation of the client_secret as the shared key.
// Use "HmacSHA256" consulting <a href="https://docs.oracle.com/javase/jp/8/docs/api/javax/crypto/Mac.html">java8 api</a>
// because it must be implemented in every java platform.
try {
clientSecret = new SecretKeySpec(clientSecretString.getBytes("UTF-8"), "HmacSHA256");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Failed to create secret key spec due to unsupported encoding.");
}
}
public String createSignedRequestToken(String clientId, String realmInfoUrl) {
JsonWebToken jwt = createRequestToken(clientId, realmInfoUrl);
// JOSE header {"alg":"HS256","typ" : "JWT"} no need "kid" due to using only one registered client secret.
// Use "HmacSHA256" consulting <a href="https://docs.oracle.com/javase/jp/8/docs/api/javax/crypto/Mac.html">java8 api</a>.
// because it must be implemented in every java platform.
return new JWSBuilder().jsonContent(jwt).hmac256(clientSecret);
}
private JsonWebToken createRequestToken(String clientId, String realmInfoUrl) {
// According to <a href="http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">OIDC's client authentication spec</a>,
// JWT claims is the same as one by private_key_jwt
JsonWebToken reqToken = new JsonWebToken();
reqToken.id(AdapterUtils.generateId());
reqToken.issuer(clientId);
reqToken.subject(clientId);
reqToken.audience(realmInfoUrl);
int now = Time.currentTime();
reqToken.issuedAt(now);
// the same as in KEYCLOAK-2986, JWTClientCredentialsProvider's timeout field
reqToken.expiration(now + 10);
reqToken.notBefore(now);
return reqToken;
}
}
@@ -18,6 +18,7 @@
package org.keycloak.adapters.authorization; package org.keycloak.adapters.authorization;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -32,7 +33,6 @@ import org.keycloak.authorization.client.ClientAuthorizationContext;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.MethodConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.Permission; import org.keycloak.representations.idm.authorization.Permission;
@@ -64,67 +64,63 @@ public abstract class AbstractPolicyEnforcer {
return createEmptyAuthorizationContext(true); return createEmptyAuthorizationContext(true);
} }
Request request = httpFacade.getRequest();
String path = getPath(request);
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext(); KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
if (securityContext == null) { if (securityContext != null) {
if (pathConfig != null) { AccessToken accessToken = securityContext.getToken();
challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
}
return createEmptyAuthorizationContext(false);
}
AccessToken accessToken = securityContext.getToken(); if (accessToken != null) {
Request request = httpFacade.getRequest();
String path = getPath(request);
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
if (accessToken != null) { LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
if (pathConfig == null) { if (pathConfig == null) {
if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) { if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
return createAuthorizationContext(accessToken, null); return createAuthorizationContext(accessToken, null);
}
LOGGER.debugf("Could not find a configuration for path [%s]", path);
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
return createAuthorizationContext(accessToken, null);
}
handleAccessDenied(httpFacade);
return createEmptyAuthorizationContext(false);
} }
LOGGER.debugf("Could not find a configuration for path [%s]", path); if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
return createEmptyAuthorizationContext(true);
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
return createAuthorizationContext(accessToken, null);
} }
handleAccessDenied(httpFacade); Set<String> requiredScopes = getRequiredScopes(pathConfig, request);
return createEmptyAuthorizationContext(false); if (isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
} try {
return createAuthorizationContext(accessToken, pathConfig);
if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) { } catch (Exception e) {
return createEmptyAuthorizationContext(true); throw new RuntimeException("Error processing path [" + pathConfig.getPath() + "].", e);
} }
MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
try {
return createAuthorizationContext(accessToken, pathConfig);
} catch (Exception e) {
throw new RuntimeException("Error processing path [" + pathConfig.getPath() + "].", e);
} }
}
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig); LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
if (!challenge(pathConfig, methodConfig, httpFacade)) { if (!challenge(pathConfig, requiredScopes, httpFacade)) {
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig); LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
handleAccessDenied(httpFacade); handleAccessDenied(httpFacade);
}
} }
} }
return createEmptyAuthorizationContext(false); return createEmptyAuthorizationContext(false);
} }
protected abstract boolean challenge(PathConfig pathConfig, MethodConfig methodConfig, OIDCHttpFacade facade); protected abstract boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade);
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) { protected boolean isAuthorized(PathConfig actualPathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
Request request = httpFacade.getRequest(); Request request = httpFacade.getRequest();
PolicyEnforcerConfig enforcerConfig = getEnforcerConfig(); PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
@@ -142,7 +138,7 @@ public abstract class AbstractPolicyEnforcer {
boolean hasPermission = false; boolean hasPermission = false;
for (Permission permission : permissions) { for (Permission permission : permissions) {
if (permission.getResourceId() != null) { if (permission.getResourceSetId() != null) {
if (isResourcePermission(actualPathConfig, permission)) { if (isResourcePermission(actualPathConfig, permission)) {
hasPermission = true; hasPermission = true;
@@ -150,7 +146,7 @@ public abstract class AbstractPolicyEnforcer {
continue; continue;
} }
if (hasResourceScopePermission(methodConfig, permission)) { if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions); LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) { if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
this.paths.remove(actualPathConfig); this.paths.remove(actualPathConfig);
@@ -159,7 +155,7 @@ public abstract class AbstractPolicyEnforcer {
} }
} }
} else { } else {
if (hasResourceScopePermission(methodConfig, permission)) { if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
hasPermission = true; hasPermission = true;
return true; return true;
} }
@@ -170,7 +166,7 @@ public abstract class AbstractPolicyEnforcer {
return true; return true;
} }
LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, permissions); LOGGER.debugf("Authorization FAILED for path [%s]. No enough permissions [%s].", actualPathConfig, permissions);
return false; return false;
} }
@@ -190,29 +186,9 @@ public abstract class AbstractPolicyEnforcer {
return false; return false;
} }
private boolean hasResourceScopePermission(MethodConfig methodConfig, Permission permission) { private boolean hasResourceScopePermission(Set<String> requiredScopes, Permission permission, PathConfig actualPathConfig) {
List<String> requiredScopes = methodConfig.getScopes();
Set<String> allowedScopes = permission.getScopes(); Set<String> allowedScopes = permission.getScopes();
return (allowedScopes.containsAll(requiredScopes) || allowedScopes.isEmpty());
if (allowedScopes.isEmpty()) {
return true;
}
PolicyEnforcerConfig.ScopeEnforcementMode enforcementMode = methodConfig.getScopesEnforcementMode();
if (PolicyEnforcerConfig.ScopeEnforcementMode.ALL.equals(enforcementMode)) {
return allowedScopes.containsAll(requiredScopes);
}
if (PolicyEnforcerConfig.ScopeEnforcementMode.ANY.equals(enforcementMode)) {
for (String requiredScope : requiredScopes) {
if (allowedScopes.contains(requiredScope)) {
return true;
}
}
}
return requiredScopes.isEmpty();
} }
protected AuthzClient getAuthzClient() { protected AuthzClient getAuthzClient() {
@@ -260,22 +236,20 @@ public abstract class AbstractPolicyEnforcer {
return request.getRelativePath(); return request.getRelativePath();
} }
private MethodConfig getRequiredScopes(PathConfig pathConfig, Request request) { private Set<String> getRequiredScopes(PathConfig pathConfig, Request request) {
Set<String> requiredScopes = new HashSet<>();
requiredScopes.addAll(pathConfig.getScopes());
String method = request.getMethod(); String method = request.getMethod();
for (MethodConfig methodConfig : pathConfig.getMethods()) { for (PolicyEnforcerConfig.MethodConfig methodConfig : pathConfig.getMethods()) {
if (methodConfig.getMethod().equals(method)) { if (methodConfig.getMethod().equals(method)) {
return methodConfig; requiredScopes.addAll(methodConfig.getScopes());
} }
} }
MethodConfig methodConfig = new MethodConfig(); return requiredScopes;
methodConfig.setMethod(request.getMethod());
methodConfig.setScopes(pathConfig.getScopes());
methodConfig.setScopesEnforcementMode(PolicyEnforcerConfig.ScopeEnforcementMode.ANY);
return methodConfig;
} }
private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) { private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
@@ -295,6 +269,6 @@ public abstract class AbstractPolicyEnforcer {
} }
private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) { private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
return permission.getResourceId().equals(actualPathConfig.getId()); return permission.getResourceSetId().equals(actualPathConfig.getId());
} }
} }
@@ -17,17 +17,16 @@
*/ */
package org.keycloak.adapters.authorization; package org.keycloak.adapters.authorization;
import java.util.HashSet; import java.util.Set;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.adapters.OIDCHttpFacade; import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthzClient; import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.resource.PermissionResource; import org.keycloak.authorization.client.resource.PermissionResource;
import org.keycloak.authorization.client.resource.ProtectionResource; import org.keycloak.authorization.client.resource.ProtectionResource;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.PermissionRequest;
/** /**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -41,41 +40,46 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
} }
@Override @Override
protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) { protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
HttpFacade.Response response = facade.getResponse(); if (getEnforcerConfig().getUserManagedAccess() != null) {
AuthzClient authzClient = getAuthzClient(); challengeUmaAuthentication(pathConfig, requiredScopes, facade);
String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient); } else {
challengeEntitlementAuthentication(facade);
if (ticket == null) {
response.setStatus(403);
return true;
}
String realm = authzClient.getConfiguration().getRealm();
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString();
response.setStatus(401);
StringBuilder wwwAuthenticate = new StringBuilder("UMA realm=\"").append(realm).append("\"").append(",as_uri=\"").append(authorizationServerUri).append("\"");
if (ticket != null) {
wwwAuthenticate.append(",ticket=\"").append(ticket).append("\"");
}
response.setHeader("WWW-Authenticate", wwwAuthenticate.toString());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending UMA challenge");
} }
return true; return true;
} }
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient) { private void challengeEntitlementAuthentication(OIDCHttpFacade facade) {
if (getEnforcerConfig().getUserManagedAccess() != null) { HttpFacade.Response response = facade.getResponse();
ProtectionResource protection = authzClient.protection(); AuthzClient authzClient = getAuthzClient();
PermissionResource permission = protection.permission(); String clientId = authzClient.getConfiguration().getResource();
PermissionRequest permissionRequest = new PermissionRequest(); String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/entitlement";
permissionRequest.setResourceId(pathConfig.getId()); response.setStatus(401);
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes())); response.setHeader("WWW-Authenticate", "KC_ETT realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\"");
return permission.create(permissionRequest).getTicket(); if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending Entitlement challenge");
} }
return null; }
private void challengeUmaAuthentication(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
HttpFacade.Response response = facade.getResponse();
AuthzClient authzClient = getAuthzClient();
String ticket = getPermissionTicket(pathConfig, requiredScopes, authzClient);
String clientId = authzClient.getConfiguration().getResource();
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "UMA realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\",ticket=\"" + ticket + "\"");
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending UMA challenge");
}
}
private String getPermissionTicket(PathConfig pathConfig, Set<String> requiredScopes, AuthzClient authzClient) {
ProtectionResource protection = authzClient.protection();
PermissionResource permission = protection.permission();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setScopes(requiredScopes);
return permission.forResource(permissionRequest).getTicket();
} }
} }
@@ -19,23 +19,24 @@ package org.keycloak.adapters.authorization;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade; import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthorizationDeniedException; import org.keycloak.authorization.client.AuthorizationDeniedException;
import org.keycloak.authorization.client.AuthzClient; import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.AuthorizationRequest;
import org.keycloak.authorization.client.representation.AuthorizationResponse;
import org.keycloak.authorization.client.representation.EntitlementRequest;
import org.keycloak.authorization.client.representation.EntitlementResponse;
import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.representation.PermissionResponse;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
import org.keycloak.representations.idm.authorization.AuthorizationResponse;
import org.keycloak.representations.idm.authorization.Permission; import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.representations.idm.authorization.PermissionRequest;
import org.keycloak.representations.idm.authorization.PermissionResponse;
/** /**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -49,14 +50,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
} }
@Override @Override
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) { protected boolean isAuthorized(PathConfig pathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
AccessToken original = accessToken; AccessToken original = accessToken;
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) { if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
return true; return true;
} }
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade); accessToken = requestAuthorizationToken(pathConfig, requiredScopes, httpFacade);
if (accessToken == null) { if (accessToken == null) {
return false; return false;
@@ -77,23 +78,17 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
original.setAuthorization(authorization); original.setAuthorization(authorization);
return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade); return super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade);
} }
@Override @Override
protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) { protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
handleAccessDenied(facade); handleAccessDenied(facade);
return true; return true;
} }
@Override @Override
protected void handleAccessDenied(OIDCHttpFacade facade) { protected void handleAccessDenied(OIDCHttpFacade facade) {
KeycloakSecurityContext securityContext = facade.getSecurityContext();
if (securityContext == null) {
return;
}
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo(); String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
HttpFacade.Response response = facade.getResponse(); HttpFacade.Response response = facade.getResponse();
@@ -105,43 +100,47 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
} }
} }
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) { private AccessToken requestAuthorizationToken(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade httpFacade) {
try { try {
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext(); String accessToken = httpFacade.getSecurityContext().getTokenString();
String accessTokenString = securityContext.getTokenString();
AuthzClient authzClient = getAuthzClient(); AuthzClient authzClient = getAuthzClient();
KeycloakDeployment deployment = getPolicyEnforcer().getDeployment(); KeycloakDeployment deployment = getPolicyEnforcer().getDeployment();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceId(pathConfig.getId());
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
AccessToken accessToken = securityContext.getToken();
AuthorizationRequest authzRequest;
if (getEnforcerConfig().getUserManagedAccess() != null) { if (getEnforcerConfig().getUserManagedAccess() != null) {
PermissionResponse permissionResponse = authzClient.protection().permission().create(permissionRequest); LOGGER.debug("Obtaining authorization for authenticated user.");
authzRequest = new AuthorizationRequest(); PermissionRequest permissionRequest = new PermissionRequest();
authzRequest.setTicket(permissionResponse.getTicket());
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setScopes(requiredScopes);
PermissionResponse permissionResponse = authzClient.protection().permission().forResource(permissionRequest);
AuthorizationRequest authzRequest = new AuthorizationRequest(permissionResponse.getTicket());
AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest);
if (authzResponse != null) {
return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
}
return null;
} else { } else {
authzRequest = new AuthorizationRequest(); LOGGER.debug("Obtaining entitlements for authenticated user.");
if (accessToken.getAuthorization() != null) { AccessToken token = httpFacade.getSecurityContext().getToken();
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
if (token.getAuthorization() == null) {
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getResource());
return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
} else {
EntitlementRequest request = new EntitlementRequest();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setResourceSetName(pathConfig.getName());
permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes()));
LOGGER.debugf("Sending entitlements request: resource_set_id [%s], resource_set_name [%s], scopes [%s].", permissionRequest.getResourceSetId(), permissionRequest.getResourceSetName(), permissionRequest.getScopes());
request.addPermission(permissionRequest);
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getResource(), request);
return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
} }
} }
if (accessToken.getAuthorization() != null) {
authzRequest.setRpt(accessTokenString);
}
LOGGER.debug("Obtaining authorization for authenticated user.");
AuthorizationResponse authzResponse = authzClient.authorization(accessTokenString).authorize(authzRequest);
if (authzResponse != null) {
return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
}
return null;
} catch (AuthorizationDeniedException e) { } catch (AuthorizationDeniedException e) {
LOGGER.debug("Authorization denied", e); LOGGER.debug("Authorization denied", e);
return null; return null;
@@ -18,8 +18,8 @@
package org.keycloak.adapters.authorization; package org.keycloak.adapters.authorization;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.keycloak.authorization.client.AuthzClient; import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.ResourceRepresentation; import org.keycloak.authorization.client.representation.ResourceRepresentation;
@@ -95,9 +95,7 @@ class PathMatcher {
} }
if (WILDCARD == expectedUri.charAt(expectedUri.length() - 1)) { if (WILDCARD == expectedUri.charAt(expectedUri.length() - 1)) {
if (matchingAnyPath == null || matchingAnyPath.getPath().length() < matchingUri.length()) { matchingAnyPath = entry;
matchingAnyPath = entry;
}
} else { } else {
int suffixIndex = expectedUri.indexOf(WILDCARD + "."); int suffixIndex = expectedUri.indexOf(WILDCARD + ".");
@@ -223,11 +221,11 @@ class PathMatcher {
private PathConfig resolvePathConfig(PathConfig originalConfig, String path) { private PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
if (originalConfig.hasPattern()) { if (originalConfig.hasPattern()) {
ProtectedResource resource = this.authzClient.protection().resource(); ProtectedResource resource = this.authzClient.protection().resource();
List<ResourceRepresentation> search = resource.findByUri(path); Set<String> search = resource.findByFilter("uri=" + path);
if (!search.isEmpty()) { if (!search.isEmpty()) {
// resource does exist on the server, cache it // resource does exist on the server, cache it
ResourceRepresentation targetResource = search.get(0); ResourceRepresentation targetResource = resource.findById(search.iterator().next()).getResourceDescription();
PathConfig config = PolicyEnforcer.createPathConfig(targetResource); PathConfig config = PolicyEnforcer.createPathConfig(targetResource);
config.setScopes(originalConfig.getScopes()); config.setScopes(originalConfig.getScopes());
@@ -17,15 +17,6 @@
*/ */
package org.keycloak.adapters.authorization; package org.keycloak.adapters.authorization;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
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.jboss.logging.Logger;
import org.keycloak.AuthorizationContext; import org.keycloak.AuthorizationContext;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
@@ -34,6 +25,7 @@ import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.authorization.client.AuthzClient; import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.ClientAuthenticator; import org.keycloak.authorization.client.ClientAuthenticator;
import org.keycloak.authorization.client.Configuration; import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.representation.RegistrationResponse;
import org.keycloak.authorization.client.representation.ResourceRepresentation; import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.representation.ScopeRepresentation; import org.keycloak.authorization.client.representation.ScopeRepresentation;
import org.keycloak.authorization.client.resource.ProtectedResource; import org.keycloak.authorization.client.resource.ProtectedResource;
@@ -42,6 +34,14 @@ import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.Permission; import org.keycloak.representations.idm.authorization.Permission;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/ */
@@ -58,15 +58,10 @@ public class PolicyEnforcer {
public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) { public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) {
this.deployment = deployment; this.deployment = deployment;
this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig(); this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig();
Configuration configuration = new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()); this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()), new ClientAuthenticator() {
this.authzClient = AuthzClient.create(configuration, new ClientAuthenticator() {
@Override @Override
public void configureClientCredentials(Map<String, List<String>> requestParams, Map<String, String> requestHeaders) { public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
Map<String, String> formparams = new HashMap<>(); ClientCredentialsProviderUtils.setClientCredentials(PolicyEnforcer.this.deployment, requestHeaders, requestParams);
ClientCredentialsProviderUtils.setClientCredentials(PolicyEnforcer.this.deployment, requestHeaders, formparams);
for (Entry<String, String> param : formparams.entrySet()) {
requestParams.put(param.getKey(), Arrays.asList(param.getValue()));
}
} }
}); });
this.pathMatcher = new PathMatcher(this.authzClient); this.pathMatcher = new PathMatcher(this.authzClient);
@@ -147,34 +142,26 @@ public class PolicyEnforcer {
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>()); Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
for (PathConfig pathConfig : enforcerConfig.getPaths()) { for (PathConfig pathConfig : enforcerConfig.getPaths()) {
ResourceRepresentation resource; Set<String> search;
String resourceName = pathConfig.getName(); String resourceName = pathConfig.getName();
String path = pathConfig.getPath(); String path = pathConfig.getPath();
if (resourceName != null) { if (resourceName != null) {
LOGGER.debugf("Trying to find resource with name [%s] for path [%s].", resourceName, path); LOGGER.debugf("Trying to find resource with name [%s] for path [%s].", resourceName, path);
resource = protectedResource.findByName(resourceName); search = protectedResource.findByFilter("name=" + resourceName);
} else { } else {
LOGGER.debugf("Trying to find resource with uri [%s] for path [%s].", path, path); LOGGER.debugf("Trying to find resource with uri [%s] for path [%s].", path, path);
List<ResourceRepresentation> resources = protectedResource.findByUri(path); search = protectedResource.findByFilter("uri=" + path);
if (resources.size() == 1) {
resource = resources.get(0);
} else if (resources.size() > 1) {
throw new RuntimeException("Multiple resources found with the same uri");
} else {
resource = null;
}
} }
if (resource == null) { if (search.isEmpty()) {
if (enforcerConfig.isCreateResources()) { if (enforcerConfig.isCreateResources()) {
LOGGER.debugf("Creating resource on server for path [%s].", pathConfig); LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
ResourceRepresentation representation = new ResourceRepresentation(); ResourceRepresentation resource = new ResourceRepresentation();
representation.setName(resourceName); resource.setName(resourceName);
representation.setType(pathConfig.getType()); resource.setType(pathConfig.getType());
representation.setUri(path); resource.setUri(path);
HashSet<ScopeRepresentation> scopes = new HashSet<>(); HashSet<ScopeRepresentation> scopes = new HashSet<>();
@@ -186,16 +173,16 @@ public class PolicyEnforcer {
scopes.add(scope); scopes.add(scope);
} }
representation.setScopes(scopes); resource.setScopes(scopes);
ResourceRepresentation registrationResponse = protectedResource.create(representation); RegistrationResponse registrationResponse = protectedResource.create(resource);
pathConfig.setId(registrationResponse.getId()); pathConfig.setId(registrationResponse.getId());
} else { } else {
throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration."); throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
} }
} else { } else {
pathConfig.setId(resource.getId()); pathConfig.setId(search.iterator().next());
} }
PathConfig existingPath = null; PathConfig existingPath = null;
@@ -223,7 +210,8 @@ public class PolicyEnforcer {
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>()); Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
for (String id : protectedResource.findAll()) { for (String id : protectedResource.findAll()) {
ResourceRepresentation resourceDescription = protectedResource.findById(id); RegistrationResponse response = protectedResource.findById(id);
ResourceRepresentation resourceDescription = response.getResourceDescription();
if (resourceDescription.getUri() != null) { if (resourceDescription.getUri() != null) {
PathConfig pathConfig = createPathConfig(resourceDescription); PathConfig pathConfig = createPathConfig(resourceDescription);
@@ -37,12 +37,8 @@ import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException; import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule; import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Principal; import java.security.Principal;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@@ -57,7 +53,7 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
public static final String KEYCLOAK_CONFIG_FILE_OPTION = "keycloak-config-file"; public static final String KEYCLOAK_CONFIG_FILE_OPTION = "keycloak-config-file";
public static final String ROLE_PRINCIPAL_CLASS_OPTION = "role-principal-class"; public static final String ROLE_PRINCIPAL_CLASS_OPTION = "role-principal-class";
public static final String PROFILE_RESOURCE = "profile:";
protected Subject subject; protected Subject subject;
protected CallbackHandler callbackHandler; protected CallbackHandler callbackHandler;
protected Auth auth; protected Auth auth;
@@ -88,27 +84,14 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
protected KeycloakDeployment resolveDeployment(String keycloakConfigFile) { protected KeycloakDeployment resolveDeployment(String keycloakConfigFile) {
try { try {
InputStream is = null; InputStream is = FindFile.findFile(keycloakConfigFile);
if (keycloakConfigFile.startsWith(PROFILE_RESOURCE)) {
try {
is = new URL(keycloakConfigFile).openStream();
} catch (MalformedURLException mfue) {
throw new RuntimeException(mfue);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
} else {
is = FindFile.findFile(keycloakConfigFile);
}
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is); KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
return kd; return kd;
} catch (RuntimeException e) { } catch (RuntimeException e) {
getLogger().debug("Unable to find or parse file " + keycloakConfigFile + " due to " + e.getMessage(), e); getLogger().debug("Unable to find or parse file " + keycloakConfigFile + " due to " + e.getMessage(), e);
throw e; throw e;
} }
} }
@Override @Override
public boolean login() throws LoginException { public boolean login() throws LoginException {
@@ -81,7 +81,7 @@ public class JWKPublicKeyLocator implements PublicKeyLocator {
private PublicKey lookupCachedKey(int publicKeyCacheTtl, int currentTime, String kid) { private PublicKey lookupCachedKey(int publicKeyCacheTtl, int currentTime, String kid) {
if (lastRequestTime + publicKeyCacheTtl > currentTime && kid != null) { if (lastRequestTime + publicKeyCacheTtl > currentTime) {
return currentKeys.get(kid); return currentKeys.get(kid);
} else { } else {
return null; return null;
@@ -16,5 +16,4 @@
# #
org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider
org.keycloak.adapters.authentication.JWTClientCredentialsProvider org.keycloak.adapters.authentication.JWTClientCredentialsProvider
org.keycloak.adapters.authentication.JWTClientSecretCredentialsProvider
@@ -21,7 +21,6 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.junit.Test; import org.junit.Test;
import org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider; import org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider;
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider; import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
import org.keycloak.adapters.authentication.JWTClientSecretCredentialsProvider;
import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator; import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
import org.keycloak.adapters.rotation.JWKPublicKeyLocator; import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.enums.RelativeUrlsUsed; import org.keycloak.common.enums.RelativeUrlsUsed;
@@ -95,9 +94,4 @@ public class KeycloakDeploymentBuilderTest {
assertEquals(JWTClientCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId()); assertEquals(JWTClientCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
} }
@Test
public void loadSecretJwtCredentials() throws Exception {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak-secret-jwt.json"));
assertEquals(JWTClientSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
}
} }
@@ -17,9 +17,7 @@
package org.keycloak.adapters; package org.keycloak.adapters;
import org.junit.Test; import org.junit.Test;
import org.keycloak.representations.adapters.config.AdapterConfig;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -48,21 +46,4 @@ public class KeycloakDeploymentTest {
assertTrue(keycloakDeployment.isOAuthQueryParameterEnabled()); assertTrue(keycloakDeployment.isOAuthQueryParameterEnabled());
} }
@Test
public void stripDefaultPorts() {
KeycloakDeployment keycloakDeployment = new KeycloakDeployment();
keycloakDeployment.setRealm("test");
AdapterConfig config = new AdapterConfig();
config.setAuthServerUrl("http://localhost:80/auth");
keycloakDeployment.setAuthServerBaseUrl(config);
assertEquals("http://localhost/auth", keycloakDeployment.getAuthServerBaseUrl());
config.setAuthServerUrl("https://localhost:443/auth");
keycloakDeployment.setAuthServerBaseUrl(config);
assertEquals("https://localhost/auth", keycloakDeployment.getAuthServerBaseUrl());
}
} }
@@ -1,12 +0,0 @@
{
"realm": "demo",
"resource": "customer-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "https://localhost:8443/auth",
"ssl-required": "external",
"credentials": {
"secret-jwt": {
"secret": "234234-234234-234234"
}
}
}
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-as7-integration-pom</artifactId> <artifactId>keycloak-as7-integration-pom</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-as7-integration-pom</artifactId> <artifactId>keycloak-as7-integration-pom</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -1,13 +0,0 @@
package org.keycloak.adapters.jbossweb;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve;
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
super(deploymentContext, next, container);
}
}
@@ -17,15 +17,11 @@
package org.keycloak.adapters.jbossweb; package org.keycloak.adapters.jbossweb;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleException;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig; import org.apache.catalina.deploy.LoginConfig;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve;
import org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve; import org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve;
import org.keycloak.adapters.tomcat.GenericPrincipalFactory; import org.keycloak.adapters.tomcat.GenericPrincipalFactory;
@@ -60,6 +56,7 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
super.start(); super.start();
} }
public void logout(Request request) { public void logout(Request request) {
logoutInternal(request); logoutInternal(request);
} }
@@ -68,9 +65,4 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
protected GenericPrincipalFactory createPrincipalFactory() { protected GenericPrincipalFactory createPrincipalFactory() {
return new JBossWebPrincipalFactory(); return new JBossWebPrincipalFactory();
} }
@Override
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
return new AuthenticatedActionsValve(deploymentContext, next, container);
}
} }
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-integration-pom</artifactId> <artifactId>keycloak-as7-integration-pom</artifactId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<name>Keycloak AS7 / JBoss EAP 6 Integration</name> <name>Keycloak AS7 / JBoss EAP 6 Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -10
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -63,15 +63,6 @@
<groupId>org.jboss.logging</groupId> <groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId> <artifactId>jboss-logging</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -53,13 +53,7 @@ public class KeycloakCliSso {
login(); login();
} else if (args[0].equalsIgnoreCase("login-manual")) { } else if (args[0].equalsIgnoreCase("login-manual")) {
loginManual(); loginManual();
} } else if (args[0].equalsIgnoreCase("token")) {
/*
else if (args[0].equalsIgnoreCase("login-cli")) {
loginCli();
}
*/
else if (args[0].equalsIgnoreCase("token")) {
token(); token();
} else if (args[0].equalsIgnoreCase("logout")) { } else if (args[0].equalsIgnoreCase("logout")) {
logout(); logout();
@@ -75,7 +69,6 @@ public class KeycloakCliSso {
System.err.println("Commands:"); System.err.println("Commands:");
System.err.println(" login - login with desktop browser if available, otherwise do manual login. Output is access token."); System.err.println(" login - login with desktop browser if available, otherwise do manual login. Output is access token.");
System.err.println(" login-manual - manual login"); System.err.println(" login-manual - manual login");
//System.err.println(" login-cli - attempt Keycloak proprietary cli protocol. Otherwise do normal login");
System.err.println(" token - print access token if logged in"); System.err.println(" token - print access token if logged in");
System.err.println(" logout - logout."); System.err.println(" logout - logout.");
System.exit(1); System.exit(1);
@@ -117,7 +110,7 @@ public class KeycloakCliSso {
return config; return config;
} }
public boolean checkToken(boolean outputToken) throws Exception { public boolean checkToken() throws Exception {
String token = getTokenResponse(); String token = getTokenResponse();
if (token == null) return false; if (token == null) return false;
@@ -134,7 +127,7 @@ public class KeycloakCliSso {
AdapterConfig config = getConfig(); AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config)); KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.refreshToken(tokenResponse.getRefreshToken()); installed.refreshToken(tokenResponse.getRefreshToken());
processResponse(installed, outputToken); processResponse(installed);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error processing existing token"); System.err.println("Error processing existing token");
@@ -191,19 +184,11 @@ public class KeycloakCliSso {
} }
public void login() throws Exception { public void login() throws Exception {
if (checkToken(true)) return; if (checkToken()) return;
AdapterConfig config = getConfig(); AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config)); KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.login(); installed.login();
processResponse(installed, true); processResponse(installed);
}
public void loginCli() throws Exception {
if (checkToken(false)) return;
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
if (!installed.loginCommandLine()) installed.login();
processResponse(installed, false);
} }
public String getHome() { public String getHome() {
@@ -225,7 +210,7 @@ public class KeycloakCliSso {
return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM"), System.getProperty("KEYCLOAK_CLIENT") + ".json").toFile(); return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM"), System.getProperty("KEYCLOAK_CLIENT") + ".json").toFile();
} }
private void processResponse(KeycloakInstalled installed, boolean outputToken) throws IOException { private void processResponse(KeycloakInstalled installed) throws IOException {
AccessTokenResponse tokenResponse = installed.getTokenResponse(); AccessTokenResponse tokenResponse = installed.getTokenResponse();
tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn()); tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
tokenResponse.setIdToken(null); tokenResponse.setIdToken(null);
@@ -235,16 +220,16 @@ public class KeycloakCliSso {
fos.write(output.getBytes("UTF-8")); fos.write(output.getBytes("UTF-8"));
fos.flush(); fos.flush();
fos.close(); fos.close();
if (outputToken) System.out.println(tokenResponse.getToken()); System.out.println(tokenResponse.getToken());
} }
public void loginManual() throws Exception { public void loginManual() throws Exception {
if (checkToken(true)) return; if (checkToken()) return;
AdapterConfig config = getConfig(); AdapterConfig config = getConfig();
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config); KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
KeycloakInstalled installed = new KeycloakInstalled(deployment); KeycloakInstalled installed = new KeycloakInstalled(deployment);
installed.loginManual(); installed.loginManual();
processResponse(installed, true); processResponse(installed);
} }
public void logout() throws Exception { public void logout() throws Exception {
@@ -17,8 +17,7 @@
package org.keycloak.adapters.installed; package org.keycloak.adapters.installed;
import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.apache.commons.codec.Charsets;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
@@ -33,11 +32,6 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import java.awt.*; import java.awt.*;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@@ -46,7 +40,6 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.PrintStream; import java.io.PrintStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.PushbackInputStream;
import java.io.Reader; import java.io.Reader;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
@@ -55,8 +48,6 @@ import java.net.URISyntaxException;
import java.util.Locale; import java.util.Locale;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -86,9 +77,6 @@ public class KeycloakInstalled {
private Locale locale; private Locale locale;
private HttpResponseWriter loginResponseWriter; private HttpResponseWriter loginResponseWriter;
private HttpResponseWriter logoutResponseWriter; private HttpResponseWriter logoutResponseWriter;
Pattern callbackPattern = Pattern.compile("callback\\s*=\\s*\"([^\"]+)\"");
Pattern paramPattern = Pattern.compile("param=\"([^\"]+)\"\\s+label=\"([^\"]+)\"\\s+mask=(\\S+)");
Pattern codePattern = Pattern.compile("code=([^&]+)");
@@ -302,86 +290,6 @@ public class KeycloakInstalled {
status = Status.LOGGED_MANUAL; status = Status.LOGGED_MANUAL;
} }
public boolean loginCommandLine() throws IOException, ServerRequest.HttpFailure, VerificationException {
String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
return loginCommandLine(redirectUri);
}
/**
* Experimental proprietary WWW-Authentication challenge protocol.
* WWW-Authentication: X-Text-Form-Challenge callback="{url}" param="{param-name}" label="{param-display-label}"
*
* @param redirectUri
* @return
* @throws IOException
* @throws ServerRequest.HttpFailure
* @throws VerificationException
*/
public boolean loginCommandLine(String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
String authUrl = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString();
ResteasyClient client = new ResteasyClientBuilder().disableTrustManager().build();
try {
Response response = client.target(authUrl).request().get();
if (response.getStatus() != 401) {
return false;
}
while (true) {
String authenticationHeader = response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE);
if (authenticationHeader == null) {
return false;
}
if (!authenticationHeader.contains("X-Text-Form-Challenge")) {
return false;
}
if (response.getMediaType() != null) {
String splash = response.readEntity(String.class);
System.console().writer().println(splash);
}
Matcher m = callbackPattern.matcher(authenticationHeader);
if (!m.find()) return false;
String callback = m.group(1);
//System.err.println("callback: " + callback);
m = paramPattern.matcher(authenticationHeader);
Form form = new Form();
while (m.find()) {
String param = m.group(1);
String label = m.group(2);
String mask = m.group(3).trim();
boolean maskInput = mask.equals("true");
String value = null;
if (maskInput) {
char[] txt = System.console().readPassword(label);
value = new String(txt);
} else {
value = System.console().readLine(label);
}
form.param(param, value);
}
response = client.target(callback).request().post(Entity.form(form));
if (response.getStatus() == 401) continue;
if (response.getStatus() != 302) return false;
String location = response.getLocation().toString();
m = codePattern.matcher(location);
if (!m.find()) return false;
String code = m.group(1);
processCode(code, redirectUri);
return true;
}
} finally {
client.close();
}
}
public String getTokenString() throws VerificationException, IOException, ServerRequest.HttpFailure { public String getTokenString() throws VerificationException, IOException, ServerRequest.HttpFailure {
return tokenString; return tokenString;
} }
@@ -474,86 +382,6 @@ public class KeycloakInstalled {
return sb.toString(); return sb.toString();
} }
public static class MaskingThread extends Thread {
private volatile boolean stop;
private char echochar = '*';
public MaskingThread() {
}
/**
* Begin masking until asked to stop.
*/
public void run() {
int priority = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
try {
stop = true;
while(stop) {
System.out.print("\010" + echochar);
try {
// attempt masking at this rate
Thread.currentThread().sleep(1);
}catch (InterruptedException iex) {
Thread.currentThread().interrupt();
return;
}
}
} finally { // restore the original priority
Thread.currentThread().setPriority(priority);
}
}
/**
* Instruct the thread to stop masking.
*/
public void stopMasking() {
this.stop = false;
}
}
public static String readMasked(Reader reader) {
MaskingThread et = new MaskingThread();
Thread mask = new Thread(et);
mask.start();
BufferedReader in = new BufferedReader(reader);
String password = "";
try {
password = in.readLine();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// stop masking
et.stopMasking();
// return the password entered by the user
return password;
}
private String readLine(Reader reader, boolean mask) throws IOException {
if (mask) {
System.out.print(" ");
return readMasked(reader);
}
StringBuilder sb = new StringBuilder();
char cb[] = new char[1];
while (reader.read(cb) != -1) {
char c = cb[0];
if ((c == '\n') || (c == '\r')) {
break;
} else {
sb.append(c);
}
}
return sb.toString();
}
public class CallbackListener extends Thread { public class CallbackListener extends Thread {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+2 -2
View File
@@ -20,7 +20,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<name>Keycloak Jetty Integration</name> <name>Keycloak Jetty Integration</name>
@@ -33,7 +33,6 @@
<modules> <modules>
<module>jetty-core</module> <module>jetty-core</module>
<module>jetty8.1</module> <module>jetty8.1</module>
<module>jetty9.1</module>
<module>jetty9.2</module> <module>jetty9.2</module>
<module>jetty9.3</module> <module>jetty9.3</module>
</modules> </modules>
@@ -47,6 +46,7 @@
</property> </property>
</activation> </activation>
<modules> <modules>
<module>jetty9.1</module>
<module>jetty9.4</module> <module>jetty9.4</module>
</modules> </modules>
</profile> </profile>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -18,14 +18,14 @@
(function( window, undefined ) { (function( window, undefined ) {
var KeycloakAuthorization = function (keycloak, options) { var KeycloakAuthorization = function (keycloak) {
var _instance = this; var _instance = this;
this.rpt = null; this.rpt = null;
this.init = function () { this.init = function () {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma2-configuration'); request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma-configuration');
request.onreadystatechange = function () { request.onreadystatechange = function () {
if (request.readyState == 4) { if (request.readyState == 4) {
if (request.status == 200) { if (request.status == 200) {
@@ -47,61 +47,68 @@
* necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol, * necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol,
* depending on how the policy enforcer at the resource server was configured. * depending on how the policy enforcer at the resource server was configured.
*/ */
this.authorize = function (authorizationRequest) { this.authorize = function (wwwAuthenticateHeader) {
this.then = function (onGrant, onDeny, onError) { this.then = function (onGrant, onDeny, onError) {
if (authorizationRequest && authorizationRequest.ticket) { if (wwwAuthenticateHeader.indexOf('UMA') != -1) {
var request = new XMLHttpRequest(); var params = wwwAuthenticateHeader.split(',');
request.open('POST', _instance.config.token_endpoint, true); for (i = 0; i < params.length; i++) {
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); var param = params[i].split('=');
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
request.onreadystatechange = function () { if (param[0] == 'ticket') {
if (request.readyState == 4) { var request = new XMLHttpRequest();
var status = request.status;
if (status >= 200 && status < 300) { request.open('POST', _instance.config.rpt_endpoint, true);
var rpt = JSON.parse(request.responseText).access_token; request.setRequestHeader('Content-Type', 'application/json')
_instance.rpt = rpt; request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
onGrant(rpt);
} else if (status == 403) { request.onreadystatechange = function () {
if (onDeny) { if (request.readyState == 4) {
onDeny(); var status = request.status;
} else {
console.error('Authorization request was denied by the server.'); if (status >= 200 && status < 300) {
var rpt = JSON.parse(request.responseText).rpt;
_instance.rpt = rpt;
onGrant(rpt);
} else if (status == 403) {
if (onDeny) {
onDeny();
} else {
console.error('Authorization request was denied by the server.');
}
} else {
if (onError) {
onError();
} else {
console.error('Could not obtain authorization data from server.');
}
}
} }
} else { };
if (onError) {
onError(); var ticket = param[1].substring(1, param[1].length - 1).trim();
} else {
console.error('Could not obtain authorization data from server.'); request.send(JSON.stringify(
{
ticket: ticket,
rpt: _instance.rpt
} }
} ));
} }
};
var params = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&client_id=" + keycloak.clientId + "&ticket=" + authorizationRequest.ticket;
if (authorizationRequest.submitRequest != undefined) {
params += "&submit_request=" + authorizationRequest.submitRequest;
} }
} else if (wwwAuthenticateHeader.indexOf('KC_ETT') != -1) {
var params = wwwAuthenticateHeader.substring('KC_ETT'.length).trim().split(',');
var clientId = null;
var metadata = authorizationRequest.metadata; for (i = 0; i < params.length; i++) {
var param = params[i].split('=');
if (metadata) { if (param[0] == 'realm') {
if (metadata.responseIncludeResourceName) { clientId = param[1].substring(1, param[1].length - 1).trim();
params += "&response_include_resource_name=" + metadata.responseIncludeResourceName;
}
if (metadata.responsePermissionsLimit) {
params += "&response_permissions_limit=" + metadata.responsePermissionsLimit;
} }
} }
if (_instance.rpt && (authorizationRequest.incrementalAuthorization == undefined || authorizationRequest.incrementalAuthorization)) { _instance.entitlement(clientId).then(onGrant, onDeny, onError);
params += "&rpt=" + _instance.rpt;
}
request.send(params);
} }
}; };
@@ -109,22 +116,20 @@
}; };
/** /**
* Obtains all entitlements from a Keycloak Server based on a given resourceServerId. * Obtains all entitlements from a Keycloak Server based on a give resourceServerId.
*/ */
this.entitlement = function (resourceServerId, authorizationRequest) { this.entitlement = function (resourceSeververId, entitlementRequest ) {
this.then = function (onGrant, onDeny, onError) { this.then = function (onGrant, onDeny, onError) {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open('POST', _instance.config.token_endpoint, true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
request.onreadystatechange = function () { request.onreadystatechange = function () {
if (request.readyState == 4) { if (request.readyState == 4) {
var status = request.status; var status = request.status;
if (status >= 200 && status < 300) { if (status >= 200 && status < 300) {
var rpt = JSON.parse(request.responseText).access_token; var rpt = JSON.parse(request.responseText).rpt;
_instance.rpt = rpt; _instance.rpt = rpt;
onGrant(rpt); onGrant(rpt);
} else if (status == 403) { } else if (status == 403) {
@@ -143,62 +148,19 @@
} }
}; };
if (!authorizationRequest) { var erJson = null
authorizationRequest = {};
if(entitlementRequest) {
request.open('POST', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
request.setRequestHeader("Content-type", "application/json");
erJson = JSON.stringify(entitlementRequest)
} else {
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
} }
var params = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&client_id=" + keycloak.clientId; request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
request.send(erJson);
if (authorizationRequest.claimToken) {
params += "&claim_token=" + authorizationRequest.claimToken;
if (authorizationRequest.claimTokenFormat) {
params += "&claim_token_format=" + authorizationRequest.claimTokenFormat;
}
}
params += "&audience=" + resourceServerId;
var permissions = authorizationRequest.permissions;
if (!permissions) {
permissions = [];
}
for (i = 0; i < permissions.length; i++) {
var resource = permissions[i];
var permission = resource.id;
if (resource.scopes && resource.scopes.length > 0) {
permission += "#";
for (j = 0; j < resource.scopes.length; j++) {
var scope = resource.scopes[j];
if (permission.indexOf('#') != permission.length - 1) {
permission += ",";
}
permission += scope;
}
}
params += "&permission=" + permission;
}
var metadata = authorizationRequest.metadata;
if (metadata) {
if (metadata.responseIncludeResourceName) {
params += "&response_include_resource_name=" + metadata.responseIncludeResourceName;
}
if (metadata.responsePermissionsLimit) {
params += "&response_permissions_limit=" + metadata.responsePermissionsLimit;
}
}
if (_instance.rpt) {
params += "&rpt=" + _instance.rpt;
}
request.send(params);
}; };
return this; return this;
+2 -7
View File
@@ -36,16 +36,11 @@ declare namespace Keycloak {
type KeycloakFlow = 'standard'|'implicit'|'hybrid'; type KeycloakFlow = 'standard'|'implicit'|'hybrid';
interface KeycloakInitOptions { interface KeycloakInitOptions {
/**
* @private Undocumented.
*/
useNonce?: boolean;
/** /**
* @private Undocumented. * @private Undocumented.
*/ */
adapter?: KeycloakAdapterName; adapter?: KeycloakAdapterName;
/** /**
* Specifies an action to do on load. * Specifies an action to do on load.
*/ */
@@ -83,7 +78,7 @@ declare namespace Keycloak {
* Set the interval to check login state (in seconds). * Set the interval to check login state (in seconds).
* @default 5 * @default 5
*/ */
checkLoginIframeInterval?: number; checkLoginIframeInterval?: boolean;
/** /**
* Set the OpenID Connect response mode to send to Keycloak upon login. * Set the OpenID Connect response mode to send to Keycloak upon login.
+163 -301
View File
@@ -40,8 +40,6 @@
} }
} }
var useNonce = true;
kc.init = function (initOptions) { kc.init = function (initOptions) {
kc.authenticated = false; kc.authenticated = false;
@@ -52,7 +50,7 @@
} else if (initOptions && initOptions.adapter === 'default') { } else if (initOptions && initOptions.adapter === 'default') {
adapter = loadAdapter(); adapter = loadAdapter();
} else { } else {
if (window.Cordova || window.cordova) { if (window.Cordova) {
adapter = loadAdapter('cordova'); adapter = loadAdapter('cordova');
} else { } else {
adapter = loadAdapter(); adapter = loadAdapter();
@@ -60,10 +58,6 @@
} }
if (initOptions) { if (initOptions) {
if (typeof initOptions.useNonce !== 'undefined') {
useNonce = initOptions.useNonce;
}
if (typeof initOptions.checkLoginIframe !== 'undefined') { if (typeof initOptions.checkLoginIframe !== 'undefined') {
loginIframe.enable = initOptions.checkLoginIframe; loginIframe.enable = initOptions.checkLoginIframe;
} }
@@ -165,15 +159,10 @@
var callback = parseCallback(window.location.href); var callback = parseCallback(window.location.href);
if (callback) { if (callback) {
setupCheckLoginIframe();
window.history.replaceState({}, null, callback.newUrl); window.history.replaceState({}, null, callback.newUrl);
} processCallback(callback, initPromise);
return;
if (callback && callback.valid) {
return setupCheckLoginIframe().success(function() {
processCallback(callback, initPromise);
}).error(function (e) {
initPromise.setError();
});
} else if (initOptions) { } else if (initOptions) {
if (initOptions.token && initOptions.refreshToken) { if (initOptions.token && initOptions.refreshToken) {
setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken); setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
@@ -241,25 +230,22 @@
callbackStorage.add(callbackState); callbackStorage.add(callbackState);
var baseUrl; var action = 'auth';
if (options && options.action == 'register') { if (options && options.action == 'register') {
baseUrl = kc.endpoints.register(); action = 'registrations';
} else {
baseUrl = kc.endpoints.authorize();
} }
var scope = (options && options.scope) ? "openid " + options.scope : "openid"; var scope = (options && options.scope) ? "openid " + options.scope : "openid";
var url = baseUrl var url = getRealmUrl()
+ '/protocol/openid-connect/' + action
+ '?client_id=' + encodeURIComponent(kc.clientId) + '?client_id=' + encodeURIComponent(kc.clientId)
+ '&redirect_uri=' + encodeURIComponent(redirectUri) + '&redirect_uri=' + encodeURIComponent(redirectUri)
+ '&state=' + encodeURIComponent(state) + '&state=' + encodeURIComponent(state)
+ '&nonce=' + encodeURIComponent(nonce)
+ '&response_mode=' + encodeURIComponent(kc.responseMode) + '&response_mode=' + encodeURIComponent(kc.responseMode)
+ '&response_type=' + encodeURIComponent(kc.responseType) + '&response_type=' + encodeURIComponent(kc.responseType)
+ '&scope=' + encodeURIComponent(scope); + '&scope=' + encodeURIComponent(scope);
if (useNonce) {
url = url + '&nonce=' + encodeURIComponent(nonce);
}
if (options && options.prompt) { if (options && options.prompt) {
url += '&prompt=' + encodeURIComponent(options.prompt); url += '&prompt=' + encodeURIComponent(options.prompt);
@@ -289,7 +275,8 @@
} }
kc.createLogoutUrl = function(options) { kc.createLogoutUrl = function(options) {
var url = kc.endpoints.logout() var url = getRealmUrl()
+ '/protocol/openid-connect/logout'
+ '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false)); + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
return url; return url;
@@ -308,14 +295,11 @@
} }
kc.createAccountUrl = function(options) { kc.createAccountUrl = function(options) {
var realm = getRealmUrl(); var url = getRealmUrl()
var url = undefined;
if (typeof realm !== 'undefined') {
url = realm
+ '/account' + '/account'
+ '?referrer=' + encodeURIComponent(kc.clientId) + '?referrer=' + encodeURIComponent(kc.clientId)
+ '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options)); + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
}
return url; return url;
} }
@@ -363,7 +347,7 @@
} }
kc.loadUserInfo = function() { kc.loadUserInfo = function() {
var url = kc.endpoints.userinfo(); var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.open('GET', url, true); req.open('GET', url, true);
req.setRequestHeader('Accept', 'application/json'); req.setRequestHeader('Accept', 'application/json');
@@ -428,7 +412,7 @@
promise.setSuccess(false); promise.setSuccess(false);
} else { } else {
var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken; var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
var url = kc.endpoints.token(); var url = getRealmUrl() + '/protocol/openid-connect/token';
refreshQueue.push(promise); refreshQueue.push(promise);
@@ -502,14 +486,10 @@
} }
function getRealmUrl() { function getRealmUrl() {
if (typeof kc.authServerUrl !== 'undefined') { if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') { return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
} else {
return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
}
} else { } else {
return undefined; return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
} }
} }
@@ -543,7 +523,7 @@
if ((kc.flow != 'implicit') && code) { if ((kc.flow != 'implicit') && code) {
var params = 'code=' + code + '&grant_type=authorization_code'; var params = 'code=' + code + '&grant_type=authorization_code';
var url = kc.endpoints.token(); var url = getRealmUrl() + '/protocol/openid-connect/token';
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.open('POST', url, true); req.open('POST', url, true);
@@ -580,9 +560,9 @@
setToken(accessToken, refreshToken, idToken, timeLocal); setToken(accessToken, refreshToken, idToken, timeLocal);
if (useNonce && ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) || if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
(kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) || (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
(kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce))) { (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
console.info('[KEYCLOAK] Invalid nonce, clearing token'); console.info('[KEYCLOAK] Invalid nonce, clearing token');
kc.clearToken(); kc.clearToken();
@@ -607,65 +587,6 @@
configUrl = config; configUrl = config;
} }
function setupOidcEndoints(oidcConfiguration) {
if (! oidcConfiguration) {
kc.endpoints = {
authorize: function() {
return getRealmUrl() + '/protocol/openid-connect/auth';
},
token: function() {
return getRealmUrl() + '/protocol/openid-connect/token';
},
logout: function() {
return getRealmUrl() + '/protocol/openid-connect/logout';
},
checkSessionIframe: function() {
var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
if (kc.iframeVersion) {
src = src + '?version=' + kc.iframeVersion;
}
return src;
},
register: function() {
return getRealmUrl() + '/protocol/openid-connect/registrations';
},
userinfo: function() {
return getRealmUrl() + '/protocol/openid-connect/userinfo';
}
};
} else {
kc.endpoints = {
authorize: function() {
return oidcConfiguration.authorization_endpoint;
},
token: function() {
return oidcConfiguration.token_endpoint;
},
logout: function() {
if (!oidcConfiguration.end_session_endpoint) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.end_session_endpoint;
},
checkSessionIframe: function() {
if (!oidcConfiguration.check_session_iframe) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.check_session_iframe;
},
register: function() {
throw 'Redirection to "Register user" page not supported in standard OIDC mode';
},
userinfo: function() {
if (!oidcConfiguration.userinfo_endpoint) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.userinfo_endpoint;
}
}
}
}
if (configUrl) { if (configUrl) {
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.open('GET', configUrl, true); req.open('GET', configUrl, true);
@@ -680,7 +601,7 @@
kc.realm = config['realm']; kc.realm = config['realm'];
kc.clientId = config['resource']; kc.clientId = config['resource'];
kc.clientSecret = (config['credentials'] || {})['secret']; kc.clientSecret = (config['credentials'] || {})['secret'];
setupOidcEndoints(null);
promise.setSuccess(); promise.setSuccess();
} else { } else {
promise.setError(); promise.setError();
@@ -690,62 +611,30 @@
req.send(); req.send();
} else { } else {
if (!config['url']) {
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.match(/.*keycloak\.js/)) {
config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
break;
}
}
}
if (!config.realm) {
throw 'realm missing';
}
if (!config.clientId) { if (!config.clientId) {
throw 'clientId missing'; throw 'clientId missing';
} }
kc.authServerUrl = config.url;
kc.realm = config.realm;
kc.clientId = config.clientId; kc.clientId = config.clientId;
kc.clientSecret = (config.credentials || {}).secret; kc.clientSecret = (config.credentials || {}).secret;
var oidcProvider = config['oidcProvider']; promise.setSuccess();
if (!oidcProvider) {
if (!config['url']) {
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.match(/.*keycloak\.js/)) {
config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
break;
}
}
}
if (!config.realm) {
throw 'realm missing';
}
kc.authServerUrl = config.url;
kc.realm = config.realm;
setupOidcEndoints(null);
promise.setSuccess();
} else {
if (typeof oidcProvider === 'string') {
var oidcProviderConfigUrl;
if (oidcProvider.charAt(oidcProvider.length - 1) == '/') {
oidcProviderConfigUrl = oidcProvider + '.well-known/openid-configuration';
} else {
oidcProviderConfigUrl = oidcProvider + '/.well-known/openid-configuration';
}
var req = new XMLHttpRequest();
req.open('GET', oidcProviderConfigUrl, true);
req.setRequestHeader('Accept', 'application/json');
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200 || fileLoaded(req)) {
var oidcProviderConfig = JSON.parse(req.responseText);
setupOidcEndoints(oidcProviderConfig);
promise.setSuccess();
} else {
promise.setError();
}
}
};
req.send();
} else {
setupOidcEndoints(oidcProvider);
promise.setSuccess();
}
}
} }
return promise.promise; return promise.promise;
@@ -864,137 +753,23 @@
} }
function parseCallback(url) { function parseCallback(url) {
var oauth = parseCallbackUrl(url); var oauth = new CallbackParser(url, kc.responseMode).parseUri();
if (!oauth) {
return;
}
var oauthState = callbackStorage.get(oauth.state); var oauthState = callbackStorage.get(oauth.state);
if (oauthState) { if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
oauth.valid = true;
oauth.redirectUri = oauthState.redirectUri; oauth.redirectUri = oauthState.redirectUri;
oauth.storedNonce = oauthState.nonce; oauth.storedNonce = oauthState.nonce;
oauth.prompt = oauthState.prompt; oauth.prompt = oauthState.prompt;
}
return oauth; if (oauth.fragment) {
} oauth.newUrl += '#' + oauth.fragment;
function parseCallbackUrl(url) {
var supportedParams;
switch (kc.flow) {
case 'standard':
supportedParams = ['code', 'state', 'session_state'];
break;
case 'implicit':
supportedParams = ['access_token', 'id_token', 'state', 'session_state'];
break;
case 'hybrid':
supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state'];
break;
}
supportedParams.push('error');
supportedParams.push('error_description');
supportedParams.push('error_uri');
var queryIndex = url.indexOf('?');
var fragmentIndex = url.indexOf('#');
var newUrl;
var parsed;
if (kc.responseMode === 'query' && queryIndex !== -1) {
newUrl = url.substring(0, queryIndex);
parsed = parseCallbackParams(url.substring(queryIndex + 1, fragmentIndex !== -1 ? fragmentIndex : url.length), supportedParams);
if (parsed.paramsString !== '') {
newUrl += '?' + parsed.paramsString;
} }
if (fragmentIndex !== -1) {
newUrl += url.substring(fragmentIndex);
}
} else if (kc.responseMode === 'fragment' && fragmentIndex !== -1) {
newUrl = url.substring(0, fragmentIndex);
parsed = parseCallbackParams(url.substring(fragmentIndex + 1), supportedParams);
if (parsed.paramsString !== '') {
newUrl += '#' + parsed.paramsString;
}
}
if (parsed && parsed.oauthParams) { return oauth;
if (kc.flow === 'standard' || kc.flow === 'hybrid') {
if ((parsed.oauthParams.code || parsed.oauthParams.error) && parsed.oauthParams.state) {
parsed.oauthParams.newUrl = newUrl;
return parsed.oauthParams;
}
} else if (kc.flow === 'implicit') {
if ((parsed.oauthParams.access_token || parsed.oauthParams.error) && parsed.oauthParams.state) {
parsed.oauthParams.newUrl = newUrl;
return parsed.oauthParams;
}
}
} }
} }
function parseCallbackParams(paramsString, supportedParams) {
var p = paramsString.split('&');
var result = {
paramsString: '',
oauthParams: {}
}
for (var i = 0; i < p.length; i++) {
var t = p[i].split('=');
if (supportedParams.indexOf(t[0]) !== -1) {
result.oauthParams[t[0]] = t[1];
} else {
if (result.paramsString !== '') {
result.paramsString += '&';
}
result.paramsString += p[i];
}
}
return result;
}
function createPromise() { function createPromise() {
if (typeof Promise === "function") {
return createNativePromise();
} else {
return createLegacyPromise();
}
}
function createNativePromise() {
// Need to create a native Promise which also preserves the
// interface of the custom promise type previously used by the API
var p = {
setSuccess: function(result) {
p.success = true;
p.resolve(result);
},
setError: function(result) {
p.success = false;
p.reject(result);
}
};
p.promise = new Promise(function(resolve, reject) {
p.resolve = resolve;
p.reject = reject;
});
p.promise.success = function(callback) {
p.promise.then(callback);
return p.promise;
}
p.promise.error = function(callback) {
p.promise.catch(callback);
return p.promise;
}
return p;
}
function createLegacyPromise() {
var p = { var p = {
setSuccess: function(result) { setSuccess: function(result) {
p.success = true; p.success = true;
@@ -1051,18 +826,22 @@
loginIframe.iframe = iframe; loginIframe.iframe = iframe;
iframe.onload = function() { iframe.onload = function() {
var authUrl = kc.endpoints.authorize(); var realmUrl = getRealmUrl();
if (authUrl.charAt(0) === '/') { if (realmUrl.charAt(0) === '/') {
loginIframe.iframeOrigin = getOrigin(); loginIframe.iframeOrigin = getOrigin();
} else { } else {
loginIframe.iframeOrigin = authUrl.substring(0, authUrl.indexOf('/', 8)); loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
} }
promise.setSuccess(); promise.setSuccess();
setTimeout(check, loginIframe.interval * 1000); setTimeout(check, loginIframe.interval * 1000);
} }
var src = kc.endpoints.checkSessionIframe(); var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
if (kc.iframeVersion) {
src = src + '?version=' + kc.iframeVersion;
}
iframe.setAttribute('src', src ); iframe.setAttribute('src', src );
iframe.setAttribute('title', 'keycloak-session-iframe' ); iframe.setAttribute('title', 'keycloak-session-iframe' );
iframe.style.display = 'none'; iframe.style.display = 'none';
@@ -1142,12 +921,7 @@
}, },
accountManagement : function() { accountManagement : function() {
var accountUrl = kc.createAccountUrl(); window.location.href = kc.createAccountUrl();
if (typeof accountUrl !== 'undefined') {
window.location.href = accountUrl;
} else {
throw "Not supported by the OIDC server";
}
return createPromise().promise; return createPromise().promise;
}, },
@@ -1161,7 +935,12 @@
} else if (kc.redirectUri) { } else if (kc.redirectUri) {
return kc.redirectUri; return kc.redirectUri;
} else { } else {
return location.href; var redirectUri = location.href;
if (location.hash && encodeHash) {
redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
}
return redirectUri;
} }
} }
}; };
@@ -1169,14 +948,7 @@
if (type == 'cordova') { if (type == 'cordova') {
loginIframe.enable = false; loginIframe.enable = false;
var cordovaOpenWindowWrapper = function(loginUrl, target, options) {
if (window.cordova && window.cordova.InAppBrowser) {
// Use inappbrowser for IOS and Android if available
return window.cordova.InAppBrowser.open(loginUrl, target, options);
} else {
return window.open(loginUrl, target, options);
}
};
return { return {
login: function(options) { login: function(options) {
var promise = createPromise(); var promise = createPromise();
@@ -1187,7 +959,8 @@
} }
var loginUrl = kc.createLoginUrl(options); var loginUrl = kc.createLoginUrl(options);
var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o); var ref = window.open(loginUrl, '_blank', o);
var completed = false; var completed = false;
ref.addEventListener('loadstart', function(event) { ref.addEventListener('loadstart', function(event) {
@@ -1220,7 +993,7 @@
var promise = createPromise(); var promise = createPromise();
var logoutUrl = kc.createLogoutUrl(options); var logoutUrl = kc.createLogoutUrl(options);
var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes'); var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
var error; var error;
@@ -1253,7 +1026,7 @@
register : function() { register : function() {
var registerUrl = kc.createRegisterUrl(); var registerUrl = kc.createRegisterUrl();
var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no'); var ref = window.open(registerUrl, '_blank', 'location=no');
ref.addEventListener('loadstart', function(event) { ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) { if (event.url.indexOf('http://localhost') == 0) {
ref.close(); ref.close();
@@ -1263,16 +1036,12 @@
accountManagement : function() { accountManagement : function() {
var accountUrl = kc.createAccountUrl(); var accountUrl = kc.createAccountUrl();
if (typeof accountUrl !== 'undefined') { var ref = window.open(accountUrl, '_blank', 'location=no');
var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no'); ref.addEventListener('loadstart', function(event) {
ref.addEventListener('loadstart', function(event) { if (event.url.indexOf('http://localhost') == 0) {
if (event.url.indexOf('http://localhost') == 0) { ref.close();
ref.close(); }
} });
});
} else {
throw "Not supported by the OIDC server";
}
}, },
redirectUri: function(options) { redirectUri: function(options) {
@@ -1402,6 +1171,99 @@
return new CookieStorage(); return new CookieStorage();
} }
var CallbackParser = function(uriToParse, responseMode) {
if (!(this instanceof CallbackParser)) {
return new CallbackParser(uriToParse, responseMode);
}
var parser = this;
var initialParse = function() {
var baseUri = null;
var queryString = null;
var fragmentString = null;
var questionMarkIndex = uriToParse.indexOf("?");
var fragmentIndex = uriToParse.indexOf("#", questionMarkIndex + 1);
if (questionMarkIndex == -1 && fragmentIndex == -1) {
baseUri = uriToParse;
} else if (questionMarkIndex != -1) {
baseUri = uriToParse.substring(0, questionMarkIndex);
queryString = uriToParse.substring(questionMarkIndex + 1);
if (fragmentIndex != -1) {
fragmentIndex = queryString.indexOf("#");
fragmentString = queryString.substring(fragmentIndex + 1);
queryString = queryString.substring(0, fragmentIndex);
}
} else {
baseUri = uriToParse.substring(0, fragmentIndex);
fragmentString = uriToParse.substring(fragmentIndex + 1);
}
return { baseUri: baseUri, queryString: queryString, fragmentString: fragmentString };
}
var parseParams = function(paramString) {
var result = {};
var params = paramString.split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
var paramName = decodeURIComponent(p[0]);
var paramValue = decodeURIComponent(p[1]);
result[paramName] = paramValue;
}
return result;
}
var handleQueryParam = function(paramName, paramValue, oauth) {
var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
if (paramName === supportedOAuthParams[i]) {
oauth[paramName] = paramValue;
return true;
}
}
return false;
}
parser.parseUri = function() {
var parsedUri = initialParse();
var queryParams = {};
if (parsedUri.queryString) {
queryParams = parseParams(parsedUri.queryString);
}
var oauth = { newUrl: parsedUri.baseUri };
for (var param in queryParams) {
switch (param) {
case 'redirect_fragment':
oauth.fragment = queryParams[param];
break;
default:
if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
}
break;
}
}
if (responseMode === 'fragment') {
var fragmentParams = {};
if (parsedUri.fragmentString) {
fragmentParams = parseParams(parsedUri.fragmentString);
}
for (var param in fragmentParams) {
oauth[param] = fragmentParams[param];
}
}
return oauth;
}
}
} }
if ( typeof module === "object" && module && typeof module.exports === "object" ) { if ( typeof module === "object" && module && typeof module.exports === "object" ) {
@@ -36,7 +36,7 @@
req.onreadystatechange = function () { req.onreadystatechange = function () {
if (req.readyState === 4) { if (req.readyState === 4) {
if (req.status === 204 || req.status === 1223) { if (req.status === 204) {
init = { init = {
clientId: clientId, clientId: clientId,
origin: origin origin: origin
@@ -79,10 +79,6 @@
function receiveMessage(event) function receiveMessage(event)
{ {
if (typeof event.data !== 'string') {
return
}
var origin = event.origin; var origin = event.origin;
var data = event.data.split(' '); var data = event.data.split(' ');
if (data.length != 2) { if (data.length != 2) {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<name>Keycloak OIDC Client Adapter Modules</name> <name>Keycloak OIDC Client Adapter Modules</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -4,7 +4,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<artifactId>spring-boot-container-bundle</artifactId> <artifactId>spring-boot-container-bundle</artifactId>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -132,8 +132,10 @@ public class KeycloakAutoConfiguration {
loginConfig.addFirstAuthMethod("KEYCLOAK"); loginConfig.addFirstAuthMethod("KEYCLOAK");
deploymentInfo.setLoginConfig(loginConfig); deploymentInfo.setLoginConfig(loginConfig);
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName()); deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
deploymentInfo.addSecurityConstraints(getSecurityConstraints()); deploymentInfo.addSecurityConstraints(getSecurityConstraints());
deploymentInfo.addServletExtension(new KeycloakServletExtension()); deploymentInfo.addServletExtension(new KeycloakServletExtension());
} }
@@ -155,6 +157,7 @@ public class KeycloakAutoConfiguration {
undertowSecurityConstraint.addWebResourceCollections(webResourceCollection); undertowSecurityConstraint.addWebResourceCollections(webResourceCollection);
} }
undertowSecurityConstraints.add(undertowSecurityConstraint); undertowSecurityConstraints.add(undertowSecurityConstraint);
} }
return undertowSecurityConstraints; return undertowSecurityConstraints;
@@ -277,11 +280,6 @@ public class KeycloakAutoConfiguration {
for (String authRole : constraint.getAuthRoles()) { for (String authRole : constraint.getAuthRoles()) {
tomcatConstraint.addAuthRole(authRole); 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()) { for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -45,7 +45,11 @@ public class HttpHeaderInspectingApiRequestMatcher implements RequestMatcher {
*/ */
@Override @Override
public boolean matches(HttpServletRequest request) { public boolean matches(HttpServletRequest request) {
return X_REQUESTED_WITH_HEADER_AJAX_VALUE.equals(request.getHeader(X_REQUESTED_WITH_HEADER)); boolean ajax = X_REQUESTED_WITH_HEADER_AJAX_VALUE.equals(request.getHeader(X_REQUESTED_WITH_HEADER));
boolean html = request.getHeader(HttpHeaders.ACCEPT) != null && request.getHeader(HttpHeaders.ACCEPT).contains(
MediaType.TEXT_HTML_VALUE);
return ajax || !html;
} }
} }
@@ -17,16 +17,7 @@
package org.keycloak.adapters.springsecurity.authentication; package org.keycloak.adapters.springsecurity.authentication;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpHeaders; import org.apache.http.HttpHeaders;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -35,6 +26,17 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/** /**
* Provides a Keycloak {@link AuthenticationEntryPoint authentication entry point}. Uses a * Provides a Keycloak {@link AuthenticationEntryPoint authentication entry point}. Uses a
* {@link RequestMatcher} to determine if the request is an interactive login request or a * {@link RequestMatcher} to determine if the request is an interactive login request or a
@@ -60,14 +62,15 @@ public class KeycloakAuthenticationEntryPoint implements AuthenticationEntryPoin
private final RequestMatcher apiRequestMatcher; private final RequestMatcher apiRequestMatcher;
private String loginUri = DEFAULT_LOGIN_URI; private String loginUri = DEFAULT_LOGIN_URI;
private String realm = DEFAULT_REALM; private String realm = DEFAULT_REALM;
private AdapterDeploymentContext adapterDeploymentContext; private AdapterDeploymentContext adapterDeploymentContext;
/** /**
* Creates a new Keycloak authentication entry point. * Creates a new Keycloak authentication entry point.
*/ */
public KeycloakAuthenticationEntryPoint(AdapterDeploymentContext adapterDeploymentContext) { public KeycloakAuthenticationEntryPoint(AdapterDeploymentContext adapterDeploymentContext) {
this(adapterDeploymentContext, DEFAULT_API_REQUEST_MATCHER); this(DEFAULT_API_REQUEST_MATCHER);
this.adapterDeploymentContext = adapterDeploymentContext;
} }
/** /**
@@ -77,15 +80,14 @@ public class KeycloakAuthenticationEntryPoint implements AuthenticationEntryPoin
* @param apiRequestMatcher the <code>RequestMatcher</code> to use to determine * @param apiRequestMatcher the <code>RequestMatcher</code> to use to determine
* if the current request is an API request or a browser request (required) * if the current request is an API request or a browser request (required)
*/ */
public KeycloakAuthenticationEntryPoint(AdapterDeploymentContext adapterDeploymentContext, RequestMatcher apiRequestMatcher) { public KeycloakAuthenticationEntryPoint(RequestMatcher apiRequestMatcher) {
Assert.notNull(apiRequestMatcher, "apiRequestMatcher required"); Assert.notNull(apiRequestMatcher, "apiRequestMatcher required");
Assert.notNull(adapterDeploymentContext, "adapterDeploymentContext required");
this.adapterDeploymentContext = adapterDeploymentContext;
this.apiRequestMatcher = apiRequestMatcher; this.apiRequestMatcher = apiRequestMatcher;
} }
@Override @Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException
{
HttpFacade facade = new SimpleHttpFacade(request, response); HttpFacade facade = new SimpleHttpFacade(request, response);
if (apiRequestMatcher.matches(request) || adapterDeploymentContext.resolveDeployment(facade).isBearerOnly()) { if (apiRequestMatcher.matches(request) || adapterDeploymentContext.resolveDeployment(facade).isBearerOnly()) {
commenceUnauthorizedResponse(request, response); commenceUnauthorizedResponse(request, response);
@@ -17,13 +17,14 @@
package org.keycloak.adapters.springsecurity.authentication; package org.keycloak.adapters.springsecurity.authentication;
import org.springframework.security.core.AuthenticationException; import java.io.IOException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
/** /**
* To return the forbidden code with the corresponding message. * To return the forbidden code with the corresponding message.
@@ -35,14 +36,6 @@ public class KeycloakAuthenticationFailureHandler implements AuthenticationFailu
@Override @Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// Check that the response was not committed yet (this may happen when another response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unable to authenticate using the Authorization header");
// part of the Keycloak adapter sends a challenge or a redirect).
if (!response.isCommitted()) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unable to authenticate using the Authorization header");
} else {
if (200 <= response.getStatus() && response.getStatus() < 300) {
throw new RuntimeException("Success response was committed while authentication failed!", exception);
}
}
} }
} }
@@ -50,7 +50,7 @@ public class KeycloakAuthenticationProvider implements AuthenticationProvider {
for (String role : token.getAccount().getRoles()) { for (String role : token.getAccount().getRoles()) {
grantedAuthorities.add(new KeycloakRole(role)); grantedAuthorities.add(new KeycloakRole(role));
} }
return new KeycloakAuthenticationToken(token.getAccount(), token.isInteractive(), mapAuthorities(grantedAuthorities)); return new KeycloakAuthenticationToken(token.getAccount(), mapAuthorities(grantedAuthorities));
} }
private Collection<? extends GrantedAuthority> mapAuthorities( private Collection<? extends GrantedAuthority> mapAuthorities(
@@ -94,7 +94,7 @@ public class SpringSecurityRequestAuthenticator extends RequestAuthenticator {
logger.debug("Completing bearer authentication. Bearer roles: {} ",roles); logger.debug("Completing bearer authentication. Bearer roles: {} ",roles);
SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account, false)); SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account));
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext); request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
} }
@@ -23,11 +23,9 @@ import org.keycloak.adapters.springsecurity.AdapterDeploymentContextFactoryBean;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler; import org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler;
import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter;
import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter; import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter;
import org.keycloak.adapters.springsecurity.filter.KeycloakCsrfRequestMatcher; import org.keycloak.adapters.springsecurity.filter.KeycloakCsrfRequestMatcher;
import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter; import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter;
import org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter;
import org.keycloak.adapters.springsecurity.management.HttpSessionManager; import org.keycloak.adapters.springsecurity.management.HttpSessionManager;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@@ -43,7 +41,6 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
/** /**
* Provides a convenient base class for creating a {@link WebSecurityConfigurer} * Provides a convenient base class for creating a {@link WebSecurityConfigurer}
@@ -94,11 +91,6 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
return new KeycloakPreAuthActionsFilter(httpSessionManager()); return new KeycloakPreAuthActionsFilter(httpSessionManager());
} }
@Bean
protected KeycloakAuthenticatedActionsFilter keycloakAuthenticatedActionsFilter() {
return new KeycloakAuthenticatedActionsFilter();
}
protected KeycloakCsrfRequestMatcher keycloakCsrfRequestMatcher() { protected KeycloakCsrfRequestMatcher keycloakCsrfRequestMatcher() {
return new KeycloakCsrfRequestMatcher(); return new KeycloakCsrfRequestMatcher();
} }
@@ -125,8 +117,6 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
.and() .and()
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class) .addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
.addFilterBefore(keycloakAuthenticationProcessingFilter(), BasicAuthenticationFilter.class) .addFilterBefore(keycloakAuthenticationProcessingFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(keycloakAuthenticatedActionsFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(keycloakSecurityContextRequestFilter(), SecurityContextHolderAwareRequestFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()) .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and() .and()
.logout() .logout()
@@ -134,9 +124,4 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
.logoutUrl("/sso/logout").permitAll() .logoutUrl("/sso/logout").permitAll()
.logoutSuccessUrl("/"); .logoutSuccessUrl("/");
} }
@Bean
protected KeycloakSecurityContextRequestFilter keycloakSecurityContextRequestFilter() {
return new KeycloakSecurityContextRequestFilter();
}
} }
@@ -24,7 +24,6 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore; import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
@@ -37,7 +36,6 @@ import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticatio
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator; import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade; import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
import org.keycloak.adapters.springsecurity.token.AdapterTokenStoreFactory; import org.keycloak.adapters.springsecurity.token.AdapterTokenStoreFactory;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.keycloak.adapters.springsecurity.token.SpringSecurityAdapterTokenStoreFactory; import org.keycloak.adapters.springsecurity.token.SpringSecurityAdapterTokenStoreFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -63,19 +61,19 @@ import org.springframework.util.Assert;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware { public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware {
public static final String DEFAULT_LOGIN_URL = "/sso/login"; public static final String DEFAULT_LOGIN_URL = "/sso/login";
public static final String AUTHORIZATION_HEADER = "Authorization"; public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String SCHEME_BEARER = "bearer ";
public static final String SCHEME_BASIC = "basic ";
/** /**
* Request matcher that matches requests to the {@link KeycloakAuthenticationEntryPoint#DEFAULT_LOGIN_URI default login URI} * Request matcher that matches requests to the {@link KeycloakAuthenticationEntryPoint#DEFAULT_LOGIN_URI default login URI}
* and any request with a <code>Authorization</code> header. * and any request with a <code>Authorization</code> header.
*/ */
public static final RequestMatcher DEFAULT_REQUEST_MATCHER = public static final RequestMatcher DEFAULT_REQUEST_MATCHER =
new OrRequestMatcher( new OrRequestMatcher(new AntPathRequestMatcher(DEFAULT_LOGIN_URL), new RequestHeaderRequestMatcher(AUTHORIZATION_HEADER));
new AntPathRequestMatcher(DEFAULT_LOGIN_URL),
new RequestHeaderRequestMatcher(AUTHORIZATION_HEADER),
new QueryParamPresenceRequestMatcher(OAuth2Constants.ACCESS_TOKEN)
);
private static final Logger log = LoggerFactory.getLogger(KeycloakAuthenticationProcessingFilter.class); private static final Logger log = LoggerFactory.getLogger(KeycloakAuthenticationProcessingFilter.class);
@@ -183,10 +181,36 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
} }
} }
/**
* Returns true if the request was made with a bearer token authorization header.
*
* @param request the current <code>HttpServletRequest</code>
*
* @return <code>true</code> if the <code>request</code> was made with a bearer token authorization header;
* <code>false</code> otherwise.
*/
protected boolean isBearerTokenRequest(HttpServletRequest request) {
String authValue = request.getHeader(AUTHORIZATION_HEADER);
return authValue != null && authValue.toLowerCase().startsWith(SCHEME_BEARER);
}
/**
* Returns true if the request was made with a Basic authentication authorization header.
*
* @param request the current <code>HttpServletRequest</code>
* @return <code>true</code> if the <code>request</code> was made with a Basic authentication authorization header;
* <code>false</code> otherwise.
*/
protected boolean isBasicAuthRequest(HttpServletRequest request) {
String authValue = request.getHeader(AUTHORIZATION_HEADER);
return authValue != null && authValue.toLowerCase().startsWith(SCHEME_BASIC);
}
@Override @Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException { Authentication authResult) throws IOException, ServletException {
if (authResult instanceof KeycloakAuthenticationToken && ((KeycloakAuthenticationToken) authResult).isInteractive()) {
if (!(this.isBearerTokenRequest(request) || this.isBasicAuthRequest(request))) {
super.successfulAuthentication(request, response, chain, authResult); super.successfulAuthentication(request, response, chain, authResult);
return; return;
} }
@@ -207,6 +231,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
} finally { } finally {
SecurityContextHolder.clearContext(); SecurityContextHolder.clearContext();
} }
} }
@Override @Override
@@ -1,113 +0,0 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.springsecurity.filter;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
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;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class KeycloakSecurityContextRequestFilter extends GenericFilterBean implements ApplicationContextAware {
private static final String FILTER_APPLIED = KeycloakSecurityContext.class.getPackage().getName() + ".token-refreshed";
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) {
RefreshableKeycloakSecurityContext refreshableSecurityContext = (RefreshableKeycloakSecurityContext) keycloakSecurityContext;
if (refreshableSecurityContext.isActive()) {
KeycloakDeployment deployment = resolveDeployment(request, response);
if (deployment.isAlwaysRefreshToken()) {
if (refreshableSecurityContext.refreshExpiredToken(false)) {
request.setAttribute(KeycloakSecurityContext.class.getName(), refreshableSecurityContext);
} else {
clearAuthenticationContext();
}
}
} else {
clearAuthenticationContext();
}
}
filterChain.doFilter(request, response);
}
@Override
protected void initFilterBean() throws ServletException {
deploymentContext = applicationContext.getBean(AdapterDeploymentContext.class);
}
@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();
}
}
@@ -1,39 +0,0 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.springsecurity.filter;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* Spring RequestMatcher that checks for the presence of a query parameter.
*
* @author <a href="mailto:glavoie@gmail.com">Gabriel Lavoie</a>
*/
public class QueryParamPresenceRequestMatcher implements RequestMatcher {
private String param;
public QueryParamPresenceRequestMatcher(String param) {
this.param = param;
}
@Override
public boolean matches(HttpServletRequest httpServletRequest) {
return param != null && httpServletRequest.getParameter(param) != null;
}
}
@@ -38,27 +38,24 @@ import java.util.Collection;
public class KeycloakAuthenticationToken extends AbstractAuthenticationToken implements Authentication { public class KeycloakAuthenticationToken extends AbstractAuthenticationToken implements Authentication {
private Principal principal; private Principal principal;
private boolean interactive;
/** /**
* Creates a new, unauthenticated Keycloak security token for the given account. * Creates a new, unauthenticated Keycloak security token for the given account.
*/ */
public KeycloakAuthenticationToken(KeycloakAccount account, boolean interactive) { public KeycloakAuthenticationToken(KeycloakAccount account) {
super(null); super(null);
Assert.notNull(account, "KeycloakAccount cannot be null"); Assert.notNull(account, "KeycloakAccount cannot be null");
Assert.notNull(account.getPrincipal(), "KeycloakAccount.getPrincipal() cannot be null"); Assert.notNull(account.getPrincipal(), "KeycloakAccount.getPrincipal() cannot be null");
this.principal = account.getPrincipal(); this.principal = account.getPrincipal();
this.setDetails(account); this.setDetails(account);
this.interactive = interactive;
} }
public KeycloakAuthenticationToken(KeycloakAccount account, boolean interactive, Collection<? extends GrantedAuthority> authorities) { public KeycloakAuthenticationToken(KeycloakAccount account, Collection<? extends GrantedAuthority> authorities) {
super(authorities); super(authorities);
Assert.notNull(account, "KeycloakAccount cannot be null"); Assert.notNull(account, "KeycloakAccount cannot be null");
Assert.notNull(account.getPrincipal(), "KeycloakAccount.getPrincipal() cannot be null"); Assert.notNull(account.getPrincipal(), "KeycloakAccount.getPrincipal() cannot be null");
this.principal = account.getPrincipal(); this.principal = account.getPrincipal();
this.setDetails(account); this.setDetails(account);
this.interactive = interactive;
setAuthenticated(true); setAuthenticated(true);
} }
@@ -75,8 +72,4 @@ public class KeycloakAuthenticationToken extends AbstractAuthenticationToken imp
public OidcKeycloakAccount getAccount() { public OidcKeycloakAccount getAccount() {
return (OidcKeycloakAccount) this.getDetails(); return (OidcKeycloakAccount) this.getDetails();
} }
public boolean isInteractive() {
return interactive;
}
} }
@@ -105,7 +105,7 @@ public class SpringSecurityTokenStore implements AdapterTokenStore {
} }
logger.debug("Saving account info {}", account); logger.debug("Saving account info {}", account);
SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account, true)); SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account));
} }
@Override @Override
@@ -39,9 +39,14 @@ public class HttpHeaderInspectingApiRequestMatcherTest {
request = new MockHttpServletRequest(); request = new MockHttpServletRequest();
} }
@Test
public void testMatches() throws Exception {
assertTrue(apiRequestMatcher.matches(request));
}
@Test @Test
public void testMatchesBrowserRequest() throws Exception { public void testMatchesBrowserRequest() throws Exception {
request.addHeader(HttpHeaders.ACCEPT, "application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); request.addHeader(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
assertFalse(apiRequestMatcher.matches(request)); assertFalse(apiRequestMatcher.matches(request));
} }
@@ -50,7 +55,7 @@ public class HttpHeaderInspectingApiRequestMatcherTest {
request.addHeader( request.addHeader(
HttpHeaderInspectingApiRequestMatcher.X_REQUESTED_WITH_HEADER, HttpHeaderInspectingApiRequestMatcher.X_REQUESTED_WITH_HEADER,
HttpHeaderInspectingApiRequestMatcher.X_REQUESTED_WITH_HEADER_AJAX_VALUE); HttpHeaderInspectingApiRequestMatcher.X_REQUESTED_WITH_HEADER_AJAX_VALUE);
assertTrue(apiRequestMatcher.matches(request)); assertTrue(apiRequestMatcher.matches(request));
} }
} }
@@ -17,27 +17,24 @@
package org.keycloak.adapters.springsecurity.authentication; package org.keycloak.adapters.springsecurity.authentication;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.http.HttpHeaders; import org.apache.http.HttpHeaders;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.spi.HttpFacade;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.util.matcher.RequestMatcher;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.spi.HttpFacade;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import org.mockito.Mock;
import static org.mockito.Mockito.when;
import org.mockito.MockitoAnnotations;
import org.springframework.context.ApplicationContext;
/** /**
* Keycloak authentication entry point tests. * Keycloak authentication entry point tests.
@@ -49,16 +46,13 @@ public class KeycloakAuthenticationEntryPointTest {
private MockHttpServletResponse response; private MockHttpServletResponse response;
@Mock @Mock
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
@Mock @Mock
private AdapterDeploymentContext adapterDeploymentContext; private AdapterDeploymentContext adapterDeploymentContext;
@Mock @Mock
private KeycloakDeployment keycloakDeployment; private KeycloakDeployment keycloakDeployment;
@Mock
private RequestMatcher requestMatcher;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
@@ -92,8 +86,8 @@ public class KeycloakAuthenticationEntryPointTest {
public void testCommenceWithUnauthorizedWithAccept() throws Exception { public void testCommenceWithUnauthorizedWithAccept() throws Exception {
request.addHeader(HttpHeaders.ACCEPT, "application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); request.addHeader(HttpHeaders.ACCEPT, "application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
authenticationEntryPoint.commence(request, response, null); authenticationEntryPoint.commence(request, response, null);
assertEquals(HttpStatus.FOUND.value(), response.getStatus()); assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatus());
assertNull(response.getHeader(HttpHeaders.WWW_AUTHENTICATE)); assertNotNull(response.getHeader(HttpHeaders.WWW_AUTHENTICATE));
} }
@Test @Test
@@ -106,14 +100,6 @@ public class KeycloakAuthenticationEntryPointTest {
assertEquals(logoutUri, response.getHeader("Location")); assertEquals(logoutUri, response.getHeader("Location"));
} }
@Test
public void testCommenceWithCustomRequestMatcher() throws Exception {
new KeycloakAuthenticationEntryPoint(adapterDeploymentContext, requestMatcher)
.commence(request, response, null);
verify(requestMatcher).matches(request);
}
private void configureBrowserRequest() { private void configureBrowserRequest() {
request.addHeader(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); request.addHeader(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
} }
@@ -44,7 +44,6 @@ import static org.mockito.Mockito.mock;
public class KeycloakAuthenticationProviderTest { public class KeycloakAuthenticationProviderTest {
private KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider(); private KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider();
private KeycloakAuthenticationToken token; private KeycloakAuthenticationToken token;
private KeycloakAuthenticationToken interactiveToken;
private Set<String> roles = Sets.newSet("user", "admin"); private Set<String> roles = Sets.newSet("user", "admin");
@Before @Before
@@ -53,18 +52,18 @@ public class KeycloakAuthenticationProviderTest {
RefreshableKeycloakSecurityContext securityContext = mock(RefreshableKeycloakSecurityContext.class); RefreshableKeycloakSecurityContext securityContext = mock(RefreshableKeycloakSecurityContext.class);
KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, securityContext); KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, securityContext);
token = new KeycloakAuthenticationToken(account, false); token = new KeycloakAuthenticationToken(account);
interactiveToken = new KeycloakAuthenticationToken(account, true);
} }
@Test @Test
public void testAuthenticate() throws Exception { public void testAuthenticate() throws Exception {
assertAuthenticationResult(provider.authenticate(token)); Authentication result = provider.authenticate(token);
} assertNotNull(result);
assertEquals(roles, AuthorityUtils.authorityListToSet(result.getAuthorities()));
@Test assertTrue(result.isAuthenticated());
public void testAuthenticateInteractive() throws Exception { assertNotNull(result.getPrincipal());
assertAuthenticationResult(provider.authenticate(interactiveToken)); assertNotNull(result.getCredentials());
assertNotNull(result.getDetails());
} }
@Test @Test
@@ -84,13 +83,4 @@ public class KeycloakAuthenticationProviderTest {
assertEquals(Sets.newSet("ROLE_USER", "ROLE_ADMIN"), assertEquals(Sets.newSet("ROLE_USER", "ROLE_ADMIN"),
AuthorityUtils.authorityListToSet(result.getAuthorities())); AuthorityUtils.authorityListToSet(result.getAuthorities()));
} }
private void assertAuthenticationResult(Authentication result) {
assertNotNull(result);
assertEquals(roles, AuthorityUtils.authorityListToSet(result.getAuthorities()));
assertTrue(result.isAuthenticated());
assertNotNull(result.getPrincipal());
assertNotNull(result.getCredentials());
assertNotNull(result.getDetails());
}
} }
@@ -28,7 +28,7 @@ public class SimpleHttpFacadeTest {
Principal principal = mock(Principal.class); Principal principal = mock(Principal.class);
RefreshableKeycloakSecurityContext keycloakSecurityContext = mock(RefreshableKeycloakSecurityContext.class); RefreshableKeycloakSecurityContext keycloakSecurityContext = mock(RefreshableKeycloakSecurityContext.class);
KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, keycloakSecurityContext); KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, keycloakSecurityContext);
KeycloakAuthenticationToken token = new KeycloakAuthenticationToken(account, false); KeycloakAuthenticationToken token = new KeycloakAuthenticationToken(account);
springSecurityContext.setAuthentication(token); springSecurityContext.setAuthentication(token);
} }
@@ -53,6 +53,7 @@ import java.util.UUID;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
@@ -123,6 +124,34 @@ public class KeycloakAuthenticationProcessingFilterTest {
filter.afterPropertiesSet(); filter.afterPropertiesSet();
} }
@Test
public void testIsBearerTokenRequest() throws Exception {
assertFalse(filter.isBearerTokenRequest(request));
this.setBearerAuthHeader(request);
assertTrue(filter.isBearerTokenRequest(request));
}
@Test
public void testIsBearerTokenRequestCaseInsensitive() throws Exception {
assertFalse(filter.isBearerTokenRequest(request));
this.setAuthorizationHeader(request, "bearer");
assertTrue(filter.isBearerTokenRequest(request));
}
@Test
public void testIsBasicAuthRequest() throws Exception {
assertFalse(filter.isBasicAuthRequest(request));
this.setBasicAuthHeader(request);
assertTrue(filter.isBasicAuthRequest(request));
}
@Test
public void testIsBasicAuthRequestCaseInsensitive() throws Exception {
assertFalse(filter.isBasicAuthRequest(request));
this.setAuthorizationHeader(request, "basic");
assertTrue(filter.isBasicAuthRequest(request));
}
@Test @Test
public void testAttemptAuthenticationExpectRedirect() throws Exception { public void testAttemptAuthenticationExpectRedirect() throws Exception {
when(keycloakDeployment.getAuthUrl()).thenReturn(KeycloakUriBuilder.fromUri("http://localhost:8080/auth")); when(keycloakDeployment.getAuthUrl()).thenReturn(KeycloakUriBuilder.fromUri("http://localhost:8080/auth"));
@@ -151,7 +180,7 @@ public class KeycloakAuthenticationProcessingFilterTest {
@Test @Test
public void testSuccessfulAuthenticationInteractive() throws Exception { public void testSuccessfulAuthenticationInteractive() throws Exception {
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, true, authorities); Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
filter.successfulAuthentication(request, response, chain, authentication); filter.successfulAuthentication(request, response, chain, authentication);
verify(successHandler).onAuthenticationSuccess(eq(request), eq(response), eq(authentication)); verify(successHandler).onAuthenticationSuccess(eq(request), eq(response), eq(authentication));
@@ -160,7 +189,7 @@ public class KeycloakAuthenticationProcessingFilterTest {
@Test @Test
public void testSuccessfulAuthenticationBearer() throws Exception { public void testSuccessfulAuthenticationBearer() throws Exception {
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, false, authorities); Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
this.setBearerAuthHeader(request); this.setBearerAuthHeader(request);
filter.successfulAuthentication(request, response, chain, authentication); filter.successfulAuthentication(request, response, chain, authentication);
@@ -171,7 +200,7 @@ public class KeycloakAuthenticationProcessingFilterTest {
@Test @Test
public void testSuccessfulAuthenticationBasicAuth() throws Exception { public void testSuccessfulAuthenticationBasicAuth() throws Exception {
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, false, authorities); Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
this.setBasicAuthHeader(request); this.setBasicAuthHeader(request);
filter.successfulAuthentication(request, response, chain, authentication); filter.successfulAuthentication(request, response, chain, authentication);
@@ -1,68 +0,0 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.springsecurity.filter;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
public class QueryParamPresenceRequestMatcherTest {
private static final String ROOT_CONTEXT_PATH = "";
private static final String VALID_PARAMETER = "access_token";
private QueryParamPresenceRequestMatcher matcher = new QueryParamPresenceRequestMatcher(VALID_PARAMETER);
private MockHttpServletRequest request;
@Before
public void setUp() throws Exception {
request = new MockHttpServletRequest();
}
@Test
public void testDoesNotMatchWithoutQueryParameter() throws Exception {
prepareRequest(HttpMethod.GET, ROOT_CONTEXT_PATH, "some/random/uri", Collections.EMPTY_MAP);
assertFalse(matcher.matches(request));
}
@Test
public void testMatchesWithValidParameter() throws Exception {
prepareRequest(HttpMethod.GET, ROOT_CONTEXT_PATH, "some/random/uri", Collections.singletonMap(VALID_PARAMETER, (Object) "123"));
assertTrue(matcher.matches(request));
}
@Test
public void testDoesNotMatchWithInvalidParameter() throws Exception {
prepareRequest(HttpMethod.GET, ROOT_CONTEXT_PATH, "some/random/uri", Collections.singletonMap("some_parameter", (Object) "123"));
assertFalse(matcher.matches(request));
}
private void prepareRequest(HttpMethod method, String contextPath, String uri, Map<String, Object> params) {
request.setMethod(method.name());
request.setContextPath(contextPath);
request.setRequestURI(contextPath + "/" + uri);
request.setParameters(params);
}
}
+1 -1
View File
@@ -20,7 +20,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<name>Keycloak Tomcat Integration</name> <name>Keycloak Tomcat Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId> <artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -17,7 +17,11 @@
package org.keycloak.adapters.tomcat; package org.keycloak.adapters.tomcat;
import org.apache.catalina.*; import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
import org.apache.catalina.authenticator.FormAuthenticator; import org.apache.catalina.authenticator.FormAuthenticator;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
@@ -132,7 +136,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
} }
context.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext); context.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
AbstractAuthenticatedActionsValve actions = createAuthenticatedActionsValve(deploymentContext, getNext(), getContainer()); AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deploymentContext, getNext(), getContainer());
setNext(actions); setNext(actions);
nodesRegistrationManagement = new NodesRegistrationManagement(); nodesRegistrationManagement = new NodesRegistrationManagement();
@@ -185,7 +189,6 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
protected abstract GenericPrincipalFactory createPrincipalFactory(); protected abstract GenericPrincipalFactory createPrincipalFactory();
protected abstract boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException; protected abstract boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException;
protected abstract AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container);
protected boolean authenticateInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException { protected boolean authenticateInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException {
CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response); CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
@@ -31,7 +31,7 @@ import javax.servlet.ServletException;
import java.io.IOException; import java.io.IOException;
/** /**
* Abstract base for pre-installed actions that must be authenticated * Pre-installed actions that must be authenticated
* <p/> * <p/>
* Actions include: * Actions include:
* <p/> * <p/>
@@ -41,17 +41,18 @@ import java.io.IOException;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public abstract class AbstractAuthenticatedActionsValve extends ValveBase { public class AuthenticatedActionsValve extends ValveBase {
private static final Logger log = Logger.getLogger(AbstractAuthenticatedActionsValve.class); private static final Logger log = Logger.getLogger(AuthenticatedActionsValve.class);
protected AdapterDeploymentContext deploymentContext; protected AdapterDeploymentContext deploymentContext;
public AbstractAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) { public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
this.deploymentContext = deploymentContext; this.deploymentContext = deploymentContext;
if (next == null) throw new RuntimeException("Next valve is null!!!"); if (next == null) throw new RuntimeException("Next valve is null!!!");
setNext(next); setNext(next);
setContainer(container); setContainer(container);
} }
@Override @Override
public void invoke(Request request, Response response) throws IOException, ServletException { public void invoke(Request request, Response response) throws IOException, ServletException {
log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI()); log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI());
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId> <artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -1,12 +0,0 @@
package org.keycloak.adapters.tomcat;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.keycloak.adapters.AdapterDeploymentContext;
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
super(deploymentContext, next, container);
}
}
@@ -17,15 +17,12 @@
package org.keycloak.adapters.tomcat; package org.keycloak.adapters.tomcat;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleException;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig; import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.realm.GenericPrincipal; import org.apache.catalina.realm.GenericPrincipal;
import org.keycloak.adapters.AdapterDeploymentContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -54,10 +51,6 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
return true; return true;
} }
@Override
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
return new AuthenticatedActionsValve(deploymentContext, next, container);
}
@Override @Override
public void start() throws LifecycleException { public void start() throws LifecycleException {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId> <artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -1,17 +0,0 @@
package org.keycloak.adapters.tomcat;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.keycloak.adapters.AdapterDeploymentContext;
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
super(deploymentContext, next, container);
}
@Override
public boolean isAsyncSupported() {
return true;
}
}
@@ -17,14 +17,11 @@
package org.keycloak.adapters.tomcat; package org.keycloak.adapters.tomcat;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig; import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.realm.GenericPrincipal; import org.apache.catalina.realm.GenericPrincipal;
import org.keycloak.adapters.AdapterDeploymentContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -72,9 +69,4 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
}; };
} }
@Override
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
return new AuthenticatedActionsValve(deploymentContext, next, container);
}
} }
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId> <artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -1,17 +0,0 @@
package org.keycloak.adapters.tomcat;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.keycloak.adapters.AdapterDeploymentContext;
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
super(deploymentContext, next, container);
}
@Override
public boolean isAsyncSupported() {
return true;
}
}
@@ -17,14 +17,11 @@
package org.keycloak.adapters.tomcat; package org.keycloak.adapters.tomcat;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.apache.catalina.authenticator.FormAuthenticator; import org.apache.catalina.authenticator.FormAuthenticator;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardContext;
import org.apache.catalina.realm.GenericPrincipal; import org.apache.catalina.realm.GenericPrincipal;
import org.apache.tomcat.util.descriptor.web.LoginConfig; import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore; import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
@@ -105,9 +102,4 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
protected AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) { protected AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) {
return super.getTokenStore(request, facade, resolvedDeployment); return super.getTokenStore(request, facade, resolvedDeployment);
} }
@Override
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
return new AuthenticatedActionsValve(deploymentContext, next, container);
}
} }
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+2 -2
View File
@@ -22,7 +22,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -40,7 +40,7 @@
<dependency> <dependency>
<groupId>org.wildfly.common</groupId> <groupId>org.wildfly.common</groupId>
<artifactId>wildfly-common</artifactId> <artifactId>wildfly-common</artifactId>
<scope>provided</scope> <version>1.2.0.Beta1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.wildfly.security</groupId> <groupId>org.wildfly.security</groupId>
@@ -53,7 +53,7 @@ public class ElytronSessionTokenStore implements ElytronTokeStore {
@Override @Override
public void checkCurrentToken() { public void checkCurrentToken() {
HttpScope session = httpFacade.getScope(Scope.SESSION); HttpScope session = httpFacade.getScope(Scope.SESSION);
if (session == null || !session.exists()) return; if (!session.exists()) return;
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) session.getAttachment(KeycloakSecurityContext.class.getName()); RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) session.getAttachment(KeycloakSecurityContext.class.getName());
if (securityContext == null) return; if (securityContext == null) return;
@@ -42,40 +42,35 @@ import java.io.InputStream;
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/ */
public class KeycloakConfigurationServletListener implements ServletContextListener { public class KeycloakConfigurationServletListener implements ServletContextListener {
static final String ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE = AdapterDeploymentContext.class.getName() + ".elytron";
@Override @Override
public void contextInitialized(ServletContextEvent sce) { public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext(); ServletContext servletContext = sce.getServletContext();
String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver"); String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
KeycloakConfigResolver configResolver; KeycloakConfigResolver configResolver;
AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) servletContext.getAttribute(AdapterDeploymentContext.class.getName()); AdapterDeploymentContext deploymentContext;
if (deploymentContext == null) { if (configResolverClass != null) {
if (configResolverClass != null) { try {
try { configResolver = (KeycloakConfigResolver) servletContext.getClassLoader().loadClass(configResolverClass).newInstance();
configResolver = (KeycloakConfigResolver) servletContext.getClassLoader().loadClass(configResolverClass).newInstance(); deploymentContext = new AdapterDeploymentContext(configResolver);
deploymentContext = new AdapterDeploymentContext(configResolver); } catch (Exception ex) {
} catch (Exception ex) { deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
}
} else {
InputStream is = getConfigInputStream(servletContext);
KeycloakDeployment deployment;
if (is == null) {
deployment = new KeycloakDeployment();
} else {
deployment = KeycloakDeploymentBuilder.build(is);
}
deploymentContext = new AdapterDeploymentContext(deployment);
} }
} else {
InputStream is = getConfigInputStream(servletContext);
KeycloakDeployment deployment;
if (is == null) {
deployment = new KeycloakDeployment();
} else {
deployment = KeycloakDeploymentBuilder.build(is);
}
deploymentContext = new AdapterDeploymentContext(deployment);
} }
servletContext.setAttribute(ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE, deploymentContext); servletContext.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
} }
@Override @Override
@@ -31,6 +31,7 @@ import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.NodesRegistrationManagement; import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.PreAuthActionsHandler; import org.keycloak.adapters.PreAuthActionsHandler;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator; import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.AuthChallenge; import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome; import org.keycloak.adapters.spi.AuthOutcome;
@@ -126,7 +127,7 @@ class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticat
private AdapterDeploymentContext getDeploymentContext(HttpServerRequest request) { private AdapterDeploymentContext getDeploymentContext(HttpServerRequest request) {
if (this.deploymentContext == null) { if (this.deploymentContext == null) {
return (AdapterDeploymentContext) request.getScope(Scope.APPLICATION).getAttachment(KeycloakConfigurationServletListener.ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE); return (AdapterDeploymentContext) request.getScope(Scope.APPLICATION).getAttachment(AdapterDeploymentContext.class.getName());
} }
return this.deploymentContext; return this.deploymentContext;
@@ -30,10 +30,8 @@ import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.wildfly.security.auth.callback.AuthenticationCompleteCallback; import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
import org.wildfly.security.auth.callback.EvidenceVerifyCallback; import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
import org.wildfly.security.auth.callback.IdentityCredentialCallback;
import org.wildfly.security.auth.callback.SecurityIdentityCallback; import org.wildfly.security.auth.callback.SecurityIdentityCallback;
import org.wildfly.security.auth.server.SecurityIdentity; import org.wildfly.security.auth.server.SecurityIdentity;
import org.wildfly.security.credential.BearerTokenCredential;
import org.wildfly.security.evidence.Evidence; import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.http.HttpAuthenticationException; import org.wildfly.security.http.HttpAuthenticationException;
@@ -65,9 +63,8 @@ final class SecurityIdentityUtil {
} }
SecurityIdentityCallback securityIdentityCallback = new SecurityIdentityCallback(); SecurityIdentityCallback securityIdentityCallback = new SecurityIdentityCallback();
IdentityCredentialCallback credentialCallback = new IdentityCredentialCallback(new BearerTokenCredential(KeycloakPrincipal.class.cast(principal).getKeycloakSecurityContext().getTokenString()), true);
callbackHandler.handle(new Callback[]{credentialCallback, AuthenticationCompleteCallback.SUCCEEDED, securityIdentityCallback}); callbackHandler.handle(new Callback[]{AuthenticationCompleteCallback.SUCCEEDED, securityIdentityCallback});
SecurityIdentity securityIdentity = securityIdentityCallback.getSecurityIdentity(); SecurityIdentity securityIdentity = securityIdentityCallback.getSecurityIdentity();
+1 -1
View File
@@ -20,7 +20,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<name>Keycloak WildFly Integration</name> <name>Keycloak WildFly Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
@@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<version>4.0.0.Beta1</version> <version>3.3.0.Final</version>
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
@@ -71,10 +71,11 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance(); KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance();
if (service.isSecureDeployment(deploymentUnit) && service.isDeploymentConfigured(deploymentUnit)) { if (service.isSecureDeployment(deploymentUnit) && service.isDeploymentConfigured(deploymentUnit)) {
addKeycloakAuthData(phaseContext, service); addKeycloakAuthData(phaseContext, service);
} else if (service.isElytronEnabled(deploymentUnit)) {
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
addConfigurationListener(warMetaData);
} }
addConfigurationListener(deploymentUnit);
// FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK // FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
// todo notsure if we need this // todo notsure if we need this
@@ -103,6 +104,10 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
loginConfig.setAuthMethod("KEYCLOAK"); loginConfig.setAuthMethod("KEYCLOAK");
loginConfig.setRealmName(service.getRealmName(deploymentUnit)); loginConfig.setRealmName(service.getRealmName(deploymentUnit));
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName()); KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
if (service.isElytronEnabled(deploymentUnit)) {
addConfigurationListener(warMetaData);
}
} }
private void addJSONData(String json, WarMetaData warMetaData) { private void addJSONData(String json, WarMetaData warMetaData) {
@@ -125,8 +130,7 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
webMetaData.setContextParams(contextParams); webMetaData.setContextParams(contextParams);
} }
private void addConfigurationListener(DeploymentUnit deploymentUnit) { private void addConfigurationListener(WarMetaData warMetaData) {
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) { if (warMetaData == null) {
return; return;
} }
@@ -276,13 +276,11 @@ public final class KeycloakAdapterConfigService {
} }
private void setJSONValues(ModelNode json, ModelNode values) { private void setJSONValues(ModelNode json, ModelNode values) {
synchronized (values) { for (Property prop : new ArrayList<>(values.asPropertyList())) {
for (Property prop : new ArrayList<>(values.asPropertyList())) { String name = prop.getName();
String name = prop.getName(); ModelNode value = prop.getValue();
ModelNode value = prop.getValue(); if (value.isDefined()) {
if (value.isDefined()) { json.get(name).set(value);
json.get(name).set(value);
}
} }
} }
} }
@@ -186,12 +186,7 @@ public class SharedAttributeDefinitons {
.setDefaultValue(new ModelNode(false)) .setDefaultValue(new ModelNode(false))
.build(); .build();
protected static final SimpleAttributeDefinition CONFIDENTIAL_PORT =
new SimpleAttributeDefinitionBuilder("confidential-port", ModelType.INT, true)
.setXmlName("confidential-port")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(8443))
.build();
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>(); protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@@ -201,7 +196,6 @@ public class SharedAttributeDefinitons {
ATTRIBUTES.add(TRUSTSTORE); ATTRIBUTES.add(TRUSTSTORE);
ATTRIBUTES.add(TRUSTSTORE_PASSWORD); ATTRIBUTES.add(TRUSTSTORE_PASSWORD);
ATTRIBUTES.add(SSL_REQUIRED); ATTRIBUTES.add(SSL_REQUIRED);
ATTRIBUTES.add(CONFIDENTIAL_PORT);
ATTRIBUTES.add(ALLOW_ANY_HOSTNAME); ATTRIBUTES.add(ALLOW_ANY_HOSTNAME);
ATTRIBUTES.add(DISABLE_TRUST_MANAGER); ATTRIBUTES.add(DISABLE_TRUST_MANAGER);
ATTRIBUTES.add(CONNECTION_POOL_SIZE); ATTRIBUTES.add(CONNECTION_POOL_SIZE);
@@ -30,7 +30,6 @@ keycloak.realm.realm-public-key=Public key of the realm
keycloak.realm.auth-server-url=Base URL of the Realm Auth Server keycloak.realm.auth-server-url=Base URL of the Realm Auth Server
keycloak.realm.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests keycloak.realm.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests
keycloak.realm.ssl-required=Specify if SSL is required (valid values are all, external and none) keycloak.realm.ssl-required=Specify if SSL is required (valid values are all, external and none)
keycloak.realm.confidential-port=Specify the confidential port (SSL/TLS) used by the Realm Auth Server
keycloak.realm.allow-any-hostname=SSL Setting keycloak.realm.allow-any-hostname=SSL Setting
keycloak.realm.truststore=Truststore used for adapter client HTTPS requests keycloak.realm.truststore=Truststore used for adapter client HTTPS requests
keycloak.realm.truststore-password=Password of the Truststore keycloak.realm.truststore-password=Password of the Truststore
@@ -61,7 +60,6 @@ keycloak.secure-deployment.realm-public-key=Public key of the realm
keycloak.secure-deployment.auth-server-url=Base URL of the Realm Auth Server keycloak.secure-deployment.auth-server-url=Base URL of the Realm Auth Server
keycloak.secure-deployment.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests keycloak.secure-deployment.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests
keycloak.secure-deployment.ssl-required=Specify if SSL is required (valid values are all, external and none) keycloak.secure-deployment.ssl-required=Specify if SSL is required (valid values are all, external and none)
keycloak.secure-deployment.confidential-port=Specify the confidential port (SSL/TLS) used by the Realm Auth Server
keycloak.secure-deployment.allow-any-hostname=SSL Setting keycloak.secure-deployment.allow-any-hostname=SSL Setting
keycloak.secure-deployment.truststore=Truststore used for adapter client HTTPS requests keycloak.secure-deployment.truststore=Truststore used for adapter client HTTPS requests
keycloak.secure-deployment.truststore-password=Password of the Truststore keycloak.secure-deployment.truststore-password=Password of the Truststore
@@ -102,7 +100,6 @@ keycloak.secure-server.realm-public-key=Public key of the realm
keycloak.secure-server.auth-server-url=Base URL of the Realm Auth Server keycloak.secure-server.auth-server-url=Base URL of the Realm Auth Server
keycloak.secure-server.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests keycloak.secure-server.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests
keycloak.secure-server.ssl-required=Specify if SSL is required (valid values are all, external and none) keycloak.secure-server.ssl-required=Specify if SSL is required (valid values are all, external and none)
keycloak.secure-server.confidential-port=Specify the confidential port (SSL/TLS) used by the Realm Auth Server
keycloak.secure-server.allow-any-hostname=SSL Setting keycloak.secure-server.allow-any-hostname=SSL Setting
keycloak.secure-server.truststore=Truststore used for adapter client HTTPS requests keycloak.secure-server.truststore=Truststore used for adapter client HTTPS requests
keycloak.secure-server.truststore-password=Password of the Truststore keycloak.secure-server.truststore-password=Password of the Truststore
@@ -58,7 +58,6 @@
<xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/> <xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" /> <xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="confidential-port" type="xs:integer" minOccurs="0" maxOccurs="1" />
<xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="cors-exposed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="cors-exposed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/> <xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
@@ -101,7 +100,6 @@
<xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/> <xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/> <xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="confidential-port" type="xs:integer" minOccurs="0" maxOccurs="1" />
<xs:element name="realm-public-key" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="realm-public-key" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="credential" type="credential-type" minOccurs="0" maxOccurs="1"/> <xs:element name="credential" type="credential-type" minOccurs="0" maxOccurs="1"/>
<xs:element name="redirect-rewrite-rule" type="redirect-rewrite-rule-type" minOccurs="0" maxOccurs="1"/> <xs:element name="redirect-rewrite-rule" type="redirect-rewrite-rule-type" minOccurs="0" maxOccurs="1"/>
@@ -51,7 +51,6 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
node.get("auth-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/login"); node.get("auth-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/login");
node.get("code-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/access/codes"); node.get("code-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/access/codes");
node.get("ssl-required").set("external"); node.get("ssl-required").set("external");
node.get("confidential-port").set(443);
node.get("expose-token").set(true); node.get("expose-token").set(true);
ModelNode jwtCredential = new ModelNode(); ModelNode jwtCredential = new ModelNode();
Loaded 100 of 5335 files, more files were not shown because too many files have changed in this diff. Show more