Repository: wicket
Updated Branches:
  refs/heads/master d8e292ecd -> 9d495f38f


WICKET-5326 Wicket doesn't encrypt links and Ajax URLs when CryptoMapper is used

(cherry picked from commit 6af2d593dbe042925c5cc9d3b0e89fc63ffe8efb)

Conflicts:
        
wicket-core/src/main/java/org/apache/wicket/core/request/mapper/CryptoMapper.java
        
wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/ded3c583
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/ded3c583
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/ded3c583

Branch: refs/heads/master
Commit: ded3c58375c9560b6bfcc499ecfe2817135133aa
Parents: d8e292e
Author: Jesse Long <[email protected]>
Authored: Thu Oct 9 17:20:39 2014 +0200
Committer: Jesse Long <[email protected]>
Committed: Tue Oct 14 23:53:15 2014 +0200

----------------------------------------------------------------------
 .../core/request/mapper/CryptoMapper.java       | 402 ++++++++++++++---
 .../core/request/mapper/CryptoMapperTest.java   | 438 +++++++++++++++----
 .../wicket/settings/ISecuritySettingsTest.java  |  46 +-
 3 files changed, 730 insertions(+), 156 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/ded3c583/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/CryptoMapper.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/CryptoMapper.java
 
b/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/CryptoMapper.java
index 05e7c30..99f7afc 100755
--- 
a/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/CryptoMapper.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/CryptoMapper.java
@@ -16,6 +16,7 @@
  */
 package org.apache.wicket.core.request.mapper;
 
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.wicket.Application;
@@ -25,25 +26,48 @@ import org.apache.wicket.request.IRequestMapper;
 import org.apache.wicket.request.Request;
 import org.apache.wicket.request.Url;
 import org.apache.wicket.request.mapper.IRequestMapperDelegate;
+import org.apache.wicket.request.mapper.info.PageComponentInfo;
 import org.apache.wicket.util.IProvider;
 import org.apache.wicket.util.crypt.ICrypt;
 import org.apache.wicket.util.crypt.ICryptFactory;
 import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.string.Strings;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Request mapper that encrypts urls generated by another mapper. The original 
URL (both segments
- * and parameters) is encrypted and is represented as URL segment. To be able 
to handle relative
- * URLs for images in .css file the same amount of URL segments that the 
original URL had are
- * appended to the encrypted URL. Each segment has a precise 5 character 
value, calculated using a
- * checksum. This helps in calculating the relative distance from the original 
URL. When a URL is
- * returned by the browser, we iterate through these checksummed placeholder 
URL segments. If the
- * segment matches the expected checksum, then the segment it deemed to be the 
corresponding segment
- * in the encrypted URL. If the segment does not match the expected checksum, 
then the segment is
- * deemed a plain text sibling of the corresponding segment in the encrypted 
URL, and all subsequent
+ * <p>
+ * A request mapper that encrypts URLs generated by another mapper. This 
mapper encrypts the segments
+ * and query parameters of URLs starting with {@code /wicket/}, and the just 
the {@link PageComponentInfo}
+ * parameter for mounted URLs.
+ * </p>
+ * 
+ * <p>
+ * This mapper can be mounted before or after mounting other pages, but will 
only encrypt URLs for
+ * pages mounted before the {@link CryptoMapper}. If required, multiple {@link 
CryptoMapper}s may be
+ * installed in an {@link Application}.
+ * </p>
+ * 
+ * <p>
+ * When encrypting URLs in the Wicket namespace (starting with {@code 
/wicket/}), the entire URL, including
+ * segments and parameters, is encrypted, with the encrypted form stored in 
the first segment of the encrypted URL.
+ * </p>
+ * 
+ * <p>
+ * To be able to handle relative URLs, like for image URLs in a CSS file, 
checksum segments are appended to the
+ * encrypted URL until the encrypted URL has the same number of segments as 
the original URL had.
+ * Each checksum segment has a precise 5 character value, calculated using a 
checksum. This helps in calculating
+ * the relative distance from the original URL. When a URL is returned by the 
browser, we iterate through these
+ * checksummed placeholder URL segments. If the segment matches the expected 
checksum, then the segment it deemed
+ * to be the corresponding segment in the original URL. If the segment does 
not match the expected checksum, then
+ * the segment is deemed a plain text sibling of the corresponding segment in 
the original URL, and all subsequent
  * segments are considered plain text children of the current segment.
+ * </p>
+ * 
+ * <p>
+ * When encrypting mounted URLs, we look for the {@link PageComponentInfo} 
parameter, and encrypt only that parameter.
+ * </p>
  * 
  * @author igor.vaynberg
  * @author Jesse Long
@@ -53,6 +77,11 @@ public class CryptoMapper implements IRequestMapperDelegate
 {
        private static final Logger log = 
LoggerFactory.getLogger(CryptoMapper.class);
 
+       /**
+        * Name of the parameter which contains encrypted page component info.
+        */
+       private static final String ENCRYPTED_PAGE_COMPONENT_INFO_PARAMETER = 
"wicket";
+
        private final IRequestMapper wrappedMapper;
        private final IProvider<ICrypt> cryptProvider;
 
@@ -87,12 +116,34 @@ public class CryptoMapper implements IRequestMapperDelegate
                this.cryptProvider = Args.notNull(cryptProvider, 
"cryptProvider");
        }
 
+       /**
+        * {@inheritDoc}
+        * <p>
+        * This implementation decrypts the URL and passes the decrypted URL to 
the wrapped mapper.
+        * </p>
+        * @param request
+        *              The request for which to get a compatability score.
+        * 
+        * @return The compatability score.
+        */
        @Override
        public int getCompatibilityScore(final Request request)
        {
-               return wrappedMapper.getCompatibilityScore(request);
+               Url decryptedUrl = decryptUrl(request, request.getUrl());
+
+               if (decryptedUrl == null)
+               {
+                       return 0;
+               }
+
+               Request decryptedRequest = request.cloneWithUrl(decryptedUrl);
+
+               return wrappedMapper.getCompatibilityScore(decryptedRequest);
        }
 
+       /**
+        * {@inheritDoc}
+        */
        @Override
        public Url mapHandler(final IRequestHandler requestHandler)
        {
@@ -112,6 +163,9 @@ public class CryptoMapper implements IRequestMapperDelegate
                return encryptUrl(url);
        }
 
+       /**
+        * {@inheritDoc}
+        */
        @Override
        public IRequestHandler mapRequest(final Request request)
        {
@@ -119,7 +173,7 @@ public class CryptoMapper implements IRequestMapperDelegate
 
                if (url == null)
                {
-                       return wrappedMapper.mapRequest(request);
+                       return null;
                }
 
                Request decryptedRequest = request.cloneWithUrl(url);
@@ -152,18 +206,44 @@ public class CryptoMapper implements 
IRequestMapperDelegate
                return wrappedMapper;
        }
 
+       /**
+        * Encrypts a URL. This method should return a new, encrypted instance 
of the URL. If the URL starts with {@code /wicket/},
+        * the entire URL is encrypted.
+        * 
+        * @param url
+        *              The URL to encrypt.
+        * 
+        * @return A new, encrypted version of the URL.
+        */
        protected Url encryptUrl(final Url url)
        {
-               if (url.getSegments().isEmpty())
+               if (url.getSegments().size() > 0
+                       && 
url.getSegments().get(0).equals(Application.get().getMapperContext().getNamespace()))
                {
-                       return url;
+                       return encryptEntireUrl(url);
                }
+               else
+               {
+                       return encryptRequestListenerParameter(url);
+               }
+       }
+
+       /**
+        * Encrypts an entire URL, segments and query parameters.
+        * 
+        * @param url
+        *              The URL to encrypt.
+        * 
+        * @return An encrypted form of the URL.
+        */
+       protected Url encryptEntireUrl(final Url url)
+       {
                String encryptedUrlString = 
getCrypt().encryptUrlSafe(url.toString());
 
                Url encryptedUrl = new Url(url.getCharset());
                encryptedUrl.getSegments().add(encryptedUrlString);
 
-               int numberOfSegments = url.getSegments().size();
+               int numberOfSegments = url.getSegments().size() - 1;
                HashedSegmentGenerator generator = new 
HashedSegmentGenerator(encryptedUrlString);
                for (int segNo = 0; segNo < numberOfSegments; segNo++)
                {
@@ -172,86 +252,274 @@ public class CryptoMapper implements 
IRequestMapperDelegate
                return encryptedUrl;
        }
 
+       /**
+        * Encrypts the {@link PageComponentInfo} query parameter in the URL, 
if any is found.
+        * 
+        * @param url
+        *              The URL to encrypt.
+        * 
+        * @return An encrypted form of the URL.
+        */
+       protected Url encryptRequestListenerParameter(final Url url)
+       {
+               Url encryptedUrl = new Url(url);
+
+               for (Iterator<Url.QueryParameter> it = 
encryptedUrl.getQueryParameters().iterator(); it.hasNext();)
+               {
+                       Url.QueryParameter qp = it.next();
+
+                       if (Strings.isEmpty(qp.getValue()) == true && 
Strings.isEmpty(qp.getName()) == false)
+                       {
+                               if (PageComponentInfo.parse(qp.getName()) != 
null)
+                               {
+                                       it.remove();
+                                       String encryptedParameterValue = 
getCrypt().encryptUrlSafe(qp.getName());
+                                       Url.QueryParameter encryptedParameter
+                                               = new 
Url.QueryParameter(ENCRYPTED_PAGE_COMPONENT_INFO_PARAMETER, 
encryptedParameterValue);
+                                       
encryptedUrl.getQueryParameters().add(0, encryptedParameter);
+                                       break;
+                               }
+                       }
+               }
+
+               return encryptedUrl;
+       }
+
+       /**
+        * Decrypts a {@link Url}. This method should return {@code null} if 
the URL is not decryptable, or if the
+        * URL should have been encrypted but was not. Returning {@code null} 
results in a 404 error.
+        * 
+        * @param request
+        *              The {@link Request}.
+        * @param encryptedUrl
+        *              The encrypted {@link Url}.
+        * 
+        * @return Returns a decrypted {@link Url}.
+        */
        protected Url decryptUrl(final Request request, final Url encryptedUrl)
        {
-               /*
-                * If the encrypted URL has no segments it is the home page 
URL, and does not need
-                * decrypting.
-                */
-               if (encryptedUrl.getSegments().isEmpty())
+               Url url = decryptEntireUrl(request, encryptedUrl);
+
+               if (url == null)
                {
-                       return encryptedUrl;
+                       if (encryptedUrl.getSegments().size() > 0
+                               && 
encryptedUrl.getSegments().get(0).equals(Application.get().getMapperContext().getNamespace()))
+                       {
+                               /*
+                                * This URL should have been encrypted, but was 
not. We should refuse to handle this, except when
+                                * there is more than one CryptoMapper 
installed, and the request was decrypted by some other
+                                * CryptoMapper.
+                                */
+                               if 
(request.getOriginalUrl().getSegments().size() > 0
+                                       && 
request.getOriginalUrl().getSegments().get(0).equals(Application.get().getMapperContext().getNamespace()))
+                               {
+                                       return null;
+                               }
+                               else
+                               {
+                                       return encryptedUrl;
+                               }
+                       }
                }
 
-               List<String> encryptedSegments = encryptedUrl.getSegments();
+               if (url == null)
+               {
+                       url = decryptRequestListenerParameter(request, 
encryptedUrl);
+               }
+
+               return url;
+       }
 
+       /**
+        * Decrypts an entire URL, which was previously encrypted by {@link 
#encryptEntireUrl(org.apache.wicket.request.Url)}.
+        * This method should return {@code null} if the URL is not decryptable.
+        * 
+        * @param request
+        *              The request that was made.
+        * @param encryptedUrl
+        *              The encrypted URL.
+        * 
+        * @return A decrypted form of the URL, or {@code null} if the URL is 
not decryptable.
+        */
+       protected Url decryptEntireUrl(final Request request, final Url 
encryptedUrl)
+       {
                Url url = new Url(request.getCharset());
+
+               List<String> encryptedSegments = encryptedUrl.getSegments();
+
+               if (encryptedSegments.isEmpty())
+               {
+                       return null;
+               }
+
+               /*
+                * The first encrypted segment contains an encrypted version of 
the entire plain text url.
+                */
+               String encryptedUrlString = encryptedSegments.get(0);
+               if (Strings.isEmpty(encryptedUrlString))
+               {
+                       return null;
+               }
+
+               String decryptedUrl;
                try
                {
+                       decryptedUrl = 
getCrypt().decryptUrlSafe(encryptedUrlString);
+               }
+               catch (Exception e)
+               {
+                       log.error("Error decrypting URL", e);
+                       return null;
+               }
+
+               if (decryptedUrl == null)
+               {
+                       return null;
+               }
+
+               Url originalUrl = Url.parse(decryptedUrl, request.getCharset());
+
+               int originalNumberOfSegments = originalUrl.getSegments().size();
+               int encryptedNumberOfSegments = 
encryptedUrl.getSegments().size();
+
+               if (originalNumberOfSegments > 0)
+               {
                        /*
-                        * The first encrypted segment contains an encrypted 
version of the entire plain text
-                        * url.
+                        * This should always be true. Home page URLs are the 
only ones without
+                        * segments, and we dont encrypt those with this method.
+                        * 
+                        * We always add the first segment of the URL, because 
we encrypt a URL like:
+                        *      /path/to/something
+                        * to:
+                        *      /encrypted_full/hash/hash
+                        * 
+                        * Notice the consistent number of segments. If we 
applied the following relative URL:
+                        *      ../../something
+                        * then the resultant URL would be:
+                        *      /something
+                        * 
+                        * Hence, the mere existence of the first, encrypted 
version of complete URL, segment
+                        * tells us that the first segment of the original URL 
is still to be used.
                         */
-                       String encryptedUrlString = encryptedSegments.get(0);
-                       if (Strings.isEmpty(encryptedUrlString))
+                       url.getSegments().add(originalUrl.getSegments().get(0));
+               }
+
+               HashedSegmentGenerator generator = new 
HashedSegmentGenerator(encryptedUrlString);
+               int segNo = 1;
+               for (; segNo < encryptedNumberOfSegments; segNo++)
+               {
+                       if (segNo > originalNumberOfSegments)
                        {
-                               return null;
+                               break;
                        }
 
-                       String decryptedUrl = 
getCrypt().decryptUrlSafe(encryptedUrlString);
-                       if (decryptedUrl == null)
+                       String next = generator.next();
+                       String encryptedSegment = encryptedSegments.get(segNo);
+                       if (!next.equals(encryptedSegment))
                        {
-                               return null;
+                               /*
+                                * This segment received from the browser is 
not the same as the expected segment generated
+                                * by the HashSegmentGenerator. Hence it, and 
all subsequent segments are considered plain
+                                * text siblings of the original encrypted url.
+                                */
+                               break;
                        }
-                       Url originalUrl = Url.parse(decryptedUrl, 
request.getCharset());
 
-                       int originalNumberOfSegments = 
originalUrl.getSegments().size();
-                       int encryptedNumberOfSegments = 
encryptedUrl.getSegments().size();
+                       /*
+                        * This segments matches the expected checksum, so we 
add the corresponding segment from the
+                        * original URL.
+                        */
+                       
url.getSegments().add(originalUrl.getSegments().get(segNo));
+               }
+               /*
+                * Add all remaining segments from the encrypted url as plain 
text segments.
+                */
+               for (; segNo < encryptedNumberOfSegments; segNo++)
+               {
+                       // modified or additional segment
+                       
url.getSegments().add(encryptedUrl.getSegments().get(segNo));
+               }
 
-                       HashedSegmentGenerator generator = new 
HashedSegmentGenerator(encryptedUrlString);
-                       int segNo = 1;
-                       for (; segNo < encryptedNumberOfSegments; segNo++)
-                       {
-                               if (segNo > originalNumberOfSegments)
-                               {
-                                       break;
-                               }
+               
url.getQueryParameters().addAll(originalUrl.getQueryParameters());
+               // WICKET-4923 additional parameters
+               
url.getQueryParameters().addAll(encryptedUrl.getQueryParameters());
+
+               return url;
+       }
+
+       /**
+        * Decrypts a URL which may contain an encrypted {@link 
PageComponentInfo} query parameter.
+        * 
+        * @param request
+        *              The request that was made.
+        * @param encryptedUrl
+        *              The (potentially) encrypted URL.
+        * 
+        * @return A decrypted form of the URL.
+        */
+       protected Url decryptRequestListenerParameter(final Request request, 
Url encryptedUrl)
+       {
+               Url url = new Url(encryptedUrl);
 
-                               String next = generator.next();
-                               String encryptedSegment = 
encryptedSegments.get(segNo);
-                               if (!next.equals(encryptedSegment))
+               url.getQueryParameters().clear();
+
+               for (Url.QueryParameter qp : encryptedUrl.getQueryParameters())
+               {
+                       if (Strings.isEmpty(qp.getValue()) && 
Strings.isEmpty(qp.getName()) == false)
+                       {
+                               if (PageComponentInfo.parse(qp.getName()) != 
null)
                                {
                                        /*
-                                        * This segment received from the 
browser is not the same as the expected
-                                        * segment generated by the 
HashSegmentGenerator. Hence it, and all subsequent
-                                        * segments are considered plain text 
siblings of the original encrypted url.
+                                        * Plain text request listener 
parameter found. This should have been encrypted, so we
+                                        * refuse to map the request unless the 
original URL did not include this parameter, which
+                                        * case there are likely to be multiple 
cryptomappers installed.
                                         */
-                                       break;
+                                       if 
(request.getOriginalUrl().getQueryParameter(qp.getName()) == null)
+                                       {
+                                               
url.getQueryParameters().add(qp);
+                                       }
+                                       else
+                                       {
+                                               return null;
+                                       }
                                }
+                       }
+                       else if 
(ENCRYPTED_PAGE_COMPONENT_INFO_PARAMETER.equals(qp.getName()))
+                       {
+                               String encryptedValue = qp.getValue();
 
-                               /*
-                                * This segments matches the expected checksum, 
so we add the corresponding segment
-                                * from the original URL.
-                                */
-                               
url.getSegments().add(originalUrl.getSegments().get(segNo - 1));
+                               if (Strings.isEmpty(encryptedValue))
+                               {
+                                       url.getQueryParameters().add(qp);
+                               }
+                               else
+                               {
+                                       String decryptedValue = null;
+
+                                       try
+                                       {
+                                               decryptedValue = 
getCrypt().decryptUrlSafe(encryptedValue);
+                                       }
+                                       catch (Exception e)
+                                       {
+                                               log.error("Error decrypting 
encrypted request listener query parameter", e);
+                                       }
+
+                                       if (Strings.isEmpty(decryptedValue))
+                                       {
+                                               
url.getQueryParameters().add(qp);
+                                       }
+                                       else
+                                       {
+                                               Url.QueryParameter 
decryptedParamter = new Url.QueryParameter(decryptedValue, "");
+                                               url.getQueryParameters().add(0, 
decryptedParamter);
+                                       }
+                               }
                        }
-                       /*
-                        * Add all remaining segments from the encrypted url as 
plain text segments.
-                        */
-                       for (; segNo < encryptedNumberOfSegments; segNo++)
+                       else
                        {
-                               // modified or additional segment
-                               
url.getSegments().add(encryptedUrl.getSegments().get(segNo));
+                               url.getQueryParameters().add(qp);
                        }
-
-                       
url.getQueryParameters().addAll(originalUrl.getQueryParameters());
-                       // WICKET-4923 additional parameters
-                       
url.getQueryParameters().addAll(encryptedUrl.getQueryParameters());
-               }
-               catch (Exception e)
-               {
-                       log.error("Error decrypting URL", e);
-                       url = null;
                }
 
                return url;

http://git-wip-us.apache.org/repos/asf/wicket/blob/ded3c583/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java
index fb4d2d7..1f7ecad 100644
--- 
a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java
+++ 
b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java
@@ -19,19 +19,25 @@ package org.apache.wicket.core.request.mapper;
 import static org.hamcrest.CoreMatchers.instanceOf;
 
 import org.apache.wicket.MockPage;
+import 
org.apache.wicket.core.request.handler.BookmarkableListenerInterfaceRequestHandler;
 import org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler;
 import org.apache.wicket.core.request.handler.PageAndComponentProvider;
 import org.apache.wicket.core.request.handler.PageProvider;
 import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
 import org.apache.wicket.core.request.handler.RequestSettingRequestHandler;
+import org.apache.wicket.markup.IMarkupFragment;
+import org.apache.wicket.markup.Markup;
+import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.markup.html.link.ILinkListener;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.IRequestHandlerDelegate;
 import org.apache.wicket.request.Request;
 import org.apache.wicket.request.Url;
 import org.apache.wicket.request.Url.StringMode;
 import org.apache.wicket.request.component.IRequestableComponent;
 import 
org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
+import org.apache.wicket.request.mapper.info.PageComponentInfo;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.PackageResourceReference;
 import org.apache.wicket.request.resource.UrlResourceReference;
@@ -41,7 +47,7 @@ import org.apache.wicket.util.crypt.CachingSunJceCryptFactory;
 import org.apache.wicket.util.crypt.ICrypt;
 import org.apache.wicket.util.crypt.ICryptFactory;
 import org.apache.wicket.util.string.StringValue;
-import org.apache.wicket.util.tester.DummyHomePage;
+import org.apache.wicket.util.string.Strings;
 import org.apache.wicket.util.tester.WicketTester;
 import org.junit.After;
 import org.junit.Before;
@@ -52,15 +58,11 @@ import org.junit.Test;
  */
 public class CryptoMapperTest extends AbstractMapperTest
 {
-       /**
-        * the encrypted version of {@link #EXPECTED_URL}
-        */
-       private static final String ENCRYPTED_URL = 
"SnPh82L4Kl4/SnPe4/4Sn8e/nPh75/h8211";
-
-       /**
-        * The url to encrypt
-        */
-       private static final Url EXPECTED_URL = Url.parse("a/b/c/d");
+       private static final String PLAIN_BOOKMARKABLE_URL = 
"wicket/bookmarkable/" + Page2.class.getName();
+       private static final String ENCRYPTED_BOOKMARKABLE_URL = 
"L7ExSNbPC4sb6TPJDblCAopL53TWmZP5y7BQEaJSJAC05HXod5M5U7gT2yNT0lK5L6L09ZAOoZkGyUhseyPrC4S5tqUUrV6zipc4_Ni877EmwR8AyCyA-A/L7E59/5y7f2";
+       private static final String PLAIN_PAGE_INSTANCE_URL = "wicket/page?5";
+       private static final String ENCRYPTED_PAGE_INSTANCE_URL = 
"fyBfZ9p6trOhokHCzsQS6Q/fyBce";
+       private static final String MOUNTED_URL = "path/to/mounted/page";
 
        private CryptoMapper mapper;
 
@@ -68,7 +70,7 @@ public class CryptoMapperTest extends AbstractMapperTest
 
        /**
         * Creates the {@link CryptoMapper}
-        * 
+        *
         * @throws Exception
         */
        @Override
@@ -78,7 +80,7 @@ public class CryptoMapperTest extends AbstractMapperTest
                tester = new WicketTester();
 
                WebApplication application = tester.getApplication();
-               application.mountPage(EXPECTED_URL.toString(), 
DummyHomePage.class);
+               application.mountPage(MOUNTED_URL, Page1.class);
 
                /**
                 * Use explicit crypt provider to prevent crypt warning output, 
see
@@ -109,51 +111,309 @@ public class CryptoMapperTest extends AbstractMapperTest
        }
 
        /**
-        * Tests that {@link CryptoMapper} wraps the original request mapper 
and encrypts the url
-        * produced by it
+        * Tests that the home page is requestable.
         */
        @Test
-       public void encrypt()
+       public void homePage()
        {
-               Url url = mapper.mapHandler(new RenderPageRequestHandler(new 
PageProvider(
-                       DummyHomePage.class, new PageParameters())));
-               assertEquals(ENCRYPTED_URL, url.toString());
+               IRequestHandler requestHandler = 
mapper.mapRequest(getRequest(Url.parse("")));
+               assertNotNull("Unable to map request for home page", 
requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+               assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
+               RenderPageRequestHandler handler = (RenderPageRequestHandler) 
requestHandler;
+               assertSame(tester.getApplication().getHomePage(), 
handler.getPageClass());
        }
 
        /**
-        * Tests that {@link CryptoMapper} decrypts the passed url and pass it 
to the original request
-        * mapper which resolves the page from the application mounts
+        * Verifies that the home page can be reached with non-encrypted query 
parameters.
+        * https://issues.apache.org/jira/browse/WICKET-4345
+        *
+        * Also, test that the URL for the home page with non-encrypted 
parameters is not encrypted, to avoid unnecessary redirects.
         */
        @Test
-       public void decrypt()
+       public void homePageWithNonEncryptedQueryParameters()
        {
-               Request request = getRequest(Url.parse(ENCRYPTED_URL));
+               String expectedEncrypted = "?namedKey1=namedValue1";
+               PageParameters expectedParameters = new PageParameters();
+               expectedParameters.add("namedKey1", "namedValue1");
+
+               RenderPageRequestHandler renderPageRequestHandler = new 
RenderPageRequestHandler(
+                       new PageProvider(tester.getApplication().getHomePage(), 
expectedParameters));
+
+               Url url = mapper.mapHandler(renderPageRequestHandler);
+               assertEquals(expectedEncrypted, url.toString());
+
+               Request request = getRequest(url);
                IRequestHandler requestHandler = mapper.mapRequest(request);
-               assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
-               requestHandler = 
((RequestSettingRequestHandler)requestHandler).getDelegateHandler();
+               assertNotNull(requestHandler);
+
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
                assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
+               RenderPageRequestHandler handler = (RenderPageRequestHandler) 
requestHandler;
+               assertEquals(tester.getApplication().getHomePage(), 
handler.getPageClass());
+               StringValue queryParam = 
handler.getPageParameters().get("namedKey1");
+               assertEquals("namedValue1", queryParam.toOptionalString());
+       }
+
+       /**
+        * Tests that we do not allow unencrypted URLs to IRequestListeners on 
the home page, like: ?0-0.ILinkListener-link
+        */
+       @Test
+       public void homePageForceEncryptionOfRequestListener()
+       {
+               PageAndComponentProvider provider = new 
PageAndComponentProvider(tester.getApplication().getHomePage(), "some:link");
+               IRequestHandler requestHandler = new 
BookmarkableListenerInterfaceRequestHandler(provider, ILinkListener.INTERFACE);
+               Url plainUrl = 
mapper.getDelegateMapper().mapHandler(requestHandler);
+               assertTrue("Plain URL for home page has segments: " + 
plainUrl.toString(), plainUrl.getSegments().isEmpty());
+               assertNull(mapper.mapRequest(getRequest(plainUrl)));
+       }
+
+       /**
+        * Tests that URLs for bookmarkable pages are encrypted.
+        */
+       @Test
+       public void bookmarkablePageEncrypt()
+       {
+               IRequestHandler renderPage2BookmarkableHandler = new 
RenderPageRequestHandler(new PageProvider(
+                       Page2.class, new PageParameters()));
+
+               Url plainTextUrl = 
mapper.getDelegateMapper().mapHandler(renderPage2BookmarkableHandler);
+
+               assertEquals(PLAIN_BOOKMARKABLE_URL, plainTextUrl.toString());
 
-               RenderPageRequestHandler handler = 
(RenderPageRequestHandler)requestHandler;
-               assertEquals(DummyHomePage.class, handler.getPageClass());
+               Url encryptedUrl = 
mapper.mapHandler(renderPage2BookmarkableHandler);
+               assertEquals(ENCRYPTED_BOOKMARKABLE_URL, 
encryptedUrl.toString());
        }
 
        /**
-        * Verifies that the home page can be reached with non-encrypted query 
parameters.
-        * https://issues.apache.org/jira/browse/WICKET-4345
+        * Tests that encrypted URLs for bookmarkable pages are decrypted and 
passed to the wrapped mapper.
         */
        @Test
-       public void decryptHomePageWithNonEncryptedQueryParameters()
+       public void bookmarkablePageDecrypt()
        {
-               Request request = getRequest(Url.parse("?named1=value1"));
+               Request request = 
getRequest(Url.parse(ENCRYPTED_BOOKMARKABLE_URL));
                IRequestHandler requestHandler = mapper.mapRequest(request);
-               assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
-               requestHandler = 
((RequestSettingRequestHandler)requestHandler).getDelegateHandler();
+
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+
                assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
 
-               RenderPageRequestHandler handler = 
(RenderPageRequestHandler)requestHandler;
-               assertEquals(tester.getApplication().getHomePage(), 
handler.getPageClass());
-               StringValue queryParam = 
handler.getPageParameters().get("named1");
-               assertEquals("value1", queryParam.toOptionalString());
+               RenderPageRequestHandler handler = (RenderPageRequestHandler) 
requestHandler;
+               assertEquals(Page2.class, handler.getPageClass());
+       }
+
+       /**
+        * Tests that encrypted URLs for bookmarkable pages are decrypted and 
passed to the wrapped mapper when there is more than
+        * one cryptomapper installed.
+        */
+       @Test
+       public void bookmarkablePageDecryptMultipleCryptoMapper()
+       {
+               Request request = 
getRequest(Url.parse(ENCRYPTED_BOOKMARKABLE_URL));
+
+               IRequestHandler requestHandler = new CryptoMapper(mapper, 
tester.getApplication())
+                       .mapRequest(request);
+
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+
+               assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
+
+               RenderPageRequestHandler handler = (RenderPageRequestHandler) 
requestHandler;
+               assertEquals(Page2.class, handler.getPageClass());
+       }
+
+       /**
+        * Tests that plain text URLs to bookmarkable pages are not mapped.
+        */
+       @Test
+       public void bookmarkablePageForceEncryption()
+       {
+               IRequestHandler requestHandler = 
mapper.mapRequest(getRequest(Url.parse(PLAIN_BOOKMARKABLE_URL)));
+               assertNull(requestHandler);
+       }
+
+       /**
+        * Tests that we do not allow unencrypted URLs to IRequestListeners on 
bookmarkable pages, like:
+        * wicket/bookmarkable/my.package.page?0-0.ILinkListener-link
+        */
+       @Test
+       public void bookmarkablePageForceEncryptionOfRequestListener()
+       {
+               PageAndComponentProvider provider = new 
PageAndComponentProvider(Page2.class, "some:link");
+               IRequestHandler requestHandler = new 
BookmarkableListenerInterfaceRequestHandler(provider, ILinkListener.INTERFACE);
+               Url plainUrl = 
mapper.getDelegateMapper().mapHandler(requestHandler);
+               assertTrue("Plain text request listener URL for bookmarkable 
page does not start with: "
+                       + PLAIN_BOOKMARKABLE_URL + ": " + plainUrl.toString(),
+                       plainUrl.toString().startsWith(PLAIN_BOOKMARKABLE_URL));
+               assertNull(mapper.mapRequest(getRequest(plainUrl)));
+       }
+
+       /**
+        * Tests that URLs for page instances are encrypted (/wicket/page?5)
+        */
+       @Test
+       public void pageInstanceEncrypt()
+       {
+               MockPage page = new MockPage(5);
+               IRequestHandler requestHandler = new 
RenderPageRequestHandler(new PageProvider(page));
+
+               assertEquals(PLAIN_PAGE_INSTANCE_URL, 
mapper.getDelegateMapper().mapHandler(requestHandler).toString());
+               assertEquals(ENCRYPTED_PAGE_INSTANCE_URL, 
mapper.mapHandler(requestHandler).toString());
+       }
+
+       /**
+        * Make sure that encrypted page instance URLs are decrypted and the 
correct handler resolved.
+        */
+       @Test
+       public void pageInstanceDecrypt()
+       {
+               IRequestHandler requestHandler = 
mapper.mapRequest(getRequest(Url.parse(ENCRYPTED_PAGE_INSTANCE_URL)));
+
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+
+               assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
+               RenderPageRequestHandler handler = (RenderPageRequestHandler) 
requestHandler;
+               assertEquals(5, handler.getPageId().intValue());
+       }
+
+       /**
+        * Make sure that encrypted page instance URLs are decrypted and the 
correct handler resolved.
+        */
+       @Test
+       public void pageInstanceDecryptMultipleCryptoMapper()
+       {
+               IRequestHandler requestHandler = new CryptoMapper(mapper, 
tester.getApplication())
+                       
.mapRequest(getRequest(Url.parse(ENCRYPTED_PAGE_INSTANCE_URL)));
+
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+
+               assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
+               RenderPageRequestHandler handler = (RenderPageRequestHandler) 
requestHandler;
+               assertEquals(5, handler.getPageId().intValue());
+       }
+
+       /**
+        * Tests that plain text requests to a page instance URL are not mapped.
+        */
+       @Test
+       public void pageInstanceForceEncryption()
+       {
+               
assertNull(mapper.mapRequest(getRequest(Url.parse(PLAIN_PAGE_INSTANCE_URL))));
+       }
+
+       /**
+        * Tests that mounted pages are still accessible through their mounted 
URL.
+        */
+       @Test
+       public void mountedPage()
+       {
+               IRequestHandler requestHandler = new 
RenderPageRequestHandler(new PageProvider(Page1.class));
+
+               assertEquals(MOUNTED_URL, 
mapper.mapHandler(requestHandler).toString());
+
+               requestHandler = 
mapper.mapRequest(getRequest(Url.parse(MOUNTED_URL)));
+
+               assertNotNull(requestHandler);
+
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+
+               assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
+
+               assertEquals(Page1.class, ((RenderPageRequestHandler) 
requestHandler).getPageClass());
+       }
+
+       /**
+        * Tests that PageComponentInfo parameters are encrypted on Mounted 
pages
+        */
+       @Test
+       public void mountedPageRequestListenerParameter()
+       {
+               final String componentPath = "some:path:to:link";
+
+               PageAndComponentProvider provider = new 
PageAndComponentProvider(Page1.class, componentPath);
+               IRequestHandler requestHandler = new 
ListenerInterfaceRequestHandler(provider, ILinkListener.INTERFACE);
+
+               Url plainUrl = 
mapper.getDelegateMapper().mapHandler(requestHandler);
+               assertTrue(plainUrl.toString().startsWith(MOUNTED_URL));
+
+               /*
+                * Do not allow unencrypted request listener urls to mounted 
pages.
+                */
+               assertNull(mapper.mapRequest(getRequest(plainUrl)));
+
+               /*
+                * Test encryption of request listener parameter.
+                */
+               Url encryptedUrl = mapper.mapHandler(requestHandler);
+
+               assertEquals(Url.parse(MOUNTED_URL).getSegments(), 
encryptedUrl.getSegments());
+               assertTrue(encryptedUrl.getQueryParameters().size() > 0);
+
+               for (Url.QueryParameter qp : encryptedUrl.getQueryParameters())
+               {
+                       if (Strings.isEmpty(qp.getValue()))
+                       {
+                               PageComponentInfo pci = 
PageComponentInfo.parse(qp.getName());
+                               assertNull("PageComponentInfo query parameter 
not encrypted", pci);
+                       }
+               }
+
+               requestHandler = mapper.mapRequest(getRequest(encryptedUrl));
+
+               assertNotNull(requestHandler);
+
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+
+               assertThat(requestHandler, 
instanceOf(ListenerInterfaceRequestHandler.class));
+
+               ListenerInterfaceRequestHandler handler = 
(ListenerInterfaceRequestHandler) requestHandler;
+               assertEquals(componentPath, handler.getComponentPath());
+               assertEquals(Page1.class, handler.getPageClass());
+
+               /*
+                * We anticipate that sometimes multiple cryptomappers will be 
used. It should still work in these situations.
+                */
+               requestHandler = new CryptoMapper(mapper, 
tester.getApplication())
+                       .mapRequest(getRequest(encryptedUrl));
+
+               assertNotNull(requestHandler);
+
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+
+               assertThat(requestHandler, 
instanceOf(ListenerInterfaceRequestHandler.class));
+
+               handler = (ListenerInterfaceRequestHandler) requestHandler;
+               assertEquals(componentPath, handler.getComponentPath());
+               assertEquals(Page1.class, handler.getPageClass());
+       }
+
+       /**
+        * Tests that the compatability score is correctly calculated from 
wrapped mapper.
+        */
+       @Test
+       public void compatabilityScore()
+       {
+               int delegateHomePageScore = 
mapper.getDelegateMapper().getCompatibilityScore(
+                       getRequest(Url.parse("")));
+               int cryptoHomePageScore = mapper.getCompatibilityScore(
+                       getRequest(Url.parse("")));
+               assertEquals(delegateHomePageScore, cryptoHomePageScore);
+
+               int delegateBookmarkableScore = 
mapper.getDelegateMapper().getCompatibilityScore(
+                       getRequest(Url.parse(PLAIN_BOOKMARKABLE_URL)));
+               int cryptoBookmarkableScore = mapper.getCompatibilityScore(
+                       getRequest(Url.parse(ENCRYPTED_BOOKMARKABLE_URL)));
+               assertEquals(delegateBookmarkableScore, 
cryptoBookmarkableScore);
+
+               int delegatePageInstanceScore = 
mapper.getDelegateMapper().getCompatibilityScore(
+                       getRequest(Url.parse(PLAIN_PAGE_INSTANCE_URL)));
+               int cryptoPageInstanceScore = mapper.getCompatibilityScore(
+                       getRequest(Url.parse(ENCRYPTED_PAGE_INSTANCE_URL)));
+               assertEquals(delegatePageInstanceScore, 
cryptoPageInstanceScore);
        }
 
        /**
@@ -175,7 +435,7 @@ public class CryptoMapperTest extends AbstractMapperTest
        @Test
        public void pageParameters()
        {
-               String expectedEncrypted = 
"ywKWg-Qpk7YQBiYCmj9MaAJSIV1gtssNinjiALijtet62VMQc2-sMK_RchttkidUpYM_cplXKeZSfGxBkvWzH_E_zWv4Ii7MNSm5nXKno7o/ywK6c/MK_c0/nji3c/Qpk1b/XKnba/c2-cd";
+               String expectedEncrypted = 
"L7ExSNbPC4sb6TPJDblCAopL53TWmZP5y7BQEaJSJAC05HXod5M5U7gT2yNT0lK5L6L09ZAOoZkGyUhseyPrC4S5tqUUrV6zipc4_Ni877FDOOoE5C_Cd7YCyK1xSScpVhno6LeBz2wiu5oWyf7hB1RKcv6zkhEBmbx8vU7K7-e4xe1_LO8Y3fhEjMSQyU9BVh7Uz4HKzkR2OxFo5LaDzQ/L7E59/yPr6a/5L6ae/OxF2c";
 
                PageParameters expectedParameters = new PageParameters();
                expectedParameters.add("namedKey1", "namedValue1");
@@ -183,47 +443,18 @@ public class CryptoMapperTest extends AbstractMapperTest
                expectedParameters.set(0, "indexedValue1");
                expectedParameters.set(1, "indexedValue2");
                RenderPageRequestHandler renderPageRequestHandler = new 
RenderPageRequestHandler(
-                       new PageProvider(DummyHomePage.class, 
expectedParameters));
+                       new PageProvider(Page2.class, expectedParameters));
                Url url = mapper.mapHandler(renderPageRequestHandler);
-               // System.err.println(url.toString());
                assertEquals(expectedEncrypted, url.toString());
 
                Request request = getRequest(url);
                IRequestHandler requestHandler = mapper.mapRequest(request);
-               assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
-               requestHandler = 
((RequestSettingRequestHandler)requestHandler).getDelegateHandler();
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
                assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
 
-               RenderPageRequestHandler handler = 
(RenderPageRequestHandler)requestHandler;
-               assertEquals(DummyHomePage.class, handler.getPageClass());
-               PageParameters actualParameters = handler.getPageParameters();
-               assertEquals(expectedParameters, actualParameters);
-       }
-
-       /**
-        * When the home page url is requested, with parameters, the url will 
contain only page
-        * parameters. It should not be encrypted, otherwise we get needless 
redirects.
-        */
-       @Test
-       public void homePageWithParameters()
-       {
-               String expectedEncrypted = "?namedKey1=namedValue1";
-               PageParameters expectedParameters = new PageParameters();
-               expectedParameters.add("namedKey1", "namedValue1");
-
-               RenderPageRequestHandler renderPageRequestHandler = new 
RenderPageRequestHandler(
-                       new PageProvider(tester.getApplication().getHomePage(), 
expectedParameters));
-               Url url = mapper.mapHandler(renderPageRequestHandler);
-               assertEquals(expectedEncrypted, url.toString());
-
-               Request request = getRequest(url);
-               IRequestHandler requestHandler = mapper.mapRequest(request);
-               assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
-               requestHandler = 
((RequestSettingRequestHandler)requestHandler).getDelegateHandler();
-               assertThat(requestHandler, 
instanceOf(RenderPageRequestHandler.class));
-
-               RenderPageRequestHandler handler = 
(RenderPageRequestHandler)requestHandler;
-               assertEquals(tester.getApplication().getHomePage(), 
handler.getPageClass());
+               RenderPageRequestHandler handler = (RenderPageRequestHandler) 
requestHandler;
+               assertEquals(Page2.class, handler.getPageClass());
                PageParameters actualParameters = handler.getPageParameters();
                assertEquals(expectedParameters, actualParameters);
        }
@@ -255,10 +486,10 @@ public class CryptoMapperTest extends AbstractMapperTest
 
                IRequestHandler requestHandler = mapper.mapRequest(request);
 
-               assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
-               requestHandler = 
((RequestSettingRequestHandler)requestHandler).getDelegateHandler();
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
                assertThat(requestHandler, 
instanceOf(ResourceReferenceRequestHandler.class));
-               ResourceReferenceRequestHandler handler = 
(ResourceReferenceRequestHandler)requestHandler;
+               ResourceReferenceRequestHandler handler = 
(ResourceReferenceRequestHandler) requestHandler;
 
                assertEquals(getClass(), 
handler.getResourceReference().getScope());
                assertEquals("crypt/crypt.txt", 
handler.getResourceReference().getName());
@@ -280,10 +511,10 @@ public class CryptoMapperTest extends AbstractMapperTest
 
                IRequestHandler requestHandler = mapper.mapRequest(request);
 
-               assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
-               requestHandler = 
((RequestSettingRequestHandler)requestHandler).getDelegateHandler();
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
                assertThat(requestHandler, 
instanceOf(ResourceReferenceRequestHandler.class));
-               ResourceReferenceRequestHandler handler = 
(ResourceReferenceRequestHandler)requestHandler;
+               ResourceReferenceRequestHandler handler = 
(ResourceReferenceRequestHandler) requestHandler;
 
                assertEquals(getClass(), 
handler.getResourceReference().getScope());
                assertEquals("crypt/modified-crypt.txt", 
handler.getResourceReference().getName());
@@ -306,10 +537,9 @@ public class CryptoMapperTest extends AbstractMapperTest
 
                IRequestHandler requestHandler = mapper.mapRequest(request);
 
-               assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
-               requestHandler = 
((RequestSettingRequestHandler)requestHandler).getDelegateHandler();
-               assertThat(requestHandler, 
instanceOf(ResourceReferenceRequestHandler.class));
-               ResourceReferenceRequestHandler handler = 
(ResourceReferenceRequestHandler)requestHandler;
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
+               ResourceReferenceRequestHandler handler = 
(ResourceReferenceRequestHandler) requestHandler;
 
                assertEquals(getClass(), 
handler.getResourceReference().getScope());
                assertEquals("crypt/more/more-crypt.txt", 
handler.getResourceReference().getName());
@@ -332,10 +562,10 @@ public class CryptoMapperTest extends AbstractMapperTest
 
                IRequestHandler requestHandler = mapper.mapRequest(request);
 
-               assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
-               requestHandler = 
((RequestSettingRequestHandler)requestHandler).getDelegateHandler();
+               assertNotNull(requestHandler);
+               requestHandler = unwrapRequestHandlerDelegate(requestHandler);
                assertThat(requestHandler, 
instanceOf(ResourceReferenceRequestHandler.class));
-               ResourceReferenceRequestHandler handler = 
(ResourceReferenceRequestHandler)requestHandler;
+               ResourceReferenceRequestHandler handler = 
(ResourceReferenceRequestHandler) requestHandler;
 
                assertEquals(getClass(), 
handler.getResourceReference().getScope());
                assertEquals("less-crypt.txt", 
handler.getResourceReference().getName());
@@ -362,7 +592,43 @@ public class CryptoMapperTest extends AbstractMapperTest
 
                assertThat(requestHandler, 
instanceOf(RequestSettingRequestHandler.class));
 
-               assertEquals("foo", 
((RequestSettingRequestHandler)requestHandler).getRequest().getUrl()
-                       .getQueryParameterValue("q").toString());
+               assertEquals("foo", ((RequestSettingRequestHandler) 
requestHandler).getRequest()
+                       .getUrl()
+                       .getQueryParameterValue("q")
+                       .toString());
+       }
+
+       private static IRequestHandler 
unwrapRequestHandlerDelegate(IRequestHandler handler)
+       {
+               while (handler instanceof IRequestHandlerDelegate)
+               {
+                       handler = ((IRequestHandlerDelegate) 
handler).getDelegateHandler();
+               }
+
+               return handler;
+       }
+
+       /**
+        * Page that is mounted
+        */
+       public static class Page1 extends WebPage
+       {
+               @Override
+               public IMarkupFragment getMarkup()
+               {
+                       return Markup.of("<html><body></body></html>");
+               }
+       }
+
+       /**
+        * Page that is not mounted
+        */
+       public static class Page2 extends WebPage
+       {
+               @Override
+               public IMarkupFragment getMarkup()
+               {
+                       return Markup.of("<html><body></body></html>");
+               }
        }
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/ded3c583/wicket-core/src/test/java/org/apache/wicket/settings/ISecuritySettingsTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/settings/ISecuritySettingsTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/settings/ISecuritySettingsTest.java
index 2f388e7..db9eaff 100644
--- 
a/wicket-core/src/test/java/org/apache/wicket/settings/ISecuritySettingsTest.java
+++ 
b/wicket-core/src/test/java/org/apache/wicket/settings/ISecuritySettingsTest.java
@@ -18,6 +18,8 @@ package org.apache.wicket.settings;
 
 import org.apache.wicket.MockPageWithLink;
 import org.apache.wicket.WicketTestCase;
+import org.apache.wicket.core.request.handler.BookmarkablePageRequestHandler;
+import org.apache.wicket.core.request.handler.PageProvider;
 import org.apache.wicket.core.request.mapper.CryptoMapper;
 import org.apache.wicket.markup.IMarkupFragment;
 import org.apache.wicket.markup.Markup;
@@ -26,6 +28,7 @@ import org.apache.wicket.markup.html.link.Link;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.protocol.https.HttpsConfig;
 import org.apache.wicket.protocol.https.HttpsMapper;
+import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.flow.RedirectToUrlException;
 import org.junit.Assert;
 import org.junit.Test;
@@ -91,9 +94,46 @@ public class ISecuritySettingsTest extends WicketTestCase
        @Test
        public void enforceMountsWithCryptoMapper()
        {
-           WebApplication app = tester.getApplication();
-           app.setRootRequestMapper(new 
CryptoMapper(app.getRootRequestMapper(), app));
-           enforceMounts();
+               WebApplication app = tester.getApplication();
+
+               IRequestHandler handler = new 
BookmarkablePageRequestHandler(new PageProvider(UnknownPage.class));
+
+               String plainTextNonMountedUrl = 
tester.urlFor(handler).toString();
+
+               assertTrue("Plain text non mounted url should start with 
wicket/bookmarkable/: " + plainTextNonMountedUrl, 
plainTextNonMountedUrl.startsWith("wicket/bookmarkable/"));
+
+               tester.executeUrl(plainTextNonMountedUrl);
+               tester.assertRenderedPage(UnknownPage.class);
+
+               app.setRootRequestMapper(new 
CryptoMapper(app.getRootRequestMapper(), app));
+
+               /*
+                * Execute dummy request to get WicketTester to re-initialise 
with CryptoMapper in place.
+                */
+               tester.executeUrl("");
+
+               String encryptedNonMountedUrl = 
tester.urlFor(handler).toString();
+
+               assertFalse("Encrypted URL should not start with 
wicket/bookmarkable/" + encryptedNonMountedUrl, 
encryptedNonMountedUrl.startsWith("wicket/bookmarkable/"));
+
+               tester.executeUrl(plainTextNonMountedUrl);
+               assertNull(tester.getLastRenderedPage());
+               tester.executeUrl(encryptedNonMountedUrl);
+               tester.assertRenderedPage(UnknownPage.class);
+
+               app.mountPackage("unknown", UnknownPage.class);
+
+               tester.executeUrl(plainTextNonMountedUrl);
+               assertNull(tester.getLastRenderedPage());
+               tester.executeUrl(encryptedNonMountedUrl);
+               tester.assertRenderedPage(UnknownPage.class);
+
+               app.getSecuritySettings().setEnforceMounts(true);
+
+               tester.executeUrl(plainTextNonMountedUrl);
+               assertNull(tester.getLastRenderedPage());
+               tester.executeUrl(encryptedNonMountedUrl);
+               assertNull(tester.getLastRenderedPage());
        }
        
        /**

Reply via email to