3 * CalDAV Server - handle ACL method
7 * @author Andrew McMillan <andrew@morphoss.com>
8 * @copyright Morphoss Ltd
9 * @license http://gnu.org/copyleft/gpl.html GNU GPL v2
11 dbg_error_log("ACL", "method handler");
13 require_once('DAVResource.php');
15 $request->NeedPrivilege('DAV::write-acl');
17 if ( ! ini_get('open_basedir') && (isset($c->dbg
['ALL']) ||
(isset($c->dbg
['put']) && $c->dbg
['put'])) ) {
18 $fh = fopen('/tmp/MOVE.txt','w');
20 fwrite($fh,$request->raw_post
);
25 $resource = new DAVResource( $request->path
);
29 (DAV:no-ace-conflict): The ACEs submitted in the ACL request MUST NOT
30 conflict with each other. This is a catchall error code indicating
31 that an implementation-specific ACL restriction has been violated.
33 (DAV:no-protected-ace-conflict): The ACEs submitted in the ACL
34 request MUST NOT conflict with the protected ACEs on the resource.
35 For example, if the resource has a protected ACE granting DAV:write
36 to a given principal, then it would not be consistent if the ACL
37 request submitted an ACE denying DAV:write to the same principal.
39 (DAV:no-inherited-ace-conflict): The ACEs submitted in the ACL
40 request MUST NOT conflict with the inherited ACEs on the resource.
41 For example, if the resource inherits an ACE from its parent
42 collection granting DAV:write to a given principal, then it would not
43 be consistent if the ACL request submitted an ACE denying DAV:write
44 to the same principal. Note that reporting of this error will be
45 implementation-dependent. Implementations MUST either report this
46 error or allow the ACE to be set, and then let normal ACE evaluation
47 rules determine whether the new ACE has any impact on the privileges
48 available to a specific principal.
50 (DAV:limited-number-of-aces): The number of ACEs submitted in the ACL
51 request MUST NOT exceed the number of ACEs allowed on that resource.
52 However, ACL-compliant servers MUST support at least one ACE granting
53 privileges to a single principal, and one ACE granting privileges to
56 (DAV:deny-before-grant): All non-inherited deny ACEs MUST precede all
57 non-inherited grant ACEs.
59 (DAV:grant-only): The ACEs submitted in the ACL request MUST NOT
60 include a deny ACE. This precondition applies only when the ACL
61 restrictions of the resource include the DAV:grant-only constraint
62 (defined in Section 5.6.1).
64 (DAV:no-invert): The ACL request MUST NOT include a DAV:invert
65 element. This precondition applies only when the ACL semantics of
66 the resource includes the DAV:no-invert constraint (defined in
69 (DAV:no-abstract): The ACL request MUST NOT attempt to grant or deny
70 an abstract privilege (see Section 5.3).
72 (DAV:not-supported-privilege): The ACEs submitted in the ACL request
73 MUST be supported by the resource.
75 (DAV:missing-required-principal): The result of the ACL request MUST
76 have at least one ACE for each principal identified in a
77 DAV:required-principal XML element in the ACL semantics of that
78 resource (see Section 5.5).
80 (DAV:recognized-principal): Every principal URL in the ACL request
81 MUST identify a principal resource.
83 (DAV:allowed-principal): The principals specified in the ACEs
84 submitted in the ACL request MUST be allowed as principals for the
85 resource. For example, a server where only authenticated principals
86 can access resources would not allow the DAV:all or
87 DAV:unauthenticated principals to be used in an ACE, since these
88 would allow unauthenticated access to resources.
92 $xmltree = BuildXMLTree( $request->xml_tags
, $position);
93 $aces = $xmltree->GetPath("/DAV::acl/*");
95 $grantor = new DAVResource($request->path
);
96 if ( ! $grantor->Exists() ) $request->DoResponse( 404 );
97 if ( ! $grantor->IsCollection() )
98 $request->PreconditionFailed(403,'not-supported-privilege','ACLs are only supported on Principals or Collections');
100 $grantor->NeedPrivilege('write-acl');
102 $cache_delete_list = array();
104 $qry = new AwlQuery('BEGIN');
105 $qry->Exec('ACL',__LINE__
,__FILE__
);
107 function process_ace( $grantor, $by_principal, $by_collection, $ace ) {
108 global $cache_delete_list;
110 $elements = $ace->GetContent();
111 $principal_node = $elements[0];
112 $grant = $elements[1];
113 if ( $principal_node->GetTag() != 'DAV::principal' ) $request->MalformedRequest('ACL request must contain a principal, not '.$principal->GetTag());
114 $grant_tag = $grant->GetTag();
115 if ( $grant_tag == 'DAV::deny' ) $request->PreconditionFailed(403,'grant-only');
116 if ( $grant_tag == 'DAV::invert' ) $request->PreconditionFailed(403,'no-invert');
117 if ( $grant->GetTag() != 'DAV::grant' ) $request->MalformedRequest('ACL request must contain a principal for each ACE');
119 $privilege_names = array();
120 $xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*");
121 foreach( $xml_privs AS $k => $priv ) {
122 $privilege_names[] = $priv->GetTag();
124 $privileges = privilege_to_bits($privilege_names);
126 $principal_content = $principal_node->GetContent();
127 if ( count($principal_content) != 1 ) $request->MalformedRequest('ACL request must contain exactly one principal per ACE');
128 $principal_content = $principal_content[0];
129 switch( $principal_content->GetTag() ) {
130 case 'DAV::property':
131 $principal_property = $principal_content->GetContent();
132 if ( $principal_property[0]->GetTag() != 'DAV::owner' ) $request->PreconditionFailed(403, 'recognized-principal' );
133 if ( privilege_to_bits('all') != $privileges ) {
134 $request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions' );
136 continue; // and then we ignore it, since it's protected
139 case 'DAV::unauthenticated':
140 $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
144 $principal_type = 'href';
145 $grantee = new DAVResource( DeconstructURL($principal_content->GetContent()) );
146 $grantee_id = $grantee->getProperty('principal_id');
147 if ( !$grantee->Exists() ||
!$grantee->IsPrincipal() )
148 $request->PreconditionFailed(403,'recognized-principal', 'Principal "' +
$principal_content->GetContent() +
'" not found.');
149 $sqlparms = array( ':to_principal' => $grantee_id);
150 $where = 'WHERE to_principal=:to_principal AND ';
151 if ( isset($by_principal) ) {
152 $sqlparms[':by_principal'] = $by_principal;
153 $where .= 'by_principal = :by_principal';
156 $sqlparms[':by_collection'] = $by_collection;
157 $where .= 'by_collection = :by_collection';
159 $qry = new AwlQuery('SELECT privileges FROM grants '.$where, $sqlparms);
160 if ( $qry->Exec('ACL',__LINE__
,__FILE__
) && $qry->rows() == 1 && $current = $qry->Fetch() ) {
161 $sql = 'UPDATE grants SET privileges=:privileges::INT::BIT(24) '.$where;
164 $sqlparms[':by_principal'] = $by_principal;
165 $sqlparms[':by_collection'] = $by_collection;
166 $sql = 'INSERT INTO grants (by_principal, by_collection, to_principal, privileges) VALUES(:by_principal, :by_collection, :to_principal, :privileges::INT::BIT(24))';
168 $sqlparms[':privileges'] = $privileges;
169 $qry = new AwlQuery($sql, $sqlparms);
170 if ( $qry->Exec('ACL',__LINE__
,__FILE__
) ) {
171 Principal
::cacheDelete('dav_name',$grantee->dav_name());
172 Principal
::cacheFlush('principal_id IN (SELECT member_id FROM group_member WHERE group_id = ?)', array($grantee_id));
176 case 'DAV::authenticated':
177 $principal_type = 'authenticated';
178 if ( bindec($grantor->GetProperty('default_privileges')) == $privileges ) continue; // There is no change, so skip it
179 $sqlparms = array( ':privileges' => $privileges );
180 if ( isset($by_collection) ) {
181 $sql = 'UPDATE collection SET default_privileges=:privileges::INT::BIT(24) WHERE collection_id=:by_collection';
182 $sqlparms[':by_collection'] = $by_collection;
185 $sql = 'UPDATE principal SET default_privileges=:privileges::INT::BIT(24) WHERE principal_id=:by_principal';
186 $sqlparms[':by_principal'] = $by_principal;
188 $qry = new AwlQuery($sql, $sqlparms);
189 if ( $qry->Exec('ACL',__LINE__
,__FILE__
) ) {
191 * Basically this has changed everyone's permissions now, so...
193 Principal
::cacheFlush('TRUE');
198 // $principal_type = 'all';
199 $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
203 $request->PreconditionFailed(403, 'recognized-principal' );
209 $by_principal = ($grantor->IsPrincipal() ?
$grantor->GetProperty('principal_id') : null);
210 $by_collection = ($grantor->IsPrincipal() ?
null : $grantor->GetProperty('collection_id'));
212 foreach( $aces AS $k => $ace ) {
213 process_ace($grantor, $by_principal, $by_collection, $ace);
216 $qry = new AwlQuery('COMMIT');
217 $qry->Exec('ACL',__LINE__
,__FILE__
);
220 $request->DoResponse( 200 );