Mercurial > hg > openjdk > lambda > jdk
changeset 10204:dd0deeb04933
8014719: HttpClient/ProxyTest.java failing with IAE HttpURLPermission.parseURI
Reviewed-by: alanb, chegar
author | michaelm |
---|---|
date | Mon, 14 Oct 2013 22:09:15 +0100 |
parents | 3a5c987ff3a0 |
children | 94d4aa2fb414 7bc67bed3c14 |
files | src/share/classes/java/net/HostPortrange.java src/share/classes/java/net/HttpURLConnection.java src/share/classes/java/net/HttpURLPermission.java src/share/classes/java/net/URLPermission.java src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java src/share/classes/sun/security/tools/policytool/PolicyTool.java test/java/net/HttpURLPermission/HttpURLPermissionTest.java test/java/net/HttpURLPermission/URLTest.java test/java/net/HttpURLPermission/policy.1 test/java/net/HttpURLPermission/policy.2 test/java/net/HttpURLPermission/policy.3 test/java/net/URLPermission/URLPermissionTest.java test/java/net/URLPermission/URLTest.java test/java/net/URLPermission/policy.1 test/java/net/URLPermission/policy.2 test/java/net/URLPermission/policy.3 |
diffstat | 16 files changed, 1561 insertions(+), 1012 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/net/HostPortrange.java Mon Oct 14 22:09:15 2013 +0100 @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.net.*; +import java.util.Formatter; +import java.util.Locale; +import sun.net.util.IPAddressUtil; + +/** + * Parses a string containing a host/domain name and port range + */ +class HostPortrange { + + String hostname; + String scheme; + int[] portrange; + + boolean wildcard; + boolean literal; + boolean ipv6, ipv4; + static final int PORT_MIN = 0; + static final int PORT_MAX = (1 << 16) -1; + + boolean equals(HostPortrange that) { + return this.hostname.equals(that.hostname) + && this.portrange[0] == that.portrange[0] + && this.portrange[1] == that.portrange[1] + && this.wildcard == that.wildcard + && this.literal == that.literal; + } + + public int hashCode() { + return hostname.hashCode() + portrange[0] + portrange[1]; + } + + HostPortrange(String scheme, String str) { + // Parse the host name. A name has up to three components, the + // hostname, a port number, or two numbers representing a port + // range. "www.sun.com:8080-9090" is a valid host name. + + // With IPv6 an address can be 2010:836B:4179::836B:4179 + // An IPv6 address needs to be enclose in [] + // For ex: [2010:836B:4179::836B:4179]:8080-9090 + // Refer to RFC 2732 for more information. + + // first separate string into two fields: hoststr, portstr + String hoststr, portstr = null; + this.scheme = scheme; + + // check for IPv6 address + if (str.charAt(0) == '[') { + ipv6 = literal = true; + int rb = str.indexOf(']'); + if (rb != -1) { + hoststr = str.substring(1, rb); + } else { + throw new IllegalArgumentException("invalid IPv6 address: " + str); + } + int sep = str.indexOf(':', rb + 1); + if (sep != -1 && str.length() > sep) { + portstr = str.substring(sep + 1); + } + // need to normalize hoststr now + byte[] ip = IPAddressUtil.textToNumericFormatV6(hoststr); + if (ip == null) { + throw new IllegalArgumentException("illegal IPv6 address"); + } + StringBuilder sb = new StringBuilder(); + Formatter formatter = new Formatter(sb, Locale.US); + formatter.format("%02x%02x:%02x%02x:%02x%02x:%02x" + + "%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], + ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15]); + hostname = sb.toString(); + } else { + // not IPv6 therefore ':' is the port separator + + int sep = str.indexOf(':'); + if (sep != -1 && str.length() > sep) { + hoststr = str.substring(0, sep); + portstr = str.substring(sep + 1); + } else { + hoststr = sep == -1 ? str : str.substring(0, sep); + } + // is this a domain wildcard specification? + if (hoststr.lastIndexOf('*') > 0) { + throw new IllegalArgumentException("invalid host wildcard specification"); + } else if (hoststr.startsWith("*")) { + wildcard = true; + if (hoststr.equals("*")) { + hoststr = ""; + } else if (hoststr.startsWith("*.")) { + hoststr = hoststr.substring(1).toLowerCase(); // leave the '.' ? + } else { + throw new IllegalArgumentException("invalid host wildcard specification"); + } + } else { + // check if ipv4 (if rightmost label a number) + // The normal way to specify ipv4 is 4 decimal labels + // but actually three, two or single label formats valid also + // So, we recognise ipv4 by just testing the rightmost label + // being a number. + int lastdot = hoststr.lastIndexOf('.'); + if (lastdot != -1 && (hoststr.length() > 1)) { + boolean ipv4 = true; + + for (int i = lastdot + 1, len = hoststr.length(); i < len; i++) { + char c = hoststr.charAt(i); + if (c < '0' || c > '9') { + ipv4 = false; + break; + } + } + this.ipv4 = this.literal = ipv4; + if (ipv4) { + byte[] ip = IPAddressUtil.textToNumericFormatV4(hoststr); + if (ip == null) { + throw new IllegalArgumentException("illegal IPv4 address"); + } + StringBuilder sb = new StringBuilder(); + Formatter formatter = new Formatter(sb, Locale.US); + formatter.format("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + hoststr = sb.toString(); + } else { + // regular domain name + hoststr = hoststr.toLowerCase(); + } + } + } + hostname = hoststr; + } + + try { + portrange = parsePort(portstr); + } catch (Exception e) { + throw new IllegalArgumentException("invalid port range: " + portstr); + } + } + + public boolean literal() { + return literal; + } + + public boolean ipv4Literal() { + return ipv4; + } + + public boolean ipv6Literal() { + return ipv6; + } + + public String hostname() { + return hostname; + } + + public int[] portrange() { + return portrange; + } + + /** + * returns true if the hostname part started with * + * hostname returns the remaining part of the host component + * eg "*.foo.com" -> ".foo.com" or "*" -> "" + * + * @return + */ + public boolean wildcard() { + return wildcard; + } + + // these shouldn't leak outside the implementation + final static int[] HTTP_PORT = {80, 80}; + final static int[] HTTPS_PORT = {443, 443}; + final static int[] NO_PORT = {-1, -1}; + + int[] defaultPort() { + if (scheme.equals("http")) { + return HTTP_PORT; + } else if (scheme.equals("https")) { + return HTTPS_PORT; + } + return NO_PORT; + } + + int[] parsePort(String port) + { + + if (port == null || port.equals("")) { + return defaultPort(); + } + + if (port.equals("*")) { + return new int[] {PORT_MIN, PORT_MAX}; + } + + try { + int dash = port.indexOf('-'); + + if (dash == -1) { + int p = Integer.parseInt(port); + return new int[] {p, p}; + } else { + String low = port.substring(0, dash); + String high = port.substring(dash+1); + int l,h; + + if (low.equals("")) { + l = PORT_MIN; + } else { + l = Integer.parseInt(low); + } + + if (high.equals("")) { + h = PORT_MAX; + } else { + h = Integer.parseInt(high); + } + if (l < 0 || h < 0 || h<l) { + return defaultPort(); + } + return new int[] {l, h}; + } + } catch (IllegalArgumentException e) { + return defaultPort(); + } + } +}
--- a/src/share/classes/java/net/HttpURLConnection.java Mon Oct 14 09:52:36 2013 -0700 +++ b/src/share/classes/java/net/HttpURLConnection.java Mon Oct 14 22:09:15 2013 +0100 @@ -57,7 +57,7 @@ * attempt to open a connection, the caller must possess either:- * <ul><li>a "connect" {@link SocketPermission} to the host/port combination of the * destination URL or</li> - * <li>a {@link HttpURLPermission} that permits this request.</li> + * <li>a {@link URLPermission} that permits this request.</li> * </ul><p> * If automatic redirection is enabled, and this request is redirected to another * destination, then the caller must also have permission to connect to the
--- a/src/share/classes/java/net/HttpURLPermission.java Mon Oct 14 09:52:36 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,408 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.net; - -import java.io.ObjectInputStream; -import java.io.IOException; -import java.util.List; -import java.util.ArrayList; -import java.util.Collections; -import java.security.Permission; - -/** - * Represents permission to access a resource or set of resources defined by a - * given http or https url, and for a given set of user-settable request methods - * and request headers. The <i>name</i> of the permission is the url string. - * The <i>actions</i> string is a concatenation of the request methods and headers. - * The range of method and header names is not restricted by this class. - * <p><b>The url</b><p> - * The url string is also used to instantiate a {@link URI} object which is - * used for comparison with other HttpURLPermission instances. Therefore, any - * references in this specification to url, mean this URI object. - * The path component of the url comprises a sequence of path segments, separated - * by '/' characters. The path is specified in a similar way to the path - * in {@link java.io.FilePermission}. There are three different ways - * as the following examples show: - * <table border> - * <caption>URL Examples</caption> - * <tr><th>Example url</th><th>Description</th></tr> - * <tr><td style="white-space:nowrap;">http://www.oracle.com/a/b/c.html</td> - * <td>A url which identifies a specific (single) resource</td> - * </tr> - * <tr><td>http://www.oracle.com/a/b/*</td> - * <td>The '*' character refers to all resources in the same "directory" - in - * other words all resources with the same number of path components, and - * which only differ in the final path component, represented by the '*'. - * </td> - * </tr> - * <tr><td>http://www.oracle.com/a/b/-</td> - * <td>The '-' character refers to all resources recursively below the - * preceding path (eg. http://www.oracle.com/a/b/c/d/e.html matches this - * example). - * </td> - * </tr> - * </table> - * <p> - * The '*' and '-' may only be specified in the final segment of a path and must be - * the only character in that segment. Any query or fragment components of the - * url are ignored when constructing HttpURLPermissions. - * <p> - * As a special case, urls of the form, "http:*" or "https:*" are accepted to - * mean any url of the given scheme. - * <p><b>The actions string</b><p> - * The actions string of a HttpURLPermission is a concatenation of the <i>method list</i> - * and the <i>request headers list</i>. These are lists of the permitted HTTP request - * methods and permitted request headers of the permission (respectively). The two lists - * are separated by a colon ':' character and elements of each list are comma separated. - * Some examples are: - * <pre> - * "POST,GET,DELETE" - * "GET:X-Foo-Request,X-Bar-Request" - * "POST,GET:Header1,Header2" - * </pre> - * The first example specifies the methods: POST, GET and DELETE, but no request headers. - * The second example specifies one request method and two headers. The third - * example specifies two request methods, and two headers. - * <p> - * The colon separator need not be present if the request headers list is empty. - * No white-space is permitted in the actions string. The action strings supplied to - * the HttpURLPermission constructors are case-insensitive and are normalized by converting - * method names to upper-case and header names to the form defines in RFC2616 (lower case - * with initial letter of each word capitalized). Either list can contain a wild-card '*' - * character which signifies all request methods or headers respectively. - * <p> - * Note. Depending on the context of use, some request methods and headers may be permitted - * at all times, and others may not be permitted at any time. For example, the - * HTTP protocol handler might disallow certain headers such as Content-Length - * from being set by application code, regardless of whether the security policy - * in force, permits it. - * - * @since 1.8 - */ -public final class HttpURLPermission extends Permission { - - private static final long serialVersionUID = -2702463814894478682L; - - private transient URI uri; - private transient List<String> methods; - private transient List<String> requestHeaders; - - // serialized field - private String actions; - - /** - * Creates a new HttpURLPermission from a url string and which permits the given - * request methods and user-settable request headers. - * The name of the permission is its url string. Only the scheme, authority - * and path components of the url are used. Any fragment or query - * components are ignored. The permissions action string is as specified above. - * - * @param url the url string - * - * @param actions the actions string - * - * @throws IllegalArgumentException if url does not result in a valid {@link URI}, - * its scheme is not http or https, or if actions contains white-space. - */ - public HttpURLPermission(String url, String actions) { - super(url); - init(actions); - } - - private void init(String actions) { - URI uri = parseURI(getName()); - int colon = actions.indexOf(':'); - if (actions.lastIndexOf(':') != colon) { - throw new IllegalArgumentException("invalid actions string"); - } - - String methods, headers; - if (colon == -1) { - methods = actions; - headers = ""; - } else { - methods = actions.substring(0, colon); - headers = actions.substring(colon+1); - } - - List<String> l = normalizeMethods(methods); - Collections.sort(l); - this.methods = Collections.unmodifiableList(l); - - l = normalizeHeaders(headers); - Collections.sort(l); - this.requestHeaders = Collections.unmodifiableList(l); - - this.actions = actions(); - this.uri = uri; - } - - /** - * Creates a HttpURLPermission with the given url string and unrestricted - * methods and request headers by invoking the two argument - * constructor as follows: HttpURLPermission(url, "*:*") - * - * @param url the url string - * - * @throws IllegalArgumentException if url does not result in a valid {@link URI} - */ - public HttpURLPermission(String url) { - this(url, "*:*"); - } - - /** - * Returns the normalized method list and request - * header list, in the form: - * <pre> - * "method-names : header-names" - * </pre> - * <p> - * where method-names is the list of methods separated by commas - * and header-names is the list of permitted headers separated by commas. - * There is no white space in the returned String. If header-names is empty - * then the colon separator will not be present. - */ - public String getActions() { - return actions; - } - - /** - * Checks if this HttpURLPermission implies the given permission. - * Specifically, the following checks are done as if in the - * following sequence: - * <p><ul> - * <li>if 'p' is not an instance of HttpURLPermission return false</li> - * <li>if any of p's methods are not in this's method list, and if - * this's method list is not equal to "*", then return false.</li> - * <li>if any of p's headers are not in this's request header list, and if - * this's request header list is not equal to "*", then return false.</li> - * <li>if this's url is equal to p's url , then return true</li> - * <li>if this's url scheme is not equal to p's url scheme return false</li> - * <li>if the scheme specific part of this's url is '*' return true</li> - * <li>if this's url authority is not equal to p's url authority - * return false</li> - * <li>if the path or paths specified by p's url are contained in the - * set of paths specified by this's url, then return true - * <li>otherwise, return false</li> - * </ul> - * <p>Some examples of how paths are matched are shown below: - * <p><table border> - * <caption>Examples of Path Matching</caption> - * <tr><th>this's path</th><th>p's path</th><th>match</th></tr> - * <tr><td>/a/b</td><td>/a/b</td><td>yes</td></tr> - * <tr><td>/a/b/*</td><td>/a/b/c</td><td>yes</td></tr> - * <tr><td>/a/b/*</td><td>/a/b/c/d</td><td>no</td></tr> - * <tr><td>/a/b/-</td><td>/a/b/c/d</td><td>yes</td></tr> - * <tr><td>/a/b/-</td><td>/a/b/c/d/e</td><td>yes</td></tr> - * <tr><td>/a/b/-</td><td>/a/b/c/*</td><td>yes</td></tr> - * <tr><td>/a/b/*</td><td>/a/b/c/-</td><td>no</td></tr> - * </table> - */ - public boolean implies(Permission p) { - if (! (p instanceof HttpURLPermission)) { - return false; - } - - HttpURLPermission that = (HttpURLPermission)p; - - if (!this.methods.get(0).equals("*") && - Collections.indexOfSubList(this.methods, that.methods) == -1) { - return false; - } - - if (this.requestHeaders.isEmpty() && !that.requestHeaders.isEmpty()) { - return false; - } - - if (!this.requestHeaders.isEmpty() && - !this.requestHeaders.get(0).equals("*") && - Collections.indexOfSubList(this.requestHeaders, - that.requestHeaders) == -1) { - return false; - } - - if (this.uri.equals(that.uri)) { - return true; - } - - if (!this.uri.getScheme().equals(that.uri.getScheme())) { - return false; - } - - if (this.uri.getSchemeSpecificPart().equals("*")) { - return true; - } - - String thisAuthority = this.uri.getAuthority(); - - if (thisAuthority != null && - !thisAuthority.equals(that.uri.getAuthority())) { - return false; - } - - String thispath = this.uri.getPath(); - String thatpath = that.uri.getPath(); - - if (thispath.endsWith("/-")) { - String thisprefix = thispath.substring(0, thispath.length() - 1); - return thatpath.startsWith(thisprefix); - } - - if (thispath.endsWith("/*")) { - String thisprefix = thispath.substring(0, thispath.length() - 1); - if (!thatpath.startsWith(thisprefix)) { - return false; - } - String thatsuffix = thatpath.substring(thisprefix.length()); - // suffix must not contain '/' chars - if (thatsuffix.indexOf('/') != -1) { - return false; - } - if (thatsuffix.equals("-")) { - return false; - } - return true; - } - return false; - } - - - /** - * Returns true if, this.getActions().equals(p.getActions()) - * and p's url equals this's url. Returns false otherwise. - */ - public boolean equals(Object p) { - if (!(p instanceof HttpURLPermission)) { - return false; - } - HttpURLPermission that = (HttpURLPermission)p; - return this.getActions().equals(that.getActions()) && - this.uri.equals(that.uri); - } - - /** - * Returns a hashcode calculated from the hashcode of the - * actions String and the url - */ - public int hashCode() { - return getActions().hashCode() + uri.hashCode(); - } - - - private List<String> normalizeMethods(String methods) { - List<String> l = new ArrayList<>(); - StringBuilder b = new StringBuilder(); - for (int i=0; i<methods.length(); i++) { - char c = methods.charAt(i); - if (c == ',') { - String s = b.toString(); - if (s.length() > 0) - l.add(s); - b = new StringBuilder(); - } else if (c == ' ' || c == '\t') { - throw new IllegalArgumentException("white space not allowed"); - } else { - if (c >= 'a' && c <= 'z') { - c += 'A' - 'a'; - } - b.append(c); - } - } - String s = b.toString(); - if (s.length() > 0) - l.add(s); - return l; - } - - private List<String> normalizeHeaders(String headers) { - List<String> l = new ArrayList<>(); - StringBuilder b = new StringBuilder(); - boolean capitalizeNext = true; - for (int i=0; i<headers.length(); i++) { - char c = headers.charAt(i); - if (c >= 'a' && c <= 'z') { - if (capitalizeNext) { - c += 'A' - 'a'; - capitalizeNext = false; - } - b.append(c); - } else if (c == ' ' || c == '\t') { - throw new IllegalArgumentException("white space not allowed"); - } else if (c == '-') { - capitalizeNext = true; - b.append(c); - } else if (c == ',') { - String s = b.toString(); - if (s.length() > 0) - l.add(s); - b = new StringBuilder(); - capitalizeNext = true; - } else { - capitalizeNext = false; - b.append(c); - } - } - String s = b.toString(); - if (s.length() > 0) - l.add(s); - return l; - } - - private URI parseURI(String url) { - URI u = URI.create(url); - String scheme = u.getScheme(); - if (!(scheme.equalsIgnoreCase("http") || - scheme.equalsIgnoreCase("https"))) { - throw new IllegalArgumentException ("unexpected URL scheme"); - } - if (!u.getSchemeSpecificPart().equals("*")) { - u = URI.create(scheme + "://" + u.getRawAuthority() + u.getRawPath()); - } - return u; - } - - private String actions() { - StringBuilder b = new StringBuilder(); - for (String s : methods) { - b.append(s); - } - b.append(":"); - for (String s : requestHeaders) { - b.append(s); - } - return b.toString(); - } - /** - * restore the state of this object from stream - */ - private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - ObjectInputStream.GetField fields = s.readFields(); - String actions = (String)fields.get("actions", null); - - init(actions); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/net/URLPermission.java Mon Oct 14 22:09:15 2013 +0100 @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.ObjectInputStream; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.security.Permission; + +/** + * Represents permission to access a resource or set of resources defined by a + * given url, and for a given set of user-settable request methods + * and request headers. The <i>name</i> of the permission is the url string. + * The <i>actions</i> string is a concatenation of the request methods and headers. + * The range of method and header names is not restricted by this class. + * <p><b>The url</b><p> + * The url string has the following expected structure. + * <pre> + * scheme : // authority [ / path ] + * </pre> + * <i>scheme</i> will typically be http or https, but is not restricted by this + * class. + * <i>authority</i> is specified as:<p> + * <pre> + * authority = hostrange [ : portrange ] + * portrange = portnumber | -portnumber | portnumber-[portnumber] | * + * hostrange = ([*.] dnsname) | IPv4address | IPv6address + * </pre> + * <i>dnsname</i> is a standard DNS host or domain name, ie. one or more labels + * separated by ".". <i>IPv4address</i> is a standard literal IPv4 address and + * <i>IPv6address</i> is as defined in <a href="http://www.ietf.org/rfc/rfc2732.txt"> + * RFC 2732</a>. Literal IPv6 addresses must however, be enclosed in '[]' characters. + * The <i>dnsname</i> specification can be preceded by "*." which means + * the name will match any hostname whose right-most domain labels are the same as + * this name. For example, "*.oracle.com" matches "foo.bar.oracle.com" + * <p> + * <i>portrange</i> is used to specify a port number, or a bounded or unbounded range of ports + * that this permission applies to. If portrange is absent or invalid, then a default + * port number is assumed if the scheme is {@code http} (default 80) or {@code https} + * (default 443). No default is assumed for other schemes. A wildcard may be specified + * which means all ports. + * <p> + * The <i>path</i> component comprises a sequence of path segments, + * separated by '/' characters. <i>path</i> may also be empty. The path is specified + * in a similar way to the path in {@link java.io.FilePermission}. There are + * three different ways as the following examples show: + * <table border> + * <caption>URL Examples</caption> + * <tr><th>Example url</th><th>Description</th></tr> + * <tr><td style="white-space:nowrap;">http://www.oracle.com/a/b/c.html</td> + * <td>A url which identifies a specific (single) resource</td> + * </tr> + * <tr><td>http://www.oracle.com/a/b/*</td> + * <td>The '*' character refers to all resources in the same "directory" - in + * other words all resources with the same number of path components, and + * which only differ in the final path component, represented by the '*'. + * </td> + * </tr> + * <tr><td>http://www.oracle.com/a/b/-</td> + * <td>The '-' character refers to all resources recursively below the + * preceding path (eg. http://www.oracle.com/a/b/c/d/e.html matches this + * example). + * </td> + * </tr> + * </table> + * <p> + * The '*' and '-' may only be specified in the final segment of a path and must be + * the only character in that segment. Any query or fragment components of the + * url are ignored when constructing URLPermissions. + * <p> + * As a special case, urls of the form, "scheme:*" are accepted to + * mean any url of the given scheme. + * <p> + * The <i>scheme</i> and <i>authority</i> components of the url string are handled + * without regard to case. This means {@link #equals(Object)}, + * {@link #hashCode()} and {@link #implies(Permission)} are case insensitive with respect + * to these components. If the <i>authority</i> contains a literal IP address, + * then the address is normalized for comparison. The path component is case sensitive. + * <p><b>The actions string</b><p> + * The actions string of a URLPermission is a concatenation of the <i>method list</i> + * and the <i>request headers list</i>. These are lists of the permitted request + * methods and permitted request headers of the permission (respectively). The two lists + * are separated by a colon ':' character and elements of each list are comma separated. + * Some examples are: + * <pre> + * "POST,GET,DELETE" + * "GET:X-Foo-Request,X-Bar-Request" + * "POST,GET:Header1,Header2" + * </pre> + * The first example specifies the methods: POST, GET and DELETE, but no request headers. + * The second example specifies one request method and two headers. The third + * example specifies two request methods, and two headers. + * <p> + * The colon separator need not be present if the request headers list is empty. + * No white-space is permitted in the actions string. The action strings supplied to + * the URLPermission constructors are case-insensitive and are normalized by converting + * method names to upper-case and header names to the form defines in RFC2616 (lower case + * with initial letter of each word capitalized). Either list can contain a wild-card '*' + * character which signifies all request methods or headers respectively. + * <p> + * Note. Depending on the context of use, some request methods and headers may be permitted + * at all times, and others may not be permitted at any time. For example, the + * HTTP protocol handler might disallow certain headers such as Content-Length + * from being set by application code, regardless of whether the security policy + * in force, permits it. + * + * @since 1.8 + */ +public final class URLPermission extends Permission { + + private static final long serialVersionUID = -2702463814894478682L; + + private transient String scheme; + private transient String ssp; // scheme specific part + private transient String path; + private transient List<String> methods; + private transient List<String> requestHeaders; + private transient Authority authority; + + // serialized field + private String actions; + + /** + * Creates a new URLPermission from a url string and which permits the given + * request methods and user-settable request headers. + * The name of the permission is the url string it was created with. Only the scheme, + * authority and path components of the url are used internally. Any fragment or query + * components are ignored. The permissions action string is as specified above. + * + * @param url the url string + * + * @param actions the actions string + * + * @exception IllegalArgumentException if url is invalid or if actions contains white-space. + */ + public URLPermission(String url, String actions) { + super(url); + init(actions); + } + + private void init(String actions) { + parseURI(getName()); + int colon = actions.indexOf(':'); + if (actions.lastIndexOf(':') != colon) { + throw new IllegalArgumentException("invalid actions string"); + } + + String methods, headers; + if (colon == -1) { + methods = actions; + headers = ""; + } else { + methods = actions.substring(0, colon); + headers = actions.substring(colon+1); + } + + List<String> l = normalizeMethods(methods); + Collections.sort(l); + this.methods = Collections.unmodifiableList(l); + + l = normalizeHeaders(headers); + Collections.sort(l); + this.requestHeaders = Collections.unmodifiableList(l); + + this.actions = actions(); + } + + /** + * Creates a URLPermission with the given url string and unrestricted + * methods and request headers by invoking the two argument + * constructor as follows: URLPermission(url, "*:*") + * + * @param url the url string + * + * @throws IllegalArgumentException if url does not result in a valid {@link URI} + */ + public URLPermission(String url) { + this(url, "*:*"); + } + + /** + * Returns the normalized method list and request + * header list, in the form: + * <pre> + * "method-names : header-names" + * </pre> + * <p> + * where method-names is the list of methods separated by commas + * and header-names is the list of permitted headers separated by commas. + * There is no white space in the returned String. If header-names is empty + * then the colon separator will not be present. + */ + public String getActions() { + return actions; + } + + /** + * Checks if this URLPermission implies the given permission. + * Specifically, the following checks are done as if in the + * following sequence: + * <p><ul> + * <li>if 'p' is not an instance of URLPermission return false</li> + * <li>if any of p's methods are not in this's method list, and if + * this's method list is not equal to "*", then return false.</li> + * <li>if any of p's headers are not in this's request header list, and if + * this's request header list is not equal to "*", then return false.</li> + * <li>if this's url scheme is not equal to p's url scheme return false</li> + * <li>if the scheme specific part of this's url is '*' return true</li> + * <li>if the set of hosts defined by p's url hostrange is not a subset of + * this's url hostrange then return false. For example, "*.foo.oracle.com" + * is a subset of "*.oracle.com". "foo.bar.oracle.com" is not + * a subset of "*.foo.oracle.com"</li> + * <li>if the portrange defined by p's url is not a subset of the + * portrange defined by this's url then return false. + * <li>if the path or paths specified by p's url are contained in the + * set of paths specified by this's url, then return true + * <li>otherwise, return false</li> + * </ul> + * <p>Some examples of how paths are matched are shown below: + * <p><table border> + * <caption>Examples of Path Matching</caption> + * <tr><th>this's path</th><th>p's path</th><th>match</th></tr> + * <tr><td>/a/b</td><td>/a/b</td><td>yes</td></tr> + * <tr><td>/a/b/*</td><td>/a/b/c</td><td>yes</td></tr> + * <tr><td>/a/b/*</td><td>/a/b/c/d</td><td>no</td></tr> + * <tr><td>/a/b/-</td><td>/a/b/c/d</td><td>yes</td></tr> + * <tr><td>/a/b/-</td><td>/a/b/c/d/e</td><td>yes</td></tr> + * <tr><td>/a/b/-</td><td>/a/b/c/*</td><td>yes</td></tr> + * <tr><td>/a/b/*</td><td>/a/b/c/-</td><td>no</td></tr> + * </table> + */ + public boolean implies(Permission p) { + if (! (p instanceof URLPermission)) { + return false; + } + + URLPermission that = (URLPermission)p; + + if (!this.methods.get(0).equals("*") && + Collections.indexOfSubList(this.methods, that.methods) == -1) { + return false; + } + + if (this.requestHeaders.isEmpty() && !that.requestHeaders.isEmpty()) { + return false; + } + + if (!this.requestHeaders.isEmpty() && + !this.requestHeaders.get(0).equals("*") && + Collections.indexOfSubList(this.requestHeaders, + that.requestHeaders) == -1) { + return false; + } + + if (!this.scheme.equals(that.scheme)) { + return false; + } + + if (this.ssp.equals("*")) { + return true; + } + + if (!this.authority.implies(that.authority)) { + return false; + } + + if (this.path == null) { + return that.path == null; + } + if (that.path == null) { + return false; + } + + if (this.path.endsWith("/-")) { + String thisprefix = this.path.substring(0, this.path.length() - 1); + return that.path.startsWith(thisprefix); + } + + if (this.path.endsWith("/*")) { + String thisprefix = this.path.substring(0, this.path.length() - 1); + if (!that.path.startsWith(thisprefix)) { + return false; + } + String thatsuffix = that.path.substring(thisprefix.length()); + // suffix must not contain '/' chars + if (thatsuffix.indexOf('/') != -1) { + return false; + } + if (thatsuffix.equals("-")) { + return false; + } + return true; + } + return this.path.equals(that.path); + } + + + /** + * Returns true if, this.getActions().equals(p.getActions()) + * and p's url equals this's url. Returns false otherwise. + */ + public boolean equals(Object p) { + if (!(p instanceof URLPermission)) { + return false; + } + URLPermission that = (URLPermission)p; + if (!this.scheme.equals(that.scheme)) { + return false; + } + if (!this.getActions().equals(that.getActions())) { + return false; + } + if (!this.authority.equals(that.authority)) { + return false; + } + if (this.path != null) { + return this.path.equals(that.path); + } else { + return that.path == null; + } + } + + /** + * Returns a hashcode calculated from the hashcode of the + * actions String and the url string. + */ + public int hashCode() { + return getActions().hashCode() + + scheme.hashCode() + + authority.hashCode() + + path == null ? 0 : path.hashCode(); + } + + + private List<String> normalizeMethods(String methods) { + List<String> l = new ArrayList<>(); + StringBuilder b = new StringBuilder(); + for (int i=0; i<methods.length(); i++) { + char c = methods.charAt(i); + if (c == ',') { + String s = b.toString(); + if (s.length() > 0) + l.add(s); + b = new StringBuilder(); + } else if (c == ' ' || c == '\t') { + throw new IllegalArgumentException("white space not allowed"); + } else { + if (c >= 'a' && c <= 'z') { + c += 'A' - 'a'; + } + b.append(c); + } + } + String s = b.toString(); + if (s.length() > 0) + l.add(s); + return l; + } + + private List<String> normalizeHeaders(String headers) { + List<String> l = new ArrayList<>(); + StringBuilder b = new StringBuilder(); + boolean capitalizeNext = true; + for (int i=0; i<headers.length(); i++) { + char c = headers.charAt(i); + if (c >= 'a' && c <= 'z') { + if (capitalizeNext) { + c += 'A' - 'a'; + capitalizeNext = false; + } + b.append(c); + } else if (c == ' ' || c == '\t') { + throw new IllegalArgumentException("white space not allowed"); + } else if (c == '-') { + capitalizeNext = true; + b.append(c); + } else if (c == ',') { + String s = b.toString(); + if (s.length() > 0) + l.add(s); + b = new StringBuilder(); + capitalizeNext = true; + } else { + capitalizeNext = false; + b.append(c); + } + } + String s = b.toString(); + if (s.length() > 0) + l.add(s); + return l; + } + + private void parseURI(String url) { + int len = url.length(); + int delim = url.indexOf(':'); + if (delim == -1 || delim + 1 == len) { + throw new IllegalArgumentException("invalid URL string"); + } + scheme = url.substring(0, delim).toLowerCase(); + this.ssp = url.substring(delim + 1); + + if (!ssp.startsWith("//")) { + this.authority = new Authority(scheme, ssp.toLowerCase()); + return; + } + String authpath = ssp.substring(2); + + delim = authpath.indexOf('/'); + String auth; + if (delim == -1) { + this.path = ""; + auth = authpath; + } else { + auth = authpath.substring(0, delim); + this.path = authpath.substring(delim); + } + this.authority = new Authority(scheme, auth.toLowerCase()); + } + + private String actions() { + StringBuilder b = new StringBuilder(); + for (String s : methods) { + b.append(s); + } + b.append(":"); + for (String s : requestHeaders) { + b.append(s); + } + return b.toString(); + } + + /** + * restore the state of this object from stream + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = s.readFields(); + String actions = (String)fields.get("actions", null); + + init(actions); + } + + static class Authority { + HostPortrange p; + + Authority(String scheme, String authority) { + p = new HostPortrange(scheme, authority); + } + + boolean implies(Authority other) { + return impliesHostrange(other) && impliesPortrange(other); + } + + private boolean impliesHostrange(Authority that) { + String thishost = this.p.hostname(); + String thathost = that.p.hostname(); + + if (p.wildcard() && thishost.equals("")) { + // this "*" implies all others + return true; + } + if (that.p.wildcard() && thathost.equals("")) { + // that "*" can only be implied by this "*" + return false; + } + if (thishost.equals(thathost)) { + // covers all cases of literal IP addresses and fixed + // domain names. + return true; + } + if (this.p.wildcard()) { + // this "*.foo.com" implies "bub.bar.foo.com" + return thathost.endsWith(thishost); + } + return false; + } + + private boolean impliesPortrange(Authority that) { + int[] thisrange = this.p.portrange(); + int[] thatrange = that.p.portrange(); + if (thisrange[0] == -1) { + /* port not specified non http/s URL */ + return true; + } + return thisrange[0] <= thatrange[0] && + thisrange[1] >= thatrange[1]; + } + + boolean equals(Authority that) { + return this.p.equals(that.p); + } + + public int hashCode() { + return p.hashCode(); + } + } +}
--- a/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Mon Oct 14 09:52:36 2013 -0700 +++ b/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Mon Oct 14 22:09:15 2013 +0100 @@ -46,7 +46,7 @@ import java.net.CacheResponse; import java.net.SecureCacheResponse; import java.net.CacheRequest; -import java.net.HttpURLPermission; +import java.net.URLPermission; import java.net.Authenticator.RequestorType; import java.security.AccessController; import java.security.PrivilegedExceptionAction; @@ -389,7 +389,7 @@ private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT; private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT; - /* A permission converted from a HttpURLPermission */ + /* A permission converted from a URLPermission */ private SocketPermission socketPermission; /* Logging support */ @@ -930,8 +930,7 @@ plainConnect0(); return null; } - } -// }, null, p -- replace line above, when limited doPriv ready + }, null, p ); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); @@ -943,7 +942,7 @@ } /** - * if the caller has a HttpURLPermission for connecting to the + * if the caller has a URLPermission for connecting to the * given URL, then return a SocketPermission which permits * access to that destination. Return null otherwise. The permission * is cached in a field (which can only be changed by redirects) @@ -969,7 +968,10 @@ String actions = getRequestMethod()+":" + getUserSetHeaders().getHeaderNamesInList(); - HttpURLPermission p = new HttpURLPermission(url.toString(), actions); + String urlstring = url.getProtocol() + "://" + url.getAuthority() + + url.getPath(); + + URLPermission p = new URLPermission(urlstring, actions); try { sm.checkPermission(p); socketPermission = newPerm; @@ -1188,8 +1190,7 @@ public OutputStream run() throws IOException { return getOutputStream0(); } - } -// }, null, p -- replace line above, when limited doPriv ready + }, null, p ); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); @@ -1372,8 +1373,7 @@ public InputStream run() throws IOException { return getInputStream0(); } - } -// }, null, p -- replace line above, when limited doPriv ready + }, null, p ); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); @@ -2507,8 +2507,7 @@ public Boolean run() throws IOException { return followRedirect0(loc, stat, locUrl0); } - } -// }, null, p -- replace line above, when limited doPriv ready + }, null, p ); } catch (PrivilegedActionException e) { throw (IOException) e.getException();
--- a/src/share/classes/sun/security/tools/policytool/PolicyTool.java Mon Oct 14 09:52:36 2013 -0700 +++ b/src/share/classes/sun/security/tools/policytool/PolicyTool.java Mon Oct 14 22:09:15 2013 +0100 @@ -1447,7 +1447,7 @@ PERM_ARRAY.add(new AWTPerm()); PERM_ARRAY.add(new DelegationPerm()); PERM_ARRAY.add(new FilePerm()); - PERM_ARRAY.add(new HttpURLPerm()); + PERM_ARRAY.add(new URLPerm()); PERM_ARRAY.add(new InqSecContextPerm()); PERM_ARRAY.add(new LogPerm()); PERM_ARRAY.add(new MgmtPerm()); @@ -3843,10 +3843,10 @@ } } -class HttpURLPerm extends Perm { - public HttpURLPerm() { - super("HttpURLPermission", - "java.net.HttpURLPermission", +class URLPerm extends Perm { + public URLPerm() { + super("URLPermission", + "java.net.URLPermission", new String[] { "<"+ PolicyTool.rb.getString("url") + ">", },
--- a/test/java/net/HttpURLPermission/HttpURLPermissionTest.java Mon Oct 14 09:52:36 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.net.HttpURLPermission; -import java.io.*; - -/** - * @test - * @bug 8010464 - */ - -public class HttpURLPermissionTest { - - // super class for all test types - abstract static class Test { - boolean expected; - abstract boolean execute(); - }; - - // Tests URL part of implies() method. This is the main test. - static class URLImpliesTest extends Test { - String arg1, arg2; - - URLImpliesTest(String arg1, String arg2, boolean expected) { - this.arg1 = arg1; - this.arg2 = arg2; - this.expected = expected; - } - - boolean execute() { - HttpURLPermission p1 = new HttpURLPermission (arg1, "GET:*"); - HttpURLPermission p2 = new HttpURLPermission (arg2, "GET:*"); - boolean result = p1.implies(p2); - return result == expected; - } - }; - - static URLImpliesTest imtest(String arg1, String arg2, boolean expected) { - return new URLImpliesTest(arg1, arg2, expected); - } - - static class ActionImpliesTest extends Test { - String arg1, arg2; - - ActionImpliesTest(String arg1, String arg2, boolean expected) { - this.arg1 = arg1; - this.arg2 = arg2; - this.expected = expected; - } - - boolean execute() { - String url1 = "http://www.foo.com/-"; - String url2 = "http://www.foo.com/a/b"; - HttpURLPermission p1 = new HttpURLPermission(url1, arg1); - HttpURLPermission p2 = new HttpURLPermission(url2, arg2); - boolean result = p1.implies(p2); - return result == expected; - } - } - - static ActionImpliesTest actest(String arg1, String arg2, boolean expected) { - return new ActionImpliesTest(arg1, arg2, expected); - } - - static Test[] pathImplies = { - // single - imtest("http://www.foo.com/", "http://www.foo.com/", true), - imtest("http://www.bar.com/", "http://www.foo.com/", false), - imtest("http://www.foo.com/a/b", "http://www.foo.com/", false), - imtest("http://www.foo.com/a/b", "http://www.foo.com/a/b/c", false), - // wildcard - imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c", true), - imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/*", true), - imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c#frag", true), - imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c#frag?foo=foo", true), - imtest("http://www.foo.com/a/b/*", "http://www.foo.com/b/b/c", false), - imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c.html", true), - imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c.html", true), - imtest("http://www.foo.com/a/b/*", "https://www.foo.com/a/b/c", false), - // recursive - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/-", true), - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c", true), - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c#frag", true), - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c#frag?foo=foo", true), - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/b/b/c", false), - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c.html", true), - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c.html", true), - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e.html", true), - imtest("https://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e.html", false), - imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e#frag", true), - imtest("http://www.foo.com/a/b/-", "https://www.foo.com/a/b/c", false), - // special cases - imtest("http:*", "https://www.foo.com/a/b/c", false), - imtest("http:*", "http://www.foo.com/a/b/c", true), - imtest("http:*", "http://foo/bar", true), - imtest("http://foo/bar", "https://foo/bar", false) - }; - - static Test[] actionImplies = { - actest("GET", "GET", true), - actest("GET", "POST", false), - actest("GET:", "PUT", false), - actest("GET:", "GET", true), - actest("GET,POST", "GET", true), - actest("GET,POST:", "GET", true), - actest("GET:X-Foo", "GET:x-foo", true), - actest("GET:X-Foo,X-bar", "GET:x-foo", true), - actest("GET:X-Foo", "GET:x-boo", false), - actest("GET:X-Foo,X-Bar", "GET:x-bar,x-foo", true), - actest("GET:X-Bar,X-Foo,X-Bar,Y-Foo", "GET:x-bar,x-foo", true), - actest("GET:*", "GET:x-bar,x-foo", true), - actest("*:*", "GET:x-bar,x-foo", true) - }; - - static boolean failed = false; - - public static void main(String args[]) throws Exception { - for (int i=0; i<pathImplies.length ; i++) { - URLImpliesTest test = (URLImpliesTest)pathImplies[i]; - Exception caught = null; - boolean result = false; - try { - result = test.execute(); - } catch (Exception e) { - caught = e; - e.printStackTrace(); - } - if (!result) { - failed = true; - System.out.println ("test failed: " + test.arg1 + ": " + - test.arg2 + " Exception: " + caught); - } - System.out.println ("path test " + i + " OK"); - - } - for (int i=0; i<actionImplies.length ; i++) { - ActionImpliesTest test = (ActionImpliesTest)actionImplies[i]; - Exception caught = null; - boolean result = false; - try { - result = test.execute(); - } catch (Exception e) { - caught = e; - e.printStackTrace(); - } - if (!result) { - failed = true; - System.out.println ("test failed: " + test.arg1 + ": " + - test.arg2 + " Exception: " + caught); - } - System.out.println ("action test " + i + " OK"); - } - - serializationTest("http://www.foo.com/-", "GET,DELETE:*"); - serializationTest("https://www.foo.com/-", "POST:X-Foo"); - serializationTest("https:*", "*:*"); - serializationTest("http://www.foo.com/a/b/s/", "POST:X-Foo"); - serializationTest("http://www.foo.com/a/b/s/*", "POST:X-Foo"); - - if (failed) { - throw new RuntimeException("some tests failed"); - } - - } - - static void serializationTest(String name, String actions) - throws Exception { - - HttpURLPermission out = new HttpURLPermission(name, actions); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream o = new ObjectOutputStream(baos); - o.writeObject(out); - ByteArrayInputStream bain = new ByteArrayInputStream(baos.toByteArray()); - ObjectInputStream i = new ObjectInputStream(bain); - HttpURLPermission in = (HttpURLPermission)i.readObject(); - if (!in.equals(out)) { - System.out.println ("FAIL"); - System.out.println ("in = " + in); - System.out.println ("out = " + out); - failed = true; - } - } -}
--- a/test/java/net/HttpURLPermission/URLTest.java Mon Oct 14 09:52:36 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.net.HttpURLPermission; -/* - * Run the tests once without security manager and once with - * - * @test - * @bug 8010464 - * @compile ../../../com/sun/net/httpserver/SimpleSSLContext.java - * @run main/othervm/policy=policy.1 URLTest one - * @run main/othervm URLTest one - * @run main/othervm/policy=policy.2 URLTest two - * @run main/othervm URLTest two - * @run main/othervm/policy=policy.3 URLTest three - * @run main/othervm URLTest three - */ - -import java.net.*; -import java.io.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; -import com.sun.net.httpserver.*; -import javax.net.ssl.*; - -public class URLTest { - static boolean failed = false; - - public static void main (String[] args) throws Exception { - boolean no = false, yes = true; - - if (System.getSecurityManager() == null) { - yes = false; - } - createServers(); - InetSocketAddress addr1 = httpServer.getAddress(); - int port1 = addr1.getPort(); - InetSocketAddress addr2 = httpsServer.getAddress(); - int port2 = addr2.getPort(); - - // each of the following cases is run with a different policy file - - switch (args[0]) { - case "one": - String url1 = "http://127.0.0.1:"+ port1 + "/foo.html"; - String url2 = "https://127.0.0.1:"+ port2 + "/foo.html"; - String url3 = "http://127.0.0.1:"+ port1 + "/bar.html"; - String url4 = "https://127.0.0.1:"+ port2 + "/bar.html"; - - // simple positive test. Should succceed - test(url1, "GET", "X-Foo", no); - test(url1, "GET", "Z-Bar", "X-Foo", no); - test(url1, "GET", "X-Foo", "Z-Bar", no); - test(url1, "GET", "Z-Bar", no); - test(url2, "POST", "X-Fob", no); - - // reverse the methods, should fail - test(url1, "POST", "X-Foo", yes); - test(url2, "GET", "X-Fob", yes); - - // different URLs, should fail - test(url3, "GET", "X-Foo", yes); - test(url4, "POST", "X-Fob", yes); - break; - - case "two": - url1 = "http://127.0.0.1:"+ port1 + "/foo.html"; - url2 = "https://127.0.0.1:"+ port2 + "/foo.html"; - url3 = "http://127.0.0.1:"+ port1 + "/bar.html"; - url4 = "https://127.0.0.1:"+ port2 + "/bar.html"; - - // simple positive test. Should succceed - test(url1, "GET", "X-Foo", no); - test(url2, "POST", "X-Fob", no); - test(url3, "GET", "X-Foo", no); - test(url4, "POST", "X-Fob", no); - break; - - case "three": - url1 = "http://127.0.0.1:"+ port1 + "/foo.html"; - url2 = "https://127.0.0.1:"+ port2 + "/a/c/d/e/foo.html"; - url3 = "http://127.0.0.1:"+ port1 + "/a/b/c"; - url4 = "https://127.0.0.1:"+ port2 + "/a/b/c"; - - test(url1, "GET", "X-Foo", yes); - test(url2, "POST", "X-Zxc", no); - test(url3, "DELETE", "Y-Foo", no); - test(url4, "POST", "Y-Foo", yes); - break; - } - shutdown(); - if (failed) { - throw new RuntimeException("Test failed"); - } - } - - public static void test ( - String u, String method, - String header, boolean exceptionExpected - ) - throws Exception - { - test(u, method, header, null, exceptionExpected); - } - - public static void test ( - String u, String method, - String header1, String header2, boolean exceptionExpected - ) - throws Exception - { - URL url = new URL(u); - System.out.println ("url=" + u + " method="+method + " header1="+header1 - +" header2 = " + header2 - +" exceptionExpected="+exceptionExpected); - HttpURLConnection urlc = (HttpURLConnection)url.openConnection(); - if (urlc instanceof HttpsURLConnection) { - HttpsURLConnection ssl = (HttpsURLConnection)urlc; - ssl.setHostnameVerifier(new HostnameVerifier() { - public boolean verify(String host, SSLSession sess) { - return true; - } - }); - ssl.setSSLSocketFactory (ctx.getSocketFactory()); - } - urlc.setRequestMethod(method); - if (header1 != null) { - urlc.addRequestProperty(header1, "foo"); - } - if (header2 != null) { - urlc.addRequestProperty(header2, "bar"); - } - try { - int g = urlc.getResponseCode(); - if (exceptionExpected) { - failed = true; - System.out.println ("FAIL"); - return; - } - if (g != 200) { - String s = Integer.toString(g); - throw new RuntimeException("unexpected response "+ s); - } - InputStream is = urlc.getInputStream(); - int c,count=0; - byte[] buf = new byte[1024]; - while ((c=is.read(buf)) != -1) { - count += c; - } - is.close(); - } catch (RuntimeException e) { - if (! (e instanceof SecurityException) && - !(e.getCause() instanceof SecurityException) || - !exceptionExpected) - { - System.out.println ("FAIL"); - //e.printStackTrace(); - failed = true; - } - } - System.out.println ("OK"); - } - - static HttpServer httpServer; - static HttpsServer httpsServer; - static HttpContext c, cs; - static ExecutorService e, es; - static SSLContext ctx; - - // These ports need to be hard-coded until we support port number - // ranges in the permission class - - static final int PORT1 = 12567; - static final int PORT2 = 12568; - - static void createServers() throws Exception { - InetSocketAddress addr1 = new InetSocketAddress (PORT1); - InetSocketAddress addr2 = new InetSocketAddress (PORT2); - httpServer = HttpServer.create (addr1, 0); - httpsServer = HttpsServer.create (addr2, 0); - - MyHandler h = new MyHandler(); - - c = httpServer.createContext ("/", h); - cs = httpsServer.createContext ("/", h); - e = Executors.newCachedThreadPool(); - es = Executors.newCachedThreadPool(); - httpServer.setExecutor (e); - httpsServer.setExecutor (es); - - // take the keystore from elsewhere in test hierarchy - String keysdir = System.getProperty("test.src") - + "/../../../com/sun/net/httpserver/"; - ctx = new SimpleSSLContext(keysdir).get(); - httpsServer.setHttpsConfigurator(new HttpsConfigurator (ctx)); - - httpServer.start(); - httpsServer.start(); - } - - static void shutdown() { - httpServer.stop(1); - httpsServer.stop(1); - e.shutdown(); - es.shutdown(); - } - - static class MyHandler implements HttpHandler { - - MyHandler() { - } - - public void handle(HttpExchange x) throws IOException { - x.sendResponseHeaders(200, -1); - x.close(); - } - } - -}
--- a/test/java/net/HttpURLPermission/policy.1 Mon Oct 14 09:52:36 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -// -// Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. -// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -// -// This code is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 2 only, as -// published by the Free Software Foundation. -// -// This code is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// version 2 for more details (a copy is included in the LICENSE file that -// accompanied this code). -// -// You should have received a copy of the GNU General Public License version -// 2 along with this work; if not, write to the Free Software Foundation, -// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -// -// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -// or visit www.oracle.com if you need additional information or have any -// questions. -// - -grant { - permission java.net.HttpURLPermission "http://127.0.0.1:12567/foo.html", "GET:X-Foo,Z-Bar"; - permission java.net.HttpURLPermission "https://127.0.0.1:12568/foo.html", "POST:X-Fob,T-Bar"; - - // needed for HttpServer - permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept"; - permission "java.util.PropertyPermission" "test.src", "read"; - permission java.io.FilePermission "${test.src}/../../../com/sun/net/httpserver/testkeys", "read"; - - //permission "java.util.logging.LoggingPermission" "control"; - //permission "java.io.FilePermission" "/tmp/-", "read,write"; - permission "java.lang.RuntimePermission" "modifyThread"; - permission "java.lang.RuntimePermission" "setFactory"; -}; - -// Normal permissions that aren't granted when run under jtreg - -grant codeBase "file:${{java.ext.dirs}}/*" { - permission java.security.AllPermission; -}; - -grant codeBase "file:${{java.home}}/jre/lib/rt.jar" { - permission java.security.AllPermission; -}; -
--- a/test/java/net/HttpURLPermission/policy.2 Mon Oct 14 09:52:36 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -// -// Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. -// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -// -// This code is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 2 only, as -// published by the Free Software Foundation. -// -// This code is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// version 2 for more details (a copy is included in the LICENSE file that -// accompanied this code). -// -// You should have received a copy of the GNU General Public License version -// 2 along with this work; if not, write to the Free Software Foundation, -// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -// -// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -// or visit www.oracle.com if you need additional information or have any -// questions. -// - -grant { - permission java.net.HttpURLPermission "http://127.0.0.1:12567/*", "GET:X-Foo"; - permission java.net.HttpURLPermission "https://127.0.0.1:12568/*", "POST:X-Fob"; - - // needed for HttpServer - permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept"; - permission "java.util.PropertyPermission" "test.src", "read"; - permission java.io.FilePermission "${test.src}/../../../com/sun/net/httpserver/testkeys", "read"; - - //permission "java.util.logging.LoggingPermission" "control"; - //permission "java.io.FilePermission" "/tmp/-", "read,write"; - permission "java.lang.RuntimePermission" "modifyThread"; - permission "java.lang.RuntimePermission" "setFactory"; -}; - -grant codeBase "file:${{java.ext.dirs}}/*" { - permission java.security.AllPermission; -}; - -grant codeBase "file:///export/repos/jdk8/build/linux-x86_64-normal-server-fastdebug/images/j2sdk-image/jre/lib/rt.jar" { - permission java.security.AllPermission; -}; -
--- a/test/java/net/HttpURLPermission/policy.3 Mon Oct 14 09:52:36 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -// -// Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. -// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -// -// This code is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 2 only, as -// published by the Free Software Foundation. -// -// This code is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// version 2 for more details (a copy is included in the LICENSE file that -// accompanied this code). -// -// You should have received a copy of the GNU General Public License version -// 2 along with this work; if not, write to the Free Software Foundation, -// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -// -// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -// or visit www.oracle.com if you need additional information or have any -// questions. -// - -grant { - permission java.net.HttpURLPermission "http://127.0.0.1:12567/a/b/-", "DELETE,GET:X-Foo,Y-Foo"; - permission java.net.HttpURLPermission "https://127.0.0.1:12568/a/c/-", "POST:*"; - - // needed for HttpServer - permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept"; - permission "java.util.PropertyPermission" "test.src", "read"; - permission java.io.FilePermission "${test.src}/../../../com/sun/net/httpserver/testkeys", "read"; - - //permission "java.util.logging.LoggingPermission" "control"; - //permission "java.io.FilePermission" "/tmp/-", "read,write"; - permission "java.lang.RuntimePermission" "modifyThread"; - permission "java.lang.RuntimePermission" "setFactory"; -}; - -// Normal permissions that aren't granted when run under jtreg - -grant codeBase "file:${{java.ext.dirs}}/*" { - permission java.security.AllPermission; -}; - -grant codeBase "file:${{java.home}}/jre/lib/rt.jar" { - permission java.security.AllPermission; -}; -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/net/URLPermission/URLPermissionTest.java Mon Oct 14 22:09:15 2013 +0100 @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.URLPermission; +import java.io.*; + +/** + * @test + * @bug 8010464 + */ + +public class URLPermissionTest { + + // super class for all test types + abstract static class Test { + boolean expected; + abstract boolean execute(); + }; + + // Should throw an IAE on construction + static class ExTest extends Test { + String arg; + ExTest(String arg) { + this.arg = arg; + } + + @Override + boolean execute() { + try { + URLPermission p = new URLPermission(arg); + return false; + } catch (IllegalArgumentException e) { + return true; + } + } + }; + + static ExTest extest(String arg) { + return new ExTest(arg); + } + + // Tests URL part of implies() method. This is the main test. + static class URLImpliesTest extends Test { + String arg1, arg2; + + URLImpliesTest(String arg1, String arg2, boolean expected) { + this.arg1 = arg1; + this.arg2 = arg2; + this.expected = expected; + } + + boolean execute() { + URLPermission p1 = new URLPermission (arg1, "GET:*"); + URLPermission p2 = new URLPermission (arg2, "GET:*"); + boolean result = p1.implies(p2); + if (result != expected) { + System.out.println("p1 = " + p1); + System.out.println("p2 = " + p2); + } + return result == expected; + } + }; + + static URLImpliesTest imtest(String arg1, String arg2, boolean expected) { + return new URLImpliesTest(arg1, arg2, expected); + } + + static class ActionImpliesTest extends Test { + String arg1, arg2; + + ActionImpliesTest(String arg1, String arg2, boolean expected) { + this.arg1 = arg1; + this.arg2 = arg2; + this.expected = expected; + } + + @Override + boolean execute() { + String url1 = "http://www.foo.com/-"; + String url2 = "http://www.foo.com/a/b"; + URLPermission p1 = new URLPermission(url1, arg1); + URLPermission p2 = new URLPermission(url2, arg2); + boolean result = p1.implies(p2); + + return result == expected; + } + } + + static ActionImpliesTest actest(String arg1, String arg2, boolean expected) { + return new ActionImpliesTest(arg1, arg2, expected); + } + + static class URLEqualityTest extends Test { + String arg1, arg2; + + URLEqualityTest(String arg1, String arg2, boolean expected) { + this.arg1 = arg1; + this.arg2 = arg2; + this.expected = expected; + } + + @Override + boolean execute() { + URLPermission p1 = new URLPermission(arg1); + URLPermission p2 = new URLPermission(arg2); + boolean result = p1.equals(p2); + + return result == expected; + } + } + + static URLEqualityTest eqtest(String arg1, String arg2, boolean expected) { + return new URLEqualityTest(arg1, arg2, expected); + } + + static Test[] pathImplies = { + // single + imtest("http://www.foo.com/", "http://www.foo.com/", true), + imtest("http://www.bar.com/", "http://www.foo.com/", false), + imtest("http://www.foo.com/a/b", "http://www.foo.com/", false), + imtest("http://www.foo.com/a/b", "http://www.foo.com/a/b/c", false), + // wildcard + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/*", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c#frag", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c#frag?foo=foo", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/b/b/c", false), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c.html", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c.html", true), + imtest("http://www.foo.com/a/b/*", "https://www.foo.com/a/b/c", false), + // recursive + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/-", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c#frag", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c#frag?foo=foo", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/b/b/c", false), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c.html", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c.html", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e.html", true), + imtest("https://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e.html", false), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e#frag", true), + imtest("http://www.foo.com/a/b/-", "https://www.foo.com/a/b/c", false), + // special cases + imtest("http:*", "https://www.foo.com/a/b/c", false), + imtest("http:*", "http://www.foo.com/a/b/c", true), + imtest("http:*", "http://foo/bar", true), + imtest("http://foo/bar", "https://foo/bar", false) + }; + + // new functionality + + static Test[] exceptionTests = { + extest("http://1.2.3.4.5/a/b/c"), + extest("http://www.*.com"), + //extest("http://www.foo.com:1-X"), + extest("http://[foo.com]:99"), + extest("http://[fec0::X]:99"), + extest("http:") + }; + + static Test[] pathImplies2 = { + imtest("http://[FE80::]:99", "http://[fe80:0::]:99", true), + + // hostnames + imtest("http://*.foo.com/a/b/-", "http://www.foo.com/a/b/c/d", true), + imtest("http://*.foo.com/a/b/-", "http://www.bar.com/a/b/c/d", false), + imtest("http://*.foo.com/a/b/-", "http://www.biz.bar.foo.com/a/b/c/d", true), + imtest("http://*.foo.com/a/b/-", "http://www.biz.bar.foo.como/a/b/c/d", false), + imtest("http://*/a/b/-", "http://www.biz.bar.foo.fuzz/a/b/c/d", true), + imtest("http://*/a/b/-", "http://*/a/b/c/d", true), + imtest("http://*.foo.com/a/b/-", "http://*/a/b/c/d", false), + imtest("http:*", "http://*/a/b/c/d", true), + + // literal IPv4 addresses + imtest("http://1.2.3.4/a/b/-", "http://www.biz.bar.foo.com/a/b/c/d", false), + imtest("http://1.2.3.4/a/b/-", "http://1.2.3.4/a/b/c/d", true), + imtest("http://1.2.3.4/a/b/-", "http://1.2.88.4/a/b/c/d", false), + imtest("http:*", "http://1.2.88.4/a/b/c/d", true), + + // literal IPv6 addresses + imtest("http://[fe80::]/a/b/-", "http://[fe80::0]/a/b/c", true), + imtest("http://[fe80::]/a/b/-", "http://[fe80::3]/a/b/c", false), + imtest("http://[1:2:3:4:5:6:7:8]/a/b/-","http://[1:002:03:4:0005:6:07:8]/a/b/c", true), + imtest("http://[1:2:3:4:5:6:7:8]/a/b/-","http://[1:002:03:4:0033:6:07:8]/a/b/c", false), + imtest("http://[1::2]/a/b/-", "http://[1:0:0:0::2]/a/b/c", true), + imtest("http://[1::2]/a/b/-", "http://[1:0:0:0::3]/a/b/c", false), + imtest("http://[FE80::]:99", "http://[fe80:0::]:99", true), + imtest("http:*", "http://[fe80:0::]:99", true), + + // portranges + imtest("http://*.foo.com:1-2/a/b/-", "http://www.foo.com:1/a/b/c/d", true), + imtest("http://*.foo.com:1-2/a/b/-", "http://www.foo.com:3/a/b/c/d", false), + imtest("http://*.foo.com:3-/a/b/-", "http://www.foo.com:1/a/b/c/d", false), + imtest("http://*.foo.com:3-/a/b/-", "http://www.foo.com:4-5/a/b/c/d", true), + imtest("http://*.foo.com:3-/a/b/-", "http://www.foo.com:3-3/a/b/c/d", true), + imtest("http://*.foo.com:3-99/a/b/-", "http://www.foo.com:55-100/a/b/c/d", false), + imtest("http://*.foo.com:-44/a/b/-", "http://www.foo.com:1/a/b/c/d", true), + imtest("http://*.foo.com:-44/a/b/-", "http://www.foo.com:1-10/a/b/c/d", true), + imtest("http://*.foo.com:-44/a/b/-", "http://www.foo.com:44/a/b/c/d", true), + imtest("http://*.foo.com:-44/a/b/-", "http://www.foo.com:45/a/b/c/d", false), + imtest("http://www.foo.com:70-90/a/b", "http://www.foo.com/a/b", true), + imtest("https://www.foo.com/a/b", "https://www.foo.com:80/a/b", false), + imtest("https://www.foo.com:70-90/a/b", "https://www.foo.com/a/b", false), + imtest("https://www.foo.com/a/b", "https://www.foo.com:443/a/b", true), + imtest("https://www.foo.com:200-500/a/b", "https://www.foo.com/a/b", true), + imtest("http://www.foo.com:*/a/b", "http://www.foo.com:1-12345/a/b", true), + + // misc + imtest("https:*", "http://www.foo.com", false), + imtest("https:*", "http:*", false) + }; + + static Test[] actionImplies = { + actest("GET", "GET", true), + actest("GET", "POST", false), + actest("GET:", "PUT", false), + actest("GET:", "GET", true), + actest("GET,POST", "GET", true), + actest("GET,POST:", "GET", true), + actest("GET:X-Foo", "GET:x-foo", true), + actest("GET:X-Foo,X-bar", "GET:x-foo", true), + actest("GET:X-Foo", "GET:x-boo", false), + actest("GET:X-Foo,X-Bar", "GET:x-bar,x-foo", true), + actest("GET:X-Bar,X-Foo,X-Bar,Y-Foo", "GET:x-bar,x-foo", true), + actest("GET:*", "GET:x-bar,x-foo", true), + actest("*:*", "GET:x-bar,x-foo", true) + }; + + static Test[] equalityTests = { + eqtest("http://www.foo.com", "http://www.FOO.CoM", true), + eqtest("http://[fe80:0:0::]:1-2", "HTTP://[FE80::]:1-2", true), + eqtest("HTTP://1.2.3.5/A/B/C", "http://1.2.3.5/A/b/C", false), + eqtest("HTTP://1.2.3.5/A/B/C", "HTTP://1.2.3.5/A/b/C", false), + eqtest("http:*", "http:*", true), + eqtest("http://www.foo.com/a/b", "https://www.foo.com/a/b", false), + eqtest("http://w.foo.com", "http://w.foo.com/", false), + eqtest("http://*.foo.com", "http://*.foo.com", true), + eqtest("http://www.foo.com/a/b", "http://www.foo.com:80/a/b", true), + eqtest("http://www.foo.com/a/b", "http://www.foo.com:82/a/b", false), + eqtest("https://www.foo.com/a/b", "https://www.foo.com:443/a/b", true), + eqtest("https://www.foo.com/a/b", "https://www.foo.com:444/a/b", false), + }; + + static boolean failed = false; + + public static void main(String args[]) throws Exception { + for (int i=0; i<pathImplies.length ; i++) { + URLImpliesTest test = (URLImpliesTest)pathImplies[i]; + Exception caught = null; + boolean result = false; + try { + result = test.execute(); + } catch (Exception e) { + caught = e; + e.printStackTrace(); + } + if (!result) { + failed = true; + System.out.printf("path test %d failed: %s : %s\n", i, test.arg1, + test.arg2); + } else { + System.out.println ("path test " + i + " OK"); + } + + } + + // new tests for functionality added in revision of API + + for (int i=0; i<pathImplies2.length ; i++) { + URLImpliesTest test = (URLImpliesTest)pathImplies2[i]; + Exception caught = null; + boolean result = false; + try { + result = test.execute(); + } catch (Exception e) { + caught = e; + e.printStackTrace(); + } + if (!result) { + failed = true; + System.out.printf("path2 test %d failed: %s : %s\n", i, test.arg1, + test.arg2); + } else { + System.out.println ("path2 test " + i + " OK"); + } + + } + + for (int i=0; i<equalityTests.length ; i++) { + URLEqualityTest test = (URLEqualityTest)equalityTests[i]; + Exception caught = null; + boolean result = false; + try { + result = test.execute(); + } catch (Exception e) { + caught = e; + e.printStackTrace(); + } + if (!result) { + failed = true; + System.out.printf("equality test %d failed: %s : %s\n", i, test.arg1, + test.arg2); + } else { + System.out.println ("equality test " + i + " OK"); + } + + } + + for (int i=0; i<exceptionTests.length; i++) { + ExTest test = (ExTest)exceptionTests[i]; + boolean result = test.execute(); + if (!result) { + System.out.println ("test failed: " + test.arg); + failed = true; + } else { + System.out.println ("exception test " + i + " OK"); + } + } + + for (int i=0; i<actionImplies.length ; i++) { + ActionImpliesTest test = (ActionImpliesTest)actionImplies[i]; + Exception caught = null; + boolean result = false; + try { + result = test.execute(); + } catch (Exception e) { + caught = e; + e.printStackTrace(); + } + if (!result) { + failed = true; + System.out.println ("test failed: " + test.arg1 + ": " + + test.arg2 + " Exception: " + caught); + } + System.out.println ("action test " + i + " OK"); + } + + serializationTest("http://www.foo.com/-", "GET,DELETE:*"); + serializationTest("https://www.foo.com/-", "POST:X-Foo"); + serializationTest("https:*", "*:*"); + serializationTest("http://www.foo.com/a/b/s/", "POST:X-Foo"); + serializationTest("http://www.foo.com/a/b/s/*", "POST:X-Foo"); + + if (failed) { + throw new RuntimeException("some tests failed"); + } + + } + + static void serializationTest(String name, String actions) + throws Exception { + + URLPermission out = new URLPermission(name, actions); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream o = new ObjectOutputStream(baos); + o.writeObject(out); + ByteArrayInputStream bain = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream i = new ObjectInputStream(bain); + URLPermission in = (URLPermission)i.readObject(); + if (!in.equals(out)) { + System.out.println ("FAIL"); + System.out.println ("in = " + in); + System.out.println ("out = " + out); + failed = true; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/net/URLPermission/URLTest.java Mon Oct 14 22:09:15 2013 +0100 @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.URLPermission; +/* + * Run the tests once without security manager and once with + * + * @test + * @bug 8010464 + * @compile ../../../com/sun/net/httpserver/SimpleSSLContext.java + * @run main/othervm/policy=policy.1 URLTest one + * @run main/othervm URLTest one + * @run main/othervm/policy=policy.2 URLTest two + * @run main/othervm URLTest two + * @run main/othervm/policy=policy.3 URLTest three + * @run main/othervm URLTest three + */ + +import java.net.*; +import java.io.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.*; +import com.sun.net.httpserver.*; +import javax.net.ssl.*; + +public class URLTest { + static boolean failed = false; + + public static void main (String[] args) throws Exception { + boolean no = false, yes = true; + + if (System.getSecurityManager() == null) { + yes = false; + } + createServers(); + InetSocketAddress addr1 = httpServer.getAddress(); + int port1 = addr1.getPort(); + InetSocketAddress addr2 = httpsServer.getAddress(); + int port2 = addr2.getPort(); + + // each of the following cases is run with a different policy file + + switch (args[0]) { + case "one": + String url1 = "http://127.0.0.1:"+ port1 + "/foo.html"; + String url2 = "https://127.0.0.1:"+ port2 + "/foo.html"; + String url3 = "http://127.0.0.1:"+ port1 + "/bar.html"; + String url4 = "https://127.0.0.1:"+ port2 + "/bar.html"; + + // simple positive test. Should succceed + test(url1, "GET", "X-Foo", no); + test(url1, "GET", "Z-Bar", "X-Foo", no); + test(url1, "GET", "X-Foo", "Z-Bar", no); + test(url1, "GET", "Z-Bar", no); + test(url2, "POST", "X-Fob", no); + + // reverse the methods, should fail + test(url1, "POST", "X-Foo", yes); + test(url2, "GET", "X-Fob", yes); + + // different URLs, should fail + test(url3, "GET", "X-Foo", yes); + test(url4, "POST", "X-Fob", yes); + break; + + case "two": + url1 = "http://127.0.0.1:"+ port1 + "/foo.html"; + url2 = "https://127.0.0.1:"+ port2 + "/foo.html"; + url3 = "http://127.0.0.1:"+ port1 + "/bar.html"; + url4 = "https://127.0.0.1:"+ port2 + "/bar.html"; + + // simple positive test. Should succceed + test(url1, "GET", "X-Foo", no); + test(url2, "POST", "X-Fob", no); + test(url3, "GET", "X-Foo", no); + test(url4, "POST", "X-Fob", no); + break; + + case "three": + url1 = "http://127.0.0.1:"+ port1 + "/foo.html"; + url2 = "https://127.0.0.1:"+ port2 + "/a/c/d/e/foo.html"; + url3 = "http://127.0.0.1:"+ port1 + "/a/b/c"; + url4 = "https://127.0.0.1:"+ port2 + "/a/b/c"; + + test(url1, "GET", "X-Foo", yes); + test(url2, "POST", "X-Zxc", no); + test(url3, "DELETE", "Y-Foo", no); + test(url4, "POST", "Y-Foo", yes); + break; + } + shutdown(); + if (failed) { + throw new RuntimeException("Test failed"); + } + } + + public static void test ( + String u, String method, + String header, boolean exceptionExpected + ) + throws Exception + { + test(u, method, header, null, exceptionExpected); + } + + public static void test ( + String u, String method, + String header1, String header2, boolean exceptionExpected + ) + throws Exception + { + URL url = new URL(u); + System.out.println ("url=" + u + " method="+method + " header1="+header1 + +" header2 = " + header2 + +" exceptionExpected="+exceptionExpected); + HttpURLConnection urlc = (HttpURLConnection)url.openConnection(); + if (urlc instanceof HttpsURLConnection) { + HttpsURLConnection ssl = (HttpsURLConnection)urlc; + ssl.setHostnameVerifier(new HostnameVerifier() { + public boolean verify(String host, SSLSession sess) { + return true; + } + }); + ssl.setSSLSocketFactory (ctx.getSocketFactory()); + } + urlc.setRequestMethod(method); + if (header1 != null) { + urlc.addRequestProperty(header1, "foo"); + } + if (header2 != null) { + urlc.addRequestProperty(header2, "bar"); + } + try { + int g = urlc.getResponseCode(); + if (exceptionExpected) { + failed = true; + System.out.println ("FAIL"); + return; + } + if (g != 200) { + String s = Integer.toString(g); + throw new RuntimeException("unexpected response "+ s); + } + InputStream is = urlc.getInputStream(); + int c,count=0; + byte[] buf = new byte[1024]; + while ((c=is.read(buf)) != -1) { + count += c; + } + is.close(); + } catch (RuntimeException e) { + if (! (e instanceof SecurityException) && + !(e.getCause() instanceof SecurityException) || + !exceptionExpected) + { + System.out.println ("FAIL"); + //e.printStackTrace(); + failed = true; + } + } + System.out.println ("OK"); + } + + static HttpServer httpServer; + static HttpsServer httpsServer; + static HttpContext c, cs; + static ExecutorService e, es; + static SSLContext ctx; + + // These ports need to be hard-coded until we support port number + // ranges in the permission class + + static final int PORT1 = 12567; + static final int PORT2 = 12568; + + static void createServers() throws Exception { + InetSocketAddress addr1 = new InetSocketAddress (PORT1); + InetSocketAddress addr2 = new InetSocketAddress (PORT2); + httpServer = HttpServer.create (addr1, 0); + httpsServer = HttpsServer.create (addr2, 0); + + MyHandler h = new MyHandler(); + + c = httpServer.createContext ("/", h); + cs = httpsServer.createContext ("/", h); + e = Executors.newCachedThreadPool(); + es = Executors.newCachedThreadPool(); + httpServer.setExecutor (e); + httpsServer.setExecutor (es); + + // take the keystore from elsewhere in test hierarchy + String keysdir = System.getProperty("test.src") + + "/../../../com/sun/net/httpserver/"; + ctx = new SimpleSSLContext(keysdir).get(); + httpsServer.setHttpsConfigurator(new HttpsConfigurator (ctx)); + + httpServer.start(); + httpsServer.start(); + } + + static void shutdown() { + httpServer.stop(1); + httpsServer.stop(1); + e.shutdown(); + es.shutdown(); + } + + static class MyHandler implements HttpHandler { + + MyHandler() { + } + + public void handle(HttpExchange x) throws IOException { + x.sendResponseHeaders(200, -1); + x.close(); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/net/URLPermission/policy.1 Mon Oct 14 22:09:15 2013 +0100 @@ -0,0 +1,48 @@ +// +// Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +// or visit www.oracle.com if you need additional information or have any +// questions. +// + +grant { + permission java.net.URLPermission "http://127.0.0.1:12567/foo.html", "GET:X-Foo,Z-Bar"; + permission java.net.URLPermission "https://127.0.0.1:12568/foo.html", "POST:X-Fob,T-Bar"; + + // needed for HttpServer + permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept"; + permission "java.util.PropertyPermission" "test.src", "read"; + permission java.io.FilePermission "${test.src}/../../../com/sun/net/httpserver/testkeys", "read"; + + //permission "java.util.logging.LoggingPermission" "control"; + //permission "java.io.FilePermission" "/tmp/-", "read,write"; + permission "java.lang.RuntimePermission" "modifyThread"; + permission "java.lang.RuntimePermission" "setFactory"; +}; + +// Normal permissions that aren't granted when run under jtreg + +grant codeBase "file:${{java.ext.dirs}}/*" { + permission java.security.AllPermission; +}; + +grant codeBase "file:${{java.home}}/jre/lib/rt.jar" { + permission java.security.AllPermission; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/net/URLPermission/policy.2 Mon Oct 14 22:09:15 2013 +0100 @@ -0,0 +1,46 @@ +// +// Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +// or visit www.oracle.com if you need additional information or have any +// questions. +// + +grant { + permission java.net.URLPermission "http://127.0.0.1:12567/*", "GET:X-Foo"; + permission java.net.URLPermission "https://127.0.0.1:12568/*", "POST:X-Fob"; + + // needed for HttpServer + permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept"; + permission "java.util.PropertyPermission" "test.src", "read"; + permission java.io.FilePermission "${test.src}/../../../com/sun/net/httpserver/testkeys", "read"; + + //permission "java.util.logging.LoggingPermission" "control"; + //permission "java.io.FilePermission" "/tmp/-", "read,write"; + permission "java.lang.RuntimePermission" "modifyThread"; + permission "java.lang.RuntimePermission" "setFactory"; +}; + +grant codeBase "file:${{java.ext.dirs}}/*" { + permission java.security.AllPermission; +}; + +grant codeBase "file:///export/repos/jdk8/build/linux-x86_64-normal-server-fastdebug/images/j2sdk-image/jre/lib/rt.jar" { + permission java.security.AllPermission; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/net/URLPermission/policy.3 Mon Oct 14 22:09:15 2013 +0100 @@ -0,0 +1,48 @@ +// +// Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +// or visit www.oracle.com if you need additional information or have any +// questions. +// + +grant { + permission java.net.URLPermission "http://127.0.0.1:12567/a/b/-", "DELETE,GET:X-Foo,Y-Foo"; + permission java.net.URLPermission "https://127.0.0.1:12568/a/c/-", "POST:*"; + + // needed for HttpServer + permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept"; + permission "java.util.PropertyPermission" "test.src", "read"; + permission java.io.FilePermission "${test.src}/../../../com/sun/net/httpserver/testkeys", "read"; + + //permission "java.util.logging.LoggingPermission" "control"; + //permission "java.io.FilePermission" "/tmp/-", "read,write"; + permission "java.lang.RuntimePermission" "modifyThread"; + permission "java.lang.RuntimePermission" "setFactory"; +}; + +// Normal permissions that aren't granted when run under jtreg + +grant codeBase "file:${{java.ext.dirs}}/*" { + permission java.security.AllPermission; +}; + +grant codeBase "file:${{java.home}}/jre/lib/rt.jar" { + permission java.security.AllPermission; +}; +