Compare commits

..
Author SHA1 Message Date
keycloak-bot 237d51429b Set version to 3.2.1.Final 2017-07-21 09:14:38 +00:00
Marek Posolda 008608eba3 Merge pull request #4338 from patriot1burke/b_3_2
KEYCLOAK-5205 KEYCLOAK-5155 KEYCLOAK-5194 KEYLOAK-5152
2017-07-20 20:49:19 +02:00
Bill Burke 2ccef825aa KEYCLOAK-5205 2017-07-20 09:50:16 -04:00
Bill Burke fb22f6d96b KEYCLOAK-5194 2017-07-20 09:42:42 -04:00
Bill Burke 10d39b4bfc KEYCLOAK-5152 2017-07-20 09:41:33 -04:00
Bill Burke 416891c76a KEYCLOAK-5155 2017-07-20 09:40:07 -04:00
Pedro Igor f888ae2d59 Merge pull request #4300 from pedroigor/KEYCLOAK-5158-3.2.x
[3.2.x] Some fixes for AuthZ UI
2017-07-10 11:05:01 -03:00
Stan Silvert e61ea5bf37 KEYCLOAK-5144: Supported locales are undefined (#4305)
* KEYCLOAK-5144: Supported locales are undefined

* KEYCLOAK-5144: Supported locales are undefined.

* KEYCLOAK-5144: Supported locales are undefined

* KEYCLOAK-5144: Supported locales are undefined
2017-07-09 20:49:40 -04:00
Pedro Igor 155cdceb40 [KEYCLOAK-5148] - Create authorization settings when creating a new client using a config file 2017-07-07 16:37:38 -03:00
Pedro Igor 7553d51071 [KEYCLOAK-5158] - Revert changes that made delete on lists and scope permission stop working 2017-07-07 09:32:07 -03:00
Pedro Igor 40b3dcf453 [KEYCLOAK-4808] - Import large authz settings a bit faster 2017-07-07 09:31:57 -03:00
keycloak-bot 9495bbe55c Set version to 3.2.1.Final-SNAPSHOT 2017-07-05 06:15:57 +00:00
keycloak-bot 1fc98ad9e3 Set version to 3.2.0.Final 2017-07-05 05:31:21 +00:00
keycloak-bot 51769f2e78 Set version to 3.2.0.Final-SNAPSHOT 2017-06-30 09:28:29 +00:00
keycloak-bot b418dcafc3 Set version to 3.2.0.CR1 2017-06-30 08:01:33 +00:00
Stian Thorgersen 124ae402e2 KEYCLOAK-5091
Revert to WildFly 10 for 3.2 release

KEYCLOAK-4189 Fix cluster tests failures
2017-06-30 09:34:12 +02:00
5582 changed files with 38977 additions and 1354266 deletions

No files matched your search

-6
View File
@@ -49,9 +49,3 @@ target
# Maven shade # Maven shade
############# #############
*dependency-reduced-pom.xml *dependency-reduced-pom.xml
# nodejs #
##########
# KEYCLOAK-5391: We will re-exclude node_modules when node_modules handling is worked out.
# For now, we keep our js libraries checked into GitHub, so we don't ignore.
#node_modules
+15 -20
View File
@@ -1,38 +1,33 @@
language: java language: java
dist: precise
cache: cache:
cache: false directories:
- $HOME/.m2
before_cache:
- rm -rf $HOME/.m2/repository/org/keycloak
env: env:
global: global:
- MAVEN_SKIP_RC=true - MAVEN_SKIP_RC=true
- MAVEN_OPTS="-Xms512m -Xmx1536m" - MAVEN_OPTS="-Xms512m -Xmx2048m"
matrix: matrix:
- TESTS=unit - TESTS=group1
- TESTS=server-group1 - TESTS=group2
- TESTS=server-group2 - TESTS=group3
- TESTS=server-group3 - TESTS=group4
- TESTS=server-group4
- TESTS=old - TESTS=old
- TESTS=crossdc
jdk: jdk:
- oraclejdk8 - oraclejdk8
install: true before_script:
- export MAVEN_SKIP_RC=true
before_install: install:
- "export PHANTOMJS_VERSION=2.1.1" - travis_wait 60 mvn install --no-snapshot-updates -Pdistribution -DskipTestsuite -B -V -q
- "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
sudo: false sudo: false
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.2.1.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,19 +161,10 @@ 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);
if(deployment.isDelegateBearerErrorResponseSending()){ facade.getResponse().sendError(401);
facade.getResponse().setStatus(401);
}
else {
facade.getResponse().sendError(401);
}
return true; return true;
} }
}; };
@@ -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;
@@ -95,8 +94,6 @@ public class KeycloakDeployment {
protected Map<String, String> redirectRewriteRules; protected Map<String, String> redirectRewriteRules;
protected boolean delegateBearerErrorResponseSending = false;
public KeycloakDeployment() { public KeycloakDeployment() {
} }
@@ -154,8 +151,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 +275,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;
} }
@@ -469,12 +456,6 @@ public class KeycloakDeployment {
public void setRewriteRedirectRules(Map<String, String> redirectRewriteRules) { public void setRewriteRedirectRules(Map<String, String> redirectRewriteRules) {
this.redirectRewriteRules = redirectRewriteRules; this.redirectRewriteRules = redirectRewriteRules;
} }
public boolean isDelegateBearerErrorResponseSending() {
return delegateBearerErrorResponseSending;
}
public void setDelegateBearerErrorResponseSending(boolean delegateBearerErrorResponseSending) {
this.delegateBearerErrorResponseSending = delegateBearerErrorResponseSending;
}
} }
@@ -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);
}
}
} }
@@ -155,9 +155,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
this.refreshToken = response.getRefreshToken(); this.refreshToken = response.getRefreshToken();
} }
this.tokenString = tokenString; this.tokenString = tokenString;
if (tokenStore != null) { tokenStore.refreshCallback(this);
tokenStore.refreshCallback(this);
}
return true; return true;
} }
@@ -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;
@@ -89,15 +89,13 @@ class PathMatcher {
pathString = "/"; pathString = "/";
} }
if (matchingUri.equals(targetUri) || pathString.equals(targetUri)) { if (matchingUri.equals(targetUri)) {
cache.put(targetUri, entry); cache.put(targetUri, entry);
return entry; return entry;
} }
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.2.1.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.2.1.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.2.1.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.2.1.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>
-9
View File
@@ -1,9 +0,0 @@
CLI Single Sign On
===================================
This java-based utility is meant for providing Keycloak integration to
command line applications that are either written in Java or another language. The
idea is that the Java app provided by this utility performs a login for a specific
client, parses responses, and exports an access token as an environment variable
that can be used by the command line utility you are accessing.
-10
View File
@@ -1,10 +0,0 @@
#!/bin/sh
export KC_AUTH_SERVER=http://localhost:8080/auth
export KC_REALM=master
export KC_CLIENT=cli
export KC_ACCESS_TOKEN=`java -DKEYCLOAK_AUTH_SERVER=$KC_AUTH_SERVER -DKEYCLOAK_REALM=$KC_REALM -DKEYCLOAK_CLIENT=$KC_CLIENT -jar target/keycloak-cli-sso-3.3.0.CR1-SNAPSHOT.jar login`
-9
View File
@@ -1,9 +0,0 @@
#!/bin/sh
java -DKEYCLOAK_AUTH_SERVER=$KC_AUTH_SERVER -DKEYCLOAK_REALM=$KC_REALM -DKEYCLOAK_CLIENT=$KC_CLIENT -jar target/keycloak-cli-sso-3.3.0.CR1-SNAPSHOT.jar logout
unset KC_ACCESS_TOKEN
-84
View File
@@ -1,84 +0,0 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-cli-sso</artifactId>
<name>Keycloak CLI SSO Framework</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-installed-adapter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.keycloak.adapters.KeycloakCliSsoMain</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,45 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters;
import org.keycloak.adapters.installed.KeycloakCliSso;
import org.keycloak.adapters.installed.KeycloakInstalled;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.util.JsonSerialization;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakCliSsoMain extends KeycloakCliSso {
public static void main(String[] args) throws Exception {
new KeycloakCliSsoMain().mainCmd(args);
}
}
+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.2.1.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>
@@ -1,281 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.installed;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.ServerRequest;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.util.JsonSerialization;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
*
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakCliSso {
public void mainCmd(String[] args) throws Exception {
if (args.length != 1) {
printHelp();
return;
}
if (args[0].equalsIgnoreCase("login")) {
login();
} else if (args[0].equalsIgnoreCase("login-manual")) {
loginManual();
}
/*
else if (args[0].equalsIgnoreCase("login-cli")) {
loginCli();
}
*/
else if (args[0].equalsIgnoreCase("token")) {
token();
} else if (args[0].equalsIgnoreCase("logout")) {
logout();
} else if (args[0].equalsIgnoreCase("env")) {
System.out.println(System.getenv().toString());
} else {
printHelp();
}
}
public void printHelp() {
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-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(" logout - logout.");
System.exit(1);
}
public AdapterConfig getConfig() {
String url = System.getProperty("KEYCLOAK_AUTH_SERVER");
if (url == null) {
System.err.println("KEYCLOAK_AUTH_SERVER property not set");
System.exit(1);
}
String realm = System.getProperty("KEYCLOAK_REALM");
if (realm == null) {
System.err.println("KEYCLOAK_REALM property not set");
System.exit(1);
}
String client = System.getProperty("KEYCLOAK_CLIENT");
if (client == null) {
System.err.println("KEYCLOAK_CLIENT property not set");
System.exit(1);
}
String secret = System.getProperty("KEYCLOAK_CLIENT_SECRET");
AdapterConfig config = new AdapterConfig();
config.setAuthServerUrl(url);
config.setRealm(realm);
config.setResource(client);
config.setSslRequired("external");
if (secret != null) {
Map<String, Object> creds = new HashMap<>();
creds.put("secret", secret);
config.setCredentials(creds);
} else {
config.setPublicClient(true);
}
return config;
}
public boolean checkToken(boolean outputToken) throws Exception {
String token = getTokenResponse();
if (token == null) return false;
if (token != null) {
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
if (m.find()) {
String json = m.group(0);
try {
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
return true;
}
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.refreshToken(tokenResponse.getRefreshToken());
processResponse(installed, outputToken);
return true;
} catch (Exception e) {
System.err.println("Error processing existing token");
e.printStackTrace();
}
}
}
return false;
}
private String getTokenResponse() throws IOException {
String token = null;
File tokenFile = getTokenFilePath();
if (tokenFile.exists()) {
FileInputStream fis = new FileInputStream(tokenFile);
byte[] data = new byte[(int) tokenFile.length()];
fis.read(data);
fis.close();
token = new String(data, "UTF-8");
}
return token;
}
public void token() throws Exception {
String token = getTokenResponse();
if (token == null) {
System.err.println("There is no token for client");
System.exit(1);
} else {
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
if (m.find()) {
String json = m.group(0);
try {
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
System.out.println(tokenResponse.getToken());
return;
} else {
System.err.println("token in response file is expired");
System.exit(1);
}
} catch (Exception e) {
System.err.println("Failure processing token response file");
e.printStackTrace();
System.exit(1);
}
} else {
System.err.println("Could not find json within token response file");
System.exit(1);
}
}
}
public void login() throws Exception {
if (checkToken(true)) return;
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.login();
processResponse(installed, true);
}
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() {
String home = System.getenv("HOME");
if (home == null) {
home = System.getProperty("HOME");
if (home == null) {
home = Paths.get("").toAbsolutePath().normalize().toString();
}
}
return home;
}
public File getTokenDirectory() {
return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM")).toFile();
}
public File getTokenFilePath() {
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 {
AccessTokenResponse tokenResponse = installed.getTokenResponse();
tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
tokenResponse.setIdToken(null);
String output = JsonSerialization.writeValueAsString(tokenResponse);
getTokenDirectory().mkdirs();
FileOutputStream fos = new FileOutputStream(getTokenFilePath());
fos.write(output.getBytes("UTF-8"));
fos.flush();
fos.close();
if (outputToken) System.out.println(tokenResponse.getToken());
}
public void loginManual() throws Exception {
if (checkToken(true)) return;
AdapterConfig config = getConfig();
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
KeycloakInstalled installed = new KeycloakInstalled(deployment);
installed.loginManual();
processResponse(installed, true);
}
public void logout() throws Exception {
String token = getTokenResponse();
if (token != null) {
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
if (m.find()) {
String json = m.group(0);
try {
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
if (Time.currentTime() > tokenResponse.getExpiresIn()) {
System.err.println("Login is expired");
System.exit(1);
}
AdapterConfig config = getConfig();
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
ServerRequest.invokeLogout(deployment, tokenResponse.getRefreshToken());
for (File fp : getTokenDirectory().listFiles()) fp.delete();
System.out.println("logout complete");
} catch (Exception e) {
System.err.println("Failure processing token response file");
e.printStackTrace();
System.exit(1);
}
} else {
System.err.println("Could not find json within token response file");
System.exit(1);
}
} else {
System.err.println("Not logged in");
System.exit(1);
}
}
}
@@ -17,8 +17,6 @@
package org.keycloak.adapters.installed; package org.keycloak.adapters.installed;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
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;
@@ -26,18 +24,12 @@ import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.ServerRequest; import org.keycloak.adapters.ServerRequest;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken; 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,28 +38,19 @@ 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;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
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>
*/ */
public class KeycloakInstalled { public class KeycloakInstalled {
public interface HttpResponseWriter {
void success(PrintWriter pw, KeycloakInstalled ki);
void failure(PrintWriter pw, KeycloakInstalled ki);
}
private static final String KEYCLOAK_JSON = "META-INF/keycloak.json"; private static final String KEYCLOAK_JSON = "META-INF/keycloak.json";
private KeycloakDeployment deployment; private KeycloakDeployment deployment;
@@ -76,21 +59,12 @@ public class KeycloakInstalled {
LOGGED_MANUAL, LOGGED_DESKTOP LOGGED_MANUAL, LOGGED_DESKTOP
} }
private AccessTokenResponse tokenResponse;
private String tokenString; private String tokenString;
private String idTokenString; private String idTokenString;
private IDToken idToken; private IDToken idToken;
private AccessToken token; private AccessToken token;
private String refreshToken; private String refreshToken;
private Status status; private Status status;
private Locale locale;
private HttpResponseWriter loginResponseWriter;
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=([^&]+)");
public KeycloakInstalled() { public KeycloakInstalled() {
InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYCLOAK_JSON); InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYCLOAK_JSON);
@@ -101,92 +75,6 @@ public class KeycloakInstalled {
deployment = KeycloakDeploymentBuilder.build(config); deployment = KeycloakDeploymentBuilder.build(config);
} }
public KeycloakInstalled(KeycloakDeployment deployment) {
this.deployment = deployment;
}
private static HttpResponseWriter defaultLoginWriter = new HttpResponseWriter() {
@Override
public void success(PrintWriter pw, KeycloakInstalled ki) {
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html><h1>Login completed.</h1><div>");
pw.println("This browser will remain logged in until you close it, logout, or the session expires.");
pw.println("</div></html>");
pw.flush();
}
@Override
public void failure(PrintWriter pw, KeycloakInstalled ki) {
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html><h1>Login attempt failed.</h1><div>");
pw.println("</div></html>");
pw.flush();
}
};
private static HttpResponseWriter defaultLogoutWriter = new HttpResponseWriter() {
@Override
public void success(PrintWriter pw, KeycloakInstalled ki) {
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html><h1>Logout completed.</h1><div>");
pw.println("You may close this browser tab.");
pw.println("</div></html>");
pw.flush();
}
@Override
public void failure(PrintWriter pw, KeycloakInstalled ki) {
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html><h1>Logout failed.</h1><div>");
pw.println("You may close this browser tab.");
pw.println("</div></html>");
pw.flush();
}
};
public HttpResponseWriter getLoginResponseWriter() {
if (loginResponseWriter == null) {
return defaultLoginWriter;
} else {
return loginResponseWriter;
}
}
public HttpResponseWriter getLogoutResponseWriter() {
if (logoutResponseWriter == null) {
return defaultLogoutWriter;
} else {
return logoutResponseWriter;
}
}
public void setLoginResponseWriter(HttpResponseWriter loginResponseWriter) {
this.loginResponseWriter = loginResponseWriter;
}
public void setLogoutResponseWriter(HttpResponseWriter logoutResponseWriter) {
this.logoutResponseWriter = logoutResponseWriter;
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public void login() throws IOException, ServerRequest.HttpFailure, VerificationException, InterruptedException, OAuthErrorException, URISyntaxException { public void login() throws IOException, ServerRequest.HttpFailure, VerificationException, InterruptedException, OAuthErrorException, URISyntaxException {
if (isDesktopSupported()) { if (isDesktopSupported()) {
loginDesktop(); loginDesktop();
@@ -220,22 +108,19 @@ public class KeycloakInstalled {
} }
public void loginDesktop() throws IOException, VerificationException, OAuthErrorException, URISyntaxException, ServerRequest.HttpFailure, InterruptedException { public void loginDesktop() throws IOException, VerificationException, OAuthErrorException, URISyntaxException, ServerRequest.HttpFailure, InterruptedException {
CallbackListener callback = new CallbackListener(getLoginResponseWriter()); CallbackListener callback = new CallbackListener();
callback.start(); callback.start();
String redirectUri = "http://localhost:" + callback.server.getLocalPort(); String redirectUri = "http://localhost:" + callback.server.getLocalPort();
String state = UUID.randomUUID().toString(); String state = UUID.randomUUID().toString();
KeycloakUriBuilder builder = deployment.getAuthUrl().clone() String authUrl = 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())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri) .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.STATE, state) .queryParam(OAuth2Constants.STATE, state)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID); .queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
if (locale != null) { .build().toString();
builder.queryParam(OAuth2Constants.UI_LOCALES_PARAM, locale.getLanguage());
}
String authUrl = builder.build().toString();
Desktop.getDesktop().browse(new URI(authUrl)); Desktop.getDesktop().browse(new URI(authUrl));
@@ -259,7 +144,7 @@ public class KeycloakInstalled {
} }
private void logoutDesktop() throws IOException, URISyntaxException, InterruptedException { private void logoutDesktop() throws IOException, URISyntaxException, InterruptedException {
CallbackListener callback = new CallbackListener(getLogoutResponseWriter()); CallbackListener callback = new CallbackListener();
callback.start(); callback.start();
String redirectUri = "http://localhost:" + callback.server.getLocalPort(); String redirectUri = "http://localhost:" + callback.server.getLocalPort();
@@ -282,6 +167,9 @@ public class KeycloakInstalled {
} }
public void loginManual(PrintStream printer, Reader reader) throws IOException, ServerRequest.HttpFailure, VerificationException { public void loginManual(PrintStream printer, Reader reader) throws IOException, ServerRequest.HttpFailure, VerificationException {
CallbackListener callback = new CallbackListener();
callback.start();
String redirectUri = "urn:ietf:wg:oauth:2.0:oob"; String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
String authUrl = deployment.getAuthUrl().clone() String authUrl = deployment.getAuthUrl().clone()
@@ -302,86 +190,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;
} }
@@ -400,14 +208,7 @@ public class KeycloakInstalled {
parseAccessToken(tokenResponse); parseAccessToken(tokenResponse);
} }
public void refreshToken(String refreshToken) throws IOException, ServerRequest.HttpFailure, VerificationException {
AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
parseAccessToken(tokenResponse);
}
private void parseAccessToken(AccessTokenResponse tokenResponse) throws VerificationException { private void parseAccessToken(AccessTokenResponse tokenResponse) throws VerificationException {
this.tokenResponse = tokenResponse;
tokenString = tokenResponse.getToken(); tokenString = tokenResponse.getToken();
refreshToken = tokenResponse.getRefreshToken(); refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken(); idTokenString = tokenResponse.getIdToken();
@@ -439,10 +240,6 @@ public class KeycloakInstalled {
return refreshToken; return refreshToken;
} }
public AccessTokenResponse getTokenResponse() {
return tokenResponse;
}
public boolean isDesktopSupported() { public boolean isDesktopSupported() {
return Desktop.isDesktopSupported(); return Desktop.isDesktopSupported();
} }
@@ -451,8 +248,6 @@ public class KeycloakInstalled {
return deployment; return deployment;
} }
private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException { private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri, null); AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri, null);
parseAccessToken(tokenResponse); parseAccessToken(tokenResponse);
@@ -474,87 +269,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 {
private ServerSocket server; private ServerSocket server;
@@ -569,19 +283,14 @@ public class KeycloakInstalled {
private String state; private String state;
private Socket socket; public CallbackListener() throws IOException {
private HttpResponseWriter writer;
public CallbackListener(HttpResponseWriter writer) throws IOException {
this.writer = writer;
server = new ServerSocket(0); server = new ServerSocket(0);
} }
@Override @Override
public void run() { public void run() {
try { try {
socket = server.accept(); Socket socket = server.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String request = br.readLine(); String request = br.readLine();
@@ -605,15 +314,10 @@ public class KeycloakInstalled {
} }
} }
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream()); PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
PrintWriter pw = new PrintWriter(out); pw.println("Please close window and return to application");
if (error == null) {
writer.success(pw, KeycloakInstalled.this);
} else {
writer.failure(pw, KeycloakInstalled.this);
}
pw.flush(); pw.flush();
socket.close(); socket.close();
} catch (IOException e) { } catch (IOException e) {
errorException = e; errorException = e;
@@ -624,8 +328,6 @@ public class KeycloakInstalled {
} catch (IOException e) { } catch (IOException e) {
} }
} }
} }
} }
+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.2.1.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.2.1.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.2.1.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.2.1.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.2.1.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.2.1.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.2.1.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.2.1.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 -6
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.2.1.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -38,11 +38,6 @@
<plugin> <plugin>
<groupId>com.samaxes.maven</groupId> <groupId>com.samaxes.maven</groupId>
<artifactId>minify-maven-plugin</artifactId> <artifactId>minify-maven-plugin</artifactId>
<configuration>
<jsEngine>CLOSURE</jsEngine>
<closureLanguageIn>ECMASCRIPT5</closureLanguageIn>
<closureCreateSourceMap>true</closureCreateSourceMap>
</configuration>
<executions> <executions>
<execution> <execution>
<id>min-js</id> <id>min-js</id>
@@ -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.
+160 -310
View File
@@ -33,15 +33,6 @@
interval: 5 interval: 5
}; };
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0];
}
}
var useNonce = true;
kc.init = function (initOptions) { kc.init = function (initOptions) {
kc.authenticated = false; kc.authenticated = false;
@@ -52,7 +43,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 +51,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 +152,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);
@@ -232,7 +214,7 @@
var callbackState = { var callbackState = {
state: state, state: state,
nonce: nonce, nonce: nonce,
redirectUri: encodeURIComponent(redirectUri) redirectUri: encodeURIComponent(redirectUri),
} }
if (options && options.prompt) { if (options && options.prompt) {
@@ -241,25 +223,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 +268,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 +288,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 +340,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 +405,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 +479,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 +516,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 +553,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 +580,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 +594,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 +604,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 +746,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,20 +819,19 @@
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';
iframe.setAttribute('src', src ); iframe.setAttribute('src', src );
iframe.setAttribute('title', 'keycloak-session-iframe' );
iframe.style.display = 'none'; iframe.style.display = 'none';
document.body.appendChild(iframe); document.body.appendChild(iframe);
@@ -1142,12 +909,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 +923,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 +936,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 +947,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 +981,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 +1014,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 +1024,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 +1159,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" ) {
@@ -28,7 +28,7 @@
} else if (!init) { } else if (!init) {
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
var url = location.href.split("?")[0] + "/init"; var url = location.href + "/init";
url += "?client_id=" + encodeURIComponent(clientId); url += "?client_id=" + encodeURIComponent(clientId);
url += "&origin=" + encodeURIComponent(origin); url += "&origin=" + encodeURIComponent(origin);
@@ -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.2.1.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -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.2.1.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>
@@ -34,7 +34,6 @@
<module>adapter-core</module> <module>adapter-core</module>
<module>as7-eap6</module> <module>as7-eap6</module>
<module>installed</module> <module>installed</module>
<module>cli-sso</module>
<module>jaxrs-oauth-client</module> <module>jaxrs-oauth-client</module>
<module>jetty</module> <module>jetty</module>
<module>js</module> <module>js</module>
+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.2.1.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.2.1.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.2.1.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<artifactId>spring-boot-container-bundle</artifactId> <artifactId>spring-boot-container-bundle</artifactId>
+3 -17
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.2.1.Final</version>
<relativePath>../../../pom.xml</relativePath> <relativePath>../../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -31,9 +31,7 @@
<description/> <description/>
<properties> <properties>
<spring-boot.version>1.4.0.RELEASE</spring-boot.version> <spring-boot.version>1.3.0.RELEASE</spring-boot.version>
<spring.version>4.1.6.RELEASE</spring.version>
<mockito.version>1.9.5</mockito.version>
</properties> </properties>
<dependencies> <dependencies>
@@ -100,19 +98,7 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional> <optional>true</optional>
@@ -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,24 +0,0 @@
package org.keycloak.adapters.springboot.client;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.web.client.RestTemplate;
public class KeycloakRestTemplateCustomizer implements RestTemplateCustomizer {
private final KeycloakSecurityContextClientRequestInterceptor keycloakInterceptor;
public KeycloakRestTemplateCustomizer() {
this(new KeycloakSecurityContextClientRequestInterceptor());
}
protected KeycloakRestTemplateCustomizer(
KeycloakSecurityContextClientRequestInterceptor keycloakInterceptor
) {
this.keycloakInterceptor = keycloakInterceptor;
}
@Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(keycloakInterceptor);
}
}
@@ -1,55 +0,0 @@
package org.keycloak.adapters.springboot.client;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.IOException;
import java.security.Principal;
/**
* Interceptor for {@link ClientHttpRequestExecution} objects created for server to server secured
* communication using OAuth2 bearer tokens issued by Keycloak.
*
* @author <a href="mailto:jmcshan1@gmail.com">James McShane</a>
* @version $Revision: 1 $
*/
public class KeycloakSecurityContextClientRequestInterceptor implements ClientHttpRequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
/**
* Returns the {@link KeycloakSecurityContext} from the Spring {@link ServletRequestAttributes}'s {@link Principal}.
*
* The principal must support retrieval of the KeycloakSecurityContext, so at this point, only {@link KeycloakPrincipal}
* values are supported
*
* @return the current <code>KeycloakSecurityContext</code>
*/
protected KeycloakSecurityContext getKeycloakSecurityContext() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
Principal principal = attributes.getRequest().getUserPrincipal();
if (principal == null) {
throw new IllegalStateException("Cannot set authorization header because there is no authenticated principal");
}
if (!(principal instanceof KeycloakPrincipal)) {
throw new IllegalStateException(
String.format(
"Cannot set authorization header because the principal type %s does not provide the KeycloakSecurityContext",
principal.getClass()));
}
return ((KeycloakPrincipal) principal).getKeycloakSecurityContext();
}
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
KeycloakSecurityContext context = this.getKeycloakSecurityContext();
httpRequest.getHeaders().set(AUTHORIZATION_HEADER, "Bearer " + context.getTokenString());
return clientHttpRequestExecution.execute(httpRequest, bytes);
}
}
@@ -1,28 +0,0 @@
package org.keycloak.adapters.springboot.client;
import org.junit.Before;
import org.junit.Test;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
public class KeycloakRestTemplateCustomizerTest {
private KeycloakRestTemplateCustomizer customizer;
private KeycloakSecurityContextClientRequestInterceptor interceptor =
mock(KeycloakSecurityContextClientRequestInterceptor.class);
@Before
public void setup() {
customizer = new KeycloakRestTemplateCustomizer(interceptor);
}
@Test
public void interceptorIsAddedToRequest() {
RestTemplate restTemplate = new RestTemplate();
customizer.customize(restTemplate);
assertTrue(restTemplate.getInterceptors().contains(interceptor));
}
}
@@ -1,87 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.springboot.client;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.security.Principal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.when;
/**
* Keycloak spring boot client request factory tests.
*/
public class KeycloakSecurityContextClientRequestInterceptorTest {
@Spy
private KeycloakSecurityContextClientRequestInterceptor factory;
private MockHttpServletRequest servletRequest;
@Mock
private KeycloakSecurityContext keycloakSecurityContext;
@Mock
private KeycloakPrincipal keycloakPrincipal;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
servletRequest = new MockHttpServletRequest();
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(servletRequest));
servletRequest.setUserPrincipal(keycloakPrincipal);
when(keycloakPrincipal.getKeycloakSecurityContext()).thenReturn(keycloakSecurityContext);
}
@Test
public void testGetKeycloakSecurityContext() throws Exception {
KeycloakSecurityContext context = factory.getKeycloakSecurityContext();
assertNotNull(context);
assertEquals(keycloakSecurityContext, context);
}
@Test(expected = IllegalStateException.class)
public void testGetKeycloakSecurityContextInvalidPrincipal() throws Exception {
servletRequest.setUserPrincipal(new MarkerPrincipal());
factory.getKeycloakSecurityContext();
}
@Test(expected = IllegalStateException.class)
public void testGetKeycloakSecurityContextNullAuthentication() throws Exception {
servletRequest.setUserPrincipal(null);
factory.getKeycloakSecurityContext();
}
private static class MarkerPrincipal implements Principal {
@Override
public String getName() {
return null;
}
}
}
+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.2.1.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();
}
} }
@@ -19,9 +19,6 @@ package org.keycloak.adapters.springsecurity.facade;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade; import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@@ -60,8 +57,7 @@ public class SimpleHttpFacade implements OIDCHttpFacade {
SecurityContext context = SecurityContextHolder.getContext(); SecurityContext context = SecurityContextHolder.getContext();
if (context != null && context.getAuthentication() != null) { if (context != null && context.getAuthentication() != null) {
KeycloakAuthenticationToken authentication = (KeycloakAuthenticationToken) context.getAuthentication(); return (KeycloakSecurityContext) context.getAuthentication().getDetails();
return authentication.getAccount().getKeycloakSecurityContext();
} }
return null; return null;
@@ -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);
@@ -136,10 +134,6 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
HttpFacade facade = new SimpleHttpFacade(request, response); HttpFacade facade = new SimpleHttpFacade(request, response);
KeycloakDeployment deployment = adapterDeploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = adapterDeploymentContext.resolveDeployment(facade);
// using Spring authenticationFailureHandler
deployment.setDelegateBearerErrorResponseSending(true);
AdapterTokenStore tokenStore = adapterTokenStoreFactory.createAdapterTokenStore(deployment, request); AdapterTokenStore tokenStore = adapterTokenStoreFactory.createAdapterTokenStore(deployment, request);
RequestAuthenticator authenticator RequestAuthenticator authenticator
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1); = new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
@@ -183,10 +177,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 +227,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());
}
} }
@@ -1,41 +0,0 @@
package org.keycloak.adapters.springsecurity.facade;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.mockito.internal.util.collections.Sets;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import java.security.Principal;
import java.util.Set;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
public class SimpleHttpFacadeTest {
@Before
public void setup() {
SecurityContext springSecurityContext = SecurityContextHolder.createEmptyContext();
SecurityContextHolder.setContext(springSecurityContext);
Set<String> roles = Sets.newSet("user");
Principal principal = mock(Principal.class);
RefreshableKeycloakSecurityContext keycloakSecurityContext = mock(RefreshableKeycloakSecurityContext.class);
KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, keycloakSecurityContext);
KeycloakAuthenticationToken token = new KeycloakAuthenticationToken(account, false);
springSecurityContext.setAuthentication(token);
}
@Test
public void shouldRetrieveKeycloakSecurityContext() {
SimpleHttpFacade facade = new SimpleHttpFacade(new MockHttpServletRequest(), new MockHttpServletResponse());
assertNotNull(facade.getSecurityContext());
}
}
@@ -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.2.1.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.2.1.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.2.1.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.2.1.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.2.1.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.2.1.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.2.1.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>
@@ -50,9 +50,7 @@ import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@@ -68,7 +66,6 @@ class ElytronHttpFacade implements OIDCHttpFacade {
private ElytronAccount account; private ElytronAccount account;
private SecurityIdentity securityIdentity; private SecurityIdentity securityIdentity;
private boolean restored; private boolean restored;
private final Map<String, String> headers = new HashMap<>();
public ElytronHttpFacade(HttpServerRequest request, AdapterDeploymentContext deploymentContext, CallbackHandler handler) { public ElytronHttpFacade(HttpServerRequest request, AdapterDeploymentContext deploymentContext, CallbackHandler handler) {
this.request = request; this.request = request;
@@ -93,11 +90,6 @@ class ElytronHttpFacade implements OIDCHttpFacade {
void authenticationComplete() { void authenticationComplete() {
if (securityIdentity != null) { if (securityIdentity != null) {
HttpScope requestScope = request.getScope(Scope.EXCHANGE);
RefreshableKeycloakSecurityContext keycloakSecurityContext = account.getKeycloakSecurityContext();
requestScope.setAttachment(KeycloakSecurityContext.class.getName(), keycloakSecurityContext);
this.request.authenticationComplete(response -> { this.request.authenticationComplete(response -> {
if (!restored) { if (!restored) {
responseConsumer.accept(response); responseConsumer.accept(response);
@@ -264,7 +256,6 @@ class ElytronHttpFacade implements OIDCHttpFacade {
@Override @Override
public Response getResponse() { public Response getResponse() {
return new Response() { return new Response() {
@Override @Override
public void setStatus(final int status) { public void setStatus(final int status) {
responseConsumer = responseConsumer.andThen(response -> response.setStatusCode(status)); responseConsumer = responseConsumer.andThen(response -> response.setStatusCode(status));
@@ -272,17 +263,7 @@ class ElytronHttpFacade implements OIDCHttpFacade {
@Override @Override
public void addHeader(final String name, final String value) { public void addHeader(final String name, final String value) {
headers.put(name, value); responseConsumer = responseConsumer.andThen(response -> response.addResponseHeader(name, value));
responseConsumer = responseConsumer.andThen(new Consumer<HttpServerResponse>() {
@Override
public void accept(HttpServerResponse response) {
String latestValue = headers.get(name);
if (latestValue.equals(value)) {
response.addResponseHeader(name, latestValue);
}
}
});
} }
@Override @Override
@@ -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
Loaded 100 of 5582 files, more files were not shown because too many files have changed in this diff. Show more