Skip to content

Commit

Permalink
Support for acl-principal-prop-set REPORT.
Browse files Browse the repository at this point in the history
Fixes #599.
  • Loading branch information
evert committed Apr 21, 2016
1 parent 836f2d2 commit b64b965
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ ChangeLog
* Functionality from `IShareableCalendar` is merged into `ISharedCalendar`.
* #751: Fixed XML responses from failing `MKCOL` requests.
* #600: Support for `principal-match` ACL `REPORT`.
* #599: Support for `acl-principal-prop-set` ACL `REPORT`.


3.1.3 (2016-04-06)
Expand Down
100 changes: 82 additions & 18 deletions lib/DAVACL/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,7 @@ function initialize(DAV\Server $server) {
// class.
$server->xml->elementMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Xml\\Property\\Href';
$server->xml->elementMap['{DAV:}acl'] = 'Sabre\\DAVACL\\Xml\\Property\\Acl';
$server->xml->elementMap['{DAV:}acl-principal-prop-set'] = 'Sabre\\DAVACL\\Xml\\Request\\AclPrincipalPropSetReport';
$server->xml->elementMap['{DAV:}expand-property'] = 'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport';
$server->xml->elementMap['{DAV:}principal-property-search'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport';
$server->xml->elementMap['{DAV:}principal-search-property-set'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport';
Expand Down Expand Up @@ -1137,19 +1138,23 @@ function report($reportName, $report, $path) {

case '{DAV:}principal-property-search' :
$this->server->transactionType = 'report-principal-property-search';
$this->principalPropertySearchReport($report);
$this->principalPropertySearchReport($path, $report);
return false;
case '{DAV:}principal-search-property-set' :
$this->server->transactionType = 'report-principal-search-property-set';
$this->principalSearchPropertySetReport($report);
$this->principalSearchPropertySetReport($path, $report);
return false;
case '{DAV:}expand-property' :
$this->server->transactionType = 'report-expand-property';
$this->expandPropertyReport($report);
$this->expandPropertyReport($path, $report);
return false;
case '{DAV:}principal-match' :
$this->server->transactionType = 'report-principal-match';
$this->principalMatchReport($report);
$this->principalMatchReport($path, $report);
return false;
case '{DAV:}acl-principal-prop-set' :
$this->server->transactionType = 'acl-principal-prop-set';
$this->aclPrincipalPropSetReport($path, $report);
return false;

}
Expand Down Expand Up @@ -1252,17 +1257,17 @@ function httpAcl(RequestInterface $request, ResponseInterface $response) {
* or a principal URL, the principal URL and principal URLs of groups that
* principal belongs to.
*
* @param string $path
* @param Xml\Request\PrincipalMatchReport $report
* @return void
*/
protected function principalMatchReport(Xml\Request\PrincipalMatchReport $report) {
protected function principalMatchReport($path, Xml\Request\PrincipalMatchReport $report) {

$depth = $this->server->getHTTPDepth(0);
if ($depth !== 0) {
throw new BadRequest('The principal-match report is only defined on Depth: 0');
}

$requestUri = $this->server->getRequestUri();
$currentPrincipals = $this->getCurrentUserPrincipals();

$result = [];
Expand All @@ -1273,7 +1278,7 @@ protected function principalMatchReport(Xml\Request\PrincipalMatchReport $report
// current principal.
foreach ($currentPrincipals as $currentPrincipal) {

if ($currentPrincipal === $requestUri || strpos($currentPrincipal, $requestUri . '/') === 0) {
if ($currentPrincipal === $path || strpos($currentPrincipal, $path . '/') === 0) {
$result[] = $currentPrincipal;
}

Expand All @@ -1284,7 +1289,7 @@ protected function principalMatchReport(Xml\Request\PrincipalMatchReport $report
// We need to find all resources that have a property that matches
// one of the current principals.
$candidates = $this->server->getPropertiesForPath(
$requestUri,
$path,
[$report->principalProperty],
1
);
Expand All @@ -1302,7 +1307,7 @@ protected function principalMatchReport(Xml\Request\PrincipalMatchReport $report
}

foreach ($hrefs->getHrefs() as $href) {
if (in_array(trim($href,'/'), $currentPrincipals)) {
if (in_array(trim($href, '/'), $currentPrincipals)) {
$result[] = $candidate['href'];
continue 2;
}
Expand Down Expand Up @@ -1359,15 +1364,15 @@ protected function principalMatchReport(Xml\Request\PrincipalMatchReport $report
* Other rfc's, such as ACL rely on this report, so it made sense to put
* it in this plugin.
*
* @param string $path
* @param Xml\Request\ExpandPropertyReport $report
* @return void
*/
protected function expandPropertyReport($report) {
protected function expandPropertyReport($path, $report) {

$depth = $this->server->getHTTPDepth(0);
$requestUri = $this->server->getRequestUri();

$result = $this->expandProperties($requestUri, $report->properties, $depth);
$result = $this->expandProperties($path, $report->properties, $depth);

$xml = $this->server->xml->write(
'{DAV:}multistatus',
Expand Down Expand Up @@ -1441,10 +1446,11 @@ protected function expandProperties($path, array $requestedProperties, $depth) {
* of properties the client may search on, using the
* {DAV:}principal-property-search report.
*
* @param string $path
* @param Xml\Request\PrincipalSearchPropertySetReport $report
* @return void
*/
protected function principalSearchPropertySetReport($report) {
protected function principalSearchPropertySetReport($path, $report) {

$httpDepth = $this->server->getHTTPDepth(0);
if ($httpDepth !== 0) {
Expand Down Expand Up @@ -1495,22 +1501,22 @@ protected function principalSearchPropertySetReport($report) {
* clients to search for groups of principals, based on the value of one
* or more properties.
*
* @param string $path
* @param Xml\Request\PrincipalPropertySearchReport $report
* @return void
*/
protected function principalPropertySearchReport($report) {
protected function principalPropertySearchReport($path, Xml\Request\PrincipalPropertySearchReport $report) {

$uri = null;
if (!$report->applyToPrincipalCollectionSet) {
$uri = $this->server->httpRequest->getPath();
if ($report->applyToPrincipalCollectionSet) {
$path = null;
}
if ($this->server->getHttpDepth('0') !== 0) {
throw new BadRequest('Depth must be 0');
}
$result = $this->principalSearch(
$report->searchProperties,
$report->properties,
$uri,
$path,
$report->test
);

Expand All @@ -1523,6 +1529,64 @@ protected function principalPropertySearchReport($report) {

}

/**
* aclPrincipalPropSet REPORT
*
* This method is responsible for handling the {DAV:}acl-principal-prop-set
* REPORT, as defined in:
*
* https://tools.ietf.org/html/rfc3744#section-9.2
*
* This REPORT allows a user to quickly fetch information about all
* principals specified in the access control list. Most commonly this
* is used to for example generate a UI with ACL rules, allowing you
* to show names for principals for every entry.
*
* @param string $path
* @param Xml\Request\AclPrincipalPropSetReport $report
* @return void
*/
protected function aclPrincipalPropSetReport($path, Xml\Request\AclPrincipalPropSetReport $report) {

if ($this->server->getHTTPDepth(0) !== 0) {
throw new BadRequest('The {DAV:}acl-principal-prop-set REPORT only supports Depth 0');
}

// Fetching ACL rules for the given path. We're using the property
// API and not the local getACL, because it will ensure that all
// business rules and restrictions are applied.
$acl = $this->server->getProperties($path, '{DAV:}acl');

if (!$acl || !isset($acl['{DAV:}acl'])) {
throw new Forbidden('Could not fetch ACL rules for this path');
}

$principals = [];
foreach ($acl['{DAV:}acl']->getPrivileges() as $ace) {

if ($ace['principal'][0] === '{') {
// It's not a principal, it's one of the special rules such as {DAV:}authenticated
continue;
}

$principals[] = $ace['principal'];

}

$properties = $this->server->getPropertiesForMultiplePaths(
$principals,
$report->properties
);

$this->server->httpResponse->setStatus(207);
$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
$this->server->httpResponse->setBody(
$this->server->generateMultiStatus($properties)
);

}


/* }}} */

/**
Expand Down
69 changes: 69 additions & 0 deletions tests/Sabre/DAVACL/AclPrincipalPropSetReportTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Sabre\DAVACL;

use Sabre\HTTP\Request;

class AclPrincipalPropSetReportTest extends \Sabre\DAVServerTest {

public $setupACL = true;
public $autoLogin = 'admin';

function testReport() {

$xml = <<<XML
<?xml version="1.0"?>
<acl-principal-prop-set xmlns="DAV:">
<prop>
<principal-URL />
<displayname />
</prop>
</acl-principal-prop-set>
XML;

$request = new Request('REPORT', '/principals/user1', ['Content-Type' => 'application/xml', 'Depth' => 0]);
$request->setBody($xml);

$response = $this->request($request, 207);

$expected = <<<XML
<?xml version="1.0"?>
<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
<d:response>
<d:href>/principals/admin/</d:href>
<d:propstat>
<d:prop>
<d:principal-URL><d:href>/principals/admin/</d:href></d:principal-URL>
<d:displayname>Admin</d:displayname>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
</d:multistatus>
XML;

$this->assertXmlStringEqualsXmlString(
$expected,
$response->getBodyAsString()
);

}

function testReportDepth1() {

$xml = <<<XML
<?xml version="1.0"?>
<acl-principal-prop-set xmlns="DAV:">
<principal-URL />
<displayname />
</acl-principal-prop-set>
XML;

$request = new Request('REPORT', '/principals/user1', ['Content-Type' => 'application/xml', 'Depth' => 1]);
$request->setBody($xml);

$this->request($request, 400);

}

}
4 changes: 2 additions & 2 deletions tests/Sabre/DAVACL/PrincipalMatchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function testPrincipalMatchProp() {
<d:href>/principals/user1/</d:href>
<d:propstat>
<d:prop>
<d:resourcetype><d:collection/><d:principal/></d:resourcetype>
<d:resourcetype><d:principal/></d:resourcetype>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
Expand Down Expand Up @@ -106,7 +106,7 @@ function testPrincipalMatchPrincipalProperty() {
<d:href>/principals/user1/</d:href>
<d:propstat>
<d:prop>
<d:resourcetype><d:collection/><d:principal/></d:resourcetype>
<d:resourcetype><d:principal/></d:resourcetype>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
Expand Down
6 changes: 5 additions & 1 deletion tests/Sabre/DAVServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,14 @@ function setUpTree() {
);
}

if ($this->setupCardDAV || $this->setupCalDAV || $this->setupACL) {
if ($this->setupCalDAV) {
$this->tree[] = new CalDAV\Principal\Collection(
$this->principalBackend
);
} elseif ($this->setupCardDAV || $this->setupACL) {
$this->tree[] = new DAVACL\PrincipalCollection(
$this->principalBackend
);
}
if ($this->setupFiles) {

Expand Down

0 comments on commit b64b965

Please sign in to comment.