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() { | |||
} | |||
} |