The Permissions system in DAViCal really needs a redesign. This is so for several reasons:
- It's arcane. The abuse of a single relationship type for relationships among a group vs. through a group is confusing for the end user.
- It's inadequate for expressing RFC3744 (DAV Access Control) which is what calendaring clients can potentially use to manipulate the relationships & permissions from outside of the web interface.
- It's inefficient. When we expand privileges, particularly in the calendar-proxy code, the calculation of the expanded privileges takes ludicrous amounts of CPU time.
- It's wrong! Permissions should be set by the owner of a collection and there should be no way to subvert this.
- It's inflexible. We need to be able to set scheduling-specific permissions on inbox & outbox and still let people have private collections next to public ones. A single user might want one collection to be available to the world, another for their team, another for the whole organisation, and a fourth just for personal stuff that even their assistant should not see.
This proposal goes significantly further than the earlier Improved Permissions proposal. In particular this proposal should provide a one to one mapping against the DAV permissions model which should mean that clients developing a UI against that model will work with DAViCal.
In the path to 0.9.8 the new permissions model is mostly implemented. Essentially the permissions are now divided into two parts, from a user perspective:
- Groups - ways of grouping a set of users together.
- Grants - ways of providing access to a user, or a group of users.
Hopefully this much simpler view is easier to use and understand. In addition, 0.9.8 adds a concept of default privileges, so that as well as granting specific privileges to a user or group, you may grant privileges to 'everyone'.
A 'group' is in effect any user, although in a normal installation these will be users who are specially set up to mediate between an individual and a set of permissions. The group (or someone with administrative rights to the group) controls who is a member.
The permissions which can be granted are fine-grained and directly map to the DAV privileges defined in RFC3744, and to the other privileges from CalDAV and so forth. All permissions are stored as a bitmap, so permission operations & tests are much simpler logical AND or OR operations.
While grants can still be applied between users, as with relationships in older DAViCal versions, then can now also be applied to collections, so a user might grant more public rights to one [calendar] collection, while restricting access to another.
Old Design Notes
Database Storage of Privilege Values
The problem: The current storage of data as characters such as 'R' is slow to process.
The solution: Privileges will be converted to a bit mask which may be processed efficiently with bitwise operators.
The problem: The current privileges model requires far too many records to be written for the default case. In general use, people want to set a background level of privilege, and then possibly override it to some extent for a subset of people and not much more than that.
The solution: Each collection will specify it's default permissions. In addition, the user will also specify default permissions for each new collection. Finally, the system administrator may configure a top-level default to apply on creation of a new user.
One starting point would be to apply stronger typing to what are currently called 'Users' in DAViCal. In the DAV specification these beasts are called 'Principals' and RFC3744 has a nice piece of eloquence to describe these (section 2):
- "A principal is a network resource that represents a distinct human or computational actor that initiates access to network resources. Users and groups are represented as principals in many implementations; other types of principals are also possible."
That's an excellent piece of phrasing and although these are really all the same kind of thing we should separate some elements of them out from the way DAViCal does things. Even just doing it at the user interface level will have some decent gains, since there will be a 'Principals' menu option with sub-menu options for 'Users', 'Groups' and 'Resources'. Each of those would have a list of the relevant records which could be edited.
Working in this way it would immediately become clear that something which was a 'Group' doesn't need all of the detail associated with a 'User', and so we can provide alternative presentations of the data to simplify the maintenance.
The current database structure does not contain a 'principal' table, using the user record to represent this. This approach has served well, but is unlikely to be sufficient for a full implementation. As of version 0.9.2 there is already a programming object interface to represent this data internally, and this should be backed by a real database object as well.
A large part of the motivation behind this redesign is to support RFC3744 in a more comprehensive manner than at present. The permissions currently available in DAViCal need extension in some areas to handle the more fine-grained authority described within RFC3744 itself, but also the extensions added in RFC4791 (CalDAV) and in the draft scheduling extensions to caldav.
One thing that needs to be noted is that all permissions are one-way.
Another important option to consider is the retrieval or action of 'VALARM' components. While it is arguably a client-side activity to select which calendars should be considered as worth notification, it remains a useful heuristic for those clients where this is not selectable, to be able to not receive the alarms for "calendars which aren't mine". Possibly the 'alarm set' could be controlled through the web interface, as a server-side implementation of the client-side lack.
Defining the available types of principals, which may be "User", "Group" or "Resource" (not to be confised with the "dav_resource" table covered below.
- principal_type_id SERIAL
- principal_type_desc TEXT
Only needs SELECT access by website.
Providing storage for principal objects.
- principal_id SERIAL
- type_id INT NOT NULL REFERENCES principal_type(principal_type_id)
- user_no NULL ALLOWED REFERENCES usr(user_no)
Allowing identification of group members.
- group_id REFERENCES principal(principal_id)
- member_id REFERENCES principal(principal_id)
All privileges granted to a group are transitive to all members of that group. Not doing it this way originally is at least partly to blame for the administrative complexity of the current mechanism.
Defining the available types of resources (DAV:resourcetype) to which access controls may be applied.
- resource_type_id SERIAL
- dav_resource_type TEXT
- resource_type_desc TEXT
Only needs SELECT access by website. dav_resource_type will be 'principal', 'collection', 'CalDAV:calendar' and so forth.
Basic entity which can be the target of a permissions grant.
- dav_id SERIAL, A unique identifier for this dav_name. Referenced by collection, caldav_data and calendar_item
- dav_name A pathname, of sorts.
- resource_type_id REFERENCES resource_type(resource_type_id)
- owner_id REFERENCES principal(principal_id)
Rules describing the set of privileges granted to access a resource.
- granted_to_id REFERENCES principal(principal_id)
- resource_id REFERENCES resource(dav_id)
- granted_by_id REFERENCES principal(principal_id)
- can_read BOOLEAN
- can_write BOOLEAN
- can_write_properties BOOLEAN
- can_write_content BOOLEAN
- can_unlock BOOLEAN
- can_read_acl BOOLEAN
- can_read_current_user_privilege_set BOOLEAN
- can_write_acl BOOLEAN
- can_bind BOOLEAN
- can_unbind BOOLEAN
- can_read_free_busy BOOLEAN CalDAV:read-free-busy privilege.
Possibly we also need something to indicate DAV:all privilege, or this could be indicated by having all of the above privileges.
The primary operation of permissions is to ascertain whether the logged in user can X the target resource. The X can be any one of the DAV/CalDAV privileges listed above and the logged in user will (always) map to a principal.
That seems simple enough, except that this can happen through a series of intermediate principals meaning that the path from logged in user to target resource could potentially be arbitrarily complex. RFC3744 specifies that "a server may allow a group to be a member of another group" so to simplify things we will limit the number of allowed intermediate steps.