From d718e818dc3d305c1168c74b32fc91201ec40e3f Mon Sep 17 00:00:00 2001 From: Andrew McMillan Date: Mon, 28 Dec 2009 23:40:13 +1300 Subject: [PATCH] Basic support for the ACL method. Working, but needs work. --- inc/caldav-ACL.php | 127 +++++++++++++++++- testing/tests/regression-suite/945-ACL.result | 25 ++++ testing/tests/regression-suite/945-ACL.test | 56 ++++++++ testing/tests/regression-suite/946-ACL.result | 13 ++ testing/tests/regression-suite/946-ACL.test | 41 ++++++ testing/tests/regression-suite/947-ACL.result | 25 ++++ testing/tests/regression-suite/947-ACL.test | 42 ++++++ 7 files changed, 327 insertions(+), 2 deletions(-) create mode 100644 testing/tests/regression-suite/945-ACL.result create mode 100644 testing/tests/regression-suite/945-ACL.test create mode 100644 testing/tests/regression-suite/946-ACL.result create mode 100644 testing/tests/regression-suite/946-ACL.test create mode 100644 testing/tests/regression-suite/947-ACL.result create mode 100644 testing/tests/regression-suite/947-ACL.test diff --git a/inc/caldav-ACL.php b/inc/caldav-ACL.php index 2628e799..75855085 100644 --- a/inc/caldav-ACL.php +++ b/inc/caldav-ACL.php @@ -88,9 +88,132 @@ $resource = new DAVResource( $request->path ); would allow unauthenticated access to resources. */ -$ace = $xmltree->GetPath("/DAV::acl/DAV::ace/*"); +function precondition_failed($precondition, $explanation = '') { + global $request; + $xmldoc = sprintf(' + + <%s/>%s +', $precondition, $explanation ); -foreach( $ace AS $k => $v ) { + $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' ); + exit(0); // Unecessary, but might clarify things } +function malformed_request( $text = 'Bad request' ) { + global $request; + $request->DoResponse( 400, $text ); + exit(0); // Unecessary, but might clarify things +} + + +$position = 0; +$xmltree = BuildXMLTree( $request->xml_tags, $position); +$aces = $xmltree->GetPath("/DAV::acl/*"); + +$grantor = new DAVResource($request->path); +if ( ! $grantor->Exists() ) $request->DoResponse( 404 ); +$by_principal = null; +$by_collection = null; +if ( $grantor->IsPrincipal() ) $by_principal = $grantor->GetProperty('principal_id'); +else if ( $grantor->IsCollection() ) $by_collection = $grantor->GetProperty('collection_id'); +else precondition_failed('not-supported-privilege','ACLs may only be applied to Principals or Collections'); + +$qry = new AwlQuery('BEGIN'); +$qry->Exec('ACL',__FILE__,__LINE__); + +foreach( $aces AS $k => $ace ) { + $elements = $ace->GetContent(); + $principal = $elements[0]; + $grant = $elements[1]; + if ( $principal->GetTag() != 'DAV::principal' ) malformed_request('ACL request must contain a principal, not '.$principal->GetTag()); + $grant_tag = $grant->GetTag(); + if ( $grant_tag == 'DAV::deny' ) precondition_failed('grant-only'); + if ( $grant_tag == 'DAV::invert' ) precondition_failed('no-invert'); + if ( $grant->GetTag() != 'DAV::grant' ) malformed_request('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->GetTag(); + } + $privileges = privilege_to_bits($privilege_names); + + $principal_content = $principal->GetContent(); + if ( count($principal_content) != 1 ) malformed_request('ACL request must contain exactly one principal per ACE'); + $principal_content = $principal_content[0]; + switch( $principal_content->GetTag() ) { + case 'DAV::property': + $principal_property = $principal_content->GetContent(); + if ( $principal_property[0]->GetTag() != 'DAV::owner' ) precondition_failed( 'recognized-principal' ); + if ( privilege_to_bits('all') != $privileges ) { + precondition_failed( 'no-protected-ace-conflict', 'Owner must always have all permissions' ); + } + continue; // and then we ignore it, since it's protected + break; + + case 'DAV::unauthenticated': + precondition_failed( 'allowed-principal', 'May not set privileges for unauthenticated users' ); + break; + + case 'DAV::href': + $principal_type = 'href'; + $principal = new DAVResource( DeconstructURL($principal_content->GetContent()) ); + if ( ! $principal->Exists() || !$principal->IsPrincipal() ) + precondition_failed('recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.'); + $sqlparms = array( ':to_principal' => $principal->GetProperty('principal_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',__FILE__,__LINE__) && $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); + $qry->Exec('ACL',__FILE__,__LINE__); + break; + + case 'DAV::authenticated': + $principal_type = 'authenticated'; + if ( bindec($grantor->GetProperty('default_privileges')) == $privileges ) continue; // 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); + $qry->Exec('ACL',__FILE__,__LINE__); + break; + + case 'DAV::all': +// $principal_type = 'all'; + precondition_failed( 'allowed-principal', 'May not set privileges for unauthenticated users' ); + break; + + default: + precondition_failed( 'recognized-principal' ); + break; + } + +} + +$qry = new AwlQuery('COMMIT'); +$qry->Exec('ACL',__FILE__,__LINE__); + + $request->DoResponse( 200 ); diff --git a/testing/tests/regression-suite/945-ACL.result b/testing/tests/regression-suite/945-ACL.result new file mode 100644 index 00000000..fe451115 --- /dev/null +++ b/testing/tests/regression-suite/945-ACL.result @@ -0,0 +1,25 @@ +HTTP/1.1 200 OK +Date: Dow, 01 Jan 2000 00:00:00 GMT +DAV: 1, 2, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy +Content-Length: 0 +Content-Type: text/plain; charset="utf-8" + + + by_collection: >NULL< + by_principal: >3< + displayname: >User 4< + privileges: >000000001111111011111111< + to_principal: >6< + + by_collection: >NULL< + by_principal: >3< + displayname: >Assistant 1< + privileges: >000000000001001011000111< + to_principal: >10< + + by_collection: >NULL< + by_principal: >3< + displayname: >Team for Client1< + privileges: >000000000001001000000001< + to_principal: >14< + diff --git a/testing/tests/regression-suite/945-ACL.test b/testing/tests/regression-suite/945-ACL.test new file mode 100644 index 00000000..9315d59a --- /dev/null +++ b/testing/tests/regression-suite/945-ACL.test @@ -0,0 +1,56 @@ +# +# ACL setting default privileges on a collection to nothing, and +# specific privileges to include read-acl. +# +TYPE=ACL +URL=http://regression.host/caldav.php/user1/ +HEADER=User-Agent: RFC3744 Spec Tests +HEADER=Content-Type: text/xml; charset="UTF-8" +HEAD + + +BEGINDATA + + + + + + + + + + + + + /caldav.php/user4/ + + + + + + + + + + + + + + + + + + + + + +ENDDATA + +QUERY +SELECT by_principal, by_collection, privileges, p_to.displayname, to_principal + FROM grants JOIN dav_principal p_to ON (to_principal=principal_id) + LEFT JOIN collection ON (by_collection=collection.collection_id) + LEFT JOIN dav_principal p_by ON (by_principal=p_by.principal_id) + WHERE p_by.username = 'user1' +ENDQUERY + diff --git a/testing/tests/regression-suite/946-ACL.result b/testing/tests/regression-suite/946-ACL.result new file mode 100644 index 00000000..682bcc7a --- /dev/null +++ b/testing/tests/regression-suite/946-ACL.result @@ -0,0 +1,13 @@ +HTTP/1.1 200 OK +Date: Dow, 01 Jan 2000 00:00:00 GMT +DAV: 1, 2, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy +Content-Length: 0 +Content-Type: text/plain; charset="utf-8" + + + by_collection: >10< + by_principal: >NULL< + displayname: >User 4< + privileges: >000000000000001000110001< + to_principal: >6< + diff --git a/testing/tests/regression-suite/946-ACL.test b/testing/tests/regression-suite/946-ACL.test new file mode 100644 index 00000000..147dc9e4 --- /dev/null +++ b/testing/tests/regression-suite/946-ACL.test @@ -0,0 +1,41 @@ +# +# ACL setting default privileges on a collection to nothing, and +# specific privileges to include read-acl. +# +TYPE=ACL +URL=http://regression.host/caldav.php/user1/home/ +HEADER=User-Agent: RFC3744 Spec Tests +HEADER=Content-Type: text/xml; charset="UTF-8" +HEAD + + +BEGINDATA + + + + + /caldav.php/user4/ + + + + + + + + + + + + + + + +ENDDATA + +QUERY +SELECT by_principal, by_collection, privileges, p_to.displayname, to_principal + FROM grants JOIN dav_principal p_to ON (to_principal=principal_id) + LEFT JOIN collection ON (by_collection=collection.collection_id) + WHERE collection.dav_name = '/user1/home/' +ENDQUERY + diff --git a/testing/tests/regression-suite/947-ACL.result b/testing/tests/regression-suite/947-ACL.result new file mode 100644 index 00000000..a4e9be3c --- /dev/null +++ b/testing/tests/regression-suite/947-ACL.result @@ -0,0 +1,25 @@ +HTTP/1.1 200 OK +Date: Dow, 01 Jan 2000 00:00:00 GMT +DAV: 1, 2, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy +Content-Length: 0 +Content-Type: text/plain; charset="utf-8" + + + by_collection: >NULL< + by_principal: >3< + displayname: >User 4< + privileges: >000000000001001000100001< + to_principal: >6< + + by_collection: >NULL< + by_principal: >3< + displayname: >Assistant 1< + privileges: >000000000001001011000111< + to_principal: >10< + + by_collection: >NULL< + by_principal: >3< + displayname: >Team for Client1< + privileges: >000000000001001000000001< + to_principal: >14< + diff --git a/testing/tests/regression-suite/947-ACL.test b/testing/tests/regression-suite/947-ACL.test new file mode 100644 index 00000000..b37933d0 --- /dev/null +++ b/testing/tests/regression-suite/947-ACL.test @@ -0,0 +1,42 @@ +# +# ACL setting default privileges on a collection to nothing, and +# specific privileges to include read-acl. +# +TYPE=ACL +URL=http://regression.host/caldav.php/user1/ +HEADER=User-Agent: RFC3744 Spec Tests +HEADER=Content-Type: text/xml; charset="UTF-8" +HEAD + + +BEGINDATA + + + + + /caldav.php/user4/ + + + + + + + + + + + + + + + +ENDDATA + +QUERY +SELECT by_principal, by_collection, privileges, p_to.displayname, to_principal + FROM grants JOIN dav_principal p_to ON (to_principal=principal_id) + LEFT JOIN collection ON (by_collection=collection.collection_id) + LEFT JOIN dav_principal p_by ON (by_principal=p_by.principal_id) + WHERE p_by.dav_name = '/user1/' +ENDQUERY +