Merge remote-tracking branch 'upstream/master'

This commit is contained in:
gerbermichi 2014-12-18 07:54:23 +01:00
commit 1cbf5a5d99
93 changed files with 1583 additions and 484 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

@ -1,17 +1,15 @@
<section id="javascript-adapter">
<title>Pure Client Javascript Adapter</title>
<title>Javascript Adapter</title>
<para>
The Keycloak Server comes with a Javascript library you can use to secure pure HTML/Javascript applications. This
The Keycloak Server comes with a Javascript library you can use to secure HTML/Javascript applications. This
library is referencable directly from the keycloak server. You can also download the adapter from Keycloak's download
site if you want a static copy of this library. It
works in the same way as other application adapters except that your browser is driving the OAuth redirect protocol
rather than the server.
</para>
<para>
The
disadvantage of using this approach is that you end up having a non-confidential, public client. This can be mitigated
by registering valid redirect URLs. You are still vulnerable if somebody hijacks the IP/DNS name of your pure
HTML/Javascript application though.
The disadvantage of using this approach is that you have a non-confidential, public client. This makes it more
important that you register valid redirect URLs and make sure your domain name is secured.
</para>
<para>
To use this adapter, you must first configure an application (or client) through the <literal>Keycloak Admin Console</literal>.
@ -138,6 +136,21 @@ keycloak.updateToken(30).success(function() {
</para>
</section>
<section>
<title>Older browsers</title>
<para>
The JavaScript adapter depends on Base64 (window.btoa and window.atob) and HTML5 History API. If you need to
support browsers that don't provide those (for example IE9) you'll need to add polyfillers. Example polyfill
libraries:
<itemizedlist>
<listitem>Base64 - <ulink url="https://github.com/davidchambers/Base64.js">https://github.com/davidchambers/Base64.js</ulink></listitem>
<listitem>HTML5 History - <ulink url="https://github.com/devote/HTML5-History-API">https://github.com/devote/HTML5-History-API</ulink></listitem>
</itemizedlist>
</para>
</section>
<section>
<title>JavaScript Adapter reference</title>

View file

@ -16,7 +16,7 @@ public class EventEntity {
@Column(name="ID", length = 36)
private String id;
@Column(name="TIME")
@Column(name="\"TIME\"")
private long time;
@Column(name="TYPE")

View file

@ -71,7 +71,6 @@
<span>Loading...</span>
</div>
</div>
<div id="serverInfo">Keycloak {{serverInfo.version}}</div>
</div>
</body>

View file

@ -863,6 +863,9 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'RealmBruteForceCtrl'
})
.when('/server-info', {
templateUrl : 'partials/server-info.html'
})
.when('/logout', {
templateUrl : 'partials/home.html',
controller : 'LogoutCtrl'

View file

@ -6,6 +6,9 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, WhoAmI, Current, $
$scope.authUrl = authUrl;
$scope.auth = Auth;
$scope.serverInfo = ServerInfo.get();
$scope.serverInfoUpdate = function() {
$scope.serverInfo = ServerInfo.get();
};
WhoAmI.get(function (data) {
Auth.user = data;

View file

@ -20,6 +20,7 @@
</a>
<ul class="dropdown-menu">
<li><a href="{{authUrl}}/realms/{{auth.user.realm}}/account?referrer=security-admin-console">Manage Account</a></li>
<li><a href="#/server-info">Server Info</a></li>
<li class="separator"><a href="" ng-click="auth.authz.logout()">Sign Out</a></li>
</ul>
</li>

View file

@ -0,0 +1,30 @@
<div id="content-area" class="col-sm-12" role="main">
<h2></h2>
<div id="content">
<h2>Server Info</h2>
<table class="table table-striped table-bordered">
<tr>
<td>Version</td>
<td>{{serverInfo.version}}</td>
</tr>
<tr>
<td>Server Time</td>
<td>{{serverInfo.serverTime}} (<a data-ng-click="serverInfoUpdate()">update</a>)</td>
</tr>
<tr>
<td>Providers</td>
<td>
<table class="table table-striped table-bordered">
<tr data-ng-repeat="(spi, providers) in serverInfo.providers">
<td>{{spi}}</td>
<td>{{providers.sort().join(', ')}}</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</div>

View file

@ -837,10 +837,3 @@ legend .kc-icon-collapse {
table table {
margin-bottom: 0 !important;
}
#serverInfo {
color: #666;
position: absolute;
bottom: 10px;
right: 10px;
}

View file

@ -33,3 +33,8 @@
border-top-color: rgba(255,255,255,0.05);
}
.dropdown-menu .separator {
border-top: 1px solid #ddd;
margin-top: 5px;
padding-top: 5px;
}

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,6 +1,8 @@
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;
@ -10,6 +12,7 @@ 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;
@ -18,14 +21,15 @@ import javax.servlet.http.HttpSession;
*
* @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 KeycloakDeployment deployment;
protected KeycloakDeployment deployment;
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
public AbstractJettySessionTokenStore(Request request, KeycloakDeployment deployment) {
this.request = request;
this.deployment = deployment;
}
@ -66,16 +70,16 @@ public class JettySessionTokenStore implements AdapterTokenStore {
securityContext.setCurrentRequestInfo(deployment, this);
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
AbstractJettyRequestAuthenticator jettyAuthenticator = (AbstractJettyRequestAuthenticator) authenticator;
JettyRequestAuthenticator jettyAuthenticator = (JettyRequestAuthenticator) authenticator;
KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = AdapterUtils.createPrincipal(deployment, securityContext);
jettyAuthenticator.principal = principal;
jettyAuthenticator.restoreRequest();
restoreRequest();
return true;
}
@Override
public void saveAccountInfo(KeycloakAccount account) {
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext)account.getKeycloakSecurityContext();
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) account.getKeycloakSecurityContext();
request.getSession().setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
}
@ -91,4 +95,5 @@ public class JettySessionTokenStore implements AdapterTokenStore {
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,11 +20,6 @@ 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
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

@ -619,7 +619,7 @@
if (event.origin !== loginIframe.iframeOrigin) {
return;
}
var data = event.data;
var data = JSON.parse(event.data);
var promise = loginIframe.callbackMap[data.callbackId];
delete loginIframe.callbackMap[data.callbackId];
@ -652,7 +652,7 @@
msg.callbackId = createCallbackId();
loginIframe.callbackMap[msg.callbackId] = promise;
var origin = loginIframe.iframeOrigin;
loginIframe.iframe.contentWindow.postMessage(msg, origin);
loginIframe.iframe.contentWindow.postMessage(JSON.stringify(msg), origin);
} else {
promise.setSuccess();
}

View file

@ -17,14 +17,15 @@
return;
}
event.data.loggedIn = false;
var data = JSON.parse(event.data);
data.loggedIn = false;
var cookie = getCookie('KEYCLOAK_SESSION');
if (cookie) {
event.data.loggedIn = true;
event.data.session = cookie;
data.loggedIn = true;
data.session = cookie;
}
event.source.postMessage(event.data,
event.source.postMessage(JSON.stringify(data),
event.origin);
}
window.addEventListener("message", receiveMessage, false);

View file

@ -40,7 +40,7 @@ public class CredentialAddHandler extends AbstractAddStepHandlerWithAttributes {
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
ckService.addCredential(operation, model);
ckService.addCredential(operation, context.resolveExpressions(model));
}
}

View file

@ -61,6 +61,6 @@ public final class RealmAddHandler extends AbstractAddStepHandler {
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
ckService.addRealm(operation, model);
ckService.addRealm(operation, context.resolveExpressions(model));
}
}

View file

@ -56,6 +56,6 @@ public final class SecureDeploymentAddHandler extends AbstractAddStepHandler {
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
ckService.addSecureDeployment(operation, model);
ckService.addSecureDeployment(operation, context.resolveExpressions(model));
}
}

View file

@ -41,7 +41,7 @@ public class CredentialAddHandler extends AbstractAddStepHandler {
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
ckService.addCredential(operation, model);
ckService.addCredential(operation, context.resolveExpressions(model));
}
}

View file

@ -35,15 +35,15 @@ import java.util.List;
/**
* Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension.
* This is used for AS7/EAP6.
* This is used for EAP6.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
// Note: Even though this class closely resembles the WildFly KeycloakAdapterConfigDeploymentProcessor
// it can not be easily refactored because the WarMetaData classes are of different types.
public class KeycloakAdapterConfigDeploymentProcessorAS7 implements DeploymentUnitProcessor {
protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessorAS7.class);
public class KeycloakAdapterConfigDeploymentProcessorEAP6 implements DeploymentUnitProcessor {
protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessorEAP6.class);
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {

View file

@ -23,11 +23,11 @@ import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoader;
/**
* Adds platform-specific modules for AS7
* Adds platform-specific modules for EAP6
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class KeycloakDependencyProcessorAS7 extends KeycloakDependencyProcessor {
public class KeycloakDependencyProcessorEAP6 extends KeycloakDependencyProcessor {
private static final ModuleIdentifier KEYCLOAK_AS7_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-as7-adapter");

View file

@ -70,7 +70,7 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
if (Environment.isWildFly()) {
return new KeycloakDependencyProcessorWildFly();
} else {
return new KeycloakDependencyProcessorAS7();
return new KeycloakDependencyProcessorEAP6();
}
}
@ -78,7 +78,7 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
if (Environment.isWildFly()) {
return new KeycloakAdapterConfigDeploymentProcessor();
} else {
return new KeycloakAdapterConfigDeploymentProcessorAS7();
return new KeycloakAdapterConfigDeploymentProcessorEAP6();
}
}

View file

@ -61,6 +61,6 @@ public final class RealmAddHandler extends AbstractAddStepHandler {
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
ckService.addRealm(operation, model);
ckService.addRealm(operation, context.resolveExpressions(model));
}
}

View file

@ -56,6 +56,6 @@ public final class SecureDeploymentAddHandler extends AbstractAddStepHandler {
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
ckService.addSecureDeployment(operation, model);
ckService.addSecureDeployment(operation, context.resolveExpressions(model));
}
}

View file

@ -51,6 +51,7 @@ public final class AuthServerAddHandler extends AbstractAddStepHandler {
for (AttributeDefinition attr : AuthServerDefinition.ALL_ATTRIBUTES) {
attr.validateAndSet(operation, model);
}
model = context.resolveExpressions(model);
// returns early if on domain controller
if (!requiresRuntime(context)) return;

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

@ -56,6 +56,7 @@ import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.OAuthRedirect;
@ -624,6 +625,14 @@ public class AccountService {
return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);
}
List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
for (UserSessionModel s : sessions) {
if (!s.getId().equals(auth.getSession().getId())) {
new ResourceAdminManager().logoutSession(uriInfo.getRequestUri(), realm, s);
session.sessions().removeUserSession(realm, s);
}
}
event.event(EventType.UPDATE_PASSWORD).client(auth.getClient()).user(auth.getUser()).success();
setReferrerOnPage();

View file

@ -9,17 +9,21 @@ import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.LoginProtocolFactory;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.social.SocialProvider;
import org.keycloak.util.ProviderLoader;
import javax.ws.rs.GET;
import javax.ws.rs.core.Context;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
/**
@ -38,15 +42,25 @@ public class ServerInfoAdminResource {
@GET
public ServerInfoRepresentation getInfo() {
ServerInfoRepresentation info = new ServerInfoRepresentation();
info.setVersion(Version.VERSION);
info.version = Version.VERSION;
info.serverTime = new Date().toString();
setSocialProviders(info);
setThemes(info);
setEventListeners(info);
setProtocols(info);
setApplicationImporters(info);
setProviders(info);
return info;
}
private void setProviders(ServerInfoRepresentation info) {
Map<String, Set<String>> providers = new HashMap<String, Set<String>>();
for (Spi spi : ServiceLoader.load(Spi.class)) {
providers.put(spi.getName(), session.listProviderIds(spi.getProviderClass()));
}
info.providers = providers;
}
private void setThemes(ServerInfoRepresentation info) {
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
info.themes = new HashMap<String, List<String>>();
@ -100,24 +114,27 @@ public class ServerInfoAdminResource {
private String version;
private String serverTime;
private Map<String, List<String>> themes;
private List<String> socialProviders;
private List<String> protocols;
private List<Map<String, String>> applicationImporters;
private Map<String, Set<String>> providers;
private List<String> eventListeners;
public ServerInfoRepresentation() {
}
public String getVersion() {
return version;
public String getServerTime() {
return serverTime;
}
public void setVersion(String version) {
this.version = version;
public String getVersion() {
return version;
}
public Map<String, List<String>> getThemes() {
@ -139,6 +156,10 @@ public class ServerInfoAdminResource {
public List<Map<String, String>> getApplicationImporters() {
return applicationImporters;
}
public Map<String, Set<String>> getProviders() {
return providers;
}
}
}

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

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