package com.github.marschall.memoryfilesystem;
import static java.nio.file.AccessMode.EXECUTE;
import static java.nio.file.AccessMode.READ;
import static java.nio.file.AccessMode.WRITE;
import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE;
import static java.nio.file.attribute.PosixFilePermission.GROUP_READ;
import static java.nio.file.attribute.PosixFilePermission.GROUP_WRITE;
import static java.nio.file.attribute.PosixFilePermission.OTHERS_EXECUTE;
import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ;
import static java.nio.file.attribute.PosixFilePermission.OTHERS_WRITE;
import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.AccessMode;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.spi.FileSystemProvider;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
import com.github.marschall.memoryfilesystem.CurrentGroup.GroupTask;
import com.github.marschall.memoryfilesystem.CurrentUser.UserTask;
public class PosixPermissionMemoryFileSystemTest {
@Rule
public final PosixPermissionFileSystemRule rule = new PosixPermissionFileSystemRule();
@Test
public void directoryRead() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
final Path directory = fileSystem.getPath("directory");
Path file = directory.resolve("file");
Files.createDirectory(directory);
Files.createFile(file);
Files.setAttribute(directory, "posix:permissions", asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_READ, OTHERS_WRITE));
UserPrincipal user = fileSystem.getUserPrincipalLookupService().lookupPrincipalByName(PosixPermissionFileSystemRule.OTHER);
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
try (DirectoryStream<?> stream = Files.newDirectoryStream(directory)) {
fail("should not be allowd to open a directory stram");
} catch (AccessDeniedException e) {
// should reach here
}
return null;
}
});
}
@Test
public void owner() throws IOException {
this.checkPermission(READ, OWNER_READ, PosixPermissionFileSystemRule.OWNER);
this.checkPermission(WRITE, OWNER_WRITE, PosixPermissionFileSystemRule.OWNER);
this.checkPermission(EXECUTE, OWNER_EXECUTE, PosixPermissionFileSystemRule.OWNER);
}
@Test
public void group() throws IOException {
this.checkPermission(READ, GROUP_READ, PosixPermissionFileSystemRule.GROUP);
this.checkPermission(WRITE, GROUP_WRITE, PosixPermissionFileSystemRule.GROUP);
this.checkPermission(EXECUTE, GROUP_EXECUTE, PosixPermissionFileSystemRule.GROUP);
}
@Test
public void others() throws IOException {
this.checkPermission(READ, OTHERS_READ, PosixPermissionFileSystemRule.OTHER);
this.checkPermission(WRITE, OTHERS_WRITE, PosixPermissionFileSystemRule.OTHER);
this.checkPermission(EXECUTE, OTHERS_EXECUTE, PosixPermissionFileSystemRule.OTHER);
}
/**
* Only the file owner can chmod
*
* @see <a href="https://github.com/marschall/memoryfilesystem/issues/50">Issue 50</a>
* @throws IOException if the test fails
*/
@Test
public void issue50() throws IOException {
Path path = this.rule.getFileSystem().getPath("readable-at-first");
Files.createFile(path, PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("r--r--r--")));
assertTrue(Files.isReadable(path));
assertFalse(Files.isWritable(path));
assertFalse(Files.isExecutable(path));
Files.setPosixFilePermissions(path, PosixFilePermissions.fromString("rw-r--r--")); // FAIL.
assertTrue(Files.isReadable(path)); // ok
assertTrue(Files.isWritable(path)); // (should be) ok
assertFalse(Files.isExecutable(path)); // ok
}
/**
* The owner should be able to read current permissions for its own file.
*/
@Test
public void issue51() throws IOException {
Path path = this.rule.getFileSystem().getPath("readable-at-first");
Files.createFile(path, PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("---------")));
assertFalse(Files.isReadable(path));
assertFalse(Files.isWritable(path));
assertFalse(Files.isExecutable(path));
Files.setPosixFilePermissions(path, PosixFilePermissions.fromString("rw-r--r--")); // FAIL.
assertTrue(Files.isReadable(path)); // ok
assertTrue(Files.isWritable(path)); // (should be) ok
assertFalse(Files.isExecutable(path)); // ok
}
private static Set<PosixFilePermission> asSet(PosixFilePermission... permissions) {
return new HashSet<>(Arrays.asList(permissions));
}
@Test
public void toSet() {
assertEquals(asSet(OWNER_READ), MemoryEntryAttributes.toSet(0b1));
assertEquals(asSet(OTHERS_EXECUTE), MemoryEntryAttributes.toSet(0b100000000));
assertEquals(asSet(OWNER_READ, OTHERS_EXECUTE), MemoryEntryAttributes.toSet(0b100000001));
}
@Test
public void toMask() {
assertEquals(0b1, MemoryEntryAttributes.toMask(asSet(OWNER_READ)));
assertEquals(0b100000000, MemoryEntryAttributes.toMask(asSet(OTHERS_EXECUTE)));
assertEquals(0b100000001, MemoryEntryAttributes.toMask(asSet(OWNER_READ, OTHERS_EXECUTE)));
}
private void checkPermission(AccessMode mode, PosixFilePermission permission, String userName) throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
final Path positive = fileSystem.getPath(userName + "-" + mode + "-" + permission + "-positive.txt");
Files.createFile(positive);
final Path negative = fileSystem.getPath(userName + "-" + mode + "-" + permission + "-negative.txt");
Files.createFile(negative);
UserPrincipal user = fileSystem.getUserPrincipalLookupService().lookupPrincipalByName(userName);
GroupPrincipal group = fileSystem.getUserPrincipalLookupService().lookupPrincipalByGroupName(userName);
this.checkPermissionPositive(mode, permission, positive, user, group);
this.checkPermissionNegative(mode, permission, negative, user, group);
}
private void checkPermissionPositive(final AccessMode mode, PosixFilePermission permission, final Path file, UserPrincipal user, final GroupPrincipal group) throws IOException {
PosixFileAttributeView view = Files.getFileAttributeView(file, PosixFileAttributeView.class);
FileSystem fileSystem = this.rule.getFileSystem();
GroupPrincipal fileGroup = fileSystem.getUserPrincipalLookupService().lookupPrincipalByGroupName(PosixPermissionFileSystemRule.GROUP);
view.setGroup(fileGroup); // change group before changing permissions
view.setPermissions(Collections.singleton(permission));
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
return CurrentGroup.useDuring(group, new GroupTask<Void>() {
@Override
public Void call() throws IOException {
FileSystemProvider provider = file.getFileSystem().provider();
provider.checkAccess(file, mode);
return null;
}
});
}
});
}
private void checkPermissionNegative(final AccessMode mode, PosixFilePermission permission, final Path file, UserPrincipal user, final GroupPrincipal group) throws IOException {
PosixFileAttributeView view = Files.getFileAttributeView(file, PosixFileAttributeView.class);
Set<PosixFilePermission> permissions = EnumSet.allOf(PosixFilePermission.class);
FileSystem fileSystem = this.rule.getFileSystem();
GroupPrincipal fileGroup = fileSystem.getUserPrincipalLookupService().lookupPrincipalByGroupName(PosixPermissionFileSystemRule.GROUP);
view.setGroup(fileGroup); // change group before changing permissions
permissions.remove(permission);
view.setPermissions(permissions);
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
return CurrentGroup.useDuring(group, new GroupTask<Void>() {
@Override
public Void call() throws IOException {
FileSystemProvider provider = file.getFileSystem().provider();
try {
provider.checkAccess(file, mode);
fail("should not be able to access");
} catch (AccessDeniedException e) {
// should reach here
}
return null;
}
});
}
});
}
@Test
public void deletePermission() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path sourceDirectory = Files.createDirectory(fileSystem.getPath("source-directory"));
final Path file = Files.createFile(sourceDirectory.resolve("file.txt"));
PosixFileAttributeView fileView = Files.getFileAttributeView(sourceDirectory, PosixFileAttributeView.class);
fileView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
PosixFileAttributeView directoryView = Files.getFileAttributeView(sourceDirectory, PosixFileAttributeView.class);
directoryView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
UserPrincipal user = fileSystem.getUserPrincipalLookupService().lookupPrincipalByName(PosixPermissionFileSystemRule.OTHER);
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
try {
Files.delete(file);
fail("should not be delete to create files");
} catch (AccessDeniedException e) {
// should reach here
}
return null;
}
});
directoryView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_WRITE));
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
Files.delete(file);
return null;
}
});
}
@Test
public void movePermission() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path sourceDirectory = Files.createDirectory(fileSystem.getPath("source-directory"));
Path targetDirectory = Files.createDirectory(fileSystem.getPath("target-directory"));
final Path source = Files.createFile(sourceDirectory.resolve("file.txt"));
final Path target = targetDirectory.resolve("file.txt");
UserPrincipal user = fileSystem.getUserPrincipalLookupService().lookupPrincipalByName(PosixPermissionFileSystemRule.OTHER);
PosixFileAttributeView sourceView = Files.getFileAttributeView(sourceDirectory, PosixFileAttributeView.class);
sourceView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
PosixFileAttributeView targetView = Files.getFileAttributeView(targetDirectory, PosixFileAttributeView.class);
targetView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
// no write permission is not enough
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
try {
Files.move(source, target);
fail("should not be allowed to move files");
} catch (AccessDeniedException e) {
// should reach here
}
return null;
}
});
sourceView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_WRITE));
targetView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
// write permission on source only is not enough
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
try {
Files.move(source, target);
fail("should not be allowed to move files");
} catch (AccessDeniedException e) {
// should reach here
}
return null;
}
});
sourceView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
targetView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_WRITE));
// write permission on target only is not enough
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
try {
Files.move(source, target);
fail("should not be allowed to move files");
} catch (AccessDeniedException e) {
// should reach here
}
return null;
}
});
sourceView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_WRITE));
targetView.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_WRITE));
// write permission on source and target is enough
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
Files.move(source, target);
return null;
}
});
}
@Test
public void createPermission() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path sourceDirectory = Files.createDirectory(fileSystem.getPath("source-directory"));
final Path file = sourceDirectory.resolve("file.txt");
PosixFileAttributeView view = Files.getFileAttributeView(sourceDirectory, PosixFileAttributeView.class);
view.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
UserPrincipal user = fileSystem.getUserPrincipalLookupService().lookupPrincipalByName(PosixPermissionFileSystemRule.OTHER);
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
try {
Files.createFile(file);
fail("should not be allowed to create files");
} catch (AccessDeniedException e) {
// should reach here
}
return null;
}
});
view.setPermissions(asSet(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_WRITE));
CurrentUser.useDuring(user, new UserTask<Void>() {
@Override
public Void call() throws IOException {
Files.createFile(file);
return null;
}
});
}
}