Submitted by: J.M. (Martijn) Kruithof (ant at kruithof dot xs4all dot nl) git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@275149 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -574,6 +574,10 @@ Other changes: | |||
| * <junit>'s XML formatter adds a new classname attribute to the <testcase> | |||
| elements. | |||
| * new <permissions> type add permission handling to the code | |||
| this type can be nested in the <java> and <junit> tasks. | |||
| Bugzilla Report 22533. | |||
| Changes from Ant 1.5.3 to Ant 1.5.4 | |||
| =================================== | |||
| @@ -212,6 +212,18 @@ support it (i.e. Java 1.1).</p> | |||
| forked VM via nested <i>env</i> elements. See the description in the | |||
| section about <a href="exec.html#env">exec</a></p> | |||
| <p>Settings will be ignored if fork is disabled.</p> | |||
| <h4>permissions</h4> | |||
| <p>Security permissions can be revoked and granted during the execution of the | |||
| class via a nested <i>permissions</i> element. For more information please | |||
| see <a href="../CoreTypes/permissions.html">permissions</a></p> | |||
| <p>When the permission RuntimePermission exitVM has not been granted (or has | |||
| been revoked) the System.exit() call will be intercepted | |||
| and treated like indicated in <i>failonerror</i>.</p> | |||
| <p>Settings will be ignored if fork is enabled.</p> | |||
| <p><em>since Ant 1.6</em>.</p> | |||
| <h3>Errors and return codes</h3> | |||
| By default the return code of a <java> is ignored. Alternatively, you can set <code>resultproperty</code> to the name | |||
| @@ -0,0 +1,148 @@ | |||
| <html> | |||
| <head> | |||
| <meta http-equiv="Content-Language" content="en-us"> | |||
| <title>Permissions type</title> | |||
| </head> | |||
| <body> | |||
| <h2><a name="permissions">Permissions</a></h2> | |||
| <p> | |||
| Permissions represents a set of security permissions granted or revoked to | |||
| a specific part code executed in the JVM where ant is running in. | |||
| The actual Permissions are specified via a set of nested permission items either | |||
| <code><grant></code>ed or <code><revoke></code>d.</p> | |||
| <p> | |||
| In the base situation a <a href="#baseset">base set</a> of permissions granted. | |||
| Extra permissions can be | |||
| granted. A granted permission can be overruled by revoking a permission. | |||
| The security manager installed by the permissions will throw an | |||
| <code>SecurityException</code> if | |||
| the code subject to these permissions try to use an permission that has not been | |||
| granted or that has been revoked.</p> | |||
| <h3>Nested elements</h3> | |||
| <h4>grant</h4> | |||
| <p> | |||
| Indicates a specific permission is always granted. Its attributes indicate which | |||
| permissions are granted.</p> | |||
| <table border="1" cellpadding="2" cellspacing="0"> | |||
| <tr> | |||
| <td valign="top"><b>Attribute</b></td> | |||
| <td valign="top"><b>Description</b></td> | |||
| <td align="center" valign="top"><b>Required</b></td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">class</td> | |||
| <td valign="top">The fully qualified name of the Permission class.</td> | |||
| <td valign="top" align="center">Yes</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">name</td> | |||
| <td valign="top">The name of the Permission. The actual contents depends on the | |||
| Permission class.</td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">actions</td> | |||
| <td valign="top">The actions allowed. The actual contents depend on the | |||
| Permission class and name.</td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| </table> | |||
| <p> | |||
| Implied permissions are granted. | |||
| </p> | |||
| <p> | |||
| Please note that some Permission classes may actually need a name and / or actions in order to function properly. The name and actions are parsed by the actual | |||
| Permission class. | |||
| </p> | |||
| <h4>revoke</h4> | |||
| <p> | |||
| Indicates a specific permission is revoked.</p> | |||
| <table border="1" cellpadding="2" cellspacing="0"> | |||
| <tr> | |||
| <td valign="top"><b>Attribute</b></td> | |||
| <td valign="top"><b>Description</b></td> | |||
| <td align="center" valign="top"><b>Required</b></td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">class</td> | |||
| <td valign="top">The fully qualified name of the Permission class.</td> | |||
| <td valign="top" align="center">Yes</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">name</td> | |||
| <td valign="top">The name of the Permission. The actual contents depends on the | |||
| Permission class.</td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">actions</td> | |||
| <td valign="top">The actions allowed. The actual contents depend on the | |||
| Permission class and name.</td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| </table> | |||
| <p> | |||
| Implied permissions are not resolved and therefore also not revoked. | |||
| </p> | |||
| <p> | |||
| The name can handle the * wildcard at the end of the name, in which case all | |||
| permissions of the specified class of which the name starts with the specified name | |||
| (excluding the *) are revoked. Note that the - wildcard often supported by the | |||
| granted properties is not supported. | |||
| If the name is left empty all names match, and are revoked. | |||
| If the actions are left empty all actions match, and are revoked. | |||
| </p> | |||
| <h3><a name="baseset">Base set</a></h3> | |||
| A permissions set implictly contains the following permissions: | |||
| <blockquote><pre> | |||
| <grant class="java.net.SocketPermission" name=localhost:1024-" actions="listen"> | |||
| <grant class="java.util.PropertyPermission" name=java.version" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.vendor" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.vendor.url" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.class.version" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=os.name" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=os.version" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=os.arch" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=file.separator" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=path.separator" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=line.separator" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.specification.version" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.specification.vendor" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.specification.name" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.vm.specification.version" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.vm.specification.vendor" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.vm.specification.name" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.vm.version" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.vm.vendor" actions="read"> | |||
| <grant class="java.util.PropertyPermission" name=java.vm.name" actions="read"> | |||
| </blockquote></pre> | |||
| These permissions can be revoked via <revoke> elements if necessary. | |||
| <h3>Examples</h3> | |||
| <blockquote><pre> | |||
| <permissions> | |||
| <grant class="java.security.AllPermission"/> | |||
| <revoke class="java.util.PropertyPermission"/> | |||
| </permissions> | |||
| </pre></blockquote> | |||
| <p> | |||
| Grants all permissions to the code except for those handling Properties. | |||
| </p> | |||
| <blockquote><pre> | |||
| <permissions> | |||
| <grant class="java.net.SocketPermission" name="foo.bar.com" action="connect"/> | |||
| <grant class="java.util.PropertyPermission" name="user.home" action="read,write"/> | |||
| </permissions> | |||
| </pre></blockquote> | |||
| <p> | |||
| Grants the base set of permissions with the addition of a SocketPermission to connect | |||
| to foo.bar.com and the permission to read and write the user.home system property. | |||
| </p> | |||
| <hr> | |||
| <p align="center">Copyright © 2003 Apache Software Foundation. | |||
| All rights Reserved.</p> | |||
| </body> | |||
| </html> | |||
| @@ -244,6 +244,15 @@ support it (i.e. Java 1.1).</p> | |||
| <p><em>since Ant 1.6</em>.</p> | |||
| <h4>permissions</h4> | |||
| <p>Security permissions can be revoked and granted during the execution of the | |||
| class via a nested <i>permissions</i> element. For more information please | |||
| see <a href="../CoreTypes/permissions.html">permissions</a></p> | |||
| <p>Settings will be ignored if fork is enabled.</p> | |||
| <p><em>since Ant 1.6</em>.</p> | |||
| <h4>formatter</h4> | |||
| <p>The results of the tests can be printed in different | |||
| @@ -25,6 +25,7 @@ | |||
| <a href="CoreTypes/filterset.html">FilterSet</a><br> | |||
| <a href="CoreTypes/patternset.html">PatternSet</a><br> | |||
| <a href="using.html#path">Path-like Structures</a><br> | |||
| <a href="CoreTypes/permissions.html">Permissions</a><br> | |||
| <a href="CoreTypes/propertyset.html">PropertySet</a><br> | |||
| <a href="CoreTypes/selectors.html">Selectors</a><br> | |||
| <a href="CoreTypes/xmlcatalog.html">XMLCatalog</a><br> | |||
| @@ -99,6 +99,7 @@ | |||
| <java classname="${app}" | |||
| classpath="${tests-classpath.value}" | |||
| resultproperty="exitcode" | |||
| fork="true" | |||
| > | |||
| </java> | |||
| <echo message="exitcode = ${exitcode}"/> | |||
| @@ -116,12 +117,40 @@ | |||
| <echo message="exitcode = ${exitcode}"/> | |||
| </target> | |||
| <target name="testResultPropertyZeroNoFork"> | |||
| <java classname="${app}" | |||
| classpath="${tests-classpath.value}" | |||
| resultproperty="exitcode" | |||
| fork="false" | |||
| > | |||
| <permissions/> | |||
| </java> | |||
| <echo message="exitcode = ${exitcode}"/> | |||
| </target> | |||
| <target name="testResultPropertyNonZeroNoFork"> | |||
| <java classname="${app}" | |||
| classpath="${tests-classpath.value}" | |||
| resultproperty="exitcode" | |||
| failonerror="false" | |||
| fork="false" | |||
| > | |||
| <arg value="-1"/> | |||
| <permissions/> | |||
| </java> | |||
| <echo message="exitcode = ${exitcode}"/> | |||
| </target> | |||
| <target name="testSpawn"> | |||
| <java classname="${spawnapp}" fork="true" spawn="true" classpath="${tests-classpath.value}"> | |||
| <arg value="${timeToWait}"/> | |||
| <arg value="${logFile}" /> | |||
| </java> | |||
| </target> | |||
| <target name="cleanup"> | |||
| <delete file="${logFile}"/> | |||
| </target> | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 2001-2002 The Apache Software Foundation. All rights | |||
| * Copyright (c) 2001-2003 The Apache Software Foundation. All rights | |||
| * reserved. | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| @@ -57,8 +57,10 @@ package org.apache.tools.ant; | |||
| * Used to report exit status of classes which call System.exit(). | |||
| * | |||
| * @see org.apache.tools.ant.util.optional.NoExitSecurityManager | |||
| * @see org.apache.tools.ant.types.Permissions | |||
| * | |||
| * @author Conor MacNeill | |||
| * @author <a href="mailto:martijn@kruithof.xs4all.nl">Martijn Kruithof</a> | |||
| */ | |||
| public class ExitException extends SecurityException { | |||
| @@ -74,6 +76,16 @@ public class ExitException extends SecurityException { | |||
| this.status = status; | |||
| } | |||
| /** | |||
| * Constructs an exit exception. | |||
| * @param msg the messge to be displayed. | |||
| * @param status the status code returned via System.exit() | |||
| */ | |||
| public ExitException(String msg, int status) { | |||
| super(msg); | |||
| this.status = status; | |||
| } | |||
| /** | |||
| * The status code returned by System.exit() | |||
| * | |||
| @@ -65,6 +65,7 @@ import org.apache.tools.ant.Task; | |||
| import org.apache.tools.ant.types.Commandline; | |||
| import org.apache.tools.ant.types.CommandlineJava; | |||
| import org.apache.tools.ant.types.Path; | |||
| import org.apache.tools.ant.types.Permissions; | |||
| import org.apache.tools.ant.util.TimeoutObserver; | |||
| import org.apache.tools.ant.util.Watchdog; | |||
| @@ -72,6 +73,7 @@ import org.apache.tools.ant.util.Watchdog; | |||
| * | |||
| * @author thomas.haas@softwired-inc.com | |||
| * @author Stefan Bodewig | |||
| * @author <a href="mailto:martijn@kruithof.xs4all.nl">Martijn Kruithof</a> | |||
| * @since Ant 1.2 | |||
| */ | |||
| public class ExecuteJava implements Runnable, TimeoutObserver { | |||
| @@ -79,6 +81,7 @@ public class ExecuteJava implements Runnable, TimeoutObserver { | |||
| private Commandline javaCommand = null; | |||
| private Path classpath = null; | |||
| private CommandlineJava.SysProperties sysProperties = null; | |||
| private Permissions perm = null; | |||
| private Method main = null; | |||
| private Long timeout = null; | |||
| private Throwable caught = null; | |||
| @@ -101,6 +104,15 @@ public class ExecuteJava implements Runnable, TimeoutObserver { | |||
| public void setSystemProperties(CommandlineJava.SysProperties s) { | |||
| sysProperties = s; | |||
| } | |||
| /** | |||
| * Permissions for the application run. | |||
| * @since Ant 1.6 | |||
| * @param permissions | |||
| */ | |||
| public void setPermissions(Permissions permissions) { | |||
| perm = permissions; | |||
| } | |||
| /** | |||
| * All output (System.out as well as System.err) will be written | |||
| @@ -208,6 +220,9 @@ public class ExecuteJava implements Runnable, TimeoutObserver { | |||
| public void run() { | |||
| final Object[] argument = {javaCommand.getArguments()}; | |||
| try { | |||
| if(perm != null) { | |||
| perm.setSecurityManager(); | |||
| } | |||
| main.invoke(null, argument); | |||
| } catch (InvocationTargetException e) { | |||
| Throwable t = e.getTargetException(); | |||
| @@ -217,6 +232,9 @@ public class ExecuteJava implements Runnable, TimeoutObserver { | |||
| } catch (Throwable t) { | |||
| caught = t; | |||
| } finally { | |||
| if(perm != null) { | |||
| perm.restoreSecurityManager(); | |||
| } | |||
| synchronized (this) { | |||
| notifyAll(); | |||
| } | |||
| @@ -68,6 +68,7 @@ import org.apache.tools.ant.types.Path; | |||
| import org.apache.tools.ant.types.PropertySet; | |||
| import org.apache.tools.ant.types.Reference; | |||
| import org.apache.tools.ant.types.Assertions; | |||
| import org.apache.tools.ant.types.Permissions; | |||
| /** | |||
| * Launcher for Java applications. Allows use of | |||
| @@ -78,6 +79,7 @@ import org.apache.tools.ant.types.Assertions; | |||
| * <a href="mailto:stefano@apache.org">stefano@apache.org</a> | |||
| * @author Stefan Bodewig | |||
| * @author <a href="mailto:donal@savvion.com">Donal Quinlan</a> | |||
| * @author <a href="mailto:martijn@kruithof.xs4all.nl">Martijn Kruithof</a> | |||
| * | |||
| * @since Ant 1.1 | |||
| * | |||
| @@ -95,7 +97,8 @@ public class Java extends Task { | |||
| private Long timeout = null; | |||
| private Redirector redirector = new Redirector(this); | |||
| private String resultProperty; | |||
| private Permissions perm; | |||
| private boolean spawn = false; | |||
| private boolean incompatibleWithSpawn = false; | |||
| /** | |||
| @@ -109,7 +112,7 @@ public class Java extends Task { | |||
| int err = -1; | |||
| try { | |||
| err = executeJava(); | |||
| if (fork && err != 0) { | |||
| if (err != 0) { | |||
| if (failOnError) { | |||
| throw new BuildException("Java returned: " + err, getLocation()); | |||
| } else { | |||
| @@ -152,6 +155,9 @@ public class Java extends Task { | |||
| + "not compatible with spawn"); | |||
| } | |||
| if (fork) { | |||
| if(perm != null) { | |||
| log("Permissions can not be set this way in forked mode.",Project.MSG_WARN); | |||
| } | |||
| log(cmdl.describeCommand(), Project.MSG_VERBOSE); | |||
| } else { | |||
| if (cmdl.getVmCommand().size() > 1) { | |||
| @@ -180,7 +186,7 @@ public class Java extends Task { | |||
| try { | |||
| if (fork) { | |||
| if (!spawn) { | |||
| return run(cmdl.getCommandline()); | |||
| return fork(cmdl.getCommandline()); | |||
| } else { | |||
| spawn(cmdl.getCommandline()); | |||
| return 0; | |||
| @@ -248,6 +254,18 @@ public class Java extends Task { | |||
| return cmdl.createBootclasspath(getProject()).createPath(); | |||
| } | |||
| /** | |||
| * Sets the permissions for the application run inside the same JVM. | |||
| * @since Ant 1.6 | |||
| * @return . | |||
| */ | |||
| public Permissions createPermissions() { | |||
| if (perm == null) { | |||
| perm = new Permissions(); | |||
| } | |||
| return perm; | |||
| } | |||
| /** | |||
| * Classpath to use, by reference. | |||
| * | |||
| @@ -662,6 +680,7 @@ public class Java extends Task { | |||
| exe.setJavaCommand(command.getJavaCommand()); | |||
| exe.setClasspath(command.getClasspath()); | |||
| exe.setSystemProperties(command.getSystemProperties()); | |||
| exe.setPermissions(perm); | |||
| exe.setTimeout(timeout); | |||
| redirector.createStreams(); | |||
| exe.execute(getProject()); | |||
| @@ -674,7 +693,7 @@ public class Java extends Task { | |||
| /** | |||
| * Executes the given classname with the given arguments in a separate VM. | |||
| */ | |||
| private int run(String[] command) throws BuildException { | |||
| private int fork(String[] command) throws BuildException { | |||
| Execute exe | |||
| = new Execute(redirector.createHandler(), createWatchdog()); | |||
| @@ -76,6 +76,7 @@ import org.apache.tools.ant.types.CommandlineJava; | |||
| import org.apache.tools.ant.types.EnumeratedAttribute; | |||
| import org.apache.tools.ant.types.Environment; | |||
| import org.apache.tools.ant.types.Path; | |||
| import org.apache.tools.ant.types.Permissions; | |||
| import org.apache.tools.ant.types.PropertySet; | |||
| import org.apache.tools.ant.util.FileUtils; | |||
| import org.apache.tools.ant.util.LoaderUtils; | |||
| @@ -180,6 +181,7 @@ public class JUnitTask extends Task { | |||
| private boolean showOutput = false; | |||
| private File tmpDir; | |||
| private AntClassLoader classLoader = null; | |||
| private Permissions perm = null; | |||
| private static final int STRING_BUFFER_SIZE = 128; | |||
| /** | |||
| @@ -570,6 +572,18 @@ public class JUnitTask extends Task { | |||
| commandline.setAssertions(asserts); | |||
| } | |||
| /** | |||
| * Sets the permissions for the application run inside the same JVM. | |||
| * @since Ant 1.6 | |||
| * @return . | |||
| */ | |||
| public Permissions createPermissions() { | |||
| if (perm == null) { | |||
| perm = new Permissions(); | |||
| } | |||
| return perm; | |||
| } | |||
| /** | |||
| * Creates a new JUnitRunner and enables fork of a new Java VM. | |||
| * | |||
| @@ -688,6 +702,10 @@ public class JUnitTask extends Task { | |||
| private int executeAsForked(JUnitTest test, ExecuteWatchdog watchdog) | |||
| throws BuildException { | |||
| if(perm != null) { | |||
| log("Permissions ignored when running in forked mode!", Project.MSG_WARN); | |||
| } | |||
| CommandlineJava cmd = (CommandlineJava) commandline.clone(); | |||
| cmd.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner"); | |||
| @@ -935,6 +953,8 @@ public class JUnitTask extends Task { | |||
| f.setOutput(getDefaultOutput()); | |||
| runner.addFormatter(f); | |||
| } | |||
| runner.setPermissions(perm); | |||
| final FormatterElement[] feArray = mergeFormatters(test); | |||
| for (int i = 0; i < feArray.length; i++) { | |||
| @@ -75,6 +75,7 @@ import junit.framework.TestResult; | |||
| import junit.framework.TestSuite; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.types.Permissions; | |||
| import org.apache.tools.ant.util.StringUtils; | |||
| import org.apache.tools.ant.util.TeeOutputStream; | |||
| @@ -95,6 +96,7 @@ import org.apache.tools.ant.util.TeeOutputStream; | |||
| * | |||
| * @author Stefan Bodewig | |||
| * @author <a href="mailto:ehatcher@apache.org">Erik Hatcher</a> | |||
| * @author <a href="mailto:martijn@kruithof.xs4all.nl">Martijn Kruithof</a> | |||
| * | |||
| * @since Ant 1.2 | |||
| */ | |||
| @@ -136,6 +138,11 @@ public class JUnitTestRunner implements TestListener { | |||
| */ | |||
| private boolean showOutput = false; | |||
| /** | |||
| * The permissions set for the test to run. | |||
| */ | |||
| private Permissions perm = null; | |||
| private static final String[] DEFAULT_TRACE_FILTERS = new String[] { | |||
| "junit.framework.TestCase", | |||
| "junit.framework.TestResult", | |||
| @@ -309,12 +316,20 @@ public class JUnitTestRunner implements TestListener { | |||
| ) | |||
| ); | |||
| } | |||
| perm = null; | |||
| } else { | |||
| if(perm != null) { | |||
| perm.setSecurityManager(); | |||
| } | |||
| } | |||
| try { | |||
| suite.run(res); | |||
| } finally { | |||
| if(perm != null) { | |||
| perm.restoreSecurityManager(); | |||
| } | |||
| if (savedOut != null) { | |||
| System.setOut(savedOut); | |||
| } | |||
| @@ -398,6 +413,15 @@ public class JUnitTestRunner implements TestListener { | |||
| res.stop(); | |||
| } | |||
| } | |||
| /** | |||
| * Permissions for the test run. | |||
| * @since Ant 1.6 | |||
| * @param permissions | |||
| */ | |||
| public void setPermissions(Permissions permissions) { | |||
| perm = permissions; | |||
| } | |||
| protected void handleOutput(String output) { | |||
| if (systemOut != null) { | |||
| @@ -0,0 +1,324 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 2003 The Apache Software Foundation. All rights | |||
| * reserved. | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * 1. Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * 2. Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in | |||
| * the documentation and/or other materials provided with the | |||
| * distribution. | |||
| * | |||
| * 3. The end-user documentation included with the redistribution, if | |||
| * any, must include the following acknowlegement: | |||
| * "This product includes software developed by the | |||
| * Apache Software Foundation (http://www.apache.org/)." | |||
| * Alternately, this acknowlegement may appear in the software itself, | |||
| * if and wherever such third-party acknowlegements normally appear. | |||
| * | |||
| * 4. The names "Ant" and "Apache Software | |||
| * Foundation" must not be used to endorse or promote products derived | |||
| * from this software without prior written permission. For written | |||
| * permission, please contact apache@apache.org. | |||
| * | |||
| * 5. Products derived from this software may not be called "Apache" | |||
| * nor may "Apache" appear in their names without prior written | |||
| * permission of the Apache Group. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
| * SUCH DAMAGE. | |||
| * ==================================================================== | |||
| * | |||
| * This software consists of voluntary contributions made by many | |||
| * individuals on behalf of the Apache Software Foundation. For more | |||
| * information on the Apache Software Foundation, please see | |||
| * <http://www.apache.org/>. | |||
| */ | |||
| package org.apache.tools.ant.types; | |||
| import java.security.UnresolvedPermission; | |||
| import java.util.HashSet; | |||
| import java.util.Iterator; | |||
| import java.util.LinkedList; | |||
| import java.util.List; | |||
| import java.util.Set; | |||
| import java.util.StringTokenizer; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.ExitException; | |||
| /** | |||
| * This class implements a security manager meant for useage by tasks that run inside the | |||
| * ant VM. An examples are the Java Task and JUnitTask. | |||
| * | |||
| * The basic functionality is that nothing (except for a base set of permissions) is allowed, unless | |||
| * the permission is granted either explicitly or implicitly. | |||
| * If an permission is granted this can be overruled by explicitly revoking the permission. | |||
| * | |||
| * It is not permissible to add permissions (either granted or revoked) while the Security Manager | |||
| * is active (after calling setSecurityManager() but before calling restoreSecurityManager()). | |||
| * | |||
| * @since Ant 1.6 | |||
| * @author <a href="mailto:martijn@kruithof.xs4all.nl">Martijn Kruithof</a> | |||
| */ | |||
| public class Permissions { | |||
| private List grantedPermissions = new LinkedList(); | |||
| private List revokedPermissions = new LinkedList(); | |||
| private java.security.Permissions granted = null; | |||
| private SecurityManager origSm = null; | |||
| private boolean active = false; | |||
| /** | |||
| * Adds a permission to be granted. | |||
| * @param perm The Permissions.Permission to be granted. | |||
| */ | |||
| public void addConfiguredGrant(Permissions.Permission perm) { | |||
| grantedPermissions.add(perm); | |||
| } | |||
| /** | |||
| * Adds a permission to be revoked. | |||
| * @param perm The Permissions.Permission to be revoked | |||
| */ | |||
| public void addConfiguredRevoke(Permissions.Permission perm) { | |||
| revokedPermissions.add(perm); | |||
| } | |||
| /** | |||
| * To be used by tasks wishing to use this security model before executing the part to be | |||
| * subject to these Permissions. Note that setting the SecurityManager too early may | |||
| * prevent your part from starting, as for instance changing classloaders may be prohibited. | |||
| * The classloader for the new situation is supposed to be present. | |||
| */ | |||
| public void setSecurityManager() throws BuildException{ | |||
| origSm = System.getSecurityManager(); | |||
| init(); | |||
| System.setSecurityManager(new MySM()); | |||
| active = true; | |||
| } | |||
| /** | |||
| * Initializes the list of granted permissions, checks the list of revoked permissions. | |||
| */ | |||
| private void init() throws BuildException { | |||
| granted = new java.security.Permissions(); | |||
| for (Iterator i = revokedPermissions.listIterator(); i.hasNext();) { | |||
| Permissions.Permission p = (Permissions.Permission) i.next(); | |||
| if (p.getClassName() == null) { | |||
| throw new BuildException("Revoked permission " + p + " does not contain a class."); | |||
| } | |||
| } | |||
| for (Iterator i = grantedPermissions.listIterator(); i.hasNext();) { | |||
| Permissions.Permission p = (Permissions.Permission) i.next(); | |||
| if (p.getClassName() == null) { | |||
| throw new BuildException("Granted permission " + p + " does not contain a class."); | |||
| } else { | |||
| java.security.Permission perm = new UnresolvedPermission(p.getClassName(),p.getName(),p.getActions(),null); | |||
| granted.add(perm); | |||
| } | |||
| } | |||
| // Add base set of permissions | |||
| granted.add(new java.net.SocketPermission("localhost:1024-", "listen")); | |||
| granted.add(new java.util.PropertyPermission("java.version", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.vendor", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.vendor.url", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.class.version", "read")); | |||
| granted.add(new java.util.PropertyPermission("os.name", "read")); | |||
| granted.add(new java.util.PropertyPermission("os.version", "read")); | |||
| granted.add(new java.util.PropertyPermission("os.arch", "read")); | |||
| granted.add(new java.util.PropertyPermission("file.separator", "read")); | |||
| granted.add(new java.util.PropertyPermission("path.separator", "read")); | |||
| granted.add(new java.util.PropertyPermission("line.separator", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.specification.version", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.specification.vendor", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.specification.name", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.vm.specification.version", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.vm.specification.vendor", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.vm.specification.name", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.vm.version", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.vm.vendor", "read")); | |||
| granted.add(new java.util.PropertyPermission("java.vm.name", "read")); | |||
| } | |||
| /** | |||
| * To be used by tasks that just finished executing the parts subject to these permissions. | |||
| */ | |||
| public void restoreSecurityManager() { | |||
| active = false; | |||
| System.setSecurityManager(origSm); | |||
| } | |||
| /** | |||
| * This inner class implements the actual SecurityManager that can be used by tasks | |||
| * supporting Permissions. | |||
| */ | |||
| private class MySM extends SecurityManager { | |||
| /** | |||
| * Exit is treated in a special way in order to be able to return the exit code towards tasks. | |||
| * An ExitException is thrown instead of a simple SecurityException to indicate the exit | |||
| * code. | |||
| * Overridden from java.lang.SecurityManager | |||
| * @param status The exit status requested. | |||
| */ | |||
| public void checkExit(int status) { | |||
| java.security.Permission perm = new java.lang.RuntimePermission("exitVM",null); | |||
| try { | |||
| checkPermission(perm); | |||
| } catch (SecurityException e) { | |||
| throw new ExitException(e.getMessage(), status); | |||
| } | |||
| } | |||
| /** | |||
| * The central point in checking permissions. | |||
| * Overridden from java.lang.SecurityManager | |||
| * | |||
| * @parem perm The permission requested. | |||
| */ | |||
| public void checkPermission(java.security.Permission perm) { | |||
| if (active) { | |||
| if (!granted.implies(perm)) { | |||
| throw new SecurityException("Permission " + perm +" was not granted."); | |||
| } | |||
| for (Iterator i = revokedPermissions.listIterator(); i.hasNext();) { | |||
| if (((Permissions.Permission)i.next()).matches(perm)) { | |||
| throw new SecurityException("Permission " + perm +" was revoked."); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /** Represents a permission. */ | |||
| public static class Permission { | |||
| private String className; | |||
| private String name; | |||
| private String actionString; | |||
| private Set actions; | |||
| /** | |||
| * Sets the class, mandatory. | |||
| * @param aClass The class name of the permission. | |||
| */ | |||
| public void setClass(String aClass) { | |||
| className = aClass.trim(); | |||
| } | |||
| /** Get the class of the permission | |||
| * @return The class name of the permission. | |||
| */ | |||
| public String getClassName() { | |||
| return className; | |||
| } | |||
| /** | |||
| * Sets the name of the permission. | |||
| * @param aName The name of the permission. | |||
| */ | |||
| public void setName(String aName) { | |||
| name = aName.trim(); | |||
| } | |||
| /** | |||
| * Get the name of the permission. | |||
| * @return The name of the permission. | |||
| */ | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| /** | |||
| * Sets the actions. | |||
| * @param actions The actions of the permission. | |||
| */ | |||
| public void setActions(String actions) { | |||
| actionString = actions; | |||
| if (actions.length() > 0) { | |||
| this.actions = parseActions(actions); | |||
| } | |||
| } | |||
| /** | |||
| * Gets the actions. | |||
| * @return The actions of the permission. | |||
| */ | |||
| public String getActions() { | |||
| return actionString; | |||
| } | |||
| /** | |||
| * Checks if the permission matches in case of a revoked permission. | |||
| * @param perm The permission to check against. | |||
| */ | |||
| boolean matches(java.security.Permission perm) { | |||
| if (!className.equals(perm.getClass().getName())) { | |||
| return false; | |||
| } | |||
| if (name != null) { | |||
| if (name.endsWith("*")) { | |||
| if (!perm.getName().startsWith(name.substring(0,name.length()-1))) { | |||
| return false; | |||
| } | |||
| } else { | |||
| if (!name.equals(perm.getName())) { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| if (actions != null) { | |||
| Set as = parseActions(perm.getActions()); | |||
| int size = as.size(); | |||
| as.removeAll(actions); | |||
| if (as.size() == size) { | |||
| // None of the actions revoked, so all allowed. | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| /** | |||
| * Parses the actions into a set of separate strings. | |||
| * @param action The actions to be parsed. | |||
| */ | |||
| private Set parseActions(String actions) { | |||
| Set result = new HashSet(); | |||
| StringTokenizer tk = new StringTokenizer(actions, ","); | |||
| while (tk.hasMoreTokens()) { | |||
| String item = tk.nextToken().trim(); | |||
| if (!item.equals("")) { | |||
| result.add(item); | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| public String toString() { | |||
| return ("Permission: " + className + " (\""+name+"\", \""+actions+"\")"); | |||
| } | |||
| } | |||
| } | |||
| @@ -64,6 +64,7 @@ import org.apache.tools.ant.util.FileUtils; | |||
| * @author steve loughran | |||
| * @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a> | |||
| * @author <a href="mailto:donal@savvion.com">Donal Quinlan</a> | |||
| * @author <a href="mailto:martijn@kruithof.xs4all.nl">Martijn Kruithof</a> | |||
| * */ | |||
| public class JavaTest extends BuildFileTest { | |||
| @@ -185,6 +186,16 @@ public class JavaTest extends BuildFileTest { | |||
| assertEquals("2",project.getProperty("exitcode")); | |||
| } | |||
| public void testResultPropertyZeroNoFork() { | |||
| executeTarget("testResultPropertyZeroNoFork"); | |||
| assertEquals("0",project.getProperty("exitcode")); | |||
| } | |||
| public void testResultPropertyNonZeroNoFork() { | |||
| executeTarget("testResultPropertyNonZeroNoFork"); | |||
| assertEquals("-1",project.getProperty("exitcode")); | |||
| } | |||
| public void testSpawn() { | |||
| FileUtils fileutils = FileUtils.newFileUtils(); | |||
| File logFile = fileutils.createTempFile("spawn","log", project.getBaseDir()); | |||
| @@ -0,0 +1,187 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 2003 The Apache Software Foundation. All rights | |||
| * reserved. | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * 1. Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * 2. Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in | |||
| * the documentation and/or other materials provided with the | |||
| * distribution. | |||
| * | |||
| * 3. The end-user documentation included with the redistribution, if | |||
| * any, must include the following acknowlegement: | |||
| * "This product includes software developed by the | |||
| * Apache Software Foundation (http://www.apache.org/)." | |||
| * Alternately, this acknowlegement may appear in the software itself, | |||
| * if and wherever such third-party acknowlegements normally appear. | |||
| * | |||
| * 4. The names "Ant" and "Apache Software | |||
| * Foundation" must not be used to endorse or promote products derived | |||
| * from this software without prior written permission. For written | |||
| * permission, please contact apache@apache.org. | |||
| * | |||
| * 5. Products derived from this software may not be called "Apache" | |||
| * nor may "Apache" appear in their names without prior written | |||
| * permission of the Apache Group. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
| * SUCH DAMAGE. | |||
| * ==================================================================== | |||
| * | |||
| * This software consists of voluntary contributions made by many | |||
| * individuals on behalf of the Apache Software Foundation. For more | |||
| * information on the Apache Software Foundation, please see | |||
| * <http://www.apache.org/>. | |||
| */ | |||
| package org.apache.tools.ant.types; | |||
| import junit.framework.TestCase; | |||
| import org.apache.tools.ant.ExitException; | |||
| /** | |||
| * JUnit 3 testcases for org.apache.tools.ant.types.Permissions. | |||
| * | |||
| * @author <a href="mailto:martijn@kruithof.xs4all.nl>Martijn Kruithof</a> | |||
| */ | |||
| public class PermissionsTest extends TestCase { | |||
| Permissions perms; | |||
| public PermissionsTest(String name) { | |||
| super(name); | |||
| } | |||
| public void setUp() { | |||
| perms = new Permissions(); | |||
| Permissions.Permission perm = new Permissions.Permission(); | |||
| // Grant extra permissions to read and write the user.* properties and read to the | |||
| // java.home property | |||
| perm.setActions("read, write"); | |||
| perm.setName("user.*"); | |||
| perm.setClass("java.util.PropertyPermission"); | |||
| perms.addConfiguredGrant(perm); | |||
| perm = new Permissions.Permission(); | |||
| perm.setActions("read"); | |||
| perm.setName("java.home"); | |||
| perm.setClass("java.util.PropertyPermission"); | |||
| perms.addConfiguredGrant(perm); | |||
| // Revoke permission to write user.home (granted above via user.*), still able to read though. | |||
| // and the default granted permission to read os.name. | |||
| perm = new Permissions.Permission(); | |||
| perm.setActions("write"); | |||
| perm.setName("user.home"); | |||
| perm.setClass("java.util.PropertyPermission"); | |||
| perms.addConfiguredRevoke(perm); | |||
| perm = new Permissions.Permission(); | |||
| perm.setActions("read"); | |||
| perm.setName("os.*"); | |||
| perm.setClass("java.util.PropertyPermission"); | |||
| perms.addConfiguredRevoke(perm); | |||
| } | |||
| /** Tests a permission that is granted per default. */ | |||
| public void testDefaultGranted() { | |||
| perms.setSecurityManager(); | |||
| try { | |||
| String s = System.getProperty("line.separator"); | |||
| } finally { | |||
| perms.restoreSecurityManager(); | |||
| } | |||
| } | |||
| /** Tests a permission that has been granted later via wildcard. */ | |||
| public void testGranted() { | |||
| perms.setSecurityManager(); | |||
| try { | |||
| String s = System.getProperty("user.name"); | |||
| System.setProperty("user.name", s); | |||
| } finally { | |||
| perms.restoreSecurityManager(); | |||
| } | |||
| } | |||
| /** Tests a permission that has been granted and revoked later. */ | |||
| public void testGrantedAndRevoked() { | |||
| perms.setSecurityManager(); | |||
| try { | |||
| String s = System.getProperty("user.home"); | |||
| System.setProperty("user.home", s); | |||
| fail("Could perform an action that should have been forbidden."); | |||
| } catch (SecurityException e){ | |||
| // Was expected, test passes | |||
| } finally { | |||
| perms.restoreSecurityManager(); | |||
| } | |||
| } | |||
| /** Tests a permission that is granted as per default but revoked later via wildcard. */ | |||
| public void testDefaultRevoked() { | |||
| perms.setSecurityManager(); | |||
| try { | |||
| System.getProperty("os.name"); | |||
| fail("Could perform an action that should have been forbidden."); | |||
| } catch (SecurityException e){ | |||
| // Was expected, test passes | |||
| } finally { | |||
| perms.restoreSecurityManager(); | |||
| } | |||
| } | |||
| /** Tests a permission that has not been granted or revoked. */ | |||
| public void testOther() { | |||
| String ls = System.getProperty("line.separator"); | |||
| perms.setSecurityManager(); | |||
| try { | |||
| String s = System.setProperty("line.separator",ls); | |||
| fail("Could perform an action that should have been forbidden."); | |||
| } catch (SecurityException e){ | |||
| // Was expected, test passes | |||
| } finally { | |||
| perms.restoreSecurityManager(); | |||
| } | |||
| } | |||
| /** Tests an exit condition. */ | |||
| public void testExit() { | |||
| perms.setSecurityManager(); | |||
| try { | |||
| System.out.println("If this is the last line on standard out the testExit f.a.i.l.e.d"); | |||
| System.exit(3); | |||
| fail("Totaly impossible that this fail is ever executed. Please let me know if it is!"); | |||
| } catch (ExitException e) { | |||
| if (e.getStatus() != 3) { | |||
| fail("Received wrong exit status in Exit Exception."); | |||
| } | |||
| System.out.println("testExit successfull."); | |||
| } finally { | |||
| perms.restoreSecurityManager(); | |||
| } | |||
| } | |||
| public void tearDown() { | |||
| } | |||
| } | |||