saved requests

This commit is contained in:
Bill Burke 2014-12-17 22:29:18 -05:00
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
View file

@ -1,6 +1,8 @@
package org.keycloak.util;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.regex.Pattern;
/**
@ -23,4 +25,42 @@ public class UriUtils {
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;
}
}

View file

@ -38,4 +38,7 @@ public interface AdapterTokenStore {
* @param securityContext context where refresh was performed
*/
void refreshCallback(RefreshableKeycloakSecurityContext securityContext);
void saveRequest();
boolean restoreRequest();
}

View file

@ -19,11 +19,12 @@ import java.util.concurrent.atomic.AtomicLong;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class OAuthRequestAuthenticator {
public class OAuthRequestAuthenticator {
private static final Logger log = Logger.getLogger(OAuthRequestAuthenticator.class);
protected KeycloakDeployment deployment;
protected RequestAuthenticator reqAuthenticator;
protected int sslRedirectPort;
protected AdapterTokenStore tokenStore;
protected String tokenString;
protected String idTokenString;
protected IDToken idToken;
@ -33,11 +34,12 @@ public abstract class OAuthRequestAuthenticator {
protected String refreshToken;
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.facade = facade;
this.deployment = deployment;
this.sslRedirectPort = sslRedirectPort;
this.tokenStore = tokenStore;
}
public AuthChallenge getChallenge() {
@ -200,7 +202,7 @@ public abstract class OAuthRequestAuthenticator {
} else {
log.debug("redirecting to auth server");
challenge = loginRedirect();
saveRequest();
tokenStore.saveRequest();
return AuthOutcome.NOT_ATTEMPTED;
}
} 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) {
return new AuthChallenge() {
@Override

View file

@ -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);
}

View file

@ -1,94 +1,99 @@
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.KeycloakAccount;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator;
import javax.servlet.http.HttpSession;
/**
* Handle storage of token info in HTTP Session. Per-request object
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JettySessionTokenStore implements AdapterTokenStore {
private static final Logger log = Logger.getLogger(JettySessionTokenStore.class);
private Request request;
private KeycloakDeployment deployment;
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
this.request = request;
this.deployment = deployment;
}
@Override
public void checkCurrentToken() {
if (request.getSession(false) == null) return;
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) request.getSession().getAttribute(KeycloakSecurityContext.class.getName());
if (session == null) return;
// just in case session got serialized
if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
// FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
// not be updated
boolean success = session.refreshExpiredToken(false);
if (success && session.isActive()) return;
// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
request.getSession().removeAttribute(KeycloakSecurityContext.class.getName());
request.getSession().invalidate();
}
@Override
public boolean isCached(RequestAuthenticator authenticator) {
if (request.getSession(false) == null || request.getSession().getAttribute(KeycloakSecurityContext.class.getName()) == null)
return false;
log.debug("remote logged in already. Establish state from session");
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) request.getSession().getAttribute(KeycloakSecurityContext.class.getName());
if (!deployment.getRealm().equals(securityContext.getRealm())) {
log.debug("Account from cookie is from a different realm than for the request.");
return false;
}
securityContext.setCurrentRequestInfo(deployment, this);
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
AbstractJettyRequestAuthenticator jettyAuthenticator = (AbstractJettyRequestAuthenticator) authenticator;
KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = AdapterUtils.createPrincipal(deployment, securityContext);
jettyAuthenticator.principal = principal;
jettyAuthenticator.restoreRequest();
return true;
}
@Override
public void saveAccountInfo(KeycloakAccount account) {
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext)account.getKeycloakSecurityContext();
request.getSession().setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
}
@Override
public void logout() {
HttpSession session = request.getSession(false);
if (session != null) {
session.removeAttribute(KeycloakSecurityContext.class.getName());
}
}
@Override
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
// no-op
}
}
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.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.KeycloakAccount;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
/**
* Handle storage of token info in HTTP Session. Per-request object
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
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(AbstractJettySessionTokenStore.class);
private Request request;
protected KeycloakDeployment deployment;
public AbstractJettySessionTokenStore(Request request, KeycloakDeployment deployment) {
this.request = request;
this.deployment = deployment;
}
@Override
public void checkCurrentToken() {
if (request.getSession(false) == null) return;
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) request.getSession().getAttribute(KeycloakSecurityContext.class.getName());
if (session == null) return;
// just in case session got serialized
if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
// FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
// not be updated
boolean success = session.refreshExpiredToken(false);
if (success && session.isActive()) return;
// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
request.getSession().removeAttribute(KeycloakSecurityContext.class.getName());
request.getSession().invalidate();
}
@Override
public boolean isCached(RequestAuthenticator authenticator) {
if (request.getSession(false) == null || request.getSession().getAttribute(KeycloakSecurityContext.class.getName()) == null)
return false;
log.debug("remote logged in already. Establish state from session");
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) request.getSession().getAttribute(KeycloakSecurityContext.class.getName());
if (!deployment.getRealm().equals(securityContext.getRealm())) {
log.debug("Account from cookie is from a different realm than for the request.");
return false;
}
securityContext.setCurrentRequestInfo(deployment, this);
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
JettyRequestAuthenticator jettyAuthenticator = (JettyRequestAuthenticator) authenticator;
KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = AdapterUtils.createPrincipal(deployment, securityContext);
jettyAuthenticator.principal = principal;
restoreRequest();
return true;
}
@Override
public void saveAccountInfo(KeycloakAccount account) {
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) account.getKeycloakSecurityContext();
request.getSession().setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
}
@Override
public void logout() {
HttpSession session = request.getSession(false);
if (session != null) {
session.removeAttribute(KeycloakSecurityContext.class.getName());
}
}
@Override
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
// no-op
}
}

View file

@ -65,14 +65,14 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
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);
if (store != null) {
return store;
}
if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) {
store = new JettySessionTokenStore(request, resolvedDeployment);
store = createSessionTokenStore(request, resolvedDeployment);
} else {
store = new JettyCookieTokenStore(request, facade, resolvedDeployment);
}
@ -81,7 +81,9 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
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());
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc != null) {
@ -212,7 +214,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
nodesRegistrationManagement.tryRegister(deployment);
tokenStore.checkCurrentToken();
AbstractJettyRequestAuthenticator authenticator = createRequestAuthenticator(request, facade, deployment, tokenStore);
JettyRequestAuthenticator authenticator = createRequestAuthenticator(request, facade, deployment, tokenStore);
AuthOutcome outcome = authenticator.authenticate();
if (outcome == AuthOutcome.AUTHENTICATED) {
if (facade.isEnded()) {
@ -238,7 +240,10 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
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
public String getAuthMethod() {

View file

@ -99,4 +99,14 @@ public class JettyCookieTokenStore implements AdapterTokenStore {
CookieTokenStore.removeCookie(facade);
return null;
}
@Override
public void saveRequest() {
}
@Override
public boolean restoreRequest() {
return false;
}
}

View file

@ -2,6 +2,8 @@ package org.keycloak.adapters.jetty;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.MultivaluedHashMap;
import org.keycloak.util.UriUtils;
import javax.security.cert.X509Certificate;
import javax.servlet.http.HttpServletResponse;
@ -21,6 +23,7 @@ public class JettyHttpFacade implements HttpFacade {
protected HttpServletResponse response;
protected RequestFacade requestFacade = new RequestFacade();
protected ResponseFacade responseFacade = new ResponseFacade();
protected MultivaluedHashMap<String, String> queryParameters;
protected class RequestFacade implements Request {
@Override
@ -39,7 +42,10 @@ public class JettyHttpFacade implements HttpFacade {
@Override
public String getQueryParamValue(String paramName) {
return request.getParameter(paramName);
if (queryParameters == null) {
queryParameters = UriUtils.decodeQueryString(request.getQueryString());
}
return queryParameters.getFirst(paramName);
}
@Override

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}
}
}

View file

@ -21,8 +21,8 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
@Override
protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
return new JettySessionTokenStore(request, resolvedDeployment);
}
@Override

View file

@ -36,6 +36,20 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-core</artifactId>
<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>
<groupId>org.apache.httpcomponents</groupId>

View file

@ -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);
}
}

View file

@ -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);
}
}
}
}
}

View file

@ -21,8 +21,8 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
@Override
protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
return new JettySessionTokenStore(request, resolvedDeployment);
}
@Override

View file

@ -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);
}
}

View file

@ -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);
}
}
}
}
}

View file

@ -20,12 +20,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
}
@Override
protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
}
@Override
@Override
protected Request resolveRequest(ServletRequest req) {
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);
}
}

View file

@ -5,9 +5,12 @@ import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.authenticator.FormAuthenticator;
import org.apache.catalina.authenticator.SavedRequest;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.tomcat.util.buf.ByteChunk;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.adapters.AdapterDeploymentContext;
@ -24,12 +27,15 @@ import org.keycloak.enums.TokenStore;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.keycloak.adapters.KeycloakConfigResolver;
@ -180,7 +186,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
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();
if (outcome == AuthOutcome.AUTHENTICATED) {
if (facade.isEnded()) {
@ -225,7 +231,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
}
if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) {
store = new CatalinaSessionTokenStore(request, resolvedDeployment, userSessionManagement, createPrincipalFactory());
store = new CatalinaSessionTokenStore(request, resolvedDeployment, userSessionManagement, createPrincipalFactory(), this);
} else {
store = new CatalinaCookieTokenStore(request, facade, resolvedDeployment, createPrincipalFactory());
}

View file

@ -84,6 +84,16 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
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
*

View file

@ -2,7 +2,9 @@ package org.keycloak.adapters.tomcat;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.MultivaluedHashMap;
import org.keycloak.util.ServerCookie;
import org.keycloak.util.UriUtils;
import javax.security.cert.X509Certificate;
import javax.servlet.http.HttpServletResponse;
@ -22,6 +24,7 @@ public class CatalinaHttpFacade implements HttpFacade {
protected HttpServletResponse response;
protected RequestFacade requestFacade = new RequestFacade();
protected ResponseFacade responseFacade = new ResponseFacade();
protected MultivaluedHashMap<String, String> queryParameters;
protected class RequestFacade implements Request {
@Override
@ -40,7 +43,10 @@ public class CatalinaHttpFacade implements HttpFacade {
@Override
public String getQueryParamValue(String paramName) {
return request.getParameter(paramName);
if (queryParameters == null) {
queryParameters = UriUtils.decodeQueryString(request.getQueryString());
}
return queryParameters.getFirst(paramName);
}
@Override

View file

@ -27,36 +27,22 @@ import javax.servlet.http.HttpSession;
*/
public class CatalinaRequestAuthenticator extends RequestAuthenticator {
private static final Logger log = Logger.getLogger(""+CatalinaRequestAuthenticator.class);
protected AbstractKeycloakAuthenticatorValve valve;
protected Request request;
protected GenericPrincipalFactory principalFactory;
public CatalinaRequestAuthenticator(KeycloakDeployment deployment,
AbstractKeycloakAuthenticatorValve valve, AdapterTokenStore tokenStore,
AdapterTokenStore tokenStore,
CatalinaHttpFacade facade,
Request request,
GenericPrincipalFactory principalFactory) {
super(facade, deployment, tokenStore, request.getConnector().getRedirectPort());
this.valve = valve;
this.request = request;
this.principalFactory = principalFactory;
}
@Override
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort) {
@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);
}
}
};
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
}
@Override
@ -99,17 +85,6 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
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
protected String getHttpSessionId(boolean create) {
HttpSession session = request.getSession(create);

View file

@ -1,5 +1,6 @@
package org.keycloak.adapters.tomcat;
import java.io.IOException;
import java.util.Set;
import java.util.logging.Logger;
@ -24,12 +25,18 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
private KeycloakDeployment deployment;
private CatalinaUserSessionManagement sessionManagement;
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.deployment = deployment;
this.sessionManagement = sessionManagement;
this.principalFactory = principalFactory;
this.valve = valve;
}
@Override
@ -81,7 +88,7 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
request.setUserPrincipal(principal);
request.setAuthType("KEYCLOAK");
((CatalinaRequestAuthenticator)authenticator).restoreRequest();
restoreRequest();
return true;
}
@ -112,4 +119,18 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
// 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);
}
}

View file

@ -53,12 +53,7 @@ public abstract class AbstractUndertowRequestAuthenticator extends RequestAuthen
@Override
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort) {
@Override
protected void saveRequest() {
// todo
}
};
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
}
@Override

View file

@ -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);
}
}
}
}
}
}

View file

@ -19,12 +19,15 @@ package org.keycloak.adapters.undertow;
import io.undertow.security.api.SecurityContext;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.handlers.ServletRequestContext;
import io.undertow.servlet.util.SavedRequest;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OAuthRequestAuthenticator;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.enums.TokenStore;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@ -43,6 +46,11 @@ public class ServletRequestAuthenticator extends AbstractUndertowRequestAuthenti
super(facade, deployment, sslRedirectPort, securityContext, exchange, tokenStore);
}
@Override
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
}
@Override
protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
super.propagateKeycloakContext(account);

View file

@ -64,6 +64,7 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
log.debug("Cached account found");
securityContext.authenticationComplete(account, "KEYCLOAK", false);
((AbstractUndertowRequestAuthenticator)authenticator).propagateKeycloakContext(account);
restoreRequest();
return true;
} else {
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
}
@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) {
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();

View file

@ -80,4 +80,14 @@ public class UndertowCookieTokenStore implements AdapterTokenStore {
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
CookieTokenStore.setTokenCookie(deployment, facade, securityContext);
}
@Override
public void saveRequest() {
}
@Override
public boolean restoreRequest() {
return false;
}
}

View file

@ -70,6 +70,16 @@ public class UndertowSessionTokenStore implements AdapterTokenStore {
}
}
@Override
public void saveRequest() {
}
@Override
public boolean restoreRequest() {
return false;
}
@Override
public void saveAccountInfo(KeycloakAccount account) {
Session session = Sessions.getOrCreateSession(exchange);

View file

@ -74,7 +74,6 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class AdapterTest {
public static final String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri("http://localhost:8081/auth")).build("demo").toString();
public static PublicKey realmPublicKey;
@ClassRule
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
@ -99,6 +98,8 @@ public class AdapterTest {
System.setProperty("my.host.name", "localhost");
url = getClass().getResource("/adapter-test/session-keycloak.json");
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();
}
@Test
public void testSavedPostRequest() throws Exception {
testStrategy.testSavedPostRequest();
}
@Test
public void testServletRequestLogout() throws Exception {
testStrategy.testServletRequestLogout();

View file

@ -87,6 +87,9 @@ public class AdapterTestStrategy extends ExternalResource {
@WebResource
protected LoginPage loginPage;
@WebResource
protected InputPage inputPage;
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) {
@ -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
public void testLoginSSOAndLogout() throws Exception {
// test login to customer-portal which does a bearer request to customer-db

View file

@ -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() {
}
}

View file

@ -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();
}
}

View file

@ -1,38 +1,38 @@
package org.keycloak.testsuite.adapter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class SessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String counter = increaseAndGetCounter(req);
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
pw.printf("<html><head><title>%s</title></head><body>", "Session Test");
pw.printf("Counter=%s", counter);
pw.print("</body></html>");
pw.flush();
}
private String increaseAndGetCounter(HttpServletRequest req) {
HttpSession session = req.getSession();
Integer counter = (Integer)session.getAttribute("counter");
counter = (counter == null) ? 1 : counter + 1;
session.setAttribute("counter", counter);
return String.valueOf(counter);
}
}
package org.keycloak.testsuite.adapter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class SessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String counter = increaseAndGetCounter(req);
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
pw.printf("<html><head><title>%s</title></head><body>", "Session Test");
pw.printf("Counter=%s", counter);
pw.print("</body></html>");
pw.flush();
}
private String increaseAndGetCounter(HttpServletRequest req) {
HttpSession session = req.getSession();
Integer counter = (Integer)session.getAttribute("counter");
counter = (counter == null) ? 1 : counter + 1;
session.setAttribute("counter", counter);
return String.valueOf(counter);
}
}

View file

@ -39,8 +39,8 @@ public abstract class AbstractPage {
isCurrent());
}
abstract boolean isCurrent();
abstract public boolean isCurrent();
abstract void open() throws Exception;
abstract public void open() throws Exception;
}

View file

@ -49,7 +49,7 @@ public class OAuthGrantPage extends AbstractPage {
}
@Override
void open() {
public void open() {
}
}

View file

@ -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) {
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);
if (null == keycloakConfigResolver) {
di.addInitParameter("keycloak.config.file", adapterConfigPath);

View file

@ -125,6 +125,16 @@
"http://localhost:8081/session-portal/*"
],
"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": [

View 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"
}
}

View file

@ -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, "product-portal").toString(), "/product-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"));
@ -117,6 +118,11 @@ public class Jetty9Test {
@Rule
public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule, true);
@Test
public void testSavedPostRequest() throws Exception {
testStrategy.testSavedPostRequest();
}
@Test
public void testLoginSSOAndLogout() throws Exception {
testStrategy.testLoginSSOAndLogout();

View file

@ -125,6 +125,16 @@
"http://localhost:8082/session-portal/*"
],
"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": [

View file

@ -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>

View 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"
}
}

View file

@ -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>

View file

@ -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, "product-portal").toString(), "/product-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"));
@ -117,6 +118,11 @@ public class Jetty9Test {
@Rule
public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule, true);
@Test
public void testSavedPostRequest() throws Exception {
testStrategy.testSavedPostRequest();
}
@Test
public void testLoginSSOAndLogout() throws Exception {
testStrategy.testLoginSSOAndLogout();

View file

@ -125,6 +125,16 @@
"http://localhost:8082/session-portal/*"
],
"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": [

View file

@ -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>

View 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"
}
}

View file

@ -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>

View file

@ -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, "product-portal").toString(), "/product-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"));
@ -122,6 +123,11 @@ public class Jetty9Test {
testStrategy.testLoginSSOAndLogout();
}
@Test
public void testSavedPostRequest() throws Exception {
testStrategy.testSavedPostRequest();
}
@Test
public void testServletRequestLogout() throws Exception {
testStrategy.testServletRequestLogout();

View file

@ -125,6 +125,16 @@
"http://localhost:8082/session-portal/*"
],
"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": [

View file

@ -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>

View 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"
}
}

View file

@ -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>

View file

@ -83,6 +83,7 @@ public class TomcatTest {
tomcat.deploy("/product-portal", "product-portal");
tomcat.deploy("/secure-portal", "secure-portal");
tomcat.deploy("/session-portal", "session-portal");
tomcat.deploy("/input-portal", "input-portal");
tomcat.start();
@ -102,6 +103,11 @@ public class TomcatTest {
testStrategy.testLoginSSOAndLogout();
}
@Test
public void testSavedPostRequest() throws Exception {
testStrategy.testSavedPostRequest();
}
@Test
public void testServletRequestLogout() throws Exception {
// can't test this. Servlet 2.5 doesn't have logout()

View file

@ -125,6 +125,16 @@
"http://localhost:8082/session-portal/*"
],
"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": [

View file

@ -0,0 +1,3 @@
<Context path="/customer-portal">
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>

View 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"
}
}

View file

@ -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>

View file

@ -87,6 +87,7 @@ public class Tomcat7Test {
tomcat.addWebapp("/product-portal", new File(base, "product-portal").toString());
tomcat.addWebapp("/secure-portal", new File(base, "secure-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.getServer().await();
@ -106,6 +107,12 @@ public class Tomcat7Test {
testStrategy.testLoginSSOAndLogout();
}
@Test
public void testSavedPostRequest() throws Exception {
testStrategy.testSavedPostRequest();
}
@Test
public void testServletRequestLogout() throws Exception {
testStrategy.testServletRequestLogout();

View file

@ -125,6 +125,16 @@
"http://localhost:8082/session-portal/*"
],
"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": [

View file

@ -0,0 +1,3 @@
<Context path="/customer-portal">
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>

View 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"
}
}

View file

@ -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>

View file

@ -87,6 +87,7 @@ public class TomcatTest {
tomcat.addWebapp("/product-portal", new File(base, "product-portal").toString());
tomcat.addWebapp("/secure-portal", new File(base, "secure-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.getServer().await();
@ -106,6 +107,13 @@ public class TomcatTest {
testStrategy.testLoginSSOAndLogout();
}
@Test
public void testSavedPostRequest() throws Exception {
testStrategy.testSavedPostRequest();
}
@Test
public void testServletRequestLogout() throws Exception {
testStrategy.testServletRequestLogout();

View file

@ -125,6 +125,16 @@
"http://localhost:8082/session-portal/*"
],
"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": [

View file

@ -0,0 +1,3 @@
<Context path="/customer-portal">
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>

View 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"
}
}

View file

@ -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>