From eea741beb8bdfa4c757e900cddf69a180ba8ebf6 Mon Sep 17 00:00:00 2001 From: Jesse McConnell Date: Thu, 11 Jun 2009 16:20:07 +0000 Subject: [PATCH] [BUG-277551] promote to trunk so we can review in full context if this implemenation is the way to go git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@365 7e9141cc-0065-0410-87d8-b60c137991c4 --- jetty-policy/pom.xml | 40 ++ .../src/main/config/lib/security/jetty.policy | 107 ++++ .../org/eclipse/jetty/policy/JettyPolicy.java | 155 ++++++ .../eclipse/jetty/policy/PolicyException.java | 40 ++ .../jetty/policy/PropertyEvaluator.java | 92 ++++ .../policy/loader/DefaultPolicyLoader.java | 267 ++++++++++ .../policy/loader/PolicyFileScanner.java | 469 ++++++++++++++++++ .../eclipse/jetty/policy/TestJettyPolicy.java | 163 ++++++ .../jetty/policy/TestJettyPolicyRuntime.java | 149 ++++++ .../resources/global-all-permission.policy | 3 + ...lobal-file-read-only-tmp-permission.policy | 10 + .../multiple-codebase-file-permission.policy | 8 + .../multiple-codebase-mixed-permission.policy | 12 + .../single-codebase-file-permission-2.policy | 3 + .../single-codebase-file-permission.policy | 3 + 15 files changed, 1521 insertions(+) create mode 100644 jetty-policy/pom.xml create mode 100644 jetty-policy/src/main/config/lib/security/jetty.policy create mode 100644 jetty-policy/src/main/java/org/eclipse/jetty/policy/JettyPolicy.java create mode 100644 jetty-policy/src/main/java/org/eclipse/jetty/policy/PolicyException.java create mode 100644 jetty-policy/src/main/java/org/eclipse/jetty/policy/PropertyEvaluator.java create mode 100644 jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java create mode 100644 jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/PolicyFileScanner.java create mode 100644 jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicy.java create mode 100644 jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicyRuntime.java create mode 100644 jetty-policy/src/test/resources/global-all-permission.policy create mode 100644 jetty-policy/src/test/resources/global-file-read-only-tmp-permission.policy create mode 100644 jetty-policy/src/test/resources/multiple-codebase-file-permission.policy create mode 100644 jetty-policy/src/test/resources/multiple-codebase-mixed-permission.policy create mode 100644 jetty-policy/src/test/resources/single-codebase-file-permission-2.policy create mode 100644 jetty-policy/src/test/resources/single-codebase-file-permission.policy diff --git a/jetty-policy/pom.xml b/jetty-policy/pom.xml new file mode 100644 index 00000000000..0890cc61a66 --- /dev/null +++ b/jetty-policy/pom.xml @@ -0,0 +1,40 @@ + + 4.0.0 + + org.eclipse.jetty + jetty-project + 7.0.0.M3-SNAPSHOT + + org.eclipse.jetty + jetty-policy + Jetty :: Policy Tool + jar + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + config.xml + + + + + + + + + + junit + junit + test + + + diff --git a/jetty-policy/src/main/config/lib/security/jetty.policy b/jetty-policy/src/main/config/lib/security/jetty.policy new file mode 100644 index 00000000000..cfacb61fc39 --- /dev/null +++ b/jetty-policy/src/main/config/lib/security/jetty.policy @@ -0,0 +1,107 @@ +// basic policy file for jetty + +// TODO update with greg's latest property changes and set better reasonable defaults for various jetty codeBases + +grant codeBase "file:${jetty.home}${/}-" { + permission java.io.FilePermission "${jetty.home}${/}-", "read"; + + permission java.io.FilePermission "${jetty.home}${/}logs${/}-", "read, write"; + + permission java.lang.RuntimePermission "createClassLoader"; + permission java.lang.RuntimePermission "setContextClassLoader"; + + permission java.security.SecurityPermission "getPolicy"; + permission java.lang.RuntimePermission "accessDeclaredMembers"; + + // makes everything work as a crutch to work on startup + permission java.security.AllPermission; +}; + +// default permissions granted to all domains +grant { + + // Allows any thread to stop itself using the java.lang.Thread.stop() + // method that takes no argument. + // Note that this permission is granted by default only to remain + // backwards compatible. + // It is strongly recommended that you either remove this permission + // from this policy file or further restrict it to code sources + // that you specify, because Thread.stop() is potentially unsafe. + // See "http://java.sun.com/notes" for more information. + permission java.lang.RuntimePermission "stopThread"; + + // allows anyone to listen on un-privileged ports + permission java.net.SocketPermission "localhost:1024-", "listen"; + + // "standard" properties that can be read by anyone + + permission java.util.PropertyPermission "java.version", "read"; + permission java.util.PropertyPermission "java.vendor", "read"; + permission java.util.PropertyPermission "java.vendor.url", "read"; + permission java.util.PropertyPermission "java.class.version", "read"; + permission java.util.PropertyPermission "os.name", "read"; + permission java.util.PropertyPermission "os.version", "read"; + permission java.util.PropertyPermission "os.arch", "read"; + permission java.util.PropertyPermission "file.separator", "read"; + permission java.util.PropertyPermission "path.separator", "read"; + permission java.util.PropertyPermission "line.separator", "read"; + + permission java.util.PropertyPermission "java.specification.version", "read"; + permission java.util.PropertyPermission "java.specification.vendor", "read"; + permission java.util.PropertyPermission "java.specification.name", "read"; + + permission java.util.PropertyPermission "java.vm.specification.version", "read"; + permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; + permission java.util.PropertyPermission "java.vm.specification.name", "read"; + permission java.util.PropertyPermission "java.vm.version", "read"; + permission java.util.PropertyPermission "java.vm.vendor", "read"; + permission java.util.PropertyPermission "java.vm.name", "read"; + + // jetty specific properties + permission java.util.PropertyPermission "org.eclipse.jetty.util.log.DEBUG", "read"; + permission java.util.PropertyPermission "START", "read"; + permission java.util.PropertyPermission "org.eclipse.jetty.util.log.VERBOSE", "read"; + permission java.util.PropertyPermission "STOP.PORT", "read"; + permission java.util.PropertyPermission "STOP.KEY", "read"; + permission java.util.PropertyPermission "org.eclipse.jetty.util.log.IGNORED", "read"; + permission java.util.PropertyPermission "CLASSPATH", "read"; + permission java.util.PropertyPermission "OPTIONS", "read"; + permission java.util.PropertyPermission "JETTY_NO_SHUTDOWN_HOOK", "read"; + permission java.util.PropertyPermission "ISO_8859_1", "read"; + permission java.util.PropertyPermission "jetty.home", "read, write"; + + permission java.util.PropertyPermission "user.home", "read"; + + permission java.util.PropertyPermission "jetty.class.path", "read, write"; + permission java.util.PropertyPermission "java.class.path", "read, write"; + + permission java.util.PropertyPermission "repository", "read, write"; + + permission java.util.PropertyPermission "jetty.lib", "read"; + permission java.util.PropertyPermission "jetty.server", "read"; + permission java.util.PropertyPermission "jetty.host", "read"; + permission java.util.PropertyPermission "jetty.port", "read"; + permission java.util.PropertyPermission "start.class", "read"; + + permission java.util.PropertyPermission "main.class", "read"; + + permission java.util.PropertyPermission "org.eclipse.jetty.util.log.class", "read"; + + permission java.util.PropertyPermission "org.eclipse.jetty.util.URI.charset", "read"; + + permission java.util.PropertyPermission "org.eclipse.jetty.util.FileResource.checkAliases", "read"; + + permission java.util.PropertyPermission "org.eclipse.jetty.xml.XmlParser.Validating", "read"; + + permission java.util.PropertyPermission "org.eclipse.jetty.io.nio.JVMBUG_THRESHHOLD", "read, write"; + + permission java.util.PropertyPermission "org.eclipse.jetty.util.TypeUtil.IntegerCacheSize", "read, write"; + + permission java.util.PropertyPermission "org.eclipse.jetty.util.TypeUtil.LongCacheSize", "read"; + + // provides access to webapps + permission java.io.FilePermission "${jetty.home}${/}webapps${/}-", "read"; // Ought to go up a specific codebase + +}; + + diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/JettyPolicy.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/JettyPolicy.java new file mode 100644 index 00000000000..c42ff7d2f9f --- /dev/null +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/JettyPolicy.java @@ -0,0 +1,155 @@ +package org.eclipse.jetty.policy; + +//======================================================================== +//Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd. +//------------------------------------------------------------------------ +//All rights reserved. This program and the accompanying materials +//are made available under the terms of the Eclipse Public License v1.0 +//and Apache License v2.0 which accompanies this distribution. +//The Eclipse Public License is available at +//http://www.eclipse.org/legal/epl-v10.html +//The Apache License v2.0 is available at +//http://www.opensource.org/licenses/apache2.0.php +//You may elect to redistribute this code under either of these licenses. +//======================================================================== + +import java.io.File; +import java.io.FileInputStream; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jetty.policy.loader.DefaultPolicyLoader; + +/** + * Policy implementation that will load a set of policy files and manage the mapping of permissions and protection domains + * + * The reason I created this class and added this mechanism are: + * + * 1) I wanted a way to be able to follow the startup mechanic that jetty uses with jetty-start using OPTIONS=policy,default + * to be able to startup a security manager and policy implementation without have to rely on the existing JVM cli options + * 2) establish a starting point to add on further functionality to permissions based security with jetty like jmx enabled + * permission tweaking or runtime creation and specification of policies for specific webapps + * 3) I wanted to have support for specifying multiple policy files to source permissions from + * + * Possible additions are: + * - jmx enabled a la #2 above + * - proxying of system security policy where we can proxy access to the system policy should the jvm have been started with + * one, I had support for this but ripped it out to add in again later + * - merging of protection domains if process multiple policy files that declare permissions for the same codebase + * - an xml policy file parser, had originally added this using modello but tore it out since it would have been + * a nightmare to get its dependencies through IP validation, could do this with jvm xml parser instead sometime + * - check performance of the synch'd map I am using for the protection domain mapping + */ +public class JettyPolicy extends Policy +{ + // Policy files that are actively managed by the aggregate policy mechanism + private Set _policies; + + private PropertyEvaluator _evaluator; + + private Map pdMapping = + Collections.synchronizedMap( new HashMap() ); + + public JettyPolicy( Set policies, Map properties ) + { + _policies = policies; + _evaluator = new PropertyEvaluator( properties ); + + // we have the policies we need and an evaluator to reference, lets refresh and save the user a call. + refresh(); + } + + public PermissionCollection getPermissions( ProtectionDomain domain ) + { + PermissionCollection perms = new Permissions(); + + for ( Iterator i = pdMapping.keySet().iterator(); i.hasNext(); ) + { + ProtectionDomain pd = (ProtectionDomain) i.next(); + + if ( pd.getCodeSource() == null || pd.getCodeSource().implies( domain.getCodeSource() ) ) + { + // gather dynamic permissions + if ( pdMapping.get( pd ) != null ) + { + for ( Enumeration e = pdMapping.get( pd ).elements(); e.hasMoreElements(); ) + { + perms.add( e.nextElement() ); + } + } + + // gather static permissions + if ( pd.getPermissions() != null ) + { + for ( Enumeration e = pd.getPermissions().elements(); e.hasMoreElements(); ) + { + perms.add( e.nextElement() ); + } + } + } + } + + return perms; + } + + public PermissionCollection getPermissions( CodeSource codesource ) + { + PermissionCollection perms = new Permissions(); + + for ( Iterator i = pdMapping.keySet().iterator(); i.hasNext(); ) + { + ProtectionDomain pd = (ProtectionDomain) i.next(); + + if ( pd.getCodeSource() == null || pd.getCodeSource().implies( codesource ) ) + { + // gather dynamic permissions + if ( pdMapping.get( pd ) != null ) + { + for ( Enumeration e = pdMapping.get( pd ).elements(); e.hasMoreElements(); ) + { + perms.add( e.nextElement() ); + } + } + + // gather static permissions + if ( pd.getPermissions() != null ) + { + for ( Enumeration e = pd.getPermissions().elements(); e.hasMoreElements(); ) + { + perms.add( e.nextElement() ); + } + } + } + } + + return perms; + } + + public void refresh() + { + try + { + pdMapping.clear(); + + for ( Iterator i = _policies.iterator(); i.hasNext(); ) + { + File policyFile = new File( i.next() ); + pdMapping.putAll( DefaultPolicyLoader.load( new FileInputStream( policyFile ), _evaluator ) ); + } + } + catch ( Exception e ) + { + e.printStackTrace(); + } + } +} diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/PolicyException.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/PolicyException.java new file mode 100644 index 00000000000..6ece8eed521 --- /dev/null +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/PolicyException.java @@ -0,0 +1,40 @@ +package org.eclipse.jetty.policy; + +//======================================================================== +//Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. +//------------------------------------------------------------------------ +//All rights reserved. This program and the accompanying materials +//are made available under the terms of the Eclipse Public License v1.0 +//and Apache License v2.0 which accompanies this distribution. +//The Eclipse Public License is available at +//http://www.eclipse.org/legal/epl-v10.html +//The Apache License v2.0 is available at +//http://www.opensource.org/licenses/apache2.0.php +//You may elect to redistribute this code under either of these licenses. +//======================================================================== + + +public class PolicyException extends Exception +{ + + public PolicyException() + { + super(); + } + + public PolicyException( final String message, final Throwable cause) + { + super( message, cause ); + } + + public PolicyException( final String message ) + { + super( message ); + } + + public PolicyException( final Throwable cause ) + { + super( cause ); + } + +} diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/PropertyEvaluator.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/PropertyEvaluator.java new file mode 100644 index 00000000000..678988f6c45 --- /dev/null +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/PropertyEvaluator.java @@ -0,0 +1,92 @@ +package org.eclipse.jetty.policy; + +//======================================================================== +//Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd. +//------------------------------------------------------------------------ +//All rights reserved. This program and the accompanying materials +//are made available under the terms of the Eclipse Public License v1.0 +//and Apache License v2.0 which accompanies this distribution. +//The Eclipse Public License is available at +//http://www.eclipse.org/legal/epl-v10.html +//The Apache License v2.0 is available at +//http://www.opensource.org/licenses/apache2.0.php +//You may elect to redistribute this code under either of these licenses. +//======================================================================== + + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/* + * Property evaluator to provide common reference object for property access and evaluation + * + * The origin of this class was in the Main.class of the jetty-start package where is where + * it picks up the convention of $() properties and ${} system properties + * + * Not exactly sun convention but a jetty convention none the less + */ +public class PropertyEvaluator extends HashMap +{ + private static final long serialVersionUID = -7745629868268683553L; + + public PropertyEvaluator( Map properties ) + { + putAll( properties ); + put("/", File.separator ); // '/' is a special case when evaluated itself, resolves to File.separator as per policy parsing convention + } + + /** + * returns the value of it exists in this map, otherwise consults the System properties + * + * @param name + * @return + */ + public String getSystemProperty(String name) + { + if (containsKey(name)) + return get(name); + return System.getProperty(name); + } + + public String getProperty(String name) + { + return get(name); + } + + /* ------------------------------------------------------------ */ + public String evaluate(String s) + { + int i1=0; + int i2=0; + while (s!=null) + { + i1=s.indexOf("$(",i2); + if (i1<0) + break; + i2=s.indexOf(")",i1+2); + if (i2<0) + break; + String name=s.substring(i1+2,i2); + String property=getSystemProperty(name); + s=s.substring(0,i1)+property+s.substring(i2+1); + } + + i1=0; + i2=0; + while (s!=null) + { + i1=s.indexOf("${",i2); + if (i1<0) + break; + i2=s.indexOf("}",i1+2); + if (i2<0) + break; + String name=s.substring(i1+2,i2); + String property=getProperty(name); + s=s.substring(0,i1)+property+s.substring(i2+1); + } + + return s; + } +} diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java new file mode 100644 index 00000000000..670f59cc69b --- /dev/null +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java @@ -0,0 +1,267 @@ +package org.eclipse.jetty.policy.loader; + +//======================================================================== +//Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. +//------------------------------------------------------------------------ +//All rights reserved. This program and the accompanying materials +//are made available under the terms of the Eclipse Public License v1.0 +//and Apache License v2.0 which accompanies this distribution. +//The Eclipse Public License is available at +//http://www.eclipse.org/legal/epl-v10.html +//The Apache License v2.0 is available at +//http://www.opensource.org/licenses/apache2.0.php +//You may elect to redistribute this code under either of these licenses. +// +//Portions of this file adapted for use from Apache Harmony code by written +//and contributed to that project by Alexey V. Varlamov under the ASL +//======================================================================== + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.policy.PolicyException; +import org.eclipse.jetty.policy.PropertyEvaluator; + +/** + * Load the policies within the stream and resolve into protection domains and permission collections + * + * TODO: currently this loading does not support keystores or certificates + */ +public class DefaultPolicyLoader +{ + + public static Map load( InputStream policyStream, PropertyEvaluator evaluator ) throws PolicyException + { + Map pdMappings = new HashMap(); + + try + { + PolicyFileScanner loader = new PolicyFileScanner(); + + Collection grantEntries = new ArrayList(); + List keystoreEntries = new ArrayList(); + + loader.scanStream( new InputStreamReader(policyStream), grantEntries, keystoreEntries ); + + for ( Iterator i = grantEntries.iterator(); i.hasNext(); ) + { + GrantEntry grant = i.next(); + + Permissions permissions = processPermissions( grant.permissions, evaluator ); + + ProtectionDomain pd; + + if ( grant.codebase == null ) // these are hereby known as global permissions (no codebase associated) + { + pd = new ProtectionDomain( null, permissions ); + } + else + { + CodeSource codeSource = resolveToCodeSource( grant.codebase, evaluator ); + pd = new ProtectionDomain( codeSource, permissions ); + } + pdMappings.put( pd, null ); + } + + return pdMappings; + } + catch ( Exception e ) + { + throw new PolicyException( e ); + } + } + + private static Permissions processPermissions( Collection collection, PropertyEvaluator evaluator ) throws PolicyException + { + Permissions permissions = new Permissions(); + + for ( Iterator i = collection.iterator(); i.hasNext(); ) + { + PermissionEntry perm = i.next(); + + Class clazz; + try + { + clazz = Class.forName( perm.klass ); + + if ( perm.name == null && perm.actions == null ) + { + permissions.add( (Permission)clazz.newInstance() ); + } + else if ( perm.name != null && perm.actions == null ) + { + Constructor c = clazz.getConstructor(new Class[] { String.class }); + permissions.add( (Permission)c.newInstance( evaluator.evaluate( perm.name ) ) ); + } + else if ( perm.name != null && perm.actions != null ) + { + Constructor c = clazz.getConstructor(new Class[] { String.class, String.class }); + permissions.add( (Permission)c.newInstance( evaluator.evaluate( perm.name ), perm.actions ) ); + } + + } + catch ( Exception e ) + { + throw new PolicyException( e ); + } + } + + return permissions; + } + + private static CodeSource resolveToCodeSource( String codeBase, PropertyEvaluator evaluator ) throws PolicyException + { + try + { + URL url = new URL( evaluator.evaluate(codeBase) ); + Certificate[] cert = null; + return new CodeSource( url, cert); //TODO support certificates + } + catch ( Exception e ) + { + throw new PolicyException( e ); + } + } + + /** + * Compound token representing keystore clause. See policy format + * {@link org.apache.harmony.security.DefaultPolicy description}for details. + * + * @see org.apache.harmony.security.fortress.DefaultPolicyParser + * @see org.apache.harmony.security.DefaultPolicyScanner + */ + public static class KeystoreEntry + { + + /** + * The URL part of keystore clause. + */ + public String url; + + /** + * The typename part of keystore clause. + */ + public String type; + } + + /** + * Compound token representing grant clause. See policy format + * {@link org.apache.harmony.security.DefaultPolicy description}for details. + * + * @see org.apache.harmony.security.fortress.DefaultPolicyParser + * @see org.apache.harmony.security.DefaultPolicyScanner + */ + public static class GrantEntry + { + + /** + * The signers part of grant clause. This is a comma-separated list of certificate aliases. + */ + public String signers; + + /** + * The codebase part of grant clause. This is an URL from which code originates. + */ + public String codebase; + + /** + * Collection of PrincipalEntries of grant clause. + */ + public Collection principals; + + /** + * Collection of PermissionEntries of grant clause. + */ + public Collection permissions; + + /** + * Adds specified element to the principals collection. If collection does not exist yet, creates a + * new one. + */ + public void addPrincipal( PrincipalEntry pe ) + { + if ( principals == null ) + { + principals = new HashSet(); + } + principals.add( pe ); + } + + } + + /** + * Compound token representing principal entry of a grant clause. See policy format + * {@link org.apache.harmony.security.DefaultPolicy description}for details. + * + * @see org.apache.harmony.security.fortress.DefaultPolicyParser + * @see org.apache.harmony.security.DefaultPolicyScanner + */ + public static class PrincipalEntry + { + + /** + * Wildcard value denotes any class and/or any name. Must be asterisk, for proper general expansion and + * PrivateCredentialsPermission wildcarding + */ + public static final String WILDCARD = "*"; //$NON-NLS-1$ + + /** + * The classname part of principal clause. + */ + public String klass; + + /** + * The name part of principal clause. + */ + public String name; + } + + /** + * Compound token representing permission entry of a grant clause. See policy format + * {@link org.apache.harmony.security.DefaultPolicy description}for details. + * + * @see org.apache.harmony.security.fortress.DefaultPolicyParser + * @see org.apache.harmony.security.DefaultPolicyScanner + */ + public static class PermissionEntry + { + + /** + * The classname part of permission clause. + */ + public String klass; + + /** + * The name part of permission clause. + */ + public String name; + + /** + * The actions part of permission clause. + */ + public String actions; + + /** + * The signers part of permission clause. This is a comma-separated list of certificate aliases. + */ + public String signers; + } + + + +} diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/PolicyFileScanner.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/PolicyFileScanner.java new file mode 100644 index 00000000000..d43c18f553b --- /dev/null +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/PolicyFileScanner.java @@ -0,0 +1,469 @@ +package org.eclipse.jetty.policy.loader; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//======================================================================== +//All rights reserved. This program and the accompanying materials +//are made available under the terms of the Eclipse Public License v1.0 +//and Apache License v2.0 which accompanies this distribution. +//The Eclipse Public License is available at +//http://www.eclipse.org/legal/epl-v10.html +//The Apache License v2.0 is available at +//http://www.opensource.org/licenses/apache2.0.php +//You may elect to redistribute this code under either of these licenses. +// +// This file adapted for use from Apache Harmony code by written and contributed +// to that project by Alexey V. Varlamov under the ASL-2.0 +// +// See CQ3380 +//======================================================================== + +import java.io.IOException; +import java.io.Reader; +import java.io.StreamTokenizer; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import org.eclipse.jetty.policy.loader.DefaultPolicyLoader.GrantEntry; +import org.eclipse.jetty.policy.loader.DefaultPolicyLoader.KeystoreEntry; +import org.eclipse.jetty.policy.loader.DefaultPolicyLoader.PermissionEntry; +import org.eclipse.jetty.policy.loader.DefaultPolicyLoader.PrincipalEntry; + + +/** + * This is a basic high-level tokenizer of policy files. It takes in a stream, analyzes data read from it and returns a + * set of structured tokens.
+ * This implementation recognizes text files, consisting of clauses with the following syntax: + * + *
+ * 
+ *     keystore "some_keystore_url", "keystore_type";
+ * 
+ * 
+ * + *
+ * 
+ *     grant [SignedBy "signer_names"] [, CodeBase "URL"]
+ *      [, Principal [principal_class_name] "principal_name"]
+ *      [, Principal [principal_class_name] "principal_name"] ... {
+ *      permission permission_class_name [ "target_name" ] [, "action"] 
+ *      [, SignedBy "signer_names"];
+ *      permission ...
+ *      };
+ * 
+ * 
+ * + * For semantical details of this format, see the {@link org.apache.harmony.security.DefaultPolicy default policy + * description}.
+ * Keywords are case-insensitive in contrast to quoted string literals. Comma-separation rule is quite forgiving, most + * commas may be just omitted. Whitespaces, line- and block comments are ignored. Symbol-level tokenization is delegated + * to java.io.StreamTokenizer.
+ *
+ * This implementation is effectively thread-safe, as it has no field references to data being processed (that is, + * passes all the data as method parameters). + * + */ +public class PolicyFileScanner +{ + + /** + * Specific exception class to signal policy file syntax error. + */ + public static class InvalidFormatException + extends Exception + { + + /** + * @serial + */ + private static final long serialVersionUID = 5789786270390222184L; + + /** + * Constructor with detailed message parameter. + */ + public InvalidFormatException( String arg0 ) + { + super( arg0 ); + } + } + + /** + * Configures passed tokenizer accordingly to supported syntax. + */ + protected StreamTokenizer configure( StreamTokenizer st ) + { + st.slashSlashComments( true ); + st.slashStarComments( true ); + st.wordChars( '_', '_' ); + st.wordChars( '$', '$' ); + return st; + } + + /** + * Performs the main parsing loop. Starts with creating and configuring a StreamTokenizer instance; then tries to + * recognize keystore or grant keyword. When found, invokes read method corresponding to the clause + * and collects result to the passed collection. + * + * @param r policy stream reader + * @param grantEntries a collection to accumulate parsed GrantEntries + * @param keystoreEntries a collection to accumulate parsed KeystoreEntries + * @throws IOException if stream reading failed + * @throws InvalidFormatException if unexpected or unknown token encountered + */ + public void scanStream( Reader r, Collection grantEntries, List keystoreEntries ) + throws IOException, InvalidFormatException + { + StreamTokenizer st = configure( new StreamTokenizer( r ) ); + // main parsing loop + parsing: while ( true ) + { + switch ( st.nextToken() ) + { + case StreamTokenizer.TT_EOF: // we've done the job + break parsing; + + case StreamTokenizer.TT_WORD: + if ( Util.equalsIgnoreCase( "keystore", st.sval ) ) { //$NON-NLS-1$ + keystoreEntries.add( readKeystoreEntry( st ) ); + } + else if ( Util.equalsIgnoreCase( "grant", st.sval ) ) { //$NON-NLS-1$ + grantEntries.add( readGrantEntry( st ) ); + } + else + { + handleUnexpectedToken( st, "Expected entries are : \"grant\" or \"keystore\"" ); //$NON-NLS-1$ + + } + break; + + case ';': // just delimiter of entries + break; + + default: + handleUnexpectedToken( st ); + break; + } + } + } + + /** + * Tries to read keystore clause fields. The expected syntax is + * + *
+     * 
+     *     "some_keystore_url"[, "keystore_type"];
+     * 
+     * 
+ * + * @return successfully parsed KeystoreEntry + * @throws IOException if stream reading failed + * @throws InvalidFormatException if unexpected or unknown token encountered + */ + protected KeystoreEntry readKeystoreEntry( StreamTokenizer st ) + throws IOException, InvalidFormatException + { + KeystoreEntry ke = new KeystoreEntry(); + if ( st.nextToken() == '"' ) + { + ke.url = st.sval; + if ( ( st.nextToken() == '"' ) || ( ( st.ttype == ',' ) && ( st.nextToken() == '"' ) ) ) + { + ke.type = st.sval; + } + else + { // handle token in the main loop + st.pushBack(); + } + } + else + { + handleUnexpectedToken( st, "Expected syntax is : keystore \"url\"[, \"type\"]" ); //$NON-NLS-1$ + + } + return ke; + } + + /** + * Tries to read grant clause.
+ * First, it reads codebase , signedby , principal entries till the '{' (opening curly brace) + * symbol. Then it calls readPermissionEntries() method to read the permissions of this clause.
+ * Principal entries (if any) are read by invoking readPrincipalEntry() method, obtained PrincipalEntries are + * accumulated.
+ * The expected syntax is + * + *
+     * 
+     *     [ [codebase "url"] | [signedby "name1,...,nameN"] | 
+     *          principal ...] ]* { ... }
+     * 
+     * 
+ * + * @return successfully parsed GrantEntry + * @throws IOException if stream reading failed + * @throws InvalidFormatException if unexpected or unknown token encountered + */ + protected GrantEntry readGrantEntry( StreamTokenizer st ) + throws IOException, InvalidFormatException + { + GrantEntry ge = new GrantEntry(); + parsing: while ( true ) + { + switch ( st.nextToken() ) + { + + case StreamTokenizer.TT_WORD: + if ( Util.equalsIgnoreCase( "signedby", st.sval ) ) { //$NON-NLS-1$ + if ( st.nextToken() == '"' ) + { + ge.signers = st.sval; + } + else + { + handleUnexpectedToken( st, "Expected syntax is : signedby \"name1,...,nameN\"" ); //$NON-NLS-1$ + } + } + else if ( Util.equalsIgnoreCase( "codebase", st.sval ) ) { //$NON-NLS-1$ + if ( st.nextToken() == '"' ) + { + ge.codebase = st.sval; + } + else + { + handleUnexpectedToken( st, "Expected syntax is : codebase \"url\"" ); //$NON-NLS-1$ + } + } + else if ( Util.equalsIgnoreCase( "principal", st.sval ) ) { //$NON-NLS-1$ + ge.addPrincipal( readPrincipalEntry( st ) ); + } + else + { + handleUnexpectedToken( st ); + } + break; + + case ',': // just delimiter of entries + break; + + case '{': + ge.permissions = readPermissionEntries( st ); + break parsing; + + default: // handle token in the main loop + st.pushBack(); + break parsing; + } + } + + return ge; + } + + /** + * Tries to read Principal entry fields. The expected syntax is + * + *
+     * 
+     *     [ principal_class_name ] "principal_name"
+     * 
+     * 
+ * + * Both class and name may be wildcards, wildcard names should not surrounded by quotes. + * + * @return successfully parsed PrincipalEntry + * @throws IOException if stream reading failed + * @throws InvalidFormatException if unexpected or unknown token encountered + */ + protected PrincipalEntry readPrincipalEntry( StreamTokenizer st ) + throws IOException, InvalidFormatException + { + PrincipalEntry pe = new PrincipalEntry(); + if ( st.nextToken() == StreamTokenizer.TT_WORD ) + { + pe.klass = st.sval; + st.nextToken(); + } + else if ( st.ttype == '*' ) + { + pe.klass = PrincipalEntry.WILDCARD; + st.nextToken(); + } + if ( st.ttype == '"' ) + { + pe.name = st.sval; + } + else if ( st.ttype == '*' ) + { + pe.name = PrincipalEntry.WILDCARD; + } + else + { + handleUnexpectedToken( st, "Expected syntax is : principal [class_name] \"principal_name\"" ); //$NON-NLS-1$ + } + return pe; + } + + /** + * Tries to read a list of permission entries. The expected syntax is + * + *
+     * 
+     *     permission permission_class_name
+     *          [ "target_name" ] [, "action_list"]
+     *          [, signedby "name1,name2,..."];
+     * 
+     * 
+ * + * List is terminated by '}' (closing curly brace) symbol. + * + * @return collection of successfully parsed PermissionEntries + * @throws IOException if stream reading failed + * @throws InvalidFormatException if unexpected or unknown token encountered + */ + protected Collection readPermissionEntries( StreamTokenizer st ) + throws IOException, InvalidFormatException + { + Collection permissions = new HashSet(); + parsing: while ( true ) + { + switch ( st.nextToken() ) + { + + case StreamTokenizer.TT_WORD: + if ( Util.equalsIgnoreCase( "permission", st.sval ) ) { //$NON-NLS-1$ + PermissionEntry pe = new PermissionEntry(); + if ( st.nextToken() == StreamTokenizer.TT_WORD ) + { + pe.klass = st.sval; + if ( st.nextToken() == '"' ) + { + pe.name = st.sval; + st.nextToken(); + } + if ( st.ttype == ',' ) + { + st.nextToken(); + } + if ( st.ttype == '"' ) + { + pe.actions = st.sval; + if ( st.nextToken() == ',' ) + { + st.nextToken(); + } + } + if ( st.ttype == StreamTokenizer.TT_WORD && Util.equalsIgnoreCase( "signedby", st.sval ) ) { //$NON-NLS-1$ + if ( st.nextToken() == '"' ) + { + pe.signers = st.sval; + } + else + { + handleUnexpectedToken( st ); + } + } + else + { // handle token in the next iteration + st.pushBack(); + } + permissions.add( pe ); + continue parsing; + } + } + handleUnexpectedToken( + st, + "Expected syntax is : permission permission_class_name [\"target_name\"] [, \"action_list\"] [, signedby \"name1,...,nameN\"]" ); //$NON-NLS-1$ + break; + + case ';': // just delimiter of entries + break; + + case '}': // end of list + break parsing; + + default: // invalid token + handleUnexpectedToken( st ); + break; + } + } + + return permissions; + } + + /** + * Formats a detailed description of tokenizer status: current token, current line number, etc. + */ + protected String composeStatus( StreamTokenizer st ) + { + return st.toString(); + } + + /** + * Throws InvalidFormatException with detailed diagnostics. + * + * @param st a tokenizer holding the erroneous token + * @param message a user-friendly comment, probably explaining expected syntax. Should not be null- use + * the overloaded single-parameter method instead. + */ + protected final void handleUnexpectedToken( StreamTokenizer st, String message ) + throws InvalidFormatException + { + throw new InvalidFormatException( "Unexpected token encountered: " + composeStatus( st ) + ". " + message ); + } + + /** + * Throws InvalidFormatException with error status: which token is unexpected on which line. + * + * @param st a tokenizer holding the erroneous token + */ + protected final void handleUnexpectedToken( StreamTokenizer st ) + throws InvalidFormatException + { + throw new InvalidFormatException( "Unexpected token encountered: " + composeStatus( st ) ); + } + + + private static class Util + { + public static String toUpperCase( String s ) + { + int len = s.length(); + StringBuilder buffer = new StringBuilder( len ); + for ( int i = 0; i < len; i++ ) + { + char c = s.charAt( i ); + if ( 'a' <= c && c <= 'z' ) + { + buffer.append( (char) ( c - ( 'a' - 'A' ) ) ); + } + else + { + buffer.append( c ); + } + } + return buffer.toString(); + } + + public static boolean equalsIgnoreCase( String s1, String s2 ) + { + s1 = toUpperCase( s1 ); + s2 = toUpperCase( s2 ); + return s1.equals( s2 ); + } + } + +} diff --git a/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicy.java b/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicy.java new file mode 100644 index 00000000000..94b8a5dabaa --- /dev/null +++ b/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicy.java @@ -0,0 +1,163 @@ +package org.eclipse.jetty.policy; +//======================================================================== +//Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd. +//------------------------------------------------------------------------ +//All rights reserved. This program and the accompanying materials +//are made available under the terms of the Eclipse Public License v1.0 +//and Apache License v2.0 which accompanies this distribution. +//The Eclipse Public License is available at +//http://www.eclipse.org/legal/epl-v10.html +//The Apache License v2.0 is available at +//http://www.opensource.org/licenses/apache2.0.php +//You may elect to redistribute this code under either of these licenses. +//======================================================================== + +import java.io.FilePermission; +import java.net.URL; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jetty.policy.JettyPolicy; +import org.eclipse.jetty.policy.PropertyEvaluator; + + +import junit.framework.TestCase; + +public class TestJettyPolicy extends TestCase +{ + PropertyEvaluator evaluator = new PropertyEvaluator( new HashMap()); + + + @Override + protected void setUp() + throws Exception + { + super.setUp(); + + evaluator.put( "jetty.home", getWorkingDirectory() ); + } + + public void testGlobalAllPermissionLoader() + throws Exception + { + + JettyPolicy ap = + new JettyPolicy( Collections.singleton( getWorkingDirectory() + "/src/test/resources/global-all-permission.policy" ), evaluator ); + + ap.refresh(); + + + PermissionCollection pc = ap.getPermissions( new ProtectionDomain( null, null ) ); + + assertNotNull( pc ); + + Permission testPerm = new FilePermission( "/tmp", "read" ); + + assertTrue( pc.implies( testPerm ) ); + + for ( Enumeration e = pc.elements(); e.hasMoreElements(); ) + { + System.out.println( "Permission: " + e.nextElement().getClass().getName() ); + } + + } + + public void testSingleCodebaseFilePermissionLoader() + throws Exception + { + JettyPolicy ap = + new JettyPolicy( Collections.singleton( getWorkingDirectory() + + "/src/test/resources/single-codebase-file-permission.policy" ), evaluator ); + + ap.refresh(); + + URL url = new URL( "file:///foo.jar" ); + CodeSource cs = new CodeSource( url, new Certificate[0]); + + PermissionCollection pc = ap.getPermissions( cs ); + + assertNotNull( pc ); + + Permission testPerm = new FilePermission( "/tmp/*", "read" ); + + assertTrue( pc.implies( testPerm ) ); + + } + + public void testMultipleCodebaseFilePermissionLoader() + throws Exception + { + JettyPolicy ap = + new JettyPolicy( Collections.singleton( getWorkingDirectory() + + "/src/test/resources/multiple-codebase-file-permission.policy" ), evaluator ); + + ap.refresh(); + + URL url = new URL( "file:///bar.jar" ); + CodeSource cs = new CodeSource( url, new Certificate[0]); + + PermissionCollection pc = ap.getPermissions( cs ); + + assertNotNull( pc ); + + Permission testPerm = new FilePermission( "/tmp/*", "read,write" ); + Permission testPerm2 = new FilePermission( "/usr/*", "write" ); // only read was granted + + assertTrue( pc.implies( testPerm ) ); + assertFalse( pc.implies( testPerm2 ) ); + + } + + public void testMultipleCodebaseMixedPermissionLoader() + throws Exception + { + JettyPolicy ap = + new JettyPolicy( Collections.singleton( getWorkingDirectory() + + "/src/test/resources/multiple-codebase-mixed-permission.policy" ), evaluator ); + + ap.refresh(); + } + + + public void testMultipleFilePermissionLoader() + throws Exception + { + Set files = new HashSet(); + + files.add( getWorkingDirectory() + "/src/test/resources/single-codebase-file-permission.policy" ); + files.add( getWorkingDirectory() + "/src/test/resources/single-codebase-file-permission-2.policy" ); + + JettyPolicy ap = new JettyPolicy( files, evaluator ); + + ap.refresh(); + + URL url = new URL( "file:///bar.jar" ); + CodeSource cs = new CodeSource( url, new Certificate[0]); + + PermissionCollection pc = ap.getPermissions( cs ); + + assertNotNull( pc ); + + Permission testPerm = new FilePermission( "/tmp/*", "read" ); + Permission testPerm2 = new FilePermission( "/usr/*", "write" ); // + + assertTrue( pc.implies( testPerm ) ); + assertFalse( pc.implies( testPerm2 ) ); + } + + + + private String getWorkingDirectory() + { + return System.getProperty( "basedir" ); // TODO work in eclipse + } + +} diff --git a/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicyRuntime.java b/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicyRuntime.java new file mode 100644 index 00000000000..e2d356dedbb --- /dev/null +++ b/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicyRuntime.java @@ -0,0 +1,149 @@ +package org.eclipse.jetty.policy; +//======================================================================== +//Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd. +//------------------------------------------------------------------------ +//All rights reserved. This program and the accompanying materials +//are made available under the terms of the Eclipse Public License v1.0 +//and Apache License v2.0 which accompanies this distribution. +//The Eclipse Public License is available at +//http://www.eclipse.org/legal/epl-v10.html +//The Apache License v2.0 is available at +//http://www.opensource.org/licenses/apache2.0.php +//You may elect to redistribute this code under either of these licenses. +//======================================================================== + +import java.io.File; +import java.security.AccessControlException; +import java.security.Policy; +import java.util.Collections; +import java.util.HashMap; + +import org.eclipse.jetty.policy.JettyPolicy; +import org.eclipse.jetty.policy.PropertyEvaluator; + + +import junit.framework.TestCase; + +public class TestJettyPolicyRuntime extends TestCase +{ + + PropertyEvaluator evaluator = new PropertyEvaluator( new HashMap()); + + @Override + protected void setUp() + throws Exception + { + super.setUp(); + + evaluator.put( "jetty.home", getWorkingDirectory() ); + } + + public void testSimplePolicyReplacement() throws Exception + { + JettyPolicy ap = + new JettyPolicy( Collections.singleton( getWorkingDirectory() + "/src/test/resources/global-all-permission.policy" ), evaluator ); + + ap.refresh(); + + Policy.setPolicy( ap ); + System.setSecurityManager( new SecurityManager() ); + + File test = new File( "/tmp" ); + + assertTrue ( test.canRead() ); + + // Policy nulling must occur after Security Manager null + System.setSecurityManager( null ); + Policy.setPolicy( null ); + } + + public void testRepeatedPolicyReplacement() throws Exception + { + JettyPolicy ap = + new JettyPolicy( Collections.singleton( getWorkingDirectory() + "/src/test/resources/global-all-permission.policy" ), evaluator ); + + ap.refresh(); + + Policy.setPolicy( ap ); + + System.setSecurityManager( new SecurityManager() ); + + // Test that the all permission policy allows us to do this + try + { + File test3 = new File( "/tmp/foo/bar/do" ); + test3.mkdirs(); + test3.delete(); + assertTrue( "Under AllPermission we are allowed", true ); + } + catch ( AccessControlException ace ) + { + //ace.printStackTrace(); + assertFalse( "Exception was thrown which it shouldn't have been", true ); + } + + JettyPolicy ap2 = + new JettyPolicy( Collections.singleton( getWorkingDirectory() + "/src/test/resources/global-file-read-only-tmp-permission.policy" ), evaluator ); + + ap2.refresh(); + + Policy.setPolicy( ap2 ); + + // Test that the new policy does replace the old one and we are now now allowed + try + { + File test3 = new File( "/tmp/foo/bar/do" ); + test3.mkdirs(); + assertFalse( "We should be restricted and not get here.", true ); + } + catch ( AccessControlException ace ) + { + //ace.printStackTrace(); + assertTrue( "Exception was thrown as it should be.", true ); + } + + System.setSecurityManager( null ); + Policy.setPolicy( null ); + } + + + public void testPolicyRestrictive() throws Exception + { + + JettyPolicy ap = + new JettyPolicy( Collections.singleton( getWorkingDirectory() + "/src/test/resources/global-file-read-only-tmp-permission.policy" ), evaluator ); + + ap.refresh(); + + Policy.setPolicy( ap ); + + System.setSecurityManager( new SecurityManager() ); + + File test = new File( "/tmp" ); + + assertTrue ( test.canRead() ); + + File test2 = new File( "/tmp/foo" ); + assertTrue ( test2.canRead() ); + + try + { + File test3 = new File( "/tmp/foo/bar/do" ); + test3.mkdirs(); + assertTrue( "we should not get here", false ); + } + catch ( AccessControlException ace ) + { + //ace.printStackTrace(); + assertTrue( "Exception was thrown", true ); + } + + System.setSecurityManager( null ); + Policy.setPolicy( null ); + } + + private String getWorkingDirectory() + { + return System.getProperty( "basedir" ); // TODO work in eclipse + } +} diff --git a/jetty-policy/src/test/resources/global-all-permission.policy b/jetty-policy/src/test/resources/global-all-permission.policy new file mode 100644 index 00000000000..ae56d98e8ed --- /dev/null +++ b/jetty-policy/src/test/resources/global-all-permission.policy @@ -0,0 +1,3 @@ +grant { + permission java.security.AllPermission; +} \ No newline at end of file diff --git a/jetty-policy/src/test/resources/global-file-read-only-tmp-permission.policy b/jetty-policy/src/test/resources/global-file-read-only-tmp-permission.policy new file mode 100644 index 00000000000..96da8d65504 --- /dev/null +++ b/jetty-policy/src/test/resources/global-file-read-only-tmp-permission.policy @@ -0,0 +1,10 @@ +grant { + permission java.lang.RuntimePermission "setContextClassLoader"; + permission java.lang.RuntimePermission "setIO"; + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.security.SecurityPermission "getPolicy"; + + permission java.io.FilePermission "/tmp", "read,write"; + permission java.io.FilePermission "/tmp/*", "read,write"; + permission java.io.FilePermission "/tmp/foo/bar/*", "read"; +} \ No newline at end of file diff --git a/jetty-policy/src/test/resources/multiple-codebase-file-permission.policy b/jetty-policy/src/test/resources/multiple-codebase-file-permission.policy new file mode 100644 index 00000000000..bb66734eb57 --- /dev/null +++ b/jetty-policy/src/test/resources/multiple-codebase-file-permission.policy @@ -0,0 +1,8 @@ +grant codeBase "file:///foo.jar" { + permission java.io.FilePermission "/tmp/*", "read,write"; +} + +grant codeBase "file:///bar.jar" { + permission java.io.FilePermission "/tmp/*", "read,write"; + permission java.io.FilePermission "/usr/*", "read"; +} \ No newline at end of file diff --git a/jetty-policy/src/test/resources/multiple-codebase-mixed-permission.policy b/jetty-policy/src/test/resources/multiple-codebase-mixed-permission.policy new file mode 100644 index 00000000000..aaa9d652537 --- /dev/null +++ b/jetty-policy/src/test/resources/multiple-codebase-mixed-permission.policy @@ -0,0 +1,12 @@ +grant codeBase "file:///foo.jar" { + permission java.io.FilePermission "/tmp/*", "read,write"; +} + +grant codeBase "file:///bar.jar" { + permission java.security.AllPermission; +} + +grant codeBase "file:///snap.jar" { + permission java.io.FilePermission "/tmp/*", "read,write"; + permission java.io.FilePermission "/usr/*", "read,write"; +} \ No newline at end of file diff --git a/jetty-policy/src/test/resources/single-codebase-file-permission-2.policy b/jetty-policy/src/test/resources/single-codebase-file-permission-2.policy new file mode 100644 index 00000000000..ea34900fba1 --- /dev/null +++ b/jetty-policy/src/test/resources/single-codebase-file-permission-2.policy @@ -0,0 +1,3 @@ +grant codeBase "file:///bar.jar" { + permission java.io.FilePermission "/tmp/*", "read"; +} \ No newline at end of file diff --git a/jetty-policy/src/test/resources/single-codebase-file-permission.policy b/jetty-policy/src/test/resources/single-codebase-file-permission.policy new file mode 100644 index 00000000000..646df090d24 --- /dev/null +++ b/jetty-policy/src/test/resources/single-codebase-file-permission.policy @@ -0,0 +1,3 @@ +grant codeBase "file:///foo.jar" { + permission java.io.FilePermission "/tmp/*", "read,write"; +} \ No newline at end of file