File systems have support for using an AccessController to control access to its entities. Access controllers are tools for helping file system clients in setting access policies for their file systems. They are not full-blown security solutions in themselves, though, since they can always be circumvented by malicious code running in the same JVM.
Most access controllers use Subject objects to
identify who is trying to access an entity. A Subject is
assigned to a running thread by calling any of the Subject's
doAs
methods.
The simplest AccessController implementation is the
AccessGranterAccessController. It uses the callback
method hasAccess
of an AccessGranter
to decide if a client has access to an entity.
The example below shows how a custom AccessGranter can be used with custom Subject objects to limit access to entities.
Example 13.1. A custom access granter
// The access granter public class MyAccessGranter implements AccessGranter { public boolean hasAccess(Subject subj, Permission p) { // Alice may only write in directories on thursdays if (p instanceof WritePermission) { WritePermission wp = (WritePermission) p; // Is the entity we are controlling access to a directory entity? if (wp.getEntity().getType() == ETDirectory.TYPE) { String subjName = ((MySubject) subj).getName(); if (subjName.equals("Alice")) { return Calendar.getInstance().get(Calendar.DAY_OF_WEEK) == Calendar.THURSDAY; } } } // All other access is permitted return true; } } // A custom Subject implementation that has a name. public class MySubject extends SimpleAccessControllerSubject { private final String m_name; public MySubject(AccessController ac, String name) { super(ac); m_name = name; } public String getName() { return m_name; } } // A PrivilegedAction that creates a new temporary file in a directory public class MyWriteToDirectoryAction implements PrivilegedAction<EFile> { private final Directory m_directory; public MyWriteToDirectoryAction(Directory d) { m_directory = d; } public EFile run() { return Directories.newTempFile(m_directory, "privileged", ".tmp"); } } // A class that uses the privileged action to try to create a temporary file. public class MyDirectoryWriter { public EFile createTempFile(MySubject subj, Directory dir) { try { return subj.doAs(new MyWriteToDirectoryAction(dir)); } catch (AccessDeniedException e) { dir.getFileSystem().getLogAdapter().logWarning("Not today"); throw e; } } }
The UnixEntityModeAccessController uses an UidGidSubject, the owner user and group id:s, and the UnixEntityMode of an entity to decide if the subject has access to it. The UidGidSubject is a subject that has a user id, UID, a primary group id, GID, and zero or more secondary GID:s. The UnixEntityMode specifies what kind of access the entity's owner, members of the same group as the entity belongs to and all other subjects have to the entity.
The UnixEntityModeAccessController requires that the file system supports any of the ECNtfsAttributes or ECUnixAttributes capabilities. They are supplied by the MetadataNtfsAttributesCapabilityProvider or the MetadataUnixAttributesCapabilityProvider.
The following example shows how a UnixEntityModeAccessController can be used in a non-locking file system.
Example 13.2. The Unix entity mode access controller on a non-locking file system
// A PrivilegedAction that creates a new text file in a directory public class NewTextFileAction implements PrivilegedAction<EFile> { private final Directory m_directory; public NewTextFileAction(Directory d) { m_directory = d; } public EFile run() { EFile res = (EFile) m_directory.newEntity(ETFile.TYPE, "t.txt", null); Files.writeText(res, "Quidquid latine dictum sit, altum sonatur"); return res; } } // A class that makes sure that the file is written public class MyFileWriter { public EFile createFile(Directory d) { // Create a subject with UID = 1, primary GID = 2 and one secondary GID = 3 UidGidSubject subj = new SimpleAccessControllerUidGidSubject( d.getFileSystem().getAccessController(), 1, 2, Arrays.asList(new Integer[] { Integer.valueOf(3) })); // Get the entity attributes for the directory. Make no assumption about // what exact kind of entity attributes that the directory has, other than // that it implements UidGidConfigurable and UnixEntityModeConfigurable EntityAttributes ea = ECEntityAttributesUtil.getAttributes(d); // Set the directory's owner to be the new subject ((UidGidConfigurable) ea).setUid(1); // Set the directory's mode to permit write and execution by its // owner. 0755 is the mode's octal representation. It will allow writes only // by the directory's owner. See UnixEntityMode for more documentation. ((UnixEntityModeConfigurable) ea).setUnixEntityMode(0755); // Save the directory's attributes ECEntityAttributesUtil.setAttributes(d, ea); // Create the text file. It will automatically be owned by the creating // Subject return subj.doAs(new NewTextFileAction(d)); } }
Since the entity attributes of an entity is a property of its parent directory, the parent directory has to be locked for reading every time an access control is performed on the entity, in other words, every time any of the access controlling entity methods, which are most of them, are called. A consequence of this is that the locking strategy of the utility classes does not work with this access controller since the utility classes do not acquire locks on an entity's parent directory before calling an entity's methods. File system clients have to do their own entity locking, as is shown in the example below.
The following example does the same as the example above on a file system that is locking. The file system's LockCommandExecutor (see Chapter 7, Locking and multi-threaded access) is used for locking all locks in an order that is compliant with the file system's locking strategy.
Example 13.3. The Unix entity mode access controller on a locking file system
// A PrivilegedAction that creates a new text file in a directory public class NewTextFileAction implements PrivilegedAction<EFile> { private final Directory m_directory; public NewTextFileAction(Directory d) { m_directory = d; } public EFile run() { FileSystem fs = m_directory.getFileSystem(); // Create the file. // Use the file system's LockCommandExecutor to // execute the lock commands according to the file system's locking strategy. Collection<LockCommand> lcmds = new ArrayList<LockCommand>(); // We need lock commands for access controls on the directory lcmds.addAll(fs.getAccessController().getLockCommandsForAccessControl(m_directory)); // ...and lock commands for locking the directory for writing lcmds.add(new LockForWritingCommand(m_directory)); EFile res; LockCollection lc = fs.getLockCommandExecutor().execute(lcmds); try { res = (EFile) m_directory.newEntity(ETFile.TYPE, "t.txt", null); } finally { lc.unlockAll(); } // Write to the file lcmds = new ArrayList<LockCommand>(); // Lock commands for access controls on the file lcmds.addAll(fs.getAccessController().getLockCommandsForAccessControl(res)); // ... and for locking the file for writing lcmds.add(new LockForWritingCommand(res)); lc = fs.getLockCommandExecutor().execute(lcmds); try { Files.writeText(res, "Vescere bracis meis!"); } finally { lc.unlockAll(); } return res; } } // A class that makes sure that the file is written public class MyFileWriter { public EFile createFile(Directory d) { // Create a subject with UID = 1, primary GID = 2 and one secondary GID = 3 UidGidSubject subj = new SimpleAccessControllerUidGidSubject( d.getFileSystem().getAccessController(), 1, 2, Arrays.asList(new Integer[] { Integer.valueOf(3) })); // Get the entity attributes for the directory. Make no assumption about // what exact kind of entity attributes that the directory has, other than // that it implements UidGidConfigurable and UnixEntityModeConfigurable EntityAttributes ea = ECEntityAttributesUtil.getAttributes(d); // Set the directory's owner to be the new subject ((UidGidConfigurable) ea).setUid(1); // Set the directory's mode to permit write and execution by its owner. // 0755 is the mode's octal representation. It will allow writes only by the // directory's owner. See UnixEntityMode for more documentation. ((UnixEntityModeConfigurable) ea).setUnixEntityMode(0755); // Save the directory's attributes ECEntityAttributesUtil.setAttributes(d, ea); // Create the text file. It will automatically be owned by the creating // Subject return subj.doAs(new NewTextFileAction(d)); } }