@@ -86,7 +86,7 @@ | |||||
by a given user.</li> | by a given user.</li> | ||||
<li><a href="#posixGroup"><code><posixGroup></code>—Select | <li><a href="#posixGroup"><code><posixGroup></code>—Select | ||||
files if they have a given POSIX group.</li> | files if they have a given POSIX group.</li> | ||||
<li><a href="#posixPermissions"><code><posixPermissions></code>—Select | |||||
<li><a href="#posixPermissions"><code><posixPermissions></code></a>—Select | |||||
files if they have given POSIX permissions.</li> | files if they have given POSIX permissions.</li> | ||||
</ul> | </ul> | ||||
@@ -925,6 +925,11 @@ | |||||
<td>Username of the expected owner</td> | <td>Username of the expected owner</td> | ||||
<td>Yes</td> | <td>Yes</td> | ||||
</tr> | </tr> | ||||
<tr> | |||||
<td>followlinks</td> | |||||
<td>Must the selector follow symbolic links?</td> | |||||
<td>No; defaults to <q>false</q> (was <q>true</q> before Ant 1.10.4)</td> | |||||
</tr> | |||||
</table> | </table> | ||||
<h4 id="posixGroup">PosixGroup Selector</h4> | <h4 id="posixGroup">PosixGroup Selector</h4> | ||||
@@ -937,16 +942,21 @@ | |||||
<p><em>Since Ant 1.10.4</em></p> | <p><em>Since Ant 1.10.4</em></p> | ||||
<table class="attr"> | <table class="attr"> | ||||
<tr> | |||||
<th scope="col">Attribute</th> | |||||
<th scope="col">Description</th> | |||||
<th scope="col">Required</th> | |||||
</tr> | |||||
<tr> | |||||
<td>group</td> | |||||
<td>POSIX group name</td> | |||||
<td>Yes</td> | |||||
</tr> | |||||
<tr> | |||||
<th scope="col">Attribute</th> | |||||
<th scope="col">Description</th> | |||||
<th scope="col">Required</th> | |||||
</tr> | |||||
<tr> | |||||
<td>group</td> | |||||
<td>POSIX group name</td> | |||||
<td>Yes</td> | |||||
</tr> | |||||
<tr> | |||||
<td>followlinks</td> | |||||
<td>Must the selector follow symbolic links?</td> | |||||
<td>No; defaults to <q>false</q></td> | |||||
</tr> | |||||
</table> | </table> | ||||
<h4 id="posixPermissions">PosixPermissions Selector</h4> | <h4 id="posixPermissions">PosixPermissions Selector</h4> | ||||
@@ -959,16 +969,21 @@ | |||||
<p><em>Since Ant 1.10.4</em></p> | <p><em>Since Ant 1.10.4</em></p> | ||||
<table class="attr"> | <table class="attr"> | ||||
<tr> | |||||
<th scope="col">Attribute</th> | |||||
<th scope="col">Description</th> | |||||
<th scope="col">Required</th> | |||||
</tr> | |||||
<tr> | |||||
<td>permissions</td> | |||||
<td>POSIX permissions in string (<q>rwxrwxrwx</q>) or octal (<q>777</q>) format</td> | |||||
<td>Yes</td> | |||||
</tr> | |||||
<tr> | |||||
<th scope="col">Attribute</th> | |||||
<th scope="col">Description</th> | |||||
<th scope="col">Required</th> | |||||
</tr> | |||||
<tr> | |||||
<td>permissions</td> | |||||
<td>POSIX permissions in string (<q>rwxrwxrwx</q>) or octal (<q>777</q>) format</td> | |||||
<td>Yes</td> | |||||
</tr> | |||||
<tr> | |||||
<td>followlinks</td> | |||||
<td>Must the selector follow symbolic links?</td> | |||||
<td>No; defaults to <q>false</q></td> | |||||
</tr> | |||||
</table> | </table> | ||||
<h4 id="scriptselector">Script Selector</h4> | <h4 id="scriptselector">Script Selector</h4> | ||||
@@ -21,9 +21,11 @@ package org.apache.tools.ant.types.selectors; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.nio.file.Files; | import java.nio.file.Files; | ||||
import java.nio.file.LinkOption; | |||||
import java.nio.file.attribute.UserPrincipal; | import java.nio.file.attribute.UserPrincipal; | ||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.PropertyHelper; | |||||
/** | /** | ||||
* A selector that selects files based on their owner. | * A selector that selects files based on their owner. | ||||
@@ -40,14 +42,24 @@ public class OwnedBySelector implements FileSelector { | |||||
private String owner; | private String owner; | ||||
private boolean followLinks = false; | |||||
/** | /** | ||||
* Sets the User-Name to look for. | |||||
* Sets the user name to look for. | |||||
* @param owner the user name | * @param owner the user name | ||||
*/ | */ | ||||
public void setOwner(String owner) { | public void setOwner(String owner) { | ||||
this.owner = owner; | this.owner = owner; | ||||
} | } | ||||
/** | |||||
* Sets the "follow links" flag. | |||||
* @param followLinks the user name | |||||
*/ | |||||
public void setFollowLinks(String followLinks) { | |||||
this.followLinks = PropertyHelper.toBoolean(followLinks); | |||||
} | |||||
@Override | @Override | ||||
public boolean isSelected(File basedir, String filename, File file) { | public boolean isSelected(File basedir, String filename, File file) { | ||||
if (owner == null) { | if (owner == null) { | ||||
@@ -55,7 +67,8 @@ public class OwnedBySelector implements FileSelector { | |||||
} | } | ||||
if (file != null) { | if (file != null) { | ||||
try { | try { | ||||
UserPrincipal user = Files.getOwner(file.toPath()); | |||||
UserPrincipal user = followLinks ? Files.getOwner(file.toPath()) | |||||
: Files.getOwner(file.toPath(), LinkOption.NOFOLLOW_LINKS); | |||||
return user != null && owner.equals(user.getName()); | return user != null && owner.equals(user.getName()); | ||||
} catch (UnsupportedOperationException | IOException ex) { | } catch (UnsupportedOperationException | IOException ex) { | ||||
// => not the expected owner | // => not the expected owner | ||||
@@ -19,6 +19,7 @@ | |||||
package org.apache.tools.ant.types.selectors; | package org.apache.tools.ant.types.selectors; | ||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.PropertyHelper; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
@@ -41,6 +42,8 @@ public class PosixGroupSelector implements FileSelector { | |||||
private String group; | private String group; | ||||
private boolean followLinks = false; | |||||
/** | /** | ||||
* Sets the group name to look for. | * Sets the group name to look for. | ||||
* @param group the group name | * @param group the group name | ||||
@@ -49,13 +52,22 @@ public class PosixGroupSelector implements FileSelector { | |||||
this.group = group; | this.group = group; | ||||
} | } | ||||
/** | |||||
* Sets the "follow links" flag. | |||||
* @param followLinks the user name | |||||
*/ | |||||
public void setFollowLinks(String followLinks) { | |||||
this.followLinks = PropertyHelper.toBoolean(followLinks); | |||||
} | |||||
@Override | @Override | ||||
public boolean isSelected(File basedir, String filename, File file) { | public boolean isSelected(File basedir, String filename, File file) { | ||||
if (group == null) { | if (group == null) { | ||||
throw new BuildException("the group attribute is required"); | throw new BuildException("the group attribute is required"); | ||||
} | } | ||||
try { | try { | ||||
GroupPrincipal actualGroup = Files.readAttributes(file.toPath(), | |||||
GroupPrincipal actualGroup = followLinks ? Files.readAttributes(file.toPath(), | |||||
PosixFileAttributes.class).group() : Files.readAttributes(file.toPath(), | |||||
PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS).group(); | PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS).group(); | ||||
return actualGroup != null && actualGroup.getName().equals(group); | return actualGroup != null && actualGroup.getName().equals(group); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
@@ -19,6 +19,7 @@ | |||||
package org.apache.tools.ant.types.selectors; | package org.apache.tools.ant.types.selectors; | ||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.PropertyHelper; | |||||
import org.apache.tools.ant.util.PermissionUtils; | import org.apache.tools.ant.util.PermissionUtils; | ||||
import java.io.File; | import java.io.File; | ||||
@@ -40,6 +41,8 @@ public class PosixPermissionsSelector implements FileSelector { | |||||
private String permissions; | private String permissions; | ||||
private boolean followLinks = false; | |||||
/** | /** | ||||
* Sets the permissions to look for. | * Sets the permissions to look for. | ||||
* @param permissions the permissions string (rwxrwxrwx or octal) | * @param permissions the permissions string (rwxrwxrwx or octal) | ||||
@@ -59,14 +62,23 @@ public class PosixPermissionsSelector implements FileSelector { | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Sets the "follow links" flag. | |||||
* @param followLinks the user name | |||||
*/ | |||||
public void setFollowLinks(String followLinks) { | |||||
this.followLinks = PropertyHelper.toBoolean(followLinks); | |||||
} | |||||
@Override | @Override | ||||
public boolean isSelected(File basedir, String filename, File file) { | public boolean isSelected(File basedir, String filename, File file) { | ||||
if (permissions == null) { | if (permissions == null) { | ||||
throw new BuildException("the permissions attribute is required"); | throw new BuildException("the permissions attribute is required"); | ||||
} | } | ||||
try { | try { | ||||
return PosixFilePermissions.toString( | |||||
Files.getPosixFilePermissions(file.toPath(), LinkOption.NOFOLLOW_LINKS)) | |||||
return PosixFilePermissions.toString(followLinks | |||||
? Files.getPosixFilePermissions(file.toPath()) | |||||
: Files.getPosixFilePermissions(file.toPath(), LinkOption.NOFOLLOW_LINKS)) | |||||
.equals(permissions); | .equals(permissions); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
// => not the expected permissions | // => not the expected permissions | ||||
@@ -19,14 +19,19 @@ | |||||
package org.apache.tools.ant.types.selectors; | package org.apache.tools.ant.types.selectors; | ||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | |||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assume.assumeFalse; | import static org.junit.Assume.assumeFalse; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | |||||
import java.nio.file.Files; | import java.nio.file.Files; | ||||
import java.nio.file.LinkOption; | |||||
import java.nio.file.Path; | |||||
import java.nio.file.attribute.UserPrincipal; | import java.nio.file.attribute.UserPrincipal; | ||||
import org.apache.tools.ant.taskdefs.condition.Os; | import org.apache.tools.ant.taskdefs.condition.Os; | ||||
import org.junit.Before; | |||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.TemporaryFolder; | import org.junit.rules.TemporaryFolder; | ||||
@@ -36,18 +41,46 @@ public class OwnedBySelectorTest { | |||||
@Rule | @Rule | ||||
public TemporaryFolder folder = new TemporaryFolder(); | public TemporaryFolder folder = new TemporaryFolder(); | ||||
@Test | |||||
public void ownedByIsTrueForSelf() throws Exception { | |||||
private final File TEST_FILE = new File("/etc/passwd"); | |||||
private final String SELF = System.getProperty("user.name"); | |||||
private final String ROOT = "root"; | |||||
private OwnedBySelector s; | |||||
@Before | |||||
public void setUp() { | |||||
// at least on Jenkins the file is owned by "BUILTIN\Administrators" | // at least on Jenkins the file is owned by "BUILTIN\Administrators" | ||||
assumeFalse(Os.isFamily("windows")); | assumeFalse(Os.isFamily("windows")); | ||||
String self = System.getProperty("user.name"); | |||||
s = new OwnedBySelector(); | |||||
} | |||||
@Test | |||||
public void ownedByIsTrueForSelf() throws Exception { | |||||
File file = folder.newFile("f.txt"); | File file = folder.newFile("f.txt"); | ||||
UserPrincipal user = Files.getOwner(file.toPath()); | UserPrincipal user = Files.getOwner(file.toPath()); | ||||
assertEquals(self, user.getName()); | |||||
assertEquals(SELF, user.getName()); | |||||
OwnedBySelector s = new OwnedBySelector(); | |||||
s.setOwner(self); | |||||
s.setOwner(SELF); | |||||
assertTrue(s.isSelected(null, null, file)); | assertTrue(s.isSelected(null, null, file)); | ||||
} | } | ||||
@Test | |||||
public void ownedByFollowSymlinks() throws IOException { | |||||
File target = new File(folder.getRoot(), "link"); | |||||
Path symbolicLink = Files.createSymbolicLink(target.toPath(), TEST_FILE.toPath()); | |||||
UserPrincipal root = Files.getOwner(symbolicLink); | |||||
assertEquals(ROOT, root.getName()); | |||||
UserPrincipal user = Files.getOwner(symbolicLink, LinkOption.NOFOLLOW_LINKS); | |||||
assertEquals(SELF, user.getName()); | |||||
s.setOwner(SELF); | |||||
assertTrue(s.isSelected(null, null, symbolicLink.toFile())); | |||||
s.setFollowLinks("yes"); | |||||
assertFalse(s.isSelected(null, null, symbolicLink.toFile())); | |||||
} | |||||
} | } |
@@ -9,10 +9,14 @@ import org.junit.rules.TemporaryFolder; | |||||
import java.io.File; | import java.io.File; | ||||
import java.nio.file.Files; | import java.nio.file.Files; | ||||
import java.nio.file.LinkOption; | import java.nio.file.LinkOption; | ||||
import java.nio.file.Path; | |||||
import java.nio.file.attribute.GroupPrincipal; | import java.nio.file.attribute.GroupPrincipal; | ||||
import java.nio.file.attribute.PosixFileAttributes; | |||||
import java.util.Map; | import java.util.Map; | ||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | |||||
import static org.junit.Assert.assertNotEquals; | |||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assume.assumeNoException; | import static org.junit.Assume.assumeNoException; | ||||
import static org.junit.Assume.assumeTrue; | import static org.junit.Assume.assumeTrue; | ||||
@@ -24,13 +28,15 @@ public class PosixGroupSelectorTest { | |||||
private final String GROUP_GETTER = "getGid"; | private final String GROUP_GETTER = "getGid"; | ||||
private final File TEST_FILE = new File("/etc/passwd"); | |||||
private Class<?> jaasProviderClass; | private Class<?> jaasProviderClass; | ||||
private PosixGroupSelector s; | private PosixGroupSelector s; | ||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
assumeTrue(Os.isFamily("unix")); | |||||
assumeTrue("Not POSIX", Os.isFamily("unix")); | |||||
String osName = System.getProperty("os.name", "unknown").toLowerCase(); | String osName = System.getProperty("os.name", "unknown").toLowerCase(); | ||||
String jaasProviderClassName = osName.contains("sunos") | String jaasProviderClassName = osName.contains("sunos") | ||||
? "com.sun.security.auth.module.SolarisSystem" | ? "com.sun.security.auth.module.SolarisSystem" | ||||
@@ -46,7 +52,7 @@ public class PosixGroupSelectorTest { | |||||
} | } | ||||
@Test | @Test | ||||
public void PosixGroupIsTrueForSelf() throws Exception { | |||||
public void posixGroupIsTrueForSelf() throws Exception { | |||||
long gid = (long) jaasProviderClass.getMethod(GROUP_GETTER) | long gid = (long) jaasProviderClass.getMethod(GROUP_GETTER) | ||||
.invoke(jaasProviderClass.newInstance()); | .invoke(jaasProviderClass.newInstance()); | ||||
@@ -61,4 +67,27 @@ public class PosixGroupSelectorTest { | |||||
assertTrue(s.isSelected(null, null, file)); | assertTrue(s.isSelected(null, null, file)); | ||||
} | } | ||||
@Test | |||||
public void posixGroupFollowSymlinks() throws Exception { | |||||
long gid = (long) jaasProviderClass.getMethod(GROUP_GETTER) | |||||
.invoke(jaasProviderClass.newInstance()); | |||||
File target = new File(folder.getRoot(), "link"); | |||||
Path symbolicLink = Files.createSymbolicLink(target.toPath(), TEST_FILE.toPath()); | |||||
Map<String, Object> linkAttributes = Files.readAttributes(target.toPath(), | |||||
"unix:group,gid", LinkOption.NOFOLLOW_LINKS); | |||||
long linkGid = (int) linkAttributes.get("gid"); | |||||
assertEquals("Different GIDs", gid, linkGid); | |||||
GroupPrincipal targetGroup = Files.readAttributes(target.toPath(), | |||||
PosixFileAttributes.class).group(); | |||||
GroupPrincipal linkGroup = (GroupPrincipal) linkAttributes.get("group"); | |||||
assertNotEquals("Same group name", linkGroup.getName(), | |||||
targetGroup.getName()); | |||||
s.setGroup(linkGroup.getName()); | |||||
assertTrue(s.isSelected(null, null, symbolicLink.toFile())); | |||||
s.setFollowLinks("yes"); | |||||
assertFalse(s.isSelected(null, null, symbolicLink.toFile())); | |||||
} | |||||
} | } |
@@ -10,9 +10,16 @@ import org.junit.rules.TemporaryFolder; | |||||
import org.junit.runner.RunWith; | import org.junit.runner.RunWith; | ||||
import org.junit.runners.Parameterized; | import org.junit.runners.Parameterized; | ||||
import java.io.File; | |||||
import java.nio.file.Files; | |||||
import java.nio.file.Path; | |||||
import java.nio.file.attribute.PosixFilePermission; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.HashSet; | |||||
import java.util.Set; | |||||
import static org.junit.Assert.assertFalse; | |||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assume.assumeTrue; | import static org.junit.Assume.assumeTrue; | ||||
@@ -35,7 +42,7 @@ public class PosixPermissionsSelectorTest { | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
assumeTrue("no POSIX", Os.isFamily("unix")); | |||||
assumeTrue("Not POSIX", Os.isFamily("unix")); | |||||
s = new PosixPermissionsSelector(); | s = new PosixPermissionsSelector(); | ||||
} | } | ||||
@@ -54,9 +61,9 @@ public class PosixPermissionsSelectorTest { | |||||
public TemporaryFolder folder = new TemporaryFolder(); | public TemporaryFolder folder = new TemporaryFolder(); | ||||
// requires JUnit 4.12 | // requires JUnit 4.12 | ||||
@Parameterized.Parameters(name = "legal argument: |{0}|") | |||||
@Parameterized.Parameters(name = "legal argument (self): |{0}|") | |||||
public static Collection<String> data() { | public static Collection<String> data() { | ||||
return Arrays.asList("755", "rwxr-xr-x"); | |||||
return Arrays.asList("750", "rwxr-x---"); | |||||
} | } | ||||
@Parameterized.Parameter | @Parameterized.Parameter | ||||
@@ -64,14 +71,62 @@ public class PosixPermissionsSelectorTest { | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
assumeTrue("No POSIX", Os.isFamily("unix")); | |||||
assumeTrue("Not POSIX", Os.isFamily("unix")); | |||||
s = new PosixPermissionsSelector(); | s = new PosixPermissionsSelector(); | ||||
} | } | ||||
@Test | @Test | ||||
public void PosixPermissionsIsTrueForSelf() throws Exception { | |||||
public void test() throws Exception { | |||||
// do not depend on default umask | |||||
File subFolder = folder.newFolder(); | |||||
Set<PosixFilePermission> permissions = new HashSet<>(); | |||||
permissions.add(PosixFilePermission.OWNER_READ); | |||||
permissions.add(PosixFilePermission.OWNER_WRITE); | |||||
permissions.add(PosixFilePermission.OWNER_EXECUTE); | |||||
permissions.add(PosixFilePermission.GROUP_READ); | |||||
permissions.add(PosixFilePermission.GROUP_EXECUTE); | |||||
Files.setPosixFilePermissions(subFolder.toPath(), permissions); | |||||
s.setPermissions(argument); | |||||
assertTrue(s.isSelected(null, null, subFolder)); | |||||
} | |||||
} | |||||
@RunWith(Parameterized.class) | |||||
public static class LegalSymbolicLinkArgumentTest { | |||||
private final File TEST_FILE = new File("/etc/passwd"); | |||||
private PosixPermissionsSelector s; | |||||
@Rule | |||||
public TemporaryFolder folder = new TemporaryFolder(); | |||||
// requires JUnit 4.12 | |||||
@Parameterized.Parameters(name = "legal argument (link): |{0}|") | |||||
public static Collection<String> data() { | |||||
return Arrays.asList("644", "rw-r--r--"); | |||||
} | |||||
@Parameterized.Parameter | |||||
public String argument; | |||||
@Before | |||||
public void setUp() { | |||||
assumeTrue("Not POSIX", Os.isFamily("unix")); | |||||
s = new PosixPermissionsSelector(); | |||||
} | |||||
@Test | |||||
public void test() throws Exception { | |||||
// symlinks have execute bit set by default | |||||
File target = new File(folder.getRoot(), "link"); | |||||
Path symbolicLink = Files.createSymbolicLink(target.toPath(), TEST_FILE.toPath()); | |||||
s.setPermissions(argument); | s.setPermissions(argument); | ||||
assertTrue(s.isSelected(null, null, folder.newFolder())); | |||||
assertFalse(s.isSelected(null, null, symbolicLink.toFile())); | |||||
s.setFollowLinks("yes"); | |||||
assertTrue(s.isSelected(null, null, symbolicLink.toFile())); | |||||
} | } | ||||
} | } | ||||