Upgrading from SabreDAV 3.1 to 3.2
sabre/dav 3.2 has been released. sabre/dav 3.2 is a new major version, and has several new features. To accomodate those features, a number of backwards-compability breaks were needed.
This document is split in three segments. First the new features, then a list of changes as they relate to CalDAV and CardDAV clients, and lastly a list of changes for users who deployed sabre/dav themselves (so server admins or developers using sabre/dav as a dependency).
New features
- Calendar sharing. You can now share a calendar read-only or read-write to a different user on the system with the default PDO backends.
- We now have full support for the PostgreSQL database and we will continue to support it for bug fixes and future migrations.
- Support for PSR-3 for logging. We're now emitting a few logging messages, but we'll increase this in the future. Support for this standard means that you can use PSR-3-compatible tools such as Monolog to get debugging information and errors out of sabre/dav.
- We're now supporting draft-pot-webdav-resource-sharing-03, which is an upcoming standard for sharing calendars, addressbooks and collections. Currently this is only implemented for calendars.
- We added
Sabre\CalDAV\Backend\SimplePDO
. This is a super simple absolutely minimalist CalDAV backend that uses PDO as storage. The reason this was added is because it can serve as a better demonstration now that the 'standard' PDO backend gets more and more features that are technically optional, such as scheduling, subscriptions, sharing, webdav sync and others. - Added
Sabre\DAVACL\ACLTrait
. If you write your own node classes that need to support ACL, this trait might reduce the boilerplate you need. - ICSExportPlugin now emits a
Content-Disposition
HTTP header so users get a more useful default filename when they save the result of the export. - Added support for Unauthenticated access to the server, if the ACL plugin is
enabled. This allows you to assign a privilege to the 'public'. If you for
example assign a "read" privilege to
{DAV:}all
or{DAV:}unauthenticated
this will allow a user to read the contents of a node without being authenticated. This is turned on by default. - Support for the
principal-match
andacl-principal-prop-set
REPORT requests from RFC3744.
For a full list of changes, consult the changelog.
Changes for CalDAV and CardDAV clients
We've made some pretty big changes to the iCalendar and vCard validation systems. We've detailed these changes in a separate blogpost. Read the whole thing here:
http://sabre.io/blog/2016/validation-changes/
API Changes and other BC breaks
Database changes
The database model changes to accomodate calendar sharing. If you use the
standard Sabre\CalDAV\Backend\PDO
you must run the database migration script.
The migration script is a command-line script that's in the bin/
directory
of sabredav. The migration script supports both Sqlite and MySQL. To get more
information on how to run it, just run:
php migrationto32.php
This will give you a help message with more information on how to invoke the script.
The Href
class has changed
If you were using custom properties, and in particular if you made use of the
Sabre\DAV\Xml\Property\Href
class, this class has now changed a bit.
In the past this class was used for two purposes:
- To generate relative URIs to paths that are on the server. For instance,
you might have specified
principals/userA
. - To generate absolute URIs, such as
http://example.org/
ormailto:foo@example.org
.
To differentiate between the two, you had to specify a second boolean argument to the property to specify that it was to be treated as a absolute path, so:
new Href('principals/userA');
new Href('http://example.org/', true);
new Href('mailto:foo@example.org', true);
This was quite confusing in a number of ways. The biggest one was that the first example actually has different encoding rules than the second and third.
In the case of the first we automatically prepend a base url, but we also assumed that it was a raw local path and we still need to percent-encode everything.
So to bring some sanity into all this, there are now two classes. To specify the same three hrefs, you now use:
new LocalHref('principals/userA');
new Href('http://example.org/');
new Href('mailto:foo@example.org');
Each of these will still result in a <d:href>..</d:href>
xml property, but
only the first is treated as a 'local sabredav path' that we will urlencode
and add the base url to. The Href
class is now strictly for URIs that target
something outside of the sabre/dav system and we will not touch the string at
all.
Corrected several ACL privileges related to calendaring scheduling
We were not using all the correct ACL privileges when doing scheduling. This has been corrected in 3.2. If you had custom ACL rules that affected CalDAV scheduling, be aware that the exact privilege definitions may have changed a bit.
We now use all the following CalDAV ACL privileges:
- schedule-deliver
- schedule-deliver-invite
- schedule-deliver-reply
- schedule-query-freebusy
- schedule-send
- schedule-send-invite
- schedule-send-reply
- schedule-send-freebusy
Many other ACL rules have been simplified
Many places now have the {DAV:}all
privilege instead of separate
{DAV:}read
and {DAV:}write
privileges.
If you request {DAV:}current-user-privilege-set
you should still see a
similar list of privileges though.
Support for unauthenticated access
By default it's now possible to specify ACL rules that allow someone who is not authenticated to perform actions on the server.
To do this, simply return a rule from getACL
from your node and instead of
a principal url, you return the string {DAV:}unauthenticated
or {DAV:}all
.
To support this new feature the server's behavior had to be changed pretty radically. In particular, before 3.1 it would work as follows:
- If the authentication plugin is enabled, check authentication headers.
- If auth headers were incorrect or not specified, immediately deny access
and return a
401
status. - Later in the request, do an access control check and return a
403
header if access was denied.
From 3.2 the behavior is as follows:
- At the start of the request, see if authentication headers were set. If they were, attempt to authenticate the user but don't deny the user if they aren't set or incorrect.
- Proceed with the request as normal. Continue until the ACL plugin needs to validate a privilege.
- If the current (authenticated or not authenticated) user did not have
access to the a specific required privilege, stop the request and:
- Return a
401
header if the user was not authenticated. - Return a
403
header if the user was authenticated.
- Return a
This radically changes the security model, as we actually let requests through, even if users are not authenticated correctly. This will behave correctly for sabre/dav internals because access control is checked everywhere.
However, if you wrote a custom plugin, and this plugin allows the user to do a dangerous operation and you're not integrating with the ACL plugin, you are creating a serious security hole.
Ideally you fix your plugin to leverage ACL, but there are two other workarounds:
- If you disable the ACL plugin, authentication will behave as before. We immediately deny the user if they did not authenticate. Disabling ACL could create other security and privilege escalation problems, so be aware.
- You can set the
$allowUnauthenticatedAccess
property to false. Setting it to false means you go back to the 3.1 ACL / Auth behavior, but you lose the public access ability.
Removed $allowAccessToNodesWithoutACL
This setting allowed you to control in the ACL plugin how it should behave
for nodes in the tree that did not support ACL. By default it was set to
true, but if it were set to false then the default would be to not allow
access to system-wide nodes that did not implement Sabre\DAVACL\IACL
.
This feature has now been replaced by a 'Default ACL' rule. By default,
similar to the old behavior, we assign the {DAV:}all
privilege to
{DAV:}authenticated
.
To mimic setting $allowAccessToNodesWithoutACL
to false, as it was possible
in the 3.1, you can now just call:
$aclPlugin->setDefaultACL([]);
But because of the nature of this new feature, it's now also possible to specify much more fine-grained permissions for these situations. You might for example choose to only give read access to these nodes, which makes a lot of sense for a lot of standard caldav/carddav servers.
No longer a direct database upgrade path from version 1.6
We're no longer maintaining the migration script from sabre/dav version 1.6 and older. People running that sabre/dav version had 3.5 years to upgrade, so this seems like enough time.
Calendar sharing has gone under heavy modifications
The internal API for calendar sharing has changed. This is important if you wrote your own CalDAV backend and added sharing support for it.
The changes for this have been detailed in a separate blog post, as they were a bit too big to be included in this document.
PDO CalDAV backend has changed the structure of calendar ids.
Note that if this affects you, your code was already broken to begin with. From this point onwards the PDO backend will now return multiple numbers as an array for calendar ids. This is to uniquely identify specific instances of shared calendars.
So if you integrated directly with the PDO backends and somewhere you assumed
that the 'id' returned from getCalendarsForUser
was always a number, it is
now an array. But ideally you should just treat this field as an opaque
identifier and don't assign any meaning to it.