test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java
branchhttp-client-branch
changeset 55764 34d7cc00f87a
child 55838 12a64276cc96
equal deleted inserted replaced
55763:634d8e14c172 55764:34d7cc00f87a
       
     1 /*
       
     2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @summary Basic security checks for WebSocket URI from the Builder
       
    27  * @compile ../DummyWebSocketServer.java ../../ProxyServer.java
       
    28  * @run testng/othervm/java.security.policy=httpclient.policy WSURLPermissionTest
       
    29  */
       
    30 
       
    31 import java.io.IOException;
       
    32 import java.net.InetSocketAddress;
       
    33 import java.net.Proxy;
       
    34 import java.net.ProxySelector;
       
    35 import java.net.SocketAddress;
       
    36 import java.net.URI;
       
    37 import java.net.URLPermission;
       
    38 import java.security.AccessControlContext;
       
    39 import java.security.AccessController;
       
    40 import java.security.Permission;
       
    41 import java.security.Permissions;
       
    42 import java.security.PrivilegedActionException;
       
    43 import java.security.PrivilegedExceptionAction;
       
    44 import java.security.ProtectionDomain;
       
    45 import java.util.List;
       
    46 import java.util.concurrent.ExecutionException;
       
    47 import jdk.incubator.http.HttpClient;
       
    48 import jdk.incubator.http.WebSocket;
       
    49 import org.testng.annotations.AfterTest;
       
    50 import org.testng.annotations.BeforeTest;
       
    51 import org.testng.annotations.DataProvider;
       
    52 import org.testng.annotations.Test;
       
    53 import static org.testng.Assert.*;
       
    54 
       
    55 public class WSURLPermissionTest {
       
    56 
       
    57     static AccessControlContext withPermissions(Permission... perms) {
       
    58         Permissions p = new Permissions();
       
    59         for (Permission perm : perms) {
       
    60             p.add(perm);
       
    61         }
       
    62         ProtectionDomain pd = new ProtectionDomain(null, p);
       
    63         return new AccessControlContext(new ProtectionDomain[]{ pd });
       
    64     }
       
    65 
       
    66     static AccessControlContext noPermissions() {
       
    67         return withPermissions(/*empty*/);
       
    68     }
       
    69 
       
    70     URI wsURI;
       
    71     DummyWebSocketServer webSocketServer;
       
    72     InetSocketAddress proxyAddress;
       
    73 
       
    74     @BeforeTest
       
    75     public void setup() throws Exception {
       
    76         ProxyServer proxyServer = new ProxyServer(0, true);
       
    77         proxyAddress = new InetSocketAddress("127.0.0.1", proxyServer.getPort());
       
    78         webSocketServer = new DummyWebSocketServer();
       
    79         webSocketServer.open();
       
    80         wsURI = webSocketServer.getURI();
       
    81 
       
    82         System.out.println("Proxy Server: " + proxyAddress);
       
    83         System.out.println("DummyWebSocketServer: " + wsURI);
       
    84     }
       
    85 
       
    86     @AfterTest
       
    87     public void teardown() {
       
    88         webSocketServer.close();
       
    89     }
       
    90 
       
    91     static class NoOpListener implements WebSocket.Listener {}
       
    92     static final WebSocket.Listener noOpListener = new NoOpListener();
       
    93 
       
    94     @DataProvider(name = "passingScenarios")
       
    95     public Object[][] passingScenarios() {
       
    96         HttpClient noProxyClient = HttpClient.newHttpClient();
       
    97         return new Object[][]{
       
    98             { (PrivilegedExceptionAction<?>)() -> {
       
    99                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   100                               .buildAsync().get().abort();
       
   101                  return null; },                                       // no actions
       
   102               new URLPermission[] { new URLPermission(wsURI.toString()) },
       
   103               "0"  /* for log file identification */ },
       
   104 
       
   105             { (PrivilegedExceptionAction<?>)() -> {
       
   106                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   107                               .buildAsync().get().abort();
       
   108                  return null; },                                       // scheme wildcard
       
   109               new URLPermission[] { new URLPermission("ws://*") },
       
   110               "0.1" },
       
   111 
       
   112             { (PrivilegedExceptionAction<?>)() -> {
       
   113                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   114                               .buildAsync().get().abort();
       
   115                  return null; },                                       // port wildcard
       
   116               new URLPermission[] { new URLPermission("ws://"+wsURI.getHost()+":*") },
       
   117               "0.2" },
       
   118 
       
   119             { (PrivilegedExceptionAction<?>)() -> {
       
   120                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   121                               .buildAsync().get().abort();
       
   122                  return null; },                                        // empty actions
       
   123               new URLPermission[] { new URLPermission(wsURI.toString(), "") },
       
   124               "1" },
       
   125 
       
   126             { (PrivilegedExceptionAction<?>)() -> {
       
   127                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   128                               .buildAsync().get().abort();
       
   129                  return null; },                                         // colon
       
   130               new URLPermission[] { new URLPermission(wsURI.toString(), ":") },
       
   131               "2" },
       
   132 
       
   133             { (PrivilegedExceptionAction<?>)() -> {
       
   134                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   135                               .buildAsync().get().abort();
       
   136                  return null; },                                        // wildcard
       
   137               new URLPermission[] { new URLPermission(wsURI.toString(), "*:*") },
       
   138               "3" },
       
   139 
       
   140             // WS permission checking is agnostic of method, any/none will do
       
   141             { (PrivilegedExceptionAction<?>)() -> {
       
   142                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   143                               .buildAsync().get().abort();
       
   144                  return null; },                                        // specific method
       
   145               new URLPermission[] { new URLPermission(wsURI.toString(), "GET") },
       
   146               "3.1" },
       
   147 
       
   148             { (PrivilegedExceptionAction<?>)() -> {
       
   149                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   150                               .buildAsync().get().abort();
       
   151                  return null; },                                        // specific method
       
   152               new URLPermission[] { new URLPermission(wsURI.toString(), "POST") },
       
   153               "3.2" },
       
   154 
       
   155             { (PrivilegedExceptionAction<?>)() -> {
       
   156                 URI uriWithPath = wsURI.resolve("/path/x");
       
   157                  noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
       
   158                               .buildAsync().get().abort();
       
   159                  return null; },                                       // path
       
   160               new URLPermission[] { new URLPermission(wsURI.resolve("/path/x").toString()) },
       
   161               "4" },
       
   162 
       
   163             { (PrivilegedExceptionAction<?>)() -> {
       
   164                 URI uriWithPath = wsURI.resolve("/path/x");
       
   165                  noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
       
   166                               .buildAsync().get().abort();
       
   167                  return null; },                                       // same dir wildcard
       
   168               new URLPermission[] { new URLPermission(wsURI.resolve("/path/*").toString()) },
       
   169               "5" },
       
   170 
       
   171             { (PrivilegedExceptionAction<?>)() -> {
       
   172                 URI uriWithPath = wsURI.resolve("/path/x");
       
   173                  noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
       
   174                               .buildAsync().get().abort();
       
   175                  return null; },                                       // recursive
       
   176               new URLPermission[] { new URLPermission(wsURI.resolve("/path/-").toString()) },
       
   177               "6" },
       
   178 
       
   179             { (PrivilegedExceptionAction<?>)() -> {
       
   180                 URI uriWithPath = wsURI.resolve("/path/x");
       
   181                  noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
       
   182                               .buildAsync().get().abort();
       
   183                  return null; },                                       // recursive top
       
   184               new URLPermission[] { new URLPermission(wsURI.resolve("/-").toString()) },
       
   185               "7" },
       
   186 
       
   187             { (PrivilegedExceptionAction<?>)() -> {
       
   188                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   189                               .header("A-Header", "A-Value")  // header
       
   190                               .buildAsync().get().abort();
       
   191                  return null; },
       
   192               new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header") },
       
   193               "8" },
       
   194 
       
   195             { (PrivilegedExceptionAction<?>)() -> {
       
   196                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   197                               .header("A-Header", "A-Value")  // header
       
   198                               .buildAsync().get().abort();
       
   199                  return null; },                                        // wildcard
       
   200               new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
       
   201               "9" },
       
   202 
       
   203             { (PrivilegedExceptionAction<?>)() -> {
       
   204                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   205                               .header("A-Header", "A-Value")  // headers
       
   206                               .header("B-Header", "B-Value")  // headers
       
   207                               .buildAsync().get().abort();
       
   208                  return null; },
       
   209               new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header,B-Header") },
       
   210               "10" },
       
   211 
       
   212             { (PrivilegedExceptionAction<?>)() -> {
       
   213                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   214                               .header("A-Header", "A-Value")  // headers
       
   215                               .header("B-Header", "B-Value")  // headers
       
   216                               .buildAsync().get().abort();
       
   217                  return null; },                                        // wildcard
       
   218               new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
       
   219               "11" },
       
   220 
       
   221             { (PrivilegedExceptionAction<?>)() -> {
       
   222                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   223                               .header("A-Header", "A-Value")  // headers
       
   224                               .header("B-Header", "B-Value")  // headers
       
   225                               .buildAsync().get().abort();
       
   226                  return null; },                                        // wildcards
       
   227               new URLPermission[] { new URLPermission(wsURI.toString(), "*:*") },
       
   228               "12" },
       
   229 
       
   230             { (PrivilegedExceptionAction<?>)() -> {
       
   231                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   232                               .header("A-Header", "A-Value")  // multi-value
       
   233                               .header("A-Header", "B-Value")  // headers
       
   234                               .buildAsync().get().abort();
       
   235                  return null; },                                        // wildcard
       
   236               new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
       
   237               "13" },
       
   238 
       
   239             { (PrivilegedExceptionAction<?>)() -> {
       
   240                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   241                               .header("A-Header", "A-Value")  // multi-value
       
   242                               .header("A-Header", "B-Value")  // headers
       
   243                               .buildAsync().get().abort();
       
   244                  return null; },                                        // single grant
       
   245               new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header") },
       
   246               "14" },
       
   247 
       
   248             // client with a DIRECT proxy
       
   249             { (PrivilegedExceptionAction<?>)() -> {
       
   250                  ProxySelector ps = ProxySelector.of(null);
       
   251                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   252                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   253                        .buildAsync().get().abort();
       
   254                  return null; },
       
   255               new URLPermission[] { new URLPermission(wsURI.toString()) },
       
   256               "15" },
       
   257 
       
   258             // client with a SOCKS proxy! ( expect implementation to ignore SOCKS )
       
   259             { (PrivilegedExceptionAction<?>)() -> {
       
   260                  ProxySelector ps = new ProxySelector() {
       
   261                      @Override public List<Proxy> select(URI uri) {
       
   262                          return List.of(new Proxy(Proxy.Type.SOCKS, proxyAddress)); }
       
   263                      @Override
       
   264                      public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { }
       
   265                  };
       
   266                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   267                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   268                        .buildAsync().get().abort();
       
   269                  return null; },
       
   270               new URLPermission[] { new URLPermission(wsURI.toString()) },
       
   271               "16" },
       
   272 
       
   273             // client with a HTTP/HTTPS proxy
       
   274             { (PrivilegedExceptionAction<?>)() -> {
       
   275                  assert proxyAddress != null;
       
   276                  ProxySelector ps = ProxySelector.of(proxyAddress);
       
   277                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   278                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   279                        .buildAsync().get().abort();
       
   280                  return null; },
       
   281               new URLPermission[] {
       
   282                     new URLPermission(wsURI.toString()),            // CONNECT action string
       
   283                     new URLPermission("socket://"+proxyAddress.getHostName()
       
   284                                       +":"+proxyAddress.getPort(), "CONNECT")},
       
   285               "17" },
       
   286 
       
   287             { (PrivilegedExceptionAction<?>)() -> {
       
   288                  assert proxyAddress != null;
       
   289                  ProxySelector ps = ProxySelector.of(proxyAddress);
       
   290                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   291                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   292                        .buildAsync().get().abort();
       
   293                  return null; },
       
   294               new URLPermission[] {
       
   295                     new URLPermission(wsURI.toString()),            // no action string
       
   296                     new URLPermission("socket://"+proxyAddress.getHostName()
       
   297                                       +":"+proxyAddress.getPort())},
       
   298               "18" },
       
   299 
       
   300             { (PrivilegedExceptionAction<?>)() -> {
       
   301                  assert proxyAddress != null;
       
   302                  ProxySelector ps = ProxySelector.of(proxyAddress);
       
   303                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   304                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   305                        .buildAsync().get().abort();
       
   306                  return null; },
       
   307               new URLPermission[] {
       
   308                     new URLPermission(wsURI.toString()),            // wildcard headers
       
   309                     new URLPermission("socket://"+proxyAddress.getHostName()
       
   310                                       +":"+proxyAddress.getPort(), "CONNECT:*")},
       
   311               "19" },
       
   312 
       
   313             { (PrivilegedExceptionAction<?>)() -> {
       
   314                  assert proxyAddress != null;
       
   315                  CountingProxySelector ps = CountingProxySelector.of(proxyAddress);
       
   316                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   317                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   318                        .buildAsync().get().abort();
       
   319                  assertEquals(ps.count(), 1);  // ps.select only invoked once
       
   320                  return null; },
       
   321               new URLPermission[] {
       
   322                     new URLPermission(wsURI.toString()),            // empty headers
       
   323                     new URLPermission("socket://"+proxyAddress.getHostName()
       
   324                                       +":"+proxyAddress.getPort(), "CONNECT:")},
       
   325               "20" },
       
   326 
       
   327             { (PrivilegedExceptionAction<?>)() -> {
       
   328                  assert proxyAddress != null;
       
   329                  ProxySelector ps = ProxySelector.of(proxyAddress);
       
   330                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   331                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   332                        .buildAsync().get().abort();
       
   333                  return null; },
       
   334               new URLPermission[] {
       
   335                     new URLPermission(wsURI.toString()),
       
   336                     new URLPermission("socket://*")},               // wildcard socket URL
       
   337               "21" },
       
   338 
       
   339             { (PrivilegedExceptionAction<?>)() -> {
       
   340                  assert proxyAddress != null;
       
   341                  ProxySelector ps = ProxySelector.of(proxyAddress);
       
   342                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   343                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   344                        .buildAsync().get().abort();
       
   345                  return null; },
       
   346               new URLPermission[] {
       
   347                     new URLPermission("ws://*"),                    // wildcard ws URL
       
   348                     new URLPermission("socket://*")},               // wildcard socket URL
       
   349               "22" },
       
   350 
       
   351         };
       
   352     }
       
   353 
       
   354     @Test(dataProvider = "passingScenarios")
       
   355     public void testWithNoSecurityManager(PrivilegedExceptionAction<?> action,
       
   356                                           URLPermission[] unused,
       
   357                                           String dataProviderId)
       
   358         throws Exception
       
   359     {
       
   360         // sanity ( no security manager )
       
   361         System.setSecurityManager(null);
       
   362         try {
       
   363             AccessController.doPrivileged(action);
       
   364         } finally {
       
   365             System.setSecurityManager(new SecurityManager());
       
   366         }
       
   367     }
       
   368 
       
   369     @Test(dataProvider = "passingScenarios")
       
   370     public void testWithAllPermissions(PrivilegedExceptionAction<?> action,
       
   371                                        URLPermission[] unused,
       
   372                                        String dataProviderId)
       
   373         throws Exception
       
   374     {
       
   375         // Run with all permissions, i.e. no further restrictions than test's AllPermission
       
   376         assert System.getSecurityManager() != null;
       
   377         AccessController.doPrivileged(action);
       
   378     }
       
   379 
       
   380     @Test(dataProvider = "passingScenarios")
       
   381     public void testWithMinimalPermissions(PrivilegedExceptionAction<?> action,
       
   382                                            URLPermission[] perms,
       
   383                                            String dataProviderId)
       
   384         throws Exception
       
   385     {
       
   386         // Run with minimal permissions, i.e. just what is required
       
   387         assert System.getSecurityManager() != null;
       
   388         AccessControlContext minimalACC = withPermissions(perms);
       
   389         AccessController.doPrivileged(action, minimalACC);
       
   390     }
       
   391 
       
   392     @Test(dataProvider = "passingScenarios")
       
   393     public void testWithNoPermissions(PrivilegedExceptionAction<?> action,
       
   394                                       URLPermission[] unused,
       
   395                                       String dataProviderId)
       
   396         throws Exception
       
   397     {
       
   398         // Run with NO permissions, i.e. expect SecurityException
       
   399         assert System.getSecurityManager() != null;
       
   400         try {
       
   401             AccessController.doPrivileged(action, noPermissions());
       
   402             fail("EXPECTED SecurityException");
       
   403         } catch (PrivilegedActionException expected) {
       
   404             Throwable t = expected.getCause();
       
   405             if (t instanceof ExecutionException)
       
   406                 t = t.getCause();
       
   407 
       
   408             if (t instanceof SecurityException)
       
   409                 System.out.println("Caught expected SE:" + expected);
       
   410             else
       
   411                 fail("Expected SecurityException, but got: " + t);
       
   412         }
       
   413     }
       
   414 
       
   415     // --- Negative tests ---
       
   416 
       
   417     @DataProvider(name = "failingScenarios")
       
   418     public Object[][] failingScenarios() {
       
   419         HttpClient noProxyClient = HttpClient.newHttpClient();
       
   420         return new Object[][]{
       
   421             { (PrivilegedExceptionAction<?>) () -> {
       
   422                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   423                               .buildAsync().get().abort();
       
   424                  return null;
       
   425               },
       
   426               new URLPermission[]{ /* no permissions */ },
       
   427               "50"  /* for log file identification */},
       
   428 
       
   429             { (PrivilegedExceptionAction<?>) () -> {
       
   430                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   431                               .buildAsync().get().abort();
       
   432                  return null;
       
   433               },                                        // wrong scheme
       
   434               new URLPermission[]{ new URLPermission("http://*") },
       
   435               "51" },
       
   436 
       
   437             { (PrivilegedExceptionAction<?>) () -> {
       
   438                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   439                               .buildAsync().get().abort();
       
   440                  return null;
       
   441               },                                        // wrong scheme
       
   442               new URLPermission[]{ new URLPermission("socket://*") },
       
   443               "52" },
       
   444 
       
   445             { (PrivilegedExceptionAction<?>) () -> {
       
   446                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   447                               .buildAsync().get().abort();
       
   448                  return null;
       
   449               },                                        // wrong host
       
   450               new URLPermission[]{ new URLPermission("ws://foo.com/") },
       
   451               "53" },
       
   452 
       
   453             { (PrivilegedExceptionAction<?>) () -> {
       
   454                  noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   455                               .buildAsync().get().abort();
       
   456                  return null;
       
   457               },                                        // wrong port
       
   458               new URLPermission[]{ new URLPermission("ws://"+ wsURI.getHost()+":5") },
       
   459               "54" },
       
   460 
       
   461             { (PrivilegedExceptionAction<?>) () -> {
       
   462                   noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   463                                .header("A-Header", "A-Value")
       
   464                                .buildAsync().get().abort();
       
   465                   return null;
       
   466               },                                                    // only perm to set B not A
       
   467               new URLPermission[] { new URLPermission(wsURI.toString(), "*:B-Header") },
       
   468               "55" },
       
   469 
       
   470             { (PrivilegedExceptionAction<?>) () -> {
       
   471                   noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
       
   472                                .header("A-Header", "A-Value")
       
   473                                .header("B-Header", "B-Value")
       
   474                                .buildAsync().get().abort();
       
   475                   return null;
       
   476               },                                                    // only perm to set B not A
       
   477               new URLPermission[] { new URLPermission(wsURI.toString(), "*:B-Header") },
       
   478               "56" },
       
   479 
       
   480             { (PrivilegedExceptionAction<?>)() -> {
       
   481                 URI uriWithPath = wsURI.resolve("/path/x");
       
   482                  noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
       
   483                               .buildAsync().get().abort();
       
   484                  return null; },                                    // wrong path
       
   485               new URLPermission[] { new URLPermission(wsURI.resolve("/aDiffPath/").toString()) },
       
   486               "57" },
       
   487 
       
   488             { (PrivilegedExceptionAction<?>)() -> {
       
   489                 URI uriWithPath = wsURI.resolve("/path/x");
       
   490                  noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
       
   491                               .buildAsync().get().abort();
       
   492                  return null; },                                    // more specific path
       
   493               new URLPermission[] { new URLPermission(wsURI.resolve("/path/x/y").toString()) },
       
   494               "58" },
       
   495 
       
   496             // client with a HTTP/HTTPS proxy
       
   497             { (PrivilegedExceptionAction<?>)() -> {
       
   498                  assert proxyAddress != null;
       
   499                  ProxySelector ps = ProxySelector.of(proxyAddress);
       
   500                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   501                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   502                        .buildAsync().get().abort();
       
   503                  return null; },                                    // missing proxy perm
       
   504               new URLPermission[] { new URLPermission(wsURI.toString()) },
       
   505               "100" },
       
   506 
       
   507             // client with a HTTP/HTTPS proxy
       
   508             { (PrivilegedExceptionAction<?>)() -> {
       
   509                  assert proxyAddress != null;
       
   510                  ProxySelector ps = ProxySelector.of(proxyAddress);
       
   511                  HttpClient client = HttpClient.newBuilder().proxy(ps).build();
       
   512                  client.newWebSocketBuilder(wsURI, noOpListener)
       
   513                        .buildAsync().get().abort();
       
   514                  return null; },
       
   515               new URLPermission[] {
       
   516                     new URLPermission(wsURI.toString()),            // missing proxy CONNECT
       
   517                     new URLPermission("socket://*", "GET") },
       
   518               "101" },
       
   519         };
       
   520     }
       
   521 
       
   522     @Test(dataProvider = "failingScenarios")
       
   523     public void testWithoutEnoughPermissions(PrivilegedExceptionAction<?> action,
       
   524                                              URLPermission[] perms,
       
   525                                              String dataProviderId)
       
   526         throws Exception
       
   527     {
       
   528         // Run without Enough permissions, i.e. expect SecurityException
       
   529         assert System.getSecurityManager() != null;
       
   530         AccessControlContext notEnoughPermsACC = withPermissions(perms);
       
   531         try {
       
   532             AccessController.doPrivileged(action, notEnoughPermsACC);
       
   533             fail("EXPECTED SecurityException");
       
   534         } catch (PrivilegedActionException expected) {
       
   535             Throwable t = expected.getCause();
       
   536             if (t instanceof ExecutionException)
       
   537                 t = t.getCause();
       
   538 
       
   539             if (t instanceof SecurityException)
       
   540                 System.out.println("Caught expected SE:" + expected);
       
   541             else
       
   542                 fail("Expected SecurityException, but got: " + t);
       
   543         }
       
   544     }
       
   545 
       
   546     /**
       
   547      * A Proxy Selector that wraps a ProxySelector.of(), and counts the number
       
   548      * of times its select method has been invoked. This can be used to ensure
       
   549      * that the Proxy Selector is invoked only once per WebSocket.Builder::buildAsync
       
   550      * invocation.
       
   551      */
       
   552     static class CountingProxySelector extends ProxySelector {
       
   553         private final ProxySelector proxySelector;
       
   554         private volatile int count; // 0
       
   555         private CountingProxySelector(InetSocketAddress proxyAddress) {
       
   556             proxySelector = ProxySelector.of(proxyAddress);
       
   557         }
       
   558 
       
   559         public static CountingProxySelector of(InetSocketAddress proxyAddress) {
       
   560             return new CountingProxySelector(proxyAddress);
       
   561         }
       
   562 
       
   563         int count() { return count; }
       
   564 
       
   565         @Override
       
   566         public List<Proxy> select(URI uri) {
       
   567             System.out.println("PS: uri");
       
   568             Throwable t = new Throwable();
       
   569             t.printStackTrace(System.out);
       
   570             count++;
       
   571             return proxySelector.select(uri);
       
   572         }
       
   573 
       
   574         @Override
       
   575         public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
       
   576             proxySelector.connectFailed(uri, sa, ioe);
       
   577         }
       
   578     }
       
   579 }