Merge pull request #2296 from patriot1burke/1.8.x

unsecure URL has principal and context
This commit is contained in:
Bill Burke committed 2016-02-29 11:05:54 -05:00
commit 3158269375
10 files changed
+119 -4

No files matched your search

@@ -1,6 +1,8 @@
package org.keycloak.adapters.jetty.core;
import org.eclipse.jetty.security.DefaultUserIdentity;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
@@ -118,10 +120,43 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
return new DefaultUserIdentity(theSubject, principal, theRoles);
}
private class DummyLoginService implements LoginService {
@Override
public String getName() {
return null;
}
@Override
public UserIdentity login(String username, Object credentials) {
return null;
}
@Override
public boolean validate(UserIdentity user) {
return false;
}
@Override
public IdentityService getIdentityService() {
return null;
}
@Override
public void setIdentityService(IdentityService service) {
}
@Override
public void logout(UserIdentity user) {
}
}
@Override
public void setConfiguration(AuthConfiguration configuration) {
//super.setConfiguration(configuration);
initializeKeycloak();
// need this so that getUserPrincipal does not throw NPE
_loginService = new DummyLoginService();
String error = configuration.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE);
setErrorPage(error);
}
@@ -69,6 +69,7 @@ public class OIDCFilterSessionStore extends FilterSessionStore implements Adapte
protected void cleanSession(HttpSession session) {
session.removeAttribute(KeycloakAccount.class.getName());
session.removeAttribute(KeycloakSecurityContext.class.getName());
clearSavedRequest(session);
}
@@ -138,6 +139,7 @@ public class OIDCFilterSessionStore extends FilterSessionStore implements Adapte
SerializableKeycloakAccount sAccount = new SerializableKeycloakAccount(roles, account.getPrincipal(), securityContext);
HttpSession httpSession = request.getSession();
httpSession.setAttribute(KeycloakAccount.class.getName(), sAccount);
httpSession.setAttribute(KeycloakSecurityContext.class.getName(), sAccount.getKeycloakSecurityContext());
if (idMapper != null) idMapper.map(account.getKeycloakSecurityContext().getToken().getClientSession(), account.getPrincipal().getName(), httpSession.getId());
//String username = securityContext.getToken().getSubject();
//log.fine("userSessionManagement.login: " + username);
@@ -52,12 +52,22 @@ public class CatalinaSessionTokenStore extends CatalinaAdapterSessionStore imple
// just in case session got serialized
if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) {
request.setAttribute(KeycloakSecurityContext.class.getName(), session);
request.setUserPrincipal(account.getPrincipal());
request.setAuthType("KEYCLOAK");
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;
if (success && session.isActive()) {
request.setAttribute(KeycloakSecurityContext.class.getName(), session);
request.setUserPrincipal(account.getPrincipal());
request.setAuthType("KEYCLOAK");
return;
}
// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
log.fine("Cleanup and expire session " + catalinaSession.getId() + " after failed refresh");
@@ -68,6 +78,7 @@ public class CatalinaSessionTokenStore extends CatalinaAdapterSessionStore imple
}
protected void cleanSession(Session catalinaSession) {
catalinaSession.getSession().removeAttribute(KeycloakSecurityContext.class.getName());
catalinaSession.getSession().removeAttribute(OidcKeycloakAccount.class.getName());
catalinaSession.setPrincipal(null);
catalinaSession.setAuthType(null);
@@ -68,7 +68,8 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
return true;
} else {
log.debug("Refresh failed. Account was not active. Returning null and invalidating Http session");
session.setAttribute(KeycloakUndertowAccount.class.getName(), null);
session.removeAttribute(KeycloakSecurityContext.class.getName());
session.removeAttribute(KeycloakUndertowAccount.class.getName());
session.invalidate();
return false;
}
@@ -79,6 +80,7 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
HttpSession session = getSession(true);
session.setAttribute(KeycloakUndertowAccount.class.getName(), account);
session.setAttribute(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
sessionManagement.login(servletRequestContext.getDeployment().getSessionManager());
}
@@ -5,6 +5,7 @@ import io.undertow.server.HttpServerExchange;
import io.undertow.server.session.Session;
import io.undertow.util.Sessions;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OidcKeycloakAccount;
@@ -65,6 +66,7 @@ public class UndertowSessionTokenStore implements AdapterTokenStore {
} else {
log.debug("Account was not active, returning false");
session.removeAttribute(KeycloakUndertowAccount.class.getName());
session.removeAttribute(KeycloakSecurityContext.class.getName());
session.invalidate(exchange);
return false;
}
@@ -84,6 +86,7 @@ public class UndertowSessionTokenStore implements AdapterTokenStore {
public void saveAccountInfo(OidcKeycloakAccount account) {
Session session = Sessions.getOrCreateSession(exchange);
session.setAttribute(KeycloakUndertowAccount.class.getName(), account);
session.setAttribute(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
sessionManagement.login(session.getSessionManager());
}
@@ -1,6 +1,8 @@
package org.keycloak.adapters.saml.jetty;
import org.eclipse.jetty.security.DefaultUserIdentity;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
@@ -108,12 +110,45 @@ public abstract class AbstractSamlAuthenticator extends LoginAuthenticator {
}
private class DummyLoginService implements LoginService {
@Override
public String getName() {
return null;
}
@Override
public UserIdentity login(String username, Object credentials) {
return null;
}
@Override
public boolean validate(UserIdentity user) {
return false;
}
@Override
public IdentityService getIdentityService() {
return null;
}
@Override
public void setIdentityService(IdentityService service) {
}
@Override
public void logout(UserIdentity user) {
}
}
@Override
public void setConfiguration(AuthConfiguration configuration) {
//super.setConfiguration(configuration);
initializeKeycloak();
// need this so that getUserPrincipal does not throw NPE
_loginService = new DummyLoginService();
initializeKeycloak();
String error = configuration.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE);
setErrorPage(error);
}
@@ -151,6 +151,12 @@ public abstract class AbstractSamlAuthenticatorValve extends FormAuthenticator i
public void invoke(Request request, Response response) throws IOException, ServletException {
log.fine("*********************** SAML ************");
try {
CatalinaHttpFacade facade = new CatalinaHttpFacade(response, request);
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null) {
// sets request UserPrincipal if logged in. we do this so that the UserPrincipal is available on unsecured, unconstrainted URLs
getTokenStore(request, facade, deployment).isLoggedIn();
}
super.invoke(request, response);
} finally {
}
@@ -143,6 +143,12 @@ public class AdapterTestStrategy extends ExternalResource {
String pageSource = driver.getPageSource();
System.out.println(pageSource);
Assert.assertTrue(pageSource.contains("parameter=hello"));
// test that user principal and KeycloakSecurityContext available
driver.navigate().to(APP_SERVER_BASE_URL + "/input-portal/insecure");
System.out.println("insecure: ");
System.out.println(driver.getPageSource());
Assert.assertTrue(driver.getPageSource().contains("Insecure Page"));
if (System.getProperty("insecure.user.principal.unsupported") == null) Assert.assertTrue(driver.getPageSource().contains("UserPrincipal"));
// test logout
@@ -105,6 +105,7 @@ public class FilterAdapterTest {
@Test
public void testSavedPostRequest() throws Exception {
System.setProperty("insecure.user.principal.unsupported", "true");
testStrategy.testSavedPostRequest();
}
@@ -1,5 +1,8 @@
package org.keycloak.testsuite.adapter;
import org.junit.Assert;
import org.keycloak.KeycloakSecurityContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -19,6 +22,17 @@ public class InputServlet extends HttpServlet {
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";
if (req.getRequestURI().endsWith("insecure")) {
if (System.getProperty("insecure.user.principal.unsupported") == null) Assert.assertNotNull(req.getUserPrincipal());
if (System.getProperty("insecure.user.principal.unsupported") == null) Assert.assertNotNull(req.getAttribute(KeycloakSecurityContext.class.getName()));
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
pw.printf("<html><head><title>%s</title></head><body>", "Insecure Page");
if (req.getUserPrincipal() != null) pw.printf("UserPrincipal: " + req.getUserPrincipal().getName());
pw.print("</body></html>");
pw.flush();
return;
}
resp.setContentType("text/html");