CORK includes flexible security support in the form of a Permissions interface. Instances of classes that implement Permissions may be assoicated with CORK-based replicated objects and can control read, write, delete, and change-permission access.
This approach is appropriately flexible, but development of previous CORK-based systems have exposed two shortcomings:
- Access control for a given piece of replicated data is rarely sensible in isolation. Futhermore, individual configuration of access control for each object is tedious. More commonly, access control requirements resemble those implemented in UNIX-style file systems:
- Access to a particular piece of data should be controlled by the container in which that data is embedded/contained. By analogy, restricting a folder generally renders its contents inaccessible to those who lack proper access.
- Access configuration should revert to user-specific defaults where delegation to a container's access configuration is not possible or practical. By analogy, the unix
umask command can be used to force newly created files to be readable and writable only by the person who created them.
- Standard UI controls for common cases are not available. The "common cases" are likely just per-user and per-group read/write control. (Delete and permissions change are effectively just sub-cases of write.)
These issues suggest several enhancements to CORK and features for bridge:
1. Addition of cork.security.DelegatedPermissions class
This would include the ObjectID of another object from which permissions should be retrieved. The implementation must support "chains" of delegated permissions for deeply nested objects, but should also check for loops.
DelegatedPermissions should also include the user id of the creating user. In the case of error (e.g., if the object with the specified object id has been deleted or if a loop is found), all operations could be restricted to the creating user.
Permissions objects currently have no means for retrieving the permissions of other objects. There are at least three possible solutions to this:
- Pass an ObjectSource (or ReplicatedObjectClient, or something similar) to the Permissions methods. If this provides access to more than Permissions (on the server side) then it introduces a potential security problem. It would also require changing all existing
Permissions implementations to accomodate the method signature changes, but fortunately there are not many of these.
- Have the CORK server handle
DelegatedPermissions explicitly. This seems generally unclean, and also doesn't handle client-side permissions lookups.
- Introduce a utility class (either singleton or with static methods) that serves as a source for permissions object. This could be initialized by
ServerImpl for server-side use and InetClient for client-side use. This is problematic on the client-side if there are multiple client objects with different logins. In theory, any request to the "PermissionsSource" object could use any client object without security risks, but the logistics of this will be complex in cases where client objects are logging in and out.
- Add a PermissionsSource interface and a setPermissionsSource(...) method in
Permissions that is called whenever permissions are checked (in the server) or retrieved (by the client). This solves the client-side problems in (3), though it requires at least minimal changes (the addition of a no-op setPermissionsSource(...) method) to existing Permissions implementations. It also presents a potential security problem if a Permissions object stores a reference to a PermissionsSource object with sensitive data on the server side and is then serialized and sent to the client. This can probably be solved by documentation: PermissionsSource objects can simply be explicitly non-serializable, and Permissions implementations should mark PermissionsSource fields as transient.
2. Addition of factory mechanism for creating Permissions objects for embedded objects.
Bridge introduces abstractions for sets of replicated objects (in the form of EmbeddedObjectSet and EmbeddedObjectDescriptor) for minimally defining containment relationships between replicated objects. The addition of a permissions factory class would allow construction of an appropriate DelegatedPermissions object based on the current logged-in user and EmbeddedObjectSet.
Given that new object creation is generally implemented in the container's code, it may also be sufficient to just implement this factory functionality as a static utility method in DelegatedPermissions that takes the ObjectID of the container and a ReplicatedObjectClient from which a user id can be extracted. This approach may be somewhat problematic for "top-level" containers, which do not have a logical parent. A separate factory method would hide the logic for a more complex defaulting scheme. A final decision on this should probably be deferred until the design of top-level containers and namespaces is further along.
3. UI support for showing permissions
The bridge ComponentViewPanel provides an opportunity to implement standard mechanisms for showing the permissions on a given object. A small label panel at the bottom of the component could show a combination of:
- Owner name
- Names of users and groups who could read and/or write
- Iconic representations of read/write access (particularly in cases where the current user cannot write), with a mechanism for getting more detail on who can read and write.
The items on the label could also support launching of UIs for changing permissions, if appropriate.
bridge.swing.PermissionsLabel implements this functionality.
4. UI support for modifying permissions
There should be a standard UI for modifying permissions on a given (embedded) object. While the availability of support for DelegatedPermissions will simplify security management, the UI should be less complex than that used in MOOsburg (flexible but tedious), but more functional that in the VS (where security was virtually non-existant).
An approach to this would be to only support read and write permissions, each of which could be set to:
- Same as container (with container name shown in the UI)
- Only me (with me's name)
- Only me and these users: (with field for users)
- Only me and anybody with this password: xxxx (Need to think about this -- generally can't store passwords in Permissions objects. The "anonymous account" idea below is a probably a better alternative. Should find out how teachers are currently handling student accounts.)
- Anybody with a non-guest account
- Anybody, even guests
Top-level containers (e.g., a user's "home" site/directory container) would default to "anybody can read, only I can write", and all subsequently created objects would delegate to their parents.
Issues include:
- Group support. CORK currently has no concept of a user group. MB would allow specification of a list of user ids when setting permissions, but did not allow such sets to be given group names. VS included elaborate group support, but this proved to be a source of considerable administration overhead. Solutions include:
- Allow users to make up their own groups, borrowing from a suitable email client group-building UI. Namespace issues could be resolved by internally prepending the creating user's id to the group id, e.g., "isenhour.role". An email server/proxy could also support these as aliases.
- Just allow the permission delegation mechanism to handle this. A group would be implicitly defined by having permission to read or write a given object, and all contained objects could simply delegate to it.
- Guest accounts. The system should optionally support unauthenticated guest account, for web browsing in particular. Making this explicitly visible in the UI largely takes care of this problem, though we probably want to further restrict permission changes and deletion by guest accounts. These are normally considered degenerate cases of write priviledges since the ability to write generally gives you the ability to delete all content, and the ability to delete all content and copy it into a private object effectively gives you the ability to change permissions. Specifically:
- Guest accounts should only be able to delete objects created by guest accounts
- Objects created by guest accounts should have public (anybody can read/write) access, and permissions should not be modifyable by the guest account
- Anonymous accounts. Maintenance of student accounts in the VS was a source of considerable administrative overhead. Individual student accounts were also problematic in that while the users are transient, the accounts accumulate over time, cluttering the user database. It is difficult to simply delete the accounts, since this risks orphaning objects. Similar problems would exist for a collaborative system used by a community group: it might make sense to create a single "real" account for the group (associated with a known contact person), but creating accounts for every member will be tedious. An alternate approach would be allow "real" accounts to create anonymous accounts that allowed login with any user id and a specified password. Issues include:
- Support for arbitrary user names is useful for the UI, since it allows the "anonymous" user to specify a name for themselves. It would cause problems, however, for the permissions mechanism, which is typically based on user id. A solution would be to append "[realid]" to the end of the arbitrary login string, where realid is the name given to the anonymous account when it was created. The permission creation and checking mechanisms would then have to be updated to extract a real id.
- User id namespace. User ids created in this way would likely have to be prepended with the id of the "real" user that created them, e.g., "msmith.ecology", to avoid conflicts. This has the advantage of giving the permissions mechanism access to enough information to revert ownership to "msmith" if "msmith.ecology" is deleted.
- Since anonymous accounts are identified entirely by password at login, "password space" is also an issue (e.g., if two different real users create anonymous accounts with the same password). Simply checking and preventing duplicate passwords is not a viable option, since it effectively reveals a password when such a conflict is detected. The best solution is likely to simply prepend the real user's user id to the anonymous user's password. Hence msmith could create an anonymous account with id "ecology" and password "frogs". Users could log into this account with any user id that they wanted (preferably one that idenitifies themselves) and password "msmith.frogs". Objects created by the anonymous user would be owned by "msmith.ecology".
- Objects with distinct "append" and "write" modes. Simplifying the permissions scheme to only force the user to set read and write access ignores the fact that some objects may need finer-grained control over write access. For example, in a discussion, it might make sense to allow any user to add to the discussion, but only allow a smaller set of users to modify the discussion setup. If these cases are sufficiently rare, it might be easier to simply separate the restricted and unrestricted parts of the data into separate objects with separate permissions.
5. ComponentView support for disabling modification of unwritable objects
Components which are operating on restricted objects may need to disable parts of their user interfaces or otherwise inform the user of current restrictions. Given that the logic for exactly how this should be done is specific to each kind of component, it is probably not useful to add setEnabled or setEditable-type methods to ComponentView. Instead each ComponentView implementation should be responsible for checking permissions and disabling parts of itself as appropriate.
To simplify this, it would be useful to include utility methods in DefaultComponentView that subclasses could use to easily discover things like basic read/write cabilities.
|