Merge pull request #827 from mposolda/master
Remove realm property from KeycloakSecurityContext. Fix multitenancy on EAP 6.3
This commit is contained in:
commit
fe27f5364a
11 changed files with 45 additions and 41 deletions
|
@ -17,7 +17,6 @@ import java.io.Serializable;
|
|||
public class KeycloakSecurityContext implements Serializable {
|
||||
protected String tokenString;
|
||||
protected String idTokenString;
|
||||
protected String realm;
|
||||
|
||||
// Don't store parsed tokens into HTTP session
|
||||
protected transient AccessToken token;
|
||||
|
@ -26,12 +25,11 @@ public class KeycloakSecurityContext implements Serializable {
|
|||
public KeycloakSecurityContext() {
|
||||
}
|
||||
|
||||
public KeycloakSecurityContext(String tokenString, AccessToken token, String idTokenString, IDToken idToken, String realm) {
|
||||
public KeycloakSecurityContext(String tokenString, AccessToken token, String idTokenString, IDToken idToken) {
|
||||
this.tokenString = tokenString;
|
||||
this.token = token;
|
||||
this.idToken = idToken;
|
||||
this.idTokenString = idTokenString;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public AccessToken getToken() {
|
||||
|
@ -51,7 +49,8 @@ public class KeycloakSecurityContext implements Serializable {
|
|||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
// Assumption that issuer contains realm name
|
||||
return token.getIssuer();
|
||||
}
|
||||
|
||||
// SERIALIZATION
|
||||
|
|
|
@ -56,7 +56,6 @@ public class SkeletonKeyTokenTest {
|
|||
|
||||
@Test
|
||||
public void testSerialization() throws Exception {
|
||||
String realm = "acme";
|
||||
AccessToken token = createSimpleToken();
|
||||
IDToken idToken = new IDToken();
|
||||
idToken.setEmail("joe@email.cz");
|
||||
|
@ -70,7 +69,7 @@ public class SkeletonKeyTokenTest {
|
|||
.jsonContent(idToken)
|
||||
.rsa256(keyPair.getPrivate());
|
||||
|
||||
KeycloakSecurityContext ctx = new KeycloakSecurityContext(encoded, token, encodedIdToken, idToken, realm);
|
||||
KeycloakSecurityContext ctx = new KeycloakSecurityContext(encoded, token, encodedIdToken, idToken);
|
||||
KeycloakPrincipal principal = new KeycloakPrincipal("joe", ctx);
|
||||
|
||||
// Serialize
|
||||
|
@ -104,6 +103,7 @@ public class SkeletonKeyTokenTest {
|
|||
private AccessToken createSimpleToken() {
|
||||
AccessToken token = new AccessToken();
|
||||
token.id("111");
|
||||
token.issuer("acme");
|
||||
token.addAccess("foo").addRole("admin");
|
||||
token.addAccess("bar").addRole("user");
|
||||
return token;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Keycloak Example - Multi Tenancy
|
||||
=======================================
|
||||
|
||||
The following example was tested on Wildfly 8.1.0.Final and should be compatible with any JBoss AS, JBoss EAP or Wildfly that supports Java EE 7.
|
||||
The following example was tested on Wildfly 8.1.0.Final and JBoss EAP 6.3. It should be compatible with any JBoss AS, JBoss EAP or Wildfly that supports Java EE 7.
|
||||
|
||||
This example demonstrates the simplest possible scenario for Keycloak Multi Tenancy support. Multi Tenancy is understood on this context as a single application (WAR) that is deployed on a single or clustered application server, authenticating users from *different realms* against a single or clustered Keycloak server.
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
|||
}
|
||||
|
||||
public RefreshableKeycloakSecurityContext(KeycloakDeployment deployment, AdapterTokenStore tokenStore, String tokenString, AccessToken token, String idTokenString, IDToken idToken, String refreshToken) {
|
||||
super(tokenString, token, idTokenString, idToken, deployment.getRealm());
|
||||
super(tokenString, token, idTokenString, idToken);
|
||||
this.deployment = deployment;
|
||||
this.tokenStore = tokenStore;
|
||||
this.refreshToken = refreshToken;
|
||||
|
@ -67,7 +67,6 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
|||
public void setCurrentRequestInfo(KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
|
||||
this.deployment = deployment;
|
||||
this.tokenStore = tokenStore;
|
||||
this.realm = deployment.getRealm();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,7 +83,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
|||
|
||||
if (this.deployment == null || refreshToken == null) return false; // Might be serialized in HttpSession?
|
||||
|
||||
if (!this.realm.equals(this.deployment.getRealm())) {
|
||||
if (!this.getRealm().equals(this.deployment.getRealm())) {
|
||||
// this should not happen, but let's check it anyway
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,12 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
|
|||
if (authenticatedPrincipal != null) {
|
||||
log.debug("remote logged in already. Establish state from cookie");
|
||||
RefreshableKeycloakSecurityContext securityContext = authenticatedPrincipal.getKeycloakSecurityContext();
|
||||
|
||||
if (!securityContext.getRealm().equals(deployment.getRealm())) {
|
||||
log.debug("Account from cookie is from a different realm than for the request.");
|
||||
return false;
|
||||
}
|
||||
|
||||
securityContext.setCurrentRequestInfo(deployment, this);
|
||||
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
|
||||
GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), authenticatedPrincipal, roles, securityContext);
|
||||
|
@ -97,11 +103,6 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
|
|||
|
||||
RefreshableKeycloakSecurityContext session = principal.getKeycloakSecurityContext();
|
||||
|
||||
if (!session.getRealm().equals(deployment.getRealm())) {
|
||||
log.debug("Account from cookie is from a different realm than for the request.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return principal;
|
||||
boolean success = session.refreshExpiredToken(false);
|
||||
if (success && session.isActive()) return principal;
|
||||
|
|
|
@ -38,11 +38,6 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
|
|||
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
|
||||
if (session == null) return;
|
||||
|
||||
if (!deployment.getRealm().equals(session.getRealm())) {
|
||||
log.debug("Account from cookie is from a different realm than for the request.");
|
||||
return;
|
||||
}
|
||||
|
||||
// just in case session got serialized
|
||||
if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
|
||||
|
||||
|
@ -69,16 +64,23 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
|
|||
if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null)
|
||||
return false;
|
||||
log.debug("remote logged in already. Establish state from session");
|
||||
GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
|
||||
request.setUserPrincipal(principal);
|
||||
request.setAuthType("KEYCLOAK");
|
||||
|
||||
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
|
||||
if (securityContext != null) {
|
||||
|
||||
if (!deployment.getRealm().equals(securityContext.getRealm())) {
|
||||
log.debug("Account from cookie is from a different realm than for the request.");
|
||||
return false;
|
||||
}
|
||||
|
||||
securityContext.setCurrentRequestInfo(deployment, this);
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||
}
|
||||
|
||||
GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
|
||||
request.setUserPrincipal(principal);
|
||||
request.setAuthType("KEYCLOAK");
|
||||
|
||||
((CatalinaRequestAuthenticator)authenticator).restoreRequest();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -66,12 +66,12 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
public void logout(Request request) throws ServletException {
|
||||
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
if (ksc != null) {
|
||||
request.removeAttribute(KeycloakSecurityContext.class.getName());
|
||||
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
|
||||
AdapterTokenStore tokenStore = getTokenStore(request, facade, deployment);
|
||||
tokenStore.logout();
|
||||
request.removeAttribute(KeycloakSecurityContext.class.getName());
|
||||
}
|
||||
super.logout(request);
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
// 4) The deployment doesn't have a keycloak.config.resolver nor keycloak.json (or equivalent)
|
||||
// Outcome: adapter is left unconfigured
|
||||
|
||||
String configResolverClass = (String) context.getServletContext().getAttribute("keycloak.config.resolver");
|
||||
String configResolverClass = context.getServletContext().getInitParameter("keycloak.config.resolver");
|
||||
if (configResolverClass != null) {
|
||||
try {
|
||||
KeycloakConfigResolver configResolver = (KeycloakConfigResolver) context.getLoader().getClassLoader().loadClass(configResolverClass).newInstance();
|
||||
|
|
|
@ -71,7 +71,7 @@ public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
|
|||
|
||||
try {
|
||||
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realmPublicKey, realm);
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null, realm);
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null);
|
||||
ResteasyProviderFactory.pushContext(KeycloakSecurityContext.class, skSession);
|
||||
|
||||
final KeycloakPrincipal<KeycloakSecurityContext> principal = new KeycloakPrincipal<KeycloakSecurityContext>(token.getSubject(), skSession);
|
||||
|
|
|
@ -47,6 +47,12 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
|
|||
if (authenticatedPrincipal != null) {
|
||||
log.fine("remote logged in already. Establish state from cookie");
|
||||
RefreshableKeycloakSecurityContext securityContext = authenticatedPrincipal.getKeycloakSecurityContext();
|
||||
|
||||
if (!securityContext.getRealm().equals(deployment.getRealm())) {
|
||||
log.fine("Account from cookie is from a different realm than for the request.");
|
||||
return false;
|
||||
}
|
||||
|
||||
securityContext.setCurrentRequestInfo(deployment, this);
|
||||
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
|
||||
GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), authenticatedPrincipal, roles, securityContext);
|
||||
|
@ -95,11 +101,6 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
|
|||
|
||||
RefreshableKeycloakSecurityContext session = principal.getKeycloakSecurityContext();
|
||||
|
||||
if (!session.getRealm().equals(deployment.getRealm())) {
|
||||
log.fine("Account from cookie is from a different realm than for the request.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return principal;
|
||||
boolean success = session.refreshExpiredToken(false);
|
||||
if (success && session.isActive()) return principal;
|
||||
|
|
|
@ -36,11 +36,6 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
|
|||
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
|
||||
if (session == null) return;
|
||||
|
||||
if (!deployment.getRealm().equals(session.getRealm())) {
|
||||
log.fine("Account from cookie is from a different realm than for the request.");
|
||||
return;
|
||||
}
|
||||
|
||||
// just in case session got serialized
|
||||
if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
|
||||
|
||||
|
@ -67,16 +62,23 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
|
|||
if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null)
|
||||
return false;
|
||||
log.fine("remote logged in already. Establish state from session");
|
||||
GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
|
||||
request.setUserPrincipal(principal);
|
||||
request.setAuthType("KEYCLOAK");
|
||||
|
||||
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
|
||||
if (securityContext != null) {
|
||||
|
||||
if (!deployment.getRealm().equals(securityContext.getRealm())) {
|
||||
log.fine("Account from cookie is from a different realm than for the request.");
|
||||
return false;
|
||||
}
|
||||
|
||||
securityContext.setCurrentRequestInfo(deployment, this);
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||
}
|
||||
|
||||
GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
|
||||
request.setUserPrincipal(principal);
|
||||
request.setAuthType("KEYCLOAK");
|
||||
|
||||
((CatalinaRequestAuthenticator)authenticator).restoreRequest();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -73,12 +73,12 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
public void logout(Request request) throws ServletException {
|
||||
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
if (ksc != null) {
|
||||
request.removeAttribute(KeycloakSecurityContext.class.getName());
|
||||
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
|
||||
AdapterTokenStore tokenStore = getTokenStore(request, facade, deployment);
|
||||
tokenStore.logout();
|
||||
request.removeAttribute(KeycloakSecurityContext.class.getName());
|
||||
}
|
||||
super.logout(request);
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
// 4) The deployment doesn't have a keycloak.config.resolver nor keycloak.json (or equivalent)
|
||||
// Outcome: adapter is left unconfigured
|
||||
|
||||
String configResolverClass = (String) context.getServletContext().getAttribute("keycloak.config.resolver");
|
||||
String configResolverClass = context.getServletContext().getInitParameter("keycloak.config.resolver");
|
||||
if (configResolverClass != null) {
|
||||
try {
|
||||
KeycloakConfigResolver configResolver = (KeycloakConfigResolver) context.getLoader().getClassLoader().loadClass(configResolverClass).newInstance();
|
||||
|
|
Loading…
Reference in a new issue