proxy constraints
This commit is contained in:
parent
21279fc9ed
commit
3a95401086
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>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RoleAuthHandler implements HttpHandler {
|
||||
public class ConstraintAuthorizationHandler implements HttpHandler {
|
||||
|
||||
protected Collection<String> roles;
|
||||
protected HttpHandler next;
|
||||
protected String errorPage;
|
||||
|
||||
public RoleAuthHandler(Collection<String> roles, HttpHandler next) {
|
||||
this.roles = roles;
|
||||
public ConstraintAuthorizationHandler(String errorPage, HttpHandler next) {
|
||||
this.errorPage = errorPage;
|
||||
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.endExchange();
|
||||
|
|
@ -15,11 +15,13 @@ public class ConstraintMatcherHandler implements HttpHandler {
|
|||
protected SecurityPathMatches matcher;
|
||||
protected HttpHandler securedHandler;
|
||||
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.securedHandler = securedHandler;
|
||||
this.unsecuredHandler = unsecuredHandler;
|
||||
this.errorPage = errorPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,8 +33,16 @@ public class ConstraintMatcherHandler implements HttpHandler {
|
|||
}
|
||||
|
||||
if (match.getRequiredRoles().isEmpty() && match.getEmptyRoleSemantic() == SecurityInfo.EmptyRoleSemantic.DENY) {
|
||||
exchange.setResponseCode(403);
|
||||
exchange.endExchange();
|
||||
if (errorPage != null) {
|
||||
exchange.setRequestPath(errorPage);
|
||||
exchange.setRelativePath(errorPage);
|
||||
exchange.setResolvedPath(errorPage);
|
||||
unsecuredHandler.handleRequest(exchange);
|
||||
} else {
|
||||
exchange.setResponseCode(403);
|
||||
exchange.endExchange();
|
||||
}
|
||||
return;
|
||||
}
|
||||
exchange.getSecurityContext().setAuthenticationRequired();
|
||||
exchange.putAttachment(CONSTRAINT_KEY, match);
|
||||
|
|
|
@ -4,7 +4,6 @@ import io.undertow.Undertow;
|
|||
import io.undertow.security.api.AuthenticationMechanism;
|
||||
import io.undertow.security.api.AuthenticationMode;
|
||||
import io.undertow.security.handlers.AuthenticationCallHandler;
|
||||
import io.undertow.security.handlers.AuthenticationConstraintHandler;
|
||||
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
|
||||
import io.undertow.security.handlers.SecurityInitialHandler;
|
||||
import io.undertow.security.idm.Account;
|
||||
|
@ -72,7 +71,7 @@ public class ProxyServerBuilder {
|
|||
proxyHandler = new HttpHandler() {
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@ -93,12 +92,21 @@ public class ProxyServerBuilder {
|
|||
protected String base;
|
||||
protected SecurityPathMatches.Builder constraintBuilder = new SecurityPathMatches.Builder();
|
||||
protected SecurityPathMatches matches;
|
||||
protected String errorPage;
|
||||
|
||||
public ApplicationBuilder base(String base) {
|
||||
this.base = base;
|
||||
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) {
|
||||
this.deployment = KeycloakDeploymentBuilder.build(config);
|
||||
this.deploymentContext = new AdapterDeploymentContext(deployment);
|
||||
|
@ -172,8 +180,16 @@ public class ProxyServerBuilder {
|
|||
private HttpHandler addSecurity(final HttpHandler toWrap) {
|
||||
HttpHandler handler = 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 ConstraintMatcherHandler(matches, handler, toWrap);
|
||||
handler = new ConstraintMatcherHandler(matches, handler, toWrap, errorPage);
|
||||
final List<AuthenticationMechanism> mechanisms = new LinkedList<AuthenticationMechanism>();
|
||||
mechanisms.add(new CachedAuthenticatedSessionMechanism());
|
||||
mechanisms.add(new UndertowAuthenticationMechanism(deploymentContext, userSessionManagement, nodesRegistrationManagement, -1));
|
||||
|
|
|
@ -98,7 +98,7 @@ public class ProxyTest {
|
|||
Integer count = (Integer)req.getSession().getAttribute("counter");
|
||||
if (count == null) count = new Integer(0);
|
||||
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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
|
@ -133,7 +145,7 @@ public class ProxyTest {
|
|||
|
||||
static Undertow proxyServer = null;
|
||||
|
||||
//@BeforeClass
|
||||
@BeforeClass
|
||||
public static void initProxy() throws Exception {
|
||||
initTomcat();
|
||||
ProxyServerBuilder builder = new ProxyServerBuilder().addHttpListener(8080, "localhost");
|
||||
|
@ -142,8 +154,13 @@ public class ProxyTest {
|
|||
|
||||
builder.target("http://localhost:8082")
|
||||
.application(config)
|
||||
.base("/customer-portal")
|
||||
.constraint("/*").add().add();
|
||||
.base("/customer-portal")
|
||||
.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.start();
|
||||
|
||||
|
@ -167,34 +184,57 @@ public class ProxyTest {
|
|||
|
||||
@Test
|
||||
public void testLoginSSOAndLogout() throws Exception {
|
||||
initProxy();
|
||||
driver.navigate().to("http://localhost:8080/customer-portal");
|
||||
driver.navigate().to("http://localhost:8080/customer-portal/users");
|
||||
System.out.println("Current url: " + driver.getCurrentUrl());
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||
loginPage.login("bburke@redhat.com", "password");
|
||||
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();
|
||||
System.out.println(pageSource);
|
||||
Assert.assertTrue(pageSource.contains("customer-portal"));
|
||||
Assert.assertTrue(pageSource.contains("0"));
|
||||
driver.navigate().to("http://localhost:8080/customer-portal");
|
||||
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal");
|
||||
Assert.assertTrue(pageSource.contains("customer-portal/users"));
|
||||
Assert.assertTrue(pageSource.contains("count:0"));
|
||||
driver.navigate().to("http://localhost:8080/customer-portal/users");
|
||||
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal/users");
|
||||
pageSource = driver.getPageSource();
|
||||
System.out.println(pageSource);
|
||||
Assert.assertTrue(pageSource.contains("customer-portal"));
|
||||
Assert.assertTrue(pageSource.contains("1")); // test http session
|
||||
Assert.assertTrue(pageSource.contains("customer-portal/users"));
|
||||
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
|
||||
|
||||
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);
|
||||
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();
|
||||
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",
|
||||
"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" ],
|
||||
"applicationRoles": {
|
||||
"account": [ "manage-account" ]
|
||||
|
|
|
@ -10,7 +10,15 @@
|
|||
<servlet-name>SendUsername</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.ProxyTest$SendUsernameServlet</servlet-class>
|
||||
</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-name>SendUsername</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
|
|
Loading…
Reference in a new issue