mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-01-27 00:33:34 +00:00
220 lines
9.1 KiB
PHP
220 lines
9.1 KiB
PHP
<?php
|
|
/**
|
|
* CalDAV Server - handle ACL method
|
|
*
|
|
* @package davical
|
|
* @subpackage caldav
|
|
* @author Andrew McMillan <andrew@morphoss.com>
|
|
* @copyright Morphoss Ltd
|
|
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
|
*/
|
|
dbg_error_log("ACL", "method handler");
|
|
|
|
require_once('DAVResource.php');
|
|
|
|
$request->NeedPrivilege('DAV::write-acl');
|
|
|
|
if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
|
|
$fh = fopen('/var/log/davical/MOVE.debug','w');
|
|
if ( $fh ) {
|
|
fwrite($fh,$request->raw_post);
|
|
fclose($fh);
|
|
}
|
|
}
|
|
|
|
$resource = new DAVResource( $request->path );
|
|
|
|
/**
|
|
* Preconditions
|
|
(DAV:no-ace-conflict): The ACEs submitted in the ACL request MUST NOT
|
|
conflict with each other. This is a catchall error code indicating
|
|
that an implementation-specific ACL restriction has been violated.
|
|
|
|
(DAV:no-protected-ace-conflict): The ACEs submitted in the ACL
|
|
request MUST NOT conflict with the protected ACEs on the resource.
|
|
For example, if the resource has a protected ACE granting DAV:write
|
|
to a given principal, then it would not be consistent if the ACL
|
|
request submitted an ACE denying DAV:write to the same principal.
|
|
|
|
(DAV:no-inherited-ace-conflict): The ACEs submitted in the ACL
|
|
request MUST NOT conflict with the inherited ACEs on the resource.
|
|
For example, if the resource inherits an ACE from its parent
|
|
collection granting DAV:write to a given principal, then it would not
|
|
be consistent if the ACL request submitted an ACE denying DAV:write
|
|
to the same principal. Note that reporting of this error will be
|
|
implementation-dependent. Implementations MUST either report this
|
|
error or allow the ACE to be set, and then let normal ACE evaluation
|
|
rules determine whether the new ACE has any impact on the privileges
|
|
available to a specific principal.
|
|
|
|
(DAV:limited-number-of-aces): The number of ACEs submitted in the ACL
|
|
request MUST NOT exceed the number of ACEs allowed on that resource.
|
|
However, ACL-compliant servers MUST support at least one ACE granting
|
|
privileges to a single principal, and one ACE granting privileges to
|
|
a group.
|
|
|
|
(DAV:deny-before-grant): All non-inherited deny ACEs MUST precede all
|
|
non-inherited grant ACEs.
|
|
|
|
(DAV:grant-only): The ACEs submitted in the ACL request MUST NOT
|
|
include a deny ACE. This precondition applies only when the ACL
|
|
restrictions of the resource include the DAV:grant-only constraint
|
|
(defined in Section 5.6.1).
|
|
|
|
(DAV:no-invert): The ACL request MUST NOT include a DAV:invert
|
|
element. This precondition applies only when the ACL semantics of
|
|
the resource includes the DAV:no-invert constraint (defined in
|
|
Section 5.6.2).
|
|
|
|
(DAV:no-abstract): The ACL request MUST NOT attempt to grant or deny
|
|
an abstract privilege (see Section 5.3).
|
|
|
|
(DAV:not-supported-privilege): The ACEs submitted in the ACL request
|
|
MUST be supported by the resource.
|
|
|
|
(DAV:missing-required-principal): The result of the ACL request MUST
|
|
have at least one ACE for each principal identified in a
|
|
DAV:required-principal XML element in the ACL semantics of that
|
|
resource (see Section 5.5).
|
|
|
|
(DAV:recognized-principal): Every principal URL in the ACL request
|
|
MUST identify a principal resource.
|
|
|
|
(DAV:allowed-principal): The principals specified in the ACEs
|
|
submitted in the ACL request MUST be allowed as principals for the
|
|
resource. For example, a server where only authenticated principals
|
|
can access resources would not allow the DAV:all or
|
|
DAV:unauthenticated principals to be used in an ACE, since these
|
|
would allow unauthenticated access to resources.
|
|
*/
|
|
|
|
$position = 0;
|
|
$xmltree = BuildXMLTree( $request->xml_tags, $position);
|
|
$aces = $xmltree->GetPath("/DAV::acl/*");
|
|
|
|
$grantor = new DAVResource($request->path);
|
|
if ( ! $grantor->Exists() ) $request->DoResponse( 404 );
|
|
if ( ! $grantor->IsCollection() )
|
|
$request->PreconditionFailed(403,'not-supported-privilege','ACLs are only supported on Principals or Collections');
|
|
|
|
$grantor->NeedPrivilege('write-acl');
|
|
|
|
$cache_delete_list = array();
|
|
|
|
$qry = new AwlQuery('BEGIN');
|
|
$qry->Exec('ACL',__LINE__,__FILE__);
|
|
|
|
function process_ace( $grantor, $by_principal, $by_collection, $ace ) {
|
|
global $cache_delete_list, $request;
|
|
|
|
$elements = $ace->GetContent();
|
|
$principal_node = $elements[0];
|
|
$grant = $elements[1];
|
|
if ( $principal_node->GetNSTag() != 'DAV::principal' ) $request->MalformedRequest('ACL request must contain a principal, not '.$principal->GetNSTag());
|
|
$grant_tag = $grant->GetNSTag();
|
|
if ( $grant_tag == 'DAV::deny' ) $request->PreconditionFailed(403,'grant-only');
|
|
if ( $grant_tag == 'DAV::invert' ) $request->PreconditionFailed(403,'no-invert');
|
|
if ( $grant->GetNSTag() != 'DAV::grant' ) $request->MalformedRequest('ACL request must contain a principal for each ACE');
|
|
|
|
$privilege_names = array();
|
|
$xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*");
|
|
foreach( $xml_privs AS $k => $priv ) {
|
|
$privilege_names[] = $priv->GetNSTag();
|
|
}
|
|
$privileges = privilege_to_bits($privilege_names);
|
|
|
|
$principal_content = $principal_node->GetContent();
|
|
if ( count($principal_content) != 1 ) $request->MalformedRequest('ACL request must contain exactly one principal per ACE');
|
|
$principal_content = $principal_content[0];
|
|
switch( $principal_content->GetNSTag() ) {
|
|
case 'DAV::property':
|
|
$principal_property = $principal_content->GetContent();
|
|
if ( $principal_property[0]->GetNSTag() != 'DAV::owner' ) $request->PreconditionFailed(403, 'recognized-principal' );
|
|
if ( privilege_to_bits('all') != $privileges ) {
|
|
$request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions' );
|
|
}
|
|
break; // and then we ignore it, since it's protected
|
|
|
|
case 'DAV::unauthenticated':
|
|
$request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
|
|
break;
|
|
|
|
case 'DAV::href':
|
|
$principal_type = 'href';
|
|
$grantee = new DAVResource( DeconstructURL($principal_content->GetContent()) );
|
|
$grantee_id = $grantee->getProperty('principal_id');
|
|
if ( !$grantee->Exists() || !$grantee->IsPrincipal() )
|
|
$request->PreconditionFailed(403,'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.');
|
|
$sqlparms = array( ':to_principal' => $grantee_id);
|
|
$where = 'WHERE to_principal=:to_principal AND ';
|
|
if ( isset($by_principal) ) {
|
|
$sqlparms[':by_principal'] = $by_principal;
|
|
$where .= 'by_principal = :by_principal';
|
|
}
|
|
else {
|
|
$sqlparms[':by_collection'] = $by_collection;
|
|
$where .= 'by_collection = :by_collection';
|
|
}
|
|
$qry = new AwlQuery('SELECT privileges FROM grants '.$where, $sqlparms);
|
|
if ( $qry->Exec('ACL',__LINE__,__FILE__) && $qry->rows() == 1 && $current = $qry->Fetch() ) {
|
|
$sql = 'UPDATE grants SET privileges=:privileges::INT::BIT(24) '.$where;
|
|
}
|
|
else {
|
|
$sqlparms[':by_principal'] = $by_principal;
|
|
$sqlparms[':by_collection'] = $by_collection;
|
|
$sql = 'INSERT INTO grants (by_principal, by_collection, to_principal, privileges) VALUES(:by_principal, :by_collection, :to_principal, :privileges::INT::BIT(24))';
|
|
}
|
|
$sqlparms[':privileges'] = $privileges;
|
|
$qry = new AwlQuery($sql, $sqlparms);
|
|
if ( $qry->Exec('ACL',__LINE__,__FILE__) ) {
|
|
Principal::cacheDelete('dav_name',$grantee->dav_name());
|
|
Principal::cacheFlush('principal_id IN (SELECT member_id FROM group_member WHERE group_id = ?)', array($grantee_id));
|
|
}
|
|
break;
|
|
|
|
case 'DAV::authenticated':
|
|
$principal_type = 'authenticated';
|
|
if ( bindec($grantor->GetProperty('default_privileges')) == $privileges ) break; // There is no change, so skip it
|
|
$sqlparms = array( ':privileges' => $privileges );
|
|
if ( isset($by_collection) ) {
|
|
$sql = 'UPDATE collection SET default_privileges=:privileges::INT::BIT(24) WHERE collection_id=:by_collection';
|
|
$sqlparms[':by_collection'] = $by_collection;
|
|
}
|
|
else {
|
|
$sql = 'UPDATE principal SET default_privileges=:privileges::INT::BIT(24) WHERE principal_id=:by_principal';
|
|
$sqlparms[':by_principal'] = $by_principal;
|
|
}
|
|
$qry = new AwlQuery($sql, $sqlparms);
|
|
if ( $qry->Exec('ACL',__LINE__,__FILE__) ) {
|
|
/**
|
|
* Basically this has changed everyone's permissions now, so...
|
|
*/
|
|
Principal::cacheFlush('TRUE');
|
|
}
|
|
break;
|
|
|
|
case 'DAV::all':
|
|
// $principal_type = 'all';
|
|
$request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
|
|
break;
|
|
|
|
default:
|
|
$request->PreconditionFailed(403, 'recognized-principal' );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
$by_principal = ($grantor->IsPrincipal() ? $grantor->GetProperty('principal_id') : null);
|
|
$by_collection = ($grantor->IsPrincipal() ? null : $grantor->GetProperty('collection_id'));
|
|
|
|
foreach( $aces AS $k => $ace ) {
|
|
process_ace($grantor, $by_principal, $by_collection, $ace);
|
|
}
|
|
|
|
$qry = new AwlQuery('COMMIT');
|
|
$qry->Exec('ACL',__LINE__,__FILE__);
|
|
|
|
|
|
$request->DoResponse( 200 );
|