| @@ -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())); | |||||
| } | } | ||||
| } | } | ||||