keycloak-uncached

Details

diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java
index bdbddc6..9eb49db 100755
--- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java
+++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java
@@ -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);
diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java
index f236579..16b8778 100755
--- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java
+++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java
@@ -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));
diff --git a/testsuite/proxy/src/test/java/org/keycloak/testsuite/ProxyTest.java b/testsuite/proxy/src/test/java/org/keycloak/testsuite/ProxyTest.java
index ecd0d59..511e02d 100755
--- a/testsuite/proxy/src/test/java/org/keycloak/testsuite/ProxyTest.java
+++ b/testsuite/proxy/src/test/java/org/keycloak/testsuite/ProxyTest.java
@@ -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"));
+
 
     }
 
diff --git a/testsuite/proxy/src/test/resources/tomcat-test/demorealm.json b/testsuite/proxy/src/test/resources/tomcat-test/demorealm.json
index a4a6ec9..29de45c 100755
--- a/testsuite/proxy/src/test/resources/tomcat-test/demorealm.json
+++ b/testsuite/proxy/src/test/resources/tomcat-test/demorealm.json
@@ -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" ]
diff --git a/testsuite/proxy/src/test/resources/tomcat-test/webapp/WEB-INF/web.xml b/testsuite/proxy/src/test/resources/tomcat-test/webapp/WEB-INF/web.xml
index 7e0b269..8e76e3e 100755
--- a/testsuite/proxy/src/test/resources/tomcat-test/webapp/WEB-INF/web.xml
+++ b/testsuite/proxy/src/test/resources/tomcat-test/webapp/WEB-INF/web.xml
@@ -10,8 +10,16 @@
         <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>
     </servlet-mapping>