commit
b53707a129
6 changed files with 122 additions and 25 deletions
|
@ -10,13 +10,13 @@ import java.util.Collection;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class RoleAuthHandler implements HttpHandler {
|
public class ConstraintAuthorizationHandler implements HttpHandler {
|
||||||
|
|
||||||
protected Collection<String> roles;
|
|
||||||
protected HttpHandler next;
|
protected HttpHandler next;
|
||||||
|
protected String errorPage;
|
||||||
|
|
||||||
public RoleAuthHandler(Collection<String> roles, HttpHandler next) {
|
public ConstraintAuthorizationHandler(String errorPage, HttpHandler next) {
|
||||||
this.roles = roles;
|
this.errorPage = errorPage;
|
||||||
this.next = next;
|
this.next = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,14 @@ public class RoleAuthHandler implements HttpHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (errorPage != null) {
|
||||||
|
exchange.setRequestPath(errorPage);
|
||||||
|
exchange.setRelativePath(errorPage);
|
||||||
|
exchange.setResolvedPath(errorPage);
|
||||||
|
next.handleRequest(exchange);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
exchange.setResponseCode(403);
|
exchange.setResponseCode(403);
|
||||||
exchange.endExchange();
|
exchange.endExchange();
|
||||||
|
|
|
@ -15,11 +15,13 @@ public class ConstraintMatcherHandler implements HttpHandler {
|
||||||
protected SecurityPathMatches matcher;
|
protected SecurityPathMatches matcher;
|
||||||
protected HttpHandler securedHandler;
|
protected HttpHandler securedHandler;
|
||||||
protected HttpHandler unsecuredHandler;
|
protected HttpHandler unsecuredHandler;
|
||||||
|
protected String errorPage;
|
||||||
|
|
||||||
public ConstraintMatcherHandler(SecurityPathMatches matcher, HttpHandler securedHandler, HttpHandler unsecuredHandler) {
|
public ConstraintMatcherHandler(SecurityPathMatches matcher, HttpHandler securedHandler, HttpHandler unsecuredHandler, String errorPage) {
|
||||||
this.matcher = matcher;
|
this.matcher = matcher;
|
||||||
this.securedHandler = securedHandler;
|
this.securedHandler = securedHandler;
|
||||||
this.unsecuredHandler = unsecuredHandler;
|
this.unsecuredHandler = unsecuredHandler;
|
||||||
|
this.errorPage = errorPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,9 +33,17 @@ public class ConstraintMatcherHandler implements HttpHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match.getRequiredRoles().isEmpty() && match.getEmptyRoleSemantic() == SecurityInfo.EmptyRoleSemantic.DENY) {
|
if (match.getRequiredRoles().isEmpty() && match.getEmptyRoleSemantic() == SecurityInfo.EmptyRoleSemantic.DENY) {
|
||||||
|
if (errorPage != null) {
|
||||||
|
exchange.setRequestPath(errorPage);
|
||||||
|
exchange.setRelativePath(errorPage);
|
||||||
|
exchange.setResolvedPath(errorPage);
|
||||||
|
unsecuredHandler.handleRequest(exchange);
|
||||||
|
} else {
|
||||||
exchange.setResponseCode(403);
|
exchange.setResponseCode(403);
|
||||||
exchange.endExchange();
|
exchange.endExchange();
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
exchange.getSecurityContext().setAuthenticationRequired();
|
exchange.getSecurityContext().setAuthenticationRequired();
|
||||||
exchange.putAttachment(CONSTRAINT_KEY, match);
|
exchange.putAttachment(CONSTRAINT_KEY, match);
|
||||||
securedHandler.handleRequest(exchange);
|
securedHandler.handleRequest(exchange);
|
||||||
|
|
|
@ -4,7 +4,6 @@ import io.undertow.Undertow;
|
||||||
import io.undertow.security.api.AuthenticationMechanism;
|
import io.undertow.security.api.AuthenticationMechanism;
|
||||||
import io.undertow.security.api.AuthenticationMode;
|
import io.undertow.security.api.AuthenticationMode;
|
||||||
import io.undertow.security.handlers.AuthenticationCallHandler;
|
import io.undertow.security.handlers.AuthenticationCallHandler;
|
||||||
import io.undertow.security.handlers.AuthenticationConstraintHandler;
|
|
||||||
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
|
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
|
||||||
import io.undertow.security.handlers.SecurityInitialHandler;
|
import io.undertow.security.handlers.SecurityInitialHandler;
|
||||||
import io.undertow.security.idm.Account;
|
import io.undertow.security.idm.Account;
|
||||||
|
@ -72,7 +71,7 @@ public class ProxyServerBuilder {
|
||||||
proxyHandler = new HttpHandler() {
|
proxyHandler = new HttpHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||||
exchange.setRelativePath(exchange.getResolvedPath()); // need this otherwise proxy forwards to chopped off path
|
exchange.setRelativePath(exchange.getRequestPath()); // need this otherwise proxy forwards to chopped off path
|
||||||
handler.handleRequest(exchange);
|
handler.handleRequest(exchange);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -93,12 +92,21 @@ public class ProxyServerBuilder {
|
||||||
protected String base;
|
protected String base;
|
||||||
protected SecurityPathMatches.Builder constraintBuilder = new SecurityPathMatches.Builder();
|
protected SecurityPathMatches.Builder constraintBuilder = new SecurityPathMatches.Builder();
|
||||||
protected SecurityPathMatches matches;
|
protected SecurityPathMatches matches;
|
||||||
|
protected String errorPage;
|
||||||
|
|
||||||
public ApplicationBuilder base(String base) {
|
public ApplicationBuilder base(String base) {
|
||||||
this.base = base;
|
this.base = base;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ApplicationBuilder errorPage(String errorPage) {
|
||||||
|
if (errorPage != null && errorPage.startsWith("/")) {
|
||||||
|
errorPage = errorPage.substring(1);
|
||||||
|
}
|
||||||
|
this.errorPage = errorPage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ApplicationBuilder(AdapterConfig config) {
|
public ApplicationBuilder(AdapterConfig config) {
|
||||||
this.deployment = KeycloakDeploymentBuilder.build(config);
|
this.deployment = KeycloakDeploymentBuilder.build(config);
|
||||||
this.deploymentContext = new AdapterDeploymentContext(deployment);
|
this.deploymentContext = new AdapterDeploymentContext(deployment);
|
||||||
|
@ -172,8 +180,16 @@ public class ProxyServerBuilder {
|
||||||
private HttpHandler addSecurity(final HttpHandler toWrap) {
|
private HttpHandler addSecurity(final HttpHandler toWrap) {
|
||||||
HttpHandler handler = toWrap;
|
HttpHandler handler = toWrap;
|
||||||
handler = new UndertowAuthenticatedActionsHandler(deploymentContext, toWrap);
|
handler = new UndertowAuthenticatedActionsHandler(deploymentContext, toWrap);
|
||||||
|
if (errorPage != null) {
|
||||||
|
if (base.endsWith("/")) {
|
||||||
|
errorPage = base + errorPage;
|
||||||
|
} else {
|
||||||
|
errorPage = base + "/" + errorPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler = new ConstraintAuthorizationHandler(errorPage, handler);
|
||||||
handler = new AuthenticationCallHandler(handler);
|
handler = new AuthenticationCallHandler(handler);
|
||||||
handler = new ConstraintMatcherHandler(matches, handler, toWrap);
|
handler = new ConstraintMatcherHandler(matches, handler, toWrap, errorPage);
|
||||||
final List<AuthenticationMechanism> mechanisms = new LinkedList<AuthenticationMechanism>();
|
final List<AuthenticationMechanism> mechanisms = new LinkedList<AuthenticationMechanism>();
|
||||||
mechanisms.add(new CachedAuthenticatedSessionMechanism());
|
mechanisms.add(new CachedAuthenticatedSessionMechanism());
|
||||||
mechanisms.add(new UndertowAuthenticationMechanism(deploymentContext, userSessionManagement, nodesRegistrationManagement, -1));
|
mechanisms.add(new UndertowAuthenticationMechanism(deploymentContext, userSessionManagement, nodesRegistrationManagement, -1));
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class ProxyTest {
|
||||||
Integer count = (Integer)req.getSession().getAttribute("counter");
|
Integer count = (Integer)req.getSession().getAttribute("counter");
|
||||||
if (count == null) count = new Integer(0);
|
if (count == null) count = new Integer(0);
|
||||||
req.getSession().setAttribute("counter", new Integer(count.intValue() + 1));
|
req.getSession().setAttribute("counter", new Integer(count.intValue() + 1));
|
||||||
stream.write(count.toString().getBytes());
|
stream.write(("count:"+count).getBytes());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,6 +108,18 @@ public class ProxyTest {
|
||||||
doGet(req, resp);
|
doGet(req, resp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static class SendError extends HttpServlet {
|
||||||
|
@Override
|
||||||
|
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
resp.setContentType("text/plain");
|
||||||
|
OutputStream stream = resp.getOutputStream();
|
||||||
|
stream.write("access error".getBytes());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
doGet(req, resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Tomcat tomcat = null;
|
static Tomcat tomcat = null;
|
||||||
|
|
||||||
|
@ -133,7 +145,7 @@ public class ProxyTest {
|
||||||
|
|
||||||
static Undertow proxyServer = null;
|
static Undertow proxyServer = null;
|
||||||
|
|
||||||
//@BeforeClass
|
@BeforeClass
|
||||||
public static void initProxy() throws Exception {
|
public static void initProxy() throws Exception {
|
||||||
initTomcat();
|
initTomcat();
|
||||||
ProxyServerBuilder builder = new ProxyServerBuilder().addHttpListener(8080, "localhost");
|
ProxyServerBuilder builder = new ProxyServerBuilder().addHttpListener(8080, "localhost");
|
||||||
|
@ -143,7 +155,12 @@ public class ProxyTest {
|
||||||
builder.target("http://localhost:8082")
|
builder.target("http://localhost:8082")
|
||||||
.application(config)
|
.application(config)
|
||||||
.base("/customer-portal")
|
.base("/customer-portal")
|
||||||
.constraint("/*").add().add();
|
.errorPage("/error.html")
|
||||||
|
.constraint("/users/*").role("user").add()
|
||||||
|
.constraint("/admins/*").role("admin").add()
|
||||||
|
.constraint("/users/permit").permit().add()
|
||||||
|
.constraint("/users/deny").deny().add()
|
||||||
|
.add();
|
||||||
proxyServer = builder.build();
|
proxyServer = builder.build();
|
||||||
proxyServer.start();
|
proxyServer.start();
|
||||||
|
|
||||||
|
@ -167,34 +184,57 @@ public class ProxyTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoginSSOAndLogout() throws Exception {
|
public void testLoginSSOAndLogout() throws Exception {
|
||||||
initProxy();
|
driver.navigate().to("http://localhost:8080/customer-portal/users");
|
||||||
driver.navigate().to("http://localhost:8080/customer-portal");
|
|
||||||
System.out.println("Current url: " + driver.getCurrentUrl());
|
System.out.println("Current url: " + driver.getCurrentUrl());
|
||||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||||
loginPage.login("bburke@redhat.com", "password");
|
loginPage.login("bburke@redhat.com", "password");
|
||||||
System.out.println("Current url: " + driver.getCurrentUrl());
|
System.out.println("Current url: " + driver.getCurrentUrl());
|
||||||
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal");
|
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal/users");
|
||||||
String pageSource = driver.getPageSource();
|
String pageSource = driver.getPageSource();
|
||||||
System.out.println(pageSource);
|
System.out.println(pageSource);
|
||||||
Assert.assertTrue(pageSource.contains("customer-portal"));
|
Assert.assertTrue(pageSource.contains("customer-portal/users"));
|
||||||
Assert.assertTrue(pageSource.contains("0"));
|
Assert.assertTrue(pageSource.contains("count:0"));
|
||||||
driver.navigate().to("http://localhost:8080/customer-portal");
|
driver.navigate().to("http://localhost:8080/customer-portal/users");
|
||||||
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal");
|
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal/users");
|
||||||
pageSource = driver.getPageSource();
|
pageSource = driver.getPageSource();
|
||||||
System.out.println(pageSource);
|
System.out.println(pageSource);
|
||||||
Assert.assertTrue(pageSource.contains("customer-portal"));
|
Assert.assertTrue(pageSource.contains("customer-portal/users"));
|
||||||
Assert.assertTrue(pageSource.contains("1")); // test http session
|
Assert.assertTrue(pageSource.contains("count:1")); // test http session
|
||||||
|
|
||||||
|
driver.navigate().to("http://localhost:8080/customer-portal/users/deny");
|
||||||
|
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal/users/deny");
|
||||||
|
pageSource = driver.getPageSource();
|
||||||
|
System.out.println(pageSource);
|
||||||
|
Assert.assertTrue(pageSource.contains("access error"));
|
||||||
|
|
||||||
|
driver.navigate().to("http://localhost:8080/customer-portal/admins");
|
||||||
|
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal/admins");
|
||||||
|
pageSource = driver.getPageSource();
|
||||||
|
System.out.println(pageSource);
|
||||||
|
Assert.assertTrue(pageSource.contains("access error"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// test logout
|
// test logout
|
||||||
|
|
||||||
String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth"))
|
String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth"))
|
||||||
.queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8080/customer-portal").build("demo").toString();
|
.queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8080/customer-portal/users").build("demo").toString();
|
||||||
driver.navigate().to(logoutUri);
|
driver.navigate().to(logoutUri);
|
||||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||||
driver.navigate().to("http://localhost:8080/customer-portal");
|
driver.navigate().to("http://localhost:8080/customer-portal/users");
|
||||||
String currentUrl = driver.getCurrentUrl();
|
String currentUrl = driver.getCurrentUrl();
|
||||||
Assert.assertTrue(currentUrl.startsWith(LOGIN_URL));
|
Assert.assertTrue(currentUrl.startsWith(LOGIN_URL));
|
||||||
|
|
||||||
|
// test unsecured page
|
||||||
|
driver.navigate().to("http://localhost:8080/customer-portal") ;
|
||||||
|
pageSource = driver.getPageSource();
|
||||||
|
System.out.println(pageSource);
|
||||||
|
Assert.assertTrue(pageSource.contains("customer-portal"));
|
||||||
|
driver.navigate().to("http://localhost:8080/customer-portal/users/permit") ;
|
||||||
|
pageSource = driver.getPageSource();
|
||||||
|
System.out.println(pageSource);
|
||||||
|
Assert.assertTrue(pageSource.contains("customer-portal/users/permit"));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,21 @@
|
||||||
{ "type" : "password",
|
{ "type" : "password",
|
||||||
"value" : "password" }
|
"value" : "password" }
|
||||||
],
|
],
|
||||||
|
"realmRoles": [ "user" ],
|
||||||
|
"applicationRoles": {
|
||||||
|
"account": [ "manage-account" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username" : "admin",
|
||||||
|
"enabled": true,
|
||||||
|
"email" : "admin.burke@redhat.com",
|
||||||
|
"firstName": "Admin",
|
||||||
|
"lastName": "Burke",
|
||||||
|
"credentials" : [
|
||||||
|
{ "type" : "password",
|
||||||
|
"value" : "password" }
|
||||||
|
],
|
||||||
"realmRoles": [ "user", "admin" ],
|
"realmRoles": [ "user", "admin" ],
|
||||||
"applicationRoles": {
|
"applicationRoles": {
|
||||||
"account": [ "manage-account" ]
|
"account": [ "manage-account" ]
|
||||||
|
|
|
@ -10,7 +10,15 @@
|
||||||
<servlet-name>SendUsername</servlet-name>
|
<servlet-name>SendUsername</servlet-name>
|
||||||
<servlet-class>org.keycloak.testsuite.ProxyTest$SendUsernameServlet</servlet-class>
|
<servlet-class>org.keycloak.testsuite.ProxyTest$SendUsernameServlet</servlet-class>
|
||||||
</servlet>
|
</servlet>
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>Error</servlet-name>
|
||||||
|
<servlet-class>org.keycloak.testsuite.ProxyTest$SendError</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>Error</servlet-name>
|
||||||
|
<url-pattern>/error.html</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
<servlet-name>SendUsername</servlet-name>
|
<servlet-name>SendUsername</servlet-name>
|
||||||
<url-pattern>/*</url-pattern>
|
<url-pattern>/*</url-pattern>
|
||||||
|
|
Loading…
Reference in a new issue