saved requests
This commit is contained in:
parent
17a8a92bb3
commit
08be04b337
70 changed files with 1465 additions and 445 deletions
40
core/src/main/java/org/keycloak/util/UriUtils.java
Normal file → Executable file
40
core/src/main/java/org/keycloak/util/UriUtils.java
Normal file → Executable file
|
@ -1,6 +1,8 @@
|
||||||
package org.keycloak.util;
|
package org.keycloak.util;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URLDecoder;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,4 +25,42 @@ public class UriUtils {
|
||||||
return originPattern.matcher(url).matches();
|
return originPattern.matcher(url).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MultivaluedHashMap<String, String> decodeQueryString(String queryString) {
|
||||||
|
MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
|
||||||
|
if (queryString == null || queryString.equals("")) return map;
|
||||||
|
|
||||||
|
String[] params = queryString.split("&");
|
||||||
|
|
||||||
|
for (String param : params)
|
||||||
|
{
|
||||||
|
if (param.indexOf('=') >= 0)
|
||||||
|
{
|
||||||
|
String[] nv = param.split("=", 2);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String name = URLDecoder.decode(nv[0], "UTF-8");
|
||||||
|
String val = nv.length > 1 ? nv[1] : "";
|
||||||
|
map.add(name, URLDecoder.decode(val, "UTF-8"));
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String name = URLDecoder.decode(param, "UTF-8");
|
||||||
|
map.add(name, "");
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
3
integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java
Normal file → Executable file
3
integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java
Normal file → Executable file
|
@ -38,4 +38,7 @@ public interface AdapterTokenStore {
|
||||||
* @param securityContext context where refresh was performed
|
* @param securityContext context where refresh was performed
|
||||||
*/
|
*/
|
||||||
void refreshCallback(RefreshableKeycloakSecurityContext securityContext);
|
void refreshCallback(RefreshableKeycloakSecurityContext securityContext);
|
||||||
|
|
||||||
|
void saveRequest();
|
||||||
|
boolean restoreRequest();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,12 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||||
* @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 OAuthRequestAuthenticator {
|
public class OAuthRequestAuthenticator {
|
||||||
private static final Logger log = Logger.getLogger(OAuthRequestAuthenticator.class);
|
private static final Logger log = Logger.getLogger(OAuthRequestAuthenticator.class);
|
||||||
protected KeycloakDeployment deployment;
|
protected KeycloakDeployment deployment;
|
||||||
protected RequestAuthenticator reqAuthenticator;
|
protected RequestAuthenticator reqAuthenticator;
|
||||||
protected int sslRedirectPort;
|
protected int sslRedirectPort;
|
||||||
|
protected AdapterTokenStore tokenStore;
|
||||||
protected String tokenString;
|
protected String tokenString;
|
||||||
protected String idTokenString;
|
protected String idTokenString;
|
||||||
protected IDToken idToken;
|
protected IDToken idToken;
|
||||||
|
@ -33,11 +34,12 @@ public abstract class OAuthRequestAuthenticator {
|
||||||
protected String refreshToken;
|
protected String refreshToken;
|
||||||
protected String strippedOauthParametersRequestUri;
|
protected String strippedOauthParametersRequestUri;
|
||||||
|
|
||||||
public OAuthRequestAuthenticator(RequestAuthenticator requestAuthenticator, HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort) {
|
public OAuthRequestAuthenticator(RequestAuthenticator requestAuthenticator, HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort, AdapterTokenStore tokenStore) {
|
||||||
this.reqAuthenticator = requestAuthenticator;
|
this.reqAuthenticator = requestAuthenticator;
|
||||||
this.facade = facade;
|
this.facade = facade;
|
||||||
this.deployment = deployment;
|
this.deployment = deployment;
|
||||||
this.sslRedirectPort = sslRedirectPort;
|
this.sslRedirectPort = sslRedirectPort;
|
||||||
|
this.tokenStore = tokenStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthChallenge getChallenge() {
|
public AuthChallenge getChallenge() {
|
||||||
|
@ -200,7 +202,7 @@ public abstract class OAuthRequestAuthenticator {
|
||||||
} else {
|
} else {
|
||||||
log.debug("redirecting to auth server");
|
log.debug("redirecting to auth server");
|
||||||
challenge = loginRedirect();
|
challenge = loginRedirect();
|
||||||
saveRequest();
|
tokenStore.saveRequest();
|
||||||
return AuthOutcome.NOT_ATTEMPTED;
|
return AuthOutcome.NOT_ATTEMPTED;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,12 +216,6 @@ public abstract class OAuthRequestAuthenticator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache the request so that when we get redirected back, it gets invoked
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected abstract void saveRequest();
|
|
||||||
|
|
||||||
protected AuthChallenge challenge(final int code) {
|
protected AuthChallenge challenge(final int code) {
|
||||||
return new AuthChallenge() {
|
return new AuthChallenge() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
package org.keycloak.adapters.jetty;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.http.MimeTypes;
|
|
||||||
import org.eclipse.jetty.security.authentication.FormAuthenticator;
|
|
||||||
import org.eclipse.jetty.server.Request;
|
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.KeycloakPrincipal;
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.AdapterUtils;
|
|
||||||
import org.keycloak.adapters.HttpFacade;
|
|
||||||
import org.keycloak.adapters.KeycloakAccount;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.OAuthRequestAuthenticator;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
|
||||||
import org.keycloak.enums.TokenStore;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public abstract class AbstractJettyRequestAuthenticator extends RequestAuthenticator {
|
|
||||||
public final static String __J_METHOD = "org.eclipse.jetty.security.HTTP_METHOD";
|
|
||||||
protected static final Logger log = Logger.getLogger(AbstractJettyRequestAuthenticator.class);
|
|
||||||
protected AbstractKeycloakJettyAuthenticator valve;
|
|
||||||
protected Request request;
|
|
||||||
protected KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal;
|
|
||||||
|
|
||||||
public AbstractJettyRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort, AbstractKeycloakJettyAuthenticator valve, Request request) {
|
|
||||||
super(facade, deployment, tokenStore, sslRedirectPort);
|
|
||||||
this.valve = valve;
|
|
||||||
this.request = request;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
|
||||||
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort) {
|
|
||||||
@Override
|
|
||||||
protected void saveRequest() {
|
|
||||||
if (deployment.getTokenStore() == TokenStore.SESSION) {
|
|
||||||
saveServletRequest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeOAuthAuthentication(final KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) {
|
|
||||||
principal = skp;
|
|
||||||
final RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext();
|
|
||||||
final Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
|
|
||||||
KeycloakAccount account = new KeycloakAccount() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Principal getPrincipal() {
|
|
||||||
return skp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getRoles() {
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeycloakSecurityContext getKeycloakSecurityContext() {
|
|
||||||
return securityContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
|
||||||
this.tokenStore.saveAccountInfo(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
|
|
||||||
this.principal = principal;
|
|
||||||
RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
|
|
||||||
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
|
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.debug("Completing bearer authentication. Bearer roles: " + roles);
|
|
||||||
}
|
|
||||||
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void restoreRequest() {
|
|
||||||
HttpSession session = request.getSession(false);
|
|
||||||
if (session == null) return;
|
|
||||||
synchronized (session) {
|
|
||||||
String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
|
|
||||||
if (j_uri != null) {
|
|
||||||
// check if the request is for the same url as the original and restore
|
|
||||||
// params if it was a post
|
|
||||||
StringBuffer buf = request.getRequestURL();
|
|
||||||
if (request.getQueryString() != null)
|
|
||||||
buf.append("?").append(request.getQueryString());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if (j_uri.equals(buf.toString())) {
|
|
||||||
*/
|
|
||||||
MultiMap<String> j_post = (MultiMap<String>) session.getAttribute(FormAuthenticator.__J_POST);
|
|
||||||
if (j_post != null) {
|
|
||||||
restoreFormParameters(j_post, request);
|
|
||||||
}
|
|
||||||
session.removeAttribute(FormAuthenticator.__J_URI);
|
|
||||||
session.removeAttribute(__J_METHOD);
|
|
||||||
session.removeAttribute(FormAuthenticator.__J_POST);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getHttpSessionId(boolean create) {
|
|
||||||
HttpSession session = request.getSession(create);
|
|
||||||
return session != null ? session.getId() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void saveServletRequest() {
|
|
||||||
// remember the current URI
|
|
||||||
HttpSession session = request.getSession();
|
|
||||||
synchronized (session) {
|
|
||||||
// But only if it is not set already, or we save every uri that leads to a login form redirect
|
|
||||||
if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
|
|
||||||
StringBuffer buf = request.getRequestURL();
|
|
||||||
if (request.getQueryString() != null)
|
|
||||||
buf.append("?").append(request.getQueryString());
|
|
||||||
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
|
|
||||||
session.setAttribute(__J_METHOD, request.getMethod());
|
|
||||||
|
|
||||||
if ("application/x-www-form-urlencoded".equals(request.getContentType()) && "POST".equalsIgnoreCase(request.getMethod())) {
|
|
||||||
MultiMap<String> formParameters = extractFormParameters(request);
|
|
||||||
session.setAttribute(FormAuthenticator.__J_POST, formParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract MultiMap<String> extractFormParameters(Request base_request);
|
|
||||||
|
|
||||||
protected abstract void restoreFormParameters(MultiMap<String> j_post, Request base_request);
|
|
||||||
}
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.keycloak.adapters.jetty;
|
package org.keycloak.adapters.jetty;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.security.authentication.FormAuthenticator;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.KeycloakPrincipal;
|
import org.keycloak.KeycloakPrincipal;
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
|
@ -10,6 +12,7 @@ import org.keycloak.adapters.KeycloakAccount;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
import org.keycloak.adapters.RequestAuthenticator;
|
||||||
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
|
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
@ -18,14 +21,15 @@ import javax.servlet.http.HttpSession;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class JettySessionTokenStore implements AdapterTokenStore {
|
public abstract class AbstractJettySessionTokenStore implements AdapterTokenStore {
|
||||||
|
public final static String __J_METHOD = "org.eclipse.jetty.security.HTTP_METHOD";
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(JettySessionTokenStore.class);
|
private static final Logger log = Logger.getLogger(AbstractJettySessionTokenStore.class);
|
||||||
|
|
||||||
private Request request;
|
private Request request;
|
||||||
private KeycloakDeployment deployment;
|
protected KeycloakDeployment deployment;
|
||||||
|
|
||||||
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
|
public AbstractJettySessionTokenStore(Request request, KeycloakDeployment deployment) {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.deployment = deployment;
|
this.deployment = deployment;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +53,7 @@ public class JettySessionTokenStore implements AdapterTokenStore {
|
||||||
// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
|
// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
|
||||||
request.getSession().removeAttribute(KeycloakSecurityContext.class.getName());
|
request.getSession().removeAttribute(KeycloakSecurityContext.class.getName());
|
||||||
request.getSession().invalidate();
|
request.getSession().invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCached(RequestAuthenticator authenticator) {
|
public boolean isCached(RequestAuthenticator authenticator) {
|
||||||
|
@ -66,16 +70,16 @@ public class JettySessionTokenStore implements AdapterTokenStore {
|
||||||
securityContext.setCurrentRequestInfo(deployment, this);
|
securityContext.setCurrentRequestInfo(deployment, this);
|
||||||
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||||
|
|
||||||
AbstractJettyRequestAuthenticator jettyAuthenticator = (AbstractJettyRequestAuthenticator) authenticator;
|
JettyRequestAuthenticator jettyAuthenticator = (JettyRequestAuthenticator) authenticator;
|
||||||
KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = AdapterUtils.createPrincipal(deployment, securityContext);
|
KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = AdapterUtils.createPrincipal(deployment, securityContext);
|
||||||
jettyAuthenticator.principal = principal;
|
jettyAuthenticator.principal = principal;
|
||||||
jettyAuthenticator.restoreRequest();
|
restoreRequest();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveAccountInfo(KeycloakAccount account) {
|
public void saveAccountInfo(KeycloakAccount account) {
|
||||||
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext)account.getKeycloakSecurityContext();
|
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) account.getKeycloakSecurityContext();
|
||||||
request.getSession().setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
request.getSession().setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,4 +95,5 @@ public class JettySessionTokenStore implements AdapterTokenStore {
|
||||||
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -65,14 +65,14 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
|
||||||
return new ByteArrayInputStream(json.getBytes());
|
return new ByteArrayInputStream(json.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) {
|
public AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) {
|
||||||
AdapterTokenStore store = (AdapterTokenStore)request.getAttribute(TOKEN_STORE_NOTE);
|
AdapterTokenStore store = (AdapterTokenStore)request.getAttribute(TOKEN_STORE_NOTE);
|
||||||
if (store != null) {
|
if (store != null) {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) {
|
if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) {
|
||||||
store = new JettySessionTokenStore(request, resolvedDeployment);
|
store = createSessionTokenStore(request, resolvedDeployment);
|
||||||
} else {
|
} else {
|
||||||
store = new JettyCookieTokenStore(request, facade, resolvedDeployment);
|
store = new JettyCookieTokenStore(request, facade, resolvedDeployment);
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,9 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void logoutCurrent(Request request) {
|
public abstract AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment);
|
||||||
|
|
||||||
|
public void logoutCurrent(Request request) {
|
||||||
AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext)request.getAttribute(AdapterDeploymentContext.class.getName());
|
AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext)request.getAttribute(AdapterDeploymentContext.class.getName());
|
||||||
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
|
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
|
||||||
if (ksc != null) {
|
if (ksc != null) {
|
||||||
|
@ -212,7 +214,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
|
||||||
nodesRegistrationManagement.tryRegister(deployment);
|
nodesRegistrationManagement.tryRegister(deployment);
|
||||||
|
|
||||||
tokenStore.checkCurrentToken();
|
tokenStore.checkCurrentToken();
|
||||||
AbstractJettyRequestAuthenticator authenticator = createRequestAuthenticator(request, facade, deployment, tokenStore);
|
JettyRequestAuthenticator authenticator = createRequestAuthenticator(request, facade, deployment, tokenStore);
|
||||||
AuthOutcome outcome = authenticator.authenticate();
|
AuthOutcome outcome = authenticator.authenticate();
|
||||||
if (outcome == AuthOutcome.AUTHENTICATED) {
|
if (outcome == AuthOutcome.AUTHENTICATED) {
|
||||||
if (facade.isEnded()) {
|
if (facade.isEnded()) {
|
||||||
|
@ -238,7 +240,10 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
|
||||||
|
|
||||||
protected abstract Request resolveRequest(ServletRequest req);
|
protected abstract Request resolveRequest(ServletRequest req);
|
||||||
|
|
||||||
protected abstract AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore);
|
protected JettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade,
|
||||||
|
KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
|
||||||
|
return new JettyRequestAuthenticator(facade, deployment, tokenStore, -1, request);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAuthMethod() {
|
public String getAuthMethod() {
|
||||||
|
|
|
@ -99,4 +99,14 @@ public class JettyCookieTokenStore implements AdapterTokenStore {
|
||||||
CookieTokenStore.removeCookie(facade);
|
CookieTokenStore.removeCookie(facade);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveRequest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package org.keycloak.adapters.jetty;
|
||||||
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
import org.keycloak.adapters.HttpFacade;
|
import org.keycloak.adapters.HttpFacade;
|
||||||
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.util.UriUtils;
|
||||||
|
|
||||||
import javax.security.cert.X509Certificate;
|
import javax.security.cert.X509Certificate;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -21,6 +23,7 @@ public class JettyHttpFacade implements HttpFacade {
|
||||||
protected HttpServletResponse response;
|
protected HttpServletResponse response;
|
||||||
protected RequestFacade requestFacade = new RequestFacade();
|
protected RequestFacade requestFacade = new RequestFacade();
|
||||||
protected ResponseFacade responseFacade = new ResponseFacade();
|
protected ResponseFacade responseFacade = new ResponseFacade();
|
||||||
|
protected MultivaluedHashMap<String, String> queryParameters;
|
||||||
|
|
||||||
protected class RequestFacade implements Request {
|
protected class RequestFacade implements Request {
|
||||||
@Override
|
@Override
|
||||||
|
@ -39,7 +42,10 @@ public class JettyHttpFacade implements HttpFacade {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getQueryParamValue(String paramName) {
|
public String getQueryParamValue(String paramName) {
|
||||||
return request.getParameter(paramName);
|
if (queryParameters == null) {
|
||||||
|
queryParameters = UriUtils.decodeQueryString(request.getQueryString());
|
||||||
|
}
|
||||||
|
return queryParameters.getFirst(paramName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
package org.keycloak.adapters.jetty;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.KeycloakPrincipal;
|
||||||
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
|
import org.keycloak.adapters.AdapterTokenStore;
|
||||||
|
import org.keycloak.adapters.AdapterUtils;
|
||||||
|
import org.keycloak.adapters.HttpFacade;
|
||||||
|
import org.keycloak.adapters.KeycloakAccount;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.OAuthRequestAuthenticator;
|
||||||
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
|
import org.keycloak.adapters.RequestAuthenticator;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class JettyRequestAuthenticator extends RequestAuthenticator {
|
||||||
|
protected static final Logger log = Logger.getLogger(JettyRequestAuthenticator.class);
|
||||||
|
protected Request request;
|
||||||
|
protected KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal;
|
||||||
|
|
||||||
|
public JettyRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort, Request request) {
|
||||||
|
super(facade, deployment, tokenStore, sslRedirectPort);
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
||||||
|
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void completeOAuthAuthentication(final KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) {
|
||||||
|
principal = skp;
|
||||||
|
final RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext();
|
||||||
|
final Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
|
||||||
|
KeycloakAccount account = new KeycloakAccount() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getPrincipal() {
|
||||||
|
return skp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeycloakSecurityContext getKeycloakSecurityContext() {
|
||||||
|
return securityContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||||
|
this.tokenStore.saveAccountInfo(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
|
||||||
|
this.principal = principal;
|
||||||
|
RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
|
||||||
|
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Completing bearer authentication. Bearer roles: " + roles);
|
||||||
|
}
|
||||||
|
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getHttpSessionId(boolean create) {
|
||||||
|
HttpSession session = request.getSession(create);
|
||||||
|
return session != null ? session.getId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,32 +0,0 @@
|
||||||
package org.keycloak.adapters.jetty;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Request;
|
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class JettyRequestAuthenticator extends AbstractJettyRequestAuthenticator {
|
|
||||||
|
|
||||||
public JettyRequestAuthenticator(KeycloakDeployment deployment,
|
|
||||||
AbstractKeycloakJettyAuthenticator valve, AdapterTokenStore tokenStore,
|
|
||||||
JettyHttpFacade facade,
|
|
||||||
Request request) {
|
|
||||||
super(facade, deployment, tokenStore, -1, valve, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MultiMap<String> extractFormParameters(Request base_request) {
|
|
||||||
MultiMap<String> formParameters = new MultiMap<String>();
|
|
||||||
base_request.extractParameters();
|
|
||||||
return base_request.getParameters();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
|
|
||||||
base_request.setParameters(j_post);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
package org.keycloak.adapters.jetty;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.security.authentication.FormAuthenticator;
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
|
||||||
|
public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
|
||||||
|
protected Request myRequest;
|
||||||
|
|
||||||
|
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
|
||||||
|
super(request, deployment);
|
||||||
|
this.myRequest = request; // for IDE/compilation purposes
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MultiMap<String> extractFormParameters(Request base_request) {
|
||||||
|
MultiMap<String> formParameters = new MultiMap<String>();
|
||||||
|
base_request.extractParameters();
|
||||||
|
return base_request.getParameters();
|
||||||
|
}
|
||||||
|
protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
|
||||||
|
base_request.setParameters(j_post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
HttpSession session = myRequest.getSession(false);
|
||||||
|
if (session == null) return false;
|
||||||
|
synchronized (session) {
|
||||||
|
String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
|
||||||
|
if (j_uri != null) {
|
||||||
|
// check if the request is for the same url as the original and restore
|
||||||
|
// params if it was a post
|
||||||
|
StringBuffer buf = myRequest.getRequestURL();
|
||||||
|
if (myRequest.getQueryString() != null)
|
||||||
|
buf.append("?").append(myRequest.getQueryString());
|
||||||
|
if (j_uri.equals(buf.toString())) {
|
||||||
|
String method = (String)session.getAttribute(__J_METHOD);
|
||||||
|
myRequest.setMethod(method);
|
||||||
|
MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
|
||||||
|
if (j_post != null) {
|
||||||
|
MultiMap<String> map = new MultiMap<String>();
|
||||||
|
for (String key : j_post.keySet()) {
|
||||||
|
for (String val : j_post.getList(key)) {
|
||||||
|
map.add(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
restoreFormParameters(map, myRequest);
|
||||||
|
}
|
||||||
|
session.removeAttribute(FormAuthenticator.__J_URI);
|
||||||
|
session.removeAttribute(__J_METHOD);
|
||||||
|
session.removeAttribute(FormAuthenticator.__J_POST);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveRequest() {
|
||||||
|
// remember the current URI
|
||||||
|
HttpSession session = myRequest.getSession();
|
||||||
|
synchronized (session) {
|
||||||
|
// But only if it is not set already, or we save every uri that leads to a login form redirect
|
||||||
|
if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
|
||||||
|
StringBuffer buf = myRequest.getRequestURL();
|
||||||
|
if (myRequest.getQueryString() != null)
|
||||||
|
buf.append("?").append(myRequest.getQueryString());
|
||||||
|
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
|
||||||
|
session.setAttribute(__J_METHOD, myRequest.getMethod());
|
||||||
|
|
||||||
|
if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
|
||||||
|
MultiMap<String> formParameters = extractFormParameters(myRequest);
|
||||||
|
MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
|
||||||
|
for (String key : formParameters.keySet()) {
|
||||||
|
for (Object value : formParameters.getValues(key)) {
|
||||||
|
map.add(key, (String) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.setAttribute(CACHED_FORM_PARAMETERS, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,8 +21,8 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
|
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
|
||||||
return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
|
return new JettySessionTokenStore(request, resolvedDeployment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -36,6 +36,20 @@
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-jetty-core</artifactId>
|
<artifactId>keycloak-jetty-core</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-util</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-security</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package org.keycloak.adapters.jetty;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Request;
|
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class JettyRequestAuthenticator extends AbstractJettyRequestAuthenticator {
|
|
||||||
|
|
||||||
public JettyRequestAuthenticator(KeycloakDeployment deployment,
|
|
||||||
AbstractKeycloakJettyAuthenticator valve, AdapterTokenStore tokenStore,
|
|
||||||
JettyHttpFacade facade,
|
|
||||||
Request request) {
|
|
||||||
super(facade, deployment, tokenStore, -1, valve, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MultiMap<String> extractFormParameters(Request base_request) {
|
|
||||||
MultiMap<String> formParameters = new MultiMap<String>();
|
|
||||||
base_request.extractParameters();
|
|
||||||
return base_request.getParameters();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
|
|
||||||
base_request.setParameters(j_post);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package org.keycloak.adapters.jetty;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.security.authentication.FormAuthenticator;
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
|
||||||
|
public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
|
||||||
|
protected Request myRequest;
|
||||||
|
|
||||||
|
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
|
||||||
|
super(request, deployment);
|
||||||
|
this.myRequest = request; // for IDE/compilation purposes
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MultiMap<String> extractFormParameters(Request base_request) {
|
||||||
|
MultiMap<String> formParameters = new MultiMap<String>();
|
||||||
|
base_request.extractParameters();
|
||||||
|
return base_request.getParameters();
|
||||||
|
}
|
||||||
|
protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
|
||||||
|
base_request.setParameters(j_post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
HttpSession session = myRequest.getSession(false);
|
||||||
|
if (session == null) return false;
|
||||||
|
synchronized (session) {
|
||||||
|
String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
|
||||||
|
if (j_uri != null) {
|
||||||
|
// check if the request is for the same url as the original and restore
|
||||||
|
// params if it was a post
|
||||||
|
StringBuffer buf = myRequest.getRequestURL();
|
||||||
|
if (myRequest.getQueryString() != null)
|
||||||
|
buf.append("?").append(myRequest.getQueryString());
|
||||||
|
if (j_uri.equals(buf.toString())) {
|
||||||
|
String method = (String)session.getAttribute(__J_METHOD);
|
||||||
|
myRequest.setMethod(HttpMethod.valueOf(method.toUpperCase()), method);
|
||||||
|
MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
|
||||||
|
if (j_post != null) {
|
||||||
|
MultiMap<String> map = new MultiMap<String>();
|
||||||
|
for (String key : j_post.keySet()) {
|
||||||
|
for (String val : j_post.getList(key)) {
|
||||||
|
map.add(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
restoreFormParameters(map, myRequest);
|
||||||
|
}
|
||||||
|
session.removeAttribute(FormAuthenticator.__J_URI);
|
||||||
|
session.removeAttribute(__J_METHOD);
|
||||||
|
session.removeAttribute(FormAuthenticator.__J_POST);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveRequest() {
|
||||||
|
// remember the current URI
|
||||||
|
HttpSession session = myRequest.getSession();
|
||||||
|
synchronized (session) {
|
||||||
|
// But only if it is not set already, or we save every uri that leads to a login form redirect
|
||||||
|
if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
|
||||||
|
StringBuffer buf = myRequest.getRequestURL();
|
||||||
|
if (myRequest.getQueryString() != null)
|
||||||
|
buf.append("?").append(myRequest.getQueryString());
|
||||||
|
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
|
||||||
|
session.setAttribute(__J_METHOD, myRequest.getMethod());
|
||||||
|
|
||||||
|
if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
|
||||||
|
MultiMap<String> formParameters = extractFormParameters(myRequest);
|
||||||
|
MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
|
||||||
|
for (String key : formParameters.keySet()) {
|
||||||
|
for (Object value : formParameters.getValues(key)) {
|
||||||
|
map.add(key, (String) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.setAttribute(CACHED_FORM_PARAMETERS, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,8 +21,8 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
|
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
|
||||||
return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
|
return new JettySessionTokenStore(request, resolvedDeployment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package org.keycloak.adapters.jetty;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Request;
|
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class JettyRequestAuthenticator extends AbstractJettyRequestAuthenticator {
|
|
||||||
|
|
||||||
public JettyRequestAuthenticator(KeycloakDeployment deployment,
|
|
||||||
AbstractKeycloakJettyAuthenticator valve, AdapterTokenStore tokenStore,
|
|
||||||
JettyHttpFacade facade,
|
|
||||||
Request request) {
|
|
||||||
super(facade, deployment, tokenStore, -1, valve, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MultiMap<String> extractFormParameters(Request base_request) {
|
|
||||||
MultiMap<String> formParameters = new MultiMap<String>();
|
|
||||||
base_request.extractFormParameters(formParameters);
|
|
||||||
return formParameters;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
|
|
||||||
base_request.setContentParameters(j_post);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package org.keycloak.adapters.jetty;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.security.authentication.FormAuthenticator;
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
|
||||||
|
public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
|
||||||
|
protected Request myRequest;
|
||||||
|
|
||||||
|
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
|
||||||
|
super(request, deployment);
|
||||||
|
this.myRequest = request; // for IDE/compilation purposes
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MultiMap<String> extractFormParameters(Request base_request) {
|
||||||
|
MultiMap<String> formParameters = new MultiMap<String>();
|
||||||
|
base_request.extractFormParameters(formParameters);
|
||||||
|
return formParameters;
|
||||||
|
}
|
||||||
|
protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
|
||||||
|
base_request.setContentParameters(j_post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
HttpSession session = myRequest.getSession(false);
|
||||||
|
if (session == null) return false;
|
||||||
|
synchronized (session) {
|
||||||
|
String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
|
||||||
|
if (j_uri != null) {
|
||||||
|
// check if the request is for the same url as the original and restore
|
||||||
|
// params if it was a post
|
||||||
|
StringBuffer buf = myRequest.getRequestURL();
|
||||||
|
if (myRequest.getQueryString() != null)
|
||||||
|
buf.append("?").append(myRequest.getQueryString());
|
||||||
|
if (j_uri.equals(buf.toString())) {
|
||||||
|
String method = (String)session.getAttribute(__J_METHOD);
|
||||||
|
myRequest.setMethod(HttpMethod.valueOf(method.toUpperCase()), method);
|
||||||
|
MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
|
||||||
|
if (j_post != null) {
|
||||||
|
MultiMap<String> map = new MultiMap<String>();
|
||||||
|
for (String key : j_post.keySet()) {
|
||||||
|
for (String val : j_post.getList(key)) {
|
||||||
|
map.add(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
restoreFormParameters(map, myRequest);
|
||||||
|
}
|
||||||
|
session.removeAttribute(FormAuthenticator.__J_URI);
|
||||||
|
session.removeAttribute(__J_METHOD);
|
||||||
|
session.removeAttribute(FormAuthenticator.__J_POST);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveRequest() {
|
||||||
|
// remember the current URI
|
||||||
|
HttpSession session = myRequest.getSession();
|
||||||
|
synchronized (session) {
|
||||||
|
// But only if it is not set already, or we save every uri that leads to a login form redirect
|
||||||
|
if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
|
||||||
|
StringBuffer buf = myRequest.getRequestURL();
|
||||||
|
if (myRequest.getQueryString() != null)
|
||||||
|
buf.append("?").append(myRequest.getQueryString());
|
||||||
|
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
|
||||||
|
session.setAttribute(__J_METHOD, myRequest.getMethod());
|
||||||
|
|
||||||
|
if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
|
||||||
|
MultiMap<String> formParameters = extractFormParameters(myRequest);
|
||||||
|
MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
|
||||||
|
for (String key : formParameters.keySet()) {
|
||||||
|
for (Object value : formParameters.getValues(key)) {
|
||||||
|
map.add(key, (String) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.setAttribute(CACHED_FORM_PARAMETERS, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,12 +20,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
|
|
||||||
return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Request resolveRequest(ServletRequest req) {
|
protected Request resolveRequest(ServletRequest req) {
|
||||||
return (req instanceof Request) ? (Request)req : HttpChannel.getCurrentHttpChannel().getRequest();
|
return (req instanceof Request) ? (Request)req : HttpChannel.getCurrentHttpChannel().getRequest();
|
||||||
}
|
}
|
||||||
|
@ -40,6 +35,8 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
|
||||||
|
return new JettySessionTokenStore(request, resolvedDeployment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,12 @@ import org.apache.catalina.Lifecycle;
|
||||||
import org.apache.catalina.LifecycleEvent;
|
import org.apache.catalina.LifecycleEvent;
|
||||||
import org.apache.catalina.LifecycleListener;
|
import org.apache.catalina.LifecycleListener;
|
||||||
import org.apache.catalina.Manager;
|
import org.apache.catalina.Manager;
|
||||||
|
import org.apache.catalina.authenticator.Constants;
|
||||||
import org.apache.catalina.authenticator.FormAuthenticator;
|
import org.apache.catalina.authenticator.FormAuthenticator;
|
||||||
|
import org.apache.catalina.authenticator.SavedRequest;
|
||||||
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.tomcat.util.buf.ByteChunk;
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
import org.keycloak.constants.AdapterConstants;
|
import org.keycloak.constants.AdapterConstants;
|
||||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||||
|
@ -24,12 +27,15 @@ import org.keycloak.enums.TokenStore;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||||
|
@ -180,7 +186,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
|
||||||
|
|
||||||
nodesRegistrationManagement.tryRegister(deployment);
|
nodesRegistrationManagement.tryRegister(deployment);
|
||||||
|
|
||||||
CatalinaRequestAuthenticator authenticator = new CatalinaRequestAuthenticator(deployment, this, tokenStore, facade, request, createPrincipalFactory());
|
CatalinaRequestAuthenticator authenticator = new CatalinaRequestAuthenticator(deployment, tokenStore, facade, request, createPrincipalFactory());
|
||||||
AuthOutcome outcome = authenticator.authenticate();
|
AuthOutcome outcome = authenticator.authenticate();
|
||||||
if (outcome == AuthOutcome.AUTHENTICATED) {
|
if (outcome == AuthOutcome.AUTHENTICATED) {
|
||||||
if (facade.isEnded()) {
|
if (facade.isEnded()) {
|
||||||
|
@ -225,7 +231,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) {
|
if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) {
|
||||||
store = new CatalinaSessionTokenStore(request, resolvedDeployment, userSessionManagement, createPrincipalFactory());
|
store = new CatalinaSessionTokenStore(request, resolvedDeployment, userSessionManagement, createPrincipalFactory(), this);
|
||||||
} else {
|
} else {
|
||||||
store = new CatalinaCookieTokenStore(request, facade, resolvedDeployment, createPrincipalFactory());
|
store = new CatalinaCookieTokenStore(request, facade, resolvedDeployment, createPrincipalFactory());
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,16 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
|
||||||
CookieTokenStore.setTokenCookie(deployment, facade, secContext);
|
CookieTokenStore.setTokenCookie(deployment, facade, secContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveRequest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify if we already have authenticated and active principal in cookie. Perform refresh if it's not active
|
* Verify if we already have authenticated and active principal in cookie. Perform refresh if it's not active
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,7 +2,9 @@ package org.keycloak.adapters.tomcat;
|
||||||
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
import org.keycloak.adapters.HttpFacade;
|
import org.keycloak.adapters.HttpFacade;
|
||||||
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
import org.keycloak.util.ServerCookie;
|
import org.keycloak.util.ServerCookie;
|
||||||
|
import org.keycloak.util.UriUtils;
|
||||||
|
|
||||||
import javax.security.cert.X509Certificate;
|
import javax.security.cert.X509Certificate;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -22,6 +24,7 @@ public class CatalinaHttpFacade implements HttpFacade {
|
||||||
protected HttpServletResponse response;
|
protected HttpServletResponse response;
|
||||||
protected RequestFacade requestFacade = new RequestFacade();
|
protected RequestFacade requestFacade = new RequestFacade();
|
||||||
protected ResponseFacade responseFacade = new ResponseFacade();
|
protected ResponseFacade responseFacade = new ResponseFacade();
|
||||||
|
protected MultivaluedHashMap<String, String> queryParameters;
|
||||||
|
|
||||||
protected class RequestFacade implements Request {
|
protected class RequestFacade implements Request {
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,7 +43,10 @@ public class CatalinaHttpFacade implements HttpFacade {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getQueryParamValue(String paramName) {
|
public String getQueryParamValue(String paramName) {
|
||||||
return request.getParameter(paramName);
|
if (queryParameters == null) {
|
||||||
|
queryParameters = UriUtils.decodeQueryString(request.getQueryString());
|
||||||
|
}
|
||||||
|
return queryParameters.getFirst(paramName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,36 +27,22 @@ import javax.servlet.http.HttpSession;
|
||||||
*/
|
*/
|
||||||
public class CatalinaRequestAuthenticator extends RequestAuthenticator {
|
public class CatalinaRequestAuthenticator extends RequestAuthenticator {
|
||||||
private static final Logger log = Logger.getLogger(""+CatalinaRequestAuthenticator.class);
|
private static final Logger log = Logger.getLogger(""+CatalinaRequestAuthenticator.class);
|
||||||
protected AbstractKeycloakAuthenticatorValve valve;
|
|
||||||
protected Request request;
|
protected Request request;
|
||||||
protected GenericPrincipalFactory principalFactory;
|
protected GenericPrincipalFactory principalFactory;
|
||||||
|
|
||||||
public CatalinaRequestAuthenticator(KeycloakDeployment deployment,
|
public CatalinaRequestAuthenticator(KeycloakDeployment deployment,
|
||||||
AbstractKeycloakAuthenticatorValve valve, AdapterTokenStore tokenStore,
|
AdapterTokenStore tokenStore,
|
||||||
CatalinaHttpFacade facade,
|
CatalinaHttpFacade facade,
|
||||||
Request request,
|
Request request,
|
||||||
GenericPrincipalFactory principalFactory) {
|
GenericPrincipalFactory principalFactory) {
|
||||||
super(facade, deployment, tokenStore, request.getConnector().getRedirectPort());
|
super(facade, deployment, tokenStore, request.getConnector().getRedirectPort());
|
||||||
this.valve = valve;
|
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.principalFactory = principalFactory;
|
this.principalFactory = principalFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
||||||
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort) {
|
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
|
||||||
@Override
|
|
||||||
protected void saveRequest() {
|
|
||||||
try {
|
|
||||||
// Support saving request just for TokenStore.SESSION TODO: Add to tokenStore spi?
|
|
||||||
if (deployment.getTokenStore() == TokenStore.SESSION) {
|
|
||||||
valve.keycloakSaveRequest(request);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,17 +85,6 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
|
||||||
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void restoreRequest() {
|
|
||||||
if (request.getSessionInternal().getNote(Constants.FORM_REQUEST_NOTE) != null) {
|
|
||||||
if (valve.keycloakRestoreRequest(request)) {
|
|
||||||
log.finer("restoreRequest");
|
|
||||||
} else {
|
|
||||||
log.finer("Restore of original request failed");
|
|
||||||
throw new RuntimeException("Restore of original request failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getHttpSessionId(boolean create) {
|
protected String getHttpSessionId(boolean create) {
|
||||||
HttpSession session = request.getSession(create);
|
HttpSession session = request.getSession(create);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.keycloak.adapters.tomcat;
|
package org.keycloak.adapters.tomcat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -24,12 +25,18 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
|
||||||
private KeycloakDeployment deployment;
|
private KeycloakDeployment deployment;
|
||||||
private CatalinaUserSessionManagement sessionManagement;
|
private CatalinaUserSessionManagement sessionManagement;
|
||||||
protected GenericPrincipalFactory principalFactory;
|
protected GenericPrincipalFactory principalFactory;
|
||||||
|
protected AbstractKeycloakAuthenticatorValve valve;
|
||||||
|
|
||||||
public CatalinaSessionTokenStore(Request request, KeycloakDeployment deployment, CatalinaUserSessionManagement sessionManagement, GenericPrincipalFactory principalFactory) {
|
|
||||||
|
public CatalinaSessionTokenStore(Request request, KeycloakDeployment deployment,
|
||||||
|
CatalinaUserSessionManagement sessionManagement,
|
||||||
|
GenericPrincipalFactory principalFactory,
|
||||||
|
AbstractKeycloakAuthenticatorValve valve) {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.deployment = deployment;
|
this.deployment = deployment;
|
||||||
this.sessionManagement = sessionManagement;
|
this.sessionManagement = sessionManagement;
|
||||||
this.principalFactory = principalFactory;
|
this.principalFactory = principalFactory;
|
||||||
|
this.valve = valve;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -81,7 +88,7 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
|
||||||
request.setUserPrincipal(principal);
|
request.setUserPrincipal(principal);
|
||||||
request.setAuthType("KEYCLOAK");
|
request.setAuthType("KEYCLOAK");
|
||||||
|
|
||||||
((CatalinaRequestAuthenticator)authenticator).restoreRequest();
|
restoreRequest();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,4 +119,18 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
|
||||||
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveRequest() {
|
||||||
|
try {
|
||||||
|
valve.keycloakSaveRequest(request);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
return valve.keycloakRestoreRequest(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,12 +53,7 @@ public abstract class AbstractUndertowRequestAuthenticator extends RequestAuthen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
||||||
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort) {
|
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
|
||||||
@Override
|
|
||||||
protected void saveRequest() {
|
|
||||||
// todo
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
package org.keycloak.adapters.undertow;
|
||||||
|
import io.undertow.UndertowLogger;
|
||||||
|
import io.undertow.UndertowOptions;
|
||||||
|
import io.undertow.server.Connectors;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import io.undertow.server.session.Session;
|
||||||
|
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||||
|
import io.undertow.servlet.spec.HttpSessionImpl;
|
||||||
|
import io.undertow.util.HeaderMap;
|
||||||
|
import io.undertow.util.HeaderValues;
|
||||||
|
import io.undertow.util.Headers;
|
||||||
|
import io.undertow.util.HttpString;
|
||||||
|
import io.undertow.util.ImmediatePooled;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saved servlet request.
|
||||||
|
*
|
||||||
|
* Note bill burke: I had to fork this because Undertow was automatically restoring the request before the code could be processed and redirected.
|
||||||
|
*
|
||||||
|
* @author Stuart Douglas
|
||||||
|
*/
|
||||||
|
public class SavedRequest implements Serializable {
|
||||||
|
|
||||||
|
private static final String SESSION_KEY = SavedRequest.class.getName();
|
||||||
|
|
||||||
|
private final byte[] data;
|
||||||
|
private final int dataLength;
|
||||||
|
private final HttpString method;
|
||||||
|
private final String requestUri;
|
||||||
|
private final HeaderMap headerMap;
|
||||||
|
|
||||||
|
public SavedRequest(byte[] data, int dataLength, HttpString method, String requestUri, HeaderMap headerMap) {
|
||||||
|
this.data = data;
|
||||||
|
this.dataLength = dataLength;
|
||||||
|
this.method = method;
|
||||||
|
this.requestUri = requestUri;
|
||||||
|
this.headerMap = headerMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void trySaveRequest(final HttpServerExchange exchange) {
|
||||||
|
int maxSize = exchange.getConnection().getUndertowOptions().get(UndertowOptions.MAX_BUFFERED_REQUEST_SIZE, 16384);
|
||||||
|
if (maxSize > 0) {
|
||||||
|
//if this request has a body try and cache the response
|
||||||
|
if (!exchange.isRequestComplete()) {
|
||||||
|
final long requestContentLength = exchange.getRequestContentLength();
|
||||||
|
if (requestContentLength > maxSize) {
|
||||||
|
UndertowLogger.REQUEST_LOGGER.debugf("Request to %s was to large to save", exchange.getRequestURI());
|
||||||
|
return;//failed to save the request, we just return
|
||||||
|
}
|
||||||
|
//TODO: we should really be used pooled buffers
|
||||||
|
//TODO: we should probably limit the number of saved requests at any given time
|
||||||
|
byte[] buffer = new byte[maxSize];
|
||||||
|
int read = 0;
|
||||||
|
int res = 0;
|
||||||
|
InputStream in = exchange.getInputStream();
|
||||||
|
try {
|
||||||
|
while ((res = in.read(buffer, read, buffer.length - read)) > 0) {
|
||||||
|
read += res;
|
||||||
|
if (read == maxSize) {
|
||||||
|
UndertowLogger.REQUEST_LOGGER.debugf("Request to %s was to large to save", exchange.getRequestURI());
|
||||||
|
return;//failed to save the request, we just return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HeaderMap headers = new HeaderMap();
|
||||||
|
for(HeaderValues entry : exchange.getRequestHeaders()) {
|
||||||
|
if(entry.getHeaderName().equals(Headers.CONTENT_LENGTH) ||
|
||||||
|
entry.getHeaderName().equals(Headers.TRANSFER_ENCODING) ||
|
||||||
|
entry.getHeaderName().equals(Headers.CONNECTION)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
headers.putAll(entry.getHeaderName(), entry);
|
||||||
|
}
|
||||||
|
SavedRequest request = new SavedRequest(buffer, read, exchange.getRequestMethod(), exchange.getRequestURI(), exchange.getRequestHeaders());
|
||||||
|
final ServletRequestContext sc = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||||
|
HttpSessionImpl session = sc.getCurrentServletContext().getSession(exchange, true);
|
||||||
|
Session underlyingSession;
|
||||||
|
if(System.getSecurityManager() == null) {
|
||||||
|
underlyingSession = session.getSession();
|
||||||
|
} else {
|
||||||
|
underlyingSession = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(session));
|
||||||
|
}
|
||||||
|
underlyingSession.setAttribute(SESSION_KEY, request);
|
||||||
|
} catch (IOException e) {
|
||||||
|
UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tryRestoreRequest(final HttpServerExchange exchange, HttpSession session) {
|
||||||
|
if(session instanceof HttpSessionImpl) {
|
||||||
|
|
||||||
|
Session underlyingSession;
|
||||||
|
if(System.getSecurityManager() == null) {
|
||||||
|
underlyingSession = ((HttpSessionImpl) session).getSession();
|
||||||
|
} else {
|
||||||
|
underlyingSession = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(session));
|
||||||
|
}
|
||||||
|
SavedRequest request = (SavedRequest) underlyingSession.getAttribute(SESSION_KEY);
|
||||||
|
if(request != null) {
|
||||||
|
if(request.requestUri.equals(exchange.getRequestURI()) && exchange.isRequestComplete()) {
|
||||||
|
UndertowLogger.REQUEST_LOGGER.debugf("restoring request body for request to %s", request.requestUri);
|
||||||
|
exchange.setRequestMethod(request.method);
|
||||||
|
Connectors.ungetRequestBytes(exchange, new ImmediatePooled<ByteBuffer>(ByteBuffer.wrap(request.data, 0, request.dataLength)));
|
||||||
|
underlyingSession.removeAttribute(SESSION_KEY);
|
||||||
|
//clear the existing header map of everything except the connection header
|
||||||
|
//TODO: are there other headers we should preserve?
|
||||||
|
Iterator<HeaderValues> headerIterator = exchange.getRequestHeaders().iterator();
|
||||||
|
while (headerIterator.hasNext()) {
|
||||||
|
HeaderValues header = headerIterator.next();
|
||||||
|
if(!header.getHeaderName().equals(Headers.CONNECTION)) {
|
||||||
|
headerIterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(HeaderValues header : request.headerMap) {
|
||||||
|
exchange.getRequestHeaders().putAll(header.getHeaderName(), header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,12 +19,15 @@ package org.keycloak.adapters.undertow;
|
||||||
import io.undertow.security.api.SecurityContext;
|
import io.undertow.security.api.SecurityContext;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||||
|
import io.undertow.servlet.util.SavedRequest;
|
||||||
import org.keycloak.KeycloakPrincipal;
|
import org.keycloak.KeycloakPrincipal;
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
import org.keycloak.adapters.AdapterTokenStore;
|
||||||
import org.keycloak.adapters.HttpFacade;
|
import org.keycloak.adapters.HttpFacade;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.OAuthRequestAuthenticator;
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
|
import org.keycloak.enums.TokenStore;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
@ -43,6 +46,11 @@ public class ServletRequestAuthenticator extends AbstractUndertowRequestAuthenti
|
||||||
super(facade, deployment, sslRedirectPort, securityContext, exchange, tokenStore);
|
super(facade, deployment, sslRedirectPort, securityContext, exchange, tokenStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
||||||
|
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
|
protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
|
||||||
super.propagateKeycloakContext(account);
|
super.propagateKeycloakContext(account);
|
||||||
|
|
|
@ -64,6 +64,7 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
|
||||||
log.debug("Cached account found");
|
log.debug("Cached account found");
|
||||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
||||||
((AbstractUndertowRequestAuthenticator)authenticator).propagateKeycloakContext(account);
|
((AbstractUndertowRequestAuthenticator)authenticator).propagateKeycloakContext(account);
|
||||||
|
restoreRequest();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
log.debug("Refresh failed. Account was not active. Returning null and invalidating Http session");
|
log.debug("Refresh failed. Account was not active. Returning null and invalidating Http session");
|
||||||
|
@ -105,6 +106,20 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveRequest() {
|
||||||
|
SavedRequest.trySaveRequest(exchange);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
HttpSession session = getSession(false);
|
||||||
|
if (session == null) return false;
|
||||||
|
SavedRequest.tryRestoreRequest(exchange, session);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected HttpSession getSession(boolean create) {
|
protected HttpSession getSession(boolean create) {
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
||||||
|
|
|
@ -80,4 +80,14 @@ public class UndertowCookieTokenStore implements AdapterTokenStore {
|
||||||
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
||||||
CookieTokenStore.setTokenCookie(deployment, facade, securityContext);
|
CookieTokenStore.setTokenCookie(deployment, facade, securityContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveRequest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,16 @@ public class UndertowSessionTokenStore implements AdapterTokenStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveRequest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean restoreRequest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveAccountInfo(KeycloakAccount account) {
|
public void saveAccountInfo(KeycloakAccount account) {
|
||||||
Session session = Sessions.getOrCreateSession(exchange);
|
Session session = Sessions.getOrCreateSession(exchange);
|
||||||
|
|
|
@ -74,7 +74,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
*/
|
*/
|
||||||
public class AdapterTest {
|
public class AdapterTest {
|
||||||
|
|
||||||
public static final String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri("http://localhost:8081/auth")).build("demo").toString();
|
|
||||||
public static PublicKey realmPublicKey;
|
public static PublicKey realmPublicKey;
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
|
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
|
||||||
|
@ -99,6 +98,8 @@ public class AdapterTest {
|
||||||
System.setProperty("my.host.name", "localhost");
|
System.setProperty("my.host.name", "localhost");
|
||||||
url = getClass().getResource("/adapter-test/session-keycloak.json");
|
url = getClass().getResource("/adapter-test/session-keycloak.json");
|
||||||
deployApplication("session-portal", "/session-portal", SessionServlet.class, url.getPath(), "user");
|
deployApplication("session-portal", "/session-portal", SessionServlet.class, url.getPath(), "user");
|
||||||
|
url = getClass().getResource("/adapter-test/input-keycloak.json");
|
||||||
|
deployApplication("input-portal", "/input-portal", InputServlet.class, url.getPath(), "user", true, null, "/secured/*");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,6 +111,11 @@ public class AdapterTest {
|
||||||
testStrategy.testLoginSSOAndLogout();
|
testStrategy.testLoginSSOAndLogout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavedPostRequest() throws Exception {
|
||||||
|
testStrategy.testSavedPostRequest();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testServletRequestLogout() throws Exception {
|
public void testServletRequestLogout() throws Exception {
|
||||||
testStrategy.testServletRequestLogout();
|
testStrategy.testServletRequestLogout();
|
||||||
|
|
|
@ -87,6 +87,9 @@ public class AdapterTestStrategy extends ExternalResource {
|
||||||
@WebResource
|
@WebResource
|
||||||
protected LoginPage loginPage;
|
protected LoginPage loginPage;
|
||||||
|
|
||||||
|
@WebResource
|
||||||
|
protected InputPage inputPage;
|
||||||
|
|
||||||
protected String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri(AUTH_SERVER_URL)).build("demo").toString();
|
protected String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri(AUTH_SERVER_URL)).build("demo").toString();
|
||||||
|
|
||||||
public AdapterTestStrategy(String AUTH_SERVER_URL, String APP_SERVER_BASE_URL, AbstractKeycloakRule keycloakRule) {
|
public AdapterTestStrategy(String AUTH_SERVER_URL, String APP_SERVER_BASE_URL, AbstractKeycloakRule keycloakRule) {
|
||||||
|
@ -132,6 +135,35 @@ public class AdapterTestStrategy extends ExternalResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavedPostRequest() throws Exception {
|
||||||
|
// test login to customer-portal which does a bearer request to customer-db
|
||||||
|
driver.navigate().to(APP_SERVER_BASE_URL + "/input-portal");
|
||||||
|
System.out.println("Current url: " + driver.getCurrentUrl());
|
||||||
|
Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/input-portal" + slash);
|
||||||
|
inputPage.execute("hello");
|
||||||
|
|
||||||
|
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||||
|
loginPage.login("bburke@redhat.com", "password");
|
||||||
|
System.out.println("Current url: " + driver.getCurrentUrl());
|
||||||
|
Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/input-portal/secured/post");
|
||||||
|
String pageSource = driver.getPageSource();
|
||||||
|
System.out.println(pageSource);
|
||||||
|
Assert.assertTrue(pageSource.contains("parameter=hello"));
|
||||||
|
|
||||||
|
// test logout
|
||||||
|
|
||||||
|
String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri(AUTH_SERVER_URL))
|
||||||
|
.queryParam(OAuth2Constants.REDIRECT_URI, APP_SERVER_BASE_URL + "/customer-portal").build("demo").toString();
|
||||||
|
driver.navigate().to(logoutUri);
|
||||||
|
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||||
|
driver.navigate().to(APP_SERVER_BASE_URL + "/product-portal");
|
||||||
|
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||||
|
driver.navigate().to(APP_SERVER_BASE_URL + "/customer-portal");
|
||||||
|
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoginSSOAndLogout() throws Exception {
|
public void testLoginSSOAndLogout() throws Exception {
|
||||||
// test login to customer-portal which does a bearer request to customer-db
|
// test login to customer-portal which does a bearer request to customer-db
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.keycloak.testsuite.adapter;
|
||||||
|
|
||||||
|
import org.keycloak.testsuite.pages.AbstractPage;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class InputPage extends AbstractPage {
|
||||||
|
@FindBy(id = "parameter")
|
||||||
|
private WebElement parameter;
|
||||||
|
|
||||||
|
@FindBy(name = "submit")
|
||||||
|
private WebElement submit;
|
||||||
|
|
||||||
|
public void execute(String param) {
|
||||||
|
parameter.clear();
|
||||||
|
parameter.sendKeys(param);
|
||||||
|
|
||||||
|
submit.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isCurrent() {
|
||||||
|
return driver.getTitle().equals("Input Page");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void open() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.keycloak.testsuite.adapter;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||||
|
*/
|
||||||
|
public class InputServlet extends HttpServlet {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
String appBase = System.getProperty("app.server.base.url", "http://localhost:8081");
|
||||||
|
String actionUrl = appBase + "/input-portal/secured/post";
|
||||||
|
|
||||||
|
|
||||||
|
resp.setContentType("text/html");
|
||||||
|
PrintWriter pw = resp.getWriter();
|
||||||
|
pw.printf("<html><head><title>%s</title></head><body>", "Input Page");
|
||||||
|
pw.printf("<form action=\"%s\" method=\"POST\">", actionUrl);
|
||||||
|
pw.println("<input id=\"parameter\" type=\"text\" name=\"parameter\">");
|
||||||
|
pw.println("<input name=\"submit\" type=\"submit\" value=\"Submit\"></form>");
|
||||||
|
pw.print("</body></html>");
|
||||||
|
pw.flush();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
resp.setContentType("text/plain");
|
||||||
|
PrintWriter pw = resp.getWriter();
|
||||||
|
pw.printf("parameter="+req.getParameter("parameter"));
|
||||||
|
pw.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
0
testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/SessionServlet.java
Normal file → Executable file
0
testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/SessionServlet.java
Normal file → Executable file
4
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractPage.java
Normal file → Executable file
4
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractPage.java
Normal file → Executable file
|
@ -39,8 +39,8 @@ public abstract class AbstractPage {
|
||||||
isCurrent());
|
isCurrent());
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract boolean isCurrent();
|
abstract public boolean isCurrent();
|
||||||
|
|
||||||
abstract void open() throws Exception;
|
abstract public void open() throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
2
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
Normal file → Executable file
2
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
Normal file → Executable file
|
@ -49,7 +49,7 @@ public class OAuthGrantPage extends AbstractPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void open() {
|
public void open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,10 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
|
||||||
|
|
||||||
public void deployApplication(String name, String contextPath, Class<? extends Servlet> servletClass, String adapterConfigPath, String role, boolean isConstrained, Class<? extends KeycloakConfigResolver> keycloakConfigResolver) {
|
public void deployApplication(String name, String contextPath, Class<? extends Servlet> servletClass, String adapterConfigPath, String role, boolean isConstrained, Class<? extends KeycloakConfigResolver> keycloakConfigResolver) {
|
||||||
String constraintUrl = "/*";
|
String constraintUrl = "/*";
|
||||||
|
deployApplication(name, contextPath, servletClass, adapterConfigPath, role, isConstrained, keycloakConfigResolver, constraintUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deployApplication(String name, String contextPath, Class<? extends Servlet> servletClass, String adapterConfigPath, String role, boolean isConstrained, Class<? extends KeycloakConfigResolver> keycloakConfigResolver, String constraintUrl) {
|
||||||
DeploymentInfo di = createDeploymentInfo(name, contextPath, servletClass);
|
DeploymentInfo di = createDeploymentInfo(name, contextPath, servletClass);
|
||||||
if (null == keycloakConfigResolver) {
|
if (null == keycloakConfigResolver) {
|
||||||
di.addInitParameter("keycloak.config.file", adapterConfigPath);
|
di.addInitParameter("keycloak.config.file", adapterConfigPath);
|
||||||
|
|
|
@ -125,6 +125,16 @@
|
||||||
"http://localhost:8081/session-portal/*"
|
"http://localhost:8081/session-portal/*"
|
||||||
],
|
],
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "input-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8081/input-portal",
|
||||||
|
"baseUrl": "http://localhost:8081/input-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8081/input-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oauthClients": [
|
"oauthClients": [
|
||||||
|
|
10
testsuite/integration/src/test/resources/adapter-test/input-keycloak.json
Executable file
10
testsuite/integration/src/test/resources/adapter-test/input-keycloak.json
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "input-portal",
|
||||||
|
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
|
"auth-server-url" : "http://${my.host.name}:8081/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"credentials" : {
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,6 +95,7 @@ public class Jetty9Test {
|
||||||
list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
|
list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
|
||||||
list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
|
list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
|
||||||
list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
|
list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
|
||||||
|
list.add(new WebAppContext(new File(base, "input-portal").toString(), "/input-portal"));
|
||||||
list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
|
list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +118,11 @@ public class Jetty9Test {
|
||||||
@Rule
|
@Rule
|
||||||
public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule, true);
|
public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule, true);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavedPostRequest() throws Exception {
|
||||||
|
testStrategy.testSavedPostRequest();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoginSSOAndLogout() throws Exception {
|
public void testLoginSSOAndLogout() throws Exception {
|
||||||
testStrategy.testLoginSSOAndLogout();
|
testStrategy.testLoginSSOAndLogout();
|
||||||
|
|
|
@ -125,6 +125,16 @@
|
||||||
"http://localhost:8082/session-portal/*"
|
"http://localhost:8082/session-portal/*"
|
||||||
],
|
],
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "input-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8082/input-portal",
|
||||||
|
"baseUrl": "http://localhost:8082/input-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8082/input-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oauthClients": [
|
"oauthClients": [
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Get name="securityHandler">
|
||||||
|
<Set name="authenticator">
|
||||||
|
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
|
||||||
|
<!--
|
||||||
|
<Set name="adapterConfig">
|
||||||
|
<New class="org.keycloak.representations.adapters.config.AdapterConfig">
|
||||||
|
<Set name="realm">tomcat</Set>
|
||||||
|
<Set name="resource">customer-portal</Set>
|
||||||
|
<Set name="authServerUrl">http://localhost:8081/auth</Set>
|
||||||
|
<Set name="sslRequired">external</Set>
|
||||||
|
<Set name="credentials">
|
||||||
|
<Map>
|
||||||
|
<Entry>
|
||||||
|
<Item>secret</Item>
|
||||||
|
<Item>password</Item>
|
||||||
|
</Entry>
|
||||||
|
</Map>
|
||||||
|
</Set>
|
||||||
|
<Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
-->
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
</Get>
|
||||||
|
</Configure>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "input-portal",
|
||||||
|
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
|
"auth-server-url" : "http://${my.host.name}:8081/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"credentials" : {
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
|
version="3.0">
|
||||||
|
|
||||||
|
<module-name>adapter-test</module-name>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Users</web-resource-name>
|
||||||
|
<url-pattern>/secured/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>demo</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
|
@ -95,6 +95,7 @@ public class Jetty9Test {
|
||||||
list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
|
list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
|
||||||
list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
|
list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
|
||||||
list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
|
list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
|
||||||
|
list.add(new WebAppContext(new File(base, "input-portal").toString(), "/input-portal"));
|
||||||
list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
|
list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +118,11 @@ public class Jetty9Test {
|
||||||
@Rule
|
@Rule
|
||||||
public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule, true);
|
public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule, true);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavedPostRequest() throws Exception {
|
||||||
|
testStrategy.testSavedPostRequest();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoginSSOAndLogout() throws Exception {
|
public void testLoginSSOAndLogout() throws Exception {
|
||||||
testStrategy.testLoginSSOAndLogout();
|
testStrategy.testLoginSSOAndLogout();
|
||||||
|
|
|
@ -125,6 +125,16 @@
|
||||||
"http://localhost:8082/session-portal/*"
|
"http://localhost:8082/session-portal/*"
|
||||||
],
|
],
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "input-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8082/input-portal",
|
||||||
|
"baseUrl": "http://localhost:8082/input-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8082/input-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oauthClients": [
|
"oauthClients": [
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Get name="securityHandler">
|
||||||
|
<Set name="authenticator">
|
||||||
|
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
|
||||||
|
<!--
|
||||||
|
<Set name="adapterConfig">
|
||||||
|
<New class="org.keycloak.representations.adapters.config.AdapterConfig">
|
||||||
|
<Set name="realm">tomcat</Set>
|
||||||
|
<Set name="resource">customer-portal</Set>
|
||||||
|
<Set name="authServerUrl">http://localhost:8081/auth</Set>
|
||||||
|
<Set name="sslRequired">external</Set>
|
||||||
|
<Set name="credentials">
|
||||||
|
<Map>
|
||||||
|
<Entry>
|
||||||
|
<Item>secret</Item>
|
||||||
|
<Item>password</Item>
|
||||||
|
</Entry>
|
||||||
|
</Map>
|
||||||
|
</Set>
|
||||||
|
<Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
-->
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
</Get>
|
||||||
|
</Configure>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "input-portal",
|
||||||
|
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
|
"auth-server-url" : "http://${my.host.name}:8081/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"credentials" : {
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
|
version="3.0">
|
||||||
|
|
||||||
|
<module-name>adapter-test</module-name>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Users</web-resource-name>
|
||||||
|
<url-pattern>/secured/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>demo</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
|
@ -95,6 +95,7 @@ public class Jetty9Test {
|
||||||
list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
|
list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
|
||||||
list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
|
list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
|
||||||
list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
|
list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
|
||||||
|
list.add(new WebAppContext(new File(base, "input-portal").toString(), "/input-portal"));
|
||||||
list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
|
list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,6 +123,11 @@ public class Jetty9Test {
|
||||||
testStrategy.testLoginSSOAndLogout();
|
testStrategy.testLoginSSOAndLogout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavedPostRequest() throws Exception {
|
||||||
|
testStrategy.testSavedPostRequest();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testServletRequestLogout() throws Exception {
|
public void testServletRequestLogout() throws Exception {
|
||||||
testStrategy.testServletRequestLogout();
|
testStrategy.testServletRequestLogout();
|
||||||
|
|
|
@ -125,6 +125,16 @@
|
||||||
"http://localhost:8082/session-portal/*"
|
"http://localhost:8082/session-portal/*"
|
||||||
],
|
],
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "input-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8082/input-portal",
|
||||||
|
"baseUrl": "http://localhost:8082/input-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8082/input-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oauthClients": [
|
"oauthClients": [
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Get name="securityHandler">
|
||||||
|
<Set name="authenticator">
|
||||||
|
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
|
||||||
|
<!--
|
||||||
|
<Set name="adapterConfig">
|
||||||
|
<New class="org.keycloak.representations.adapters.config.AdapterConfig">
|
||||||
|
<Set name="realm">tomcat</Set>
|
||||||
|
<Set name="resource">customer-portal</Set>
|
||||||
|
<Set name="authServerUrl">http://localhost:8081/auth</Set>
|
||||||
|
<Set name="sslRequired">external</Set>
|
||||||
|
<Set name="credentials">
|
||||||
|
<Map>
|
||||||
|
<Entry>
|
||||||
|
<Item>secret</Item>
|
||||||
|
<Item>password</Item>
|
||||||
|
</Entry>
|
||||||
|
</Map>
|
||||||
|
</Set>
|
||||||
|
<Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
-->
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
</Get>
|
||||||
|
</Configure>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "input-portal",
|
||||||
|
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
|
"auth-server-url" : "http://${my.host.name}:8081/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"credentials" : {
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
|
version="3.0">
|
||||||
|
|
||||||
|
<module-name>adapter-test</module-name>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Users</web-resource-name>
|
||||||
|
<url-pattern>/secured/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>demo</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
|
@ -83,6 +83,7 @@ public class TomcatTest {
|
||||||
tomcat.deploy("/product-portal", "product-portal");
|
tomcat.deploy("/product-portal", "product-portal");
|
||||||
tomcat.deploy("/secure-portal", "secure-portal");
|
tomcat.deploy("/secure-portal", "secure-portal");
|
||||||
tomcat.deploy("/session-portal", "session-portal");
|
tomcat.deploy("/session-portal", "session-portal");
|
||||||
|
tomcat.deploy("/input-portal", "input-portal");
|
||||||
|
|
||||||
|
|
||||||
tomcat.start();
|
tomcat.start();
|
||||||
|
@ -102,6 +103,11 @@ public class TomcatTest {
|
||||||
testStrategy.testLoginSSOAndLogout();
|
testStrategy.testLoginSSOAndLogout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavedPostRequest() throws Exception {
|
||||||
|
testStrategy.testSavedPostRequest();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testServletRequestLogout() throws Exception {
|
public void testServletRequestLogout() throws Exception {
|
||||||
// can't test this. Servlet 2.5 doesn't have logout()
|
// can't test this. Servlet 2.5 doesn't have logout()
|
||||||
|
|
|
@ -125,6 +125,16 @@
|
||||||
"http://localhost:8082/session-portal/*"
|
"http://localhost:8082/session-portal/*"
|
||||||
],
|
],
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "input-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8082/input-portal",
|
||||||
|
"baseUrl": "http://localhost:8082/input-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8082/input-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oauthClients": [
|
"oauthClients": [
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<Context path="/customer-portal">
|
||||||
|
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
|
||||||
|
</Context>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "input-portal",
|
||||||
|
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
|
"auth-server-url" : "http://${my.host.name}:8081/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"credentials" : {
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
|
version="3.0">
|
||||||
|
|
||||||
|
<module-name>adapter-test</module-name>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Users</web-resource-name>
|
||||||
|
<url-pattern>/secured/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>demo</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
|
@ -87,6 +87,7 @@ public class Tomcat7Test {
|
||||||
tomcat.addWebapp("/product-portal", new File(base, "product-portal").toString());
|
tomcat.addWebapp("/product-portal", new File(base, "product-portal").toString());
|
||||||
tomcat.addWebapp("/secure-portal", new File(base, "secure-portal").toString());
|
tomcat.addWebapp("/secure-portal", new File(base, "secure-portal").toString());
|
||||||
tomcat.addWebapp("/session-portal", new File(base, "session-portal").toString());
|
tomcat.addWebapp("/session-portal", new File(base, "session-portal").toString());
|
||||||
|
tomcat.addWebapp("/input-portal", new File(base, "input-portal").toString());
|
||||||
|
|
||||||
tomcat.start();
|
tomcat.start();
|
||||||
//tomcat.getServer().await();
|
//tomcat.getServer().await();
|
||||||
|
@ -106,6 +107,12 @@ public class Tomcat7Test {
|
||||||
testStrategy.testLoginSSOAndLogout();
|
testStrategy.testLoginSSOAndLogout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavedPostRequest() throws Exception {
|
||||||
|
testStrategy.testSavedPostRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testServletRequestLogout() throws Exception {
|
public void testServletRequestLogout() throws Exception {
|
||||||
testStrategy.testServletRequestLogout();
|
testStrategy.testServletRequestLogout();
|
||||||
|
|
|
@ -125,6 +125,16 @@
|
||||||
"http://localhost:8082/session-portal/*"
|
"http://localhost:8082/session-portal/*"
|
||||||
],
|
],
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "input-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8082/input-portal",
|
||||||
|
"baseUrl": "http://localhost:8082/input-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8082/input-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oauthClients": [
|
"oauthClients": [
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<Context path="/customer-portal">
|
||||||
|
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
|
||||||
|
</Context>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "input-portal",
|
||||||
|
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
|
"auth-server-url" : "http://${my.host.name}:8081/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"credentials" : {
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
|
version="3.0">
|
||||||
|
|
||||||
|
<module-name>adapter-test</module-name>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Users</web-resource-name>
|
||||||
|
<url-pattern>/secured/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>demo</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
|
@ -87,6 +87,7 @@ public class TomcatTest {
|
||||||
tomcat.addWebapp("/product-portal", new File(base, "product-portal").toString());
|
tomcat.addWebapp("/product-portal", new File(base, "product-portal").toString());
|
||||||
tomcat.addWebapp("/secure-portal", new File(base, "secure-portal").toString());
|
tomcat.addWebapp("/secure-portal", new File(base, "secure-portal").toString());
|
||||||
tomcat.addWebapp("/session-portal", new File(base, "session-portal").toString());
|
tomcat.addWebapp("/session-portal", new File(base, "session-portal").toString());
|
||||||
|
tomcat.addWebapp("/input-portal", new File(base, "input-portal").toString());
|
||||||
|
|
||||||
tomcat.start();
|
tomcat.start();
|
||||||
//tomcat.getServer().await();
|
//tomcat.getServer().await();
|
||||||
|
@ -106,6 +107,13 @@ public class TomcatTest {
|
||||||
testStrategy.testLoginSSOAndLogout();
|
testStrategy.testLoginSSOAndLogout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSavedPostRequest() throws Exception {
|
||||||
|
testStrategy.testSavedPostRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testServletRequestLogout() throws Exception {
|
public void testServletRequestLogout() throws Exception {
|
||||||
testStrategy.testServletRequestLogout();
|
testStrategy.testServletRequestLogout();
|
||||||
|
|
|
@ -125,6 +125,16 @@
|
||||||
"http://localhost:8082/session-portal/*"
|
"http://localhost:8082/session-portal/*"
|
||||||
],
|
],
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "input-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8082/input-portal",
|
||||||
|
"baseUrl": "http://localhost:8082/input-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8082/input-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oauthClients": [
|
"oauthClients": [
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<Context path="/customer-portal">
|
||||||
|
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
|
||||||
|
</Context>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "input-portal",
|
||||||
|
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
|
"auth-server-url" : "http://${my.host.name}:8081/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"credentials" : {
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
|
version="3.0">
|
||||||
|
|
||||||
|
<module-name>adapter-test</module-name>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>Servlet</servlet-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Users</web-resource-name>
|
||||||
|
<url-pattern>/secured/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>demo</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
Loading…
Reference in a new issue