| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- <?php
- declare(strict_types=1);
- namespace Sabre\DAV\Sharing;
- use Sabre\DAV\Exception\BadRequest;
- use Sabre\DAV\Exception\Forbidden;
- use Sabre\DAV\INode;
- use Sabre\DAV\PropFind;
- use Sabre\DAV\Server;
- use Sabre\DAV\ServerPlugin;
- use Sabre\DAV\Xml\Element\Sharee;
- use Sabre\DAV\Xml\Property;
- use Sabre\HTTP\RequestInterface;
- use Sabre\HTTP\ResponseInterface;
- /**
- * This plugin implements HTTP requests and properties related to:.
- *
- * draft-pot-webdav-resource-sharing
- *
- * This specification allows people to share webdav resources with others.
- *
- * @copyright Copyright (C) 2007-2015 fruux GmbH. (https://fruux.com/)
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
- class Plugin extends ServerPlugin
- {
- const ACCESS_NOTSHARED = 0;
- const ACCESS_SHAREDOWNER = 1;
- const ACCESS_READ = 2;
- const ACCESS_READWRITE = 3;
- const ACCESS_NOACCESS = 4;
- const INVITE_NORESPONSE = 1;
- const INVITE_ACCEPTED = 2;
- const INVITE_DECLINED = 3;
- const INVITE_INVALID = 4;
- /**
- * Reference to SabreDAV server object.
- *
- * @var Server
- */
- protected $server;
- /**
- * This method should return a list of server-features.
- *
- * This is for example 'versioning' and is added to the DAV: header
- * in an OPTIONS response.
- *
- * @return array
- */
- public function getFeatures()
- {
- return ['resource-sharing'];
- }
- /**
- * Returns a plugin name.
- *
- * Using this name other plugins will be able to access other plugins
- * using \Sabre\DAV\Server::getPlugin
- *
- * @return string
- */
- public function getPluginName()
- {
- return 'sharing';
- }
- /**
- * This initializes the plugin.
- *
- * This function is called by Sabre\DAV\Server, after
- * addPlugin is called.
- *
- * This method should set up the required event subscriptions.
- */
- public function initialize(Server $server)
- {
- $this->server = $server;
- $server->xml->elementMap['{DAV:}share-resource'] = 'Sabre\\DAV\\Xml\\Request\\ShareResource';
- array_push(
- $server->protectedProperties,
- '{DAV:}share-mode'
- );
- $server->on('method:POST', [$this, 'httpPost']);
- $server->on('propFind', [$this, 'propFind']);
- $server->on('getSupportedPrivilegeSet', [$this, 'getSupportedPrivilegeSet']);
- $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']);
- $server->on('onBrowserPostAction', [$this, 'browserPostAction']);
- }
- /**
- * Updates the list of sharees on a shared resource.
- *
- * The sharees array is a list of people that are to be added modified
- * or removed in the list of shares.
- *
- * @param string $path
- * @param Sharee[] $sharees
- */
- public function shareResource($path, array $sharees)
- {
- $node = $this->server->tree->getNodeForPath($path);
- if (!$node instanceof ISharedNode) {
- throw new Forbidden('Sharing is not allowed on this node');
- }
- // Getting ACL info
- $acl = $this->server->getPlugin('acl');
- // If there's no ACL support, we allow everything
- if ($acl) {
- $acl->checkPrivileges($path, '{DAV:}share');
- }
- foreach ($sharees as $sharee) {
- // We're going to attempt to get a local principal uri for a share
- // href by emitting the getPrincipalByUri event.
- $principal = null;
- $this->server->emit('getPrincipalByUri', [$sharee->href, &$principal]);
- $sharee->principal = $principal;
- }
- $node->updateInvites($sharees);
- }
- /**
- * This event is triggered when properties are requested for nodes.
- *
- * This allows us to inject any sharings-specific properties.
- */
- public function propFind(PropFind $propFind, INode $node)
- {
- if ($node instanceof ISharedNode) {
- $propFind->handle('{DAV:}share-access', function () use ($node) {
- return new Property\ShareAccess($node->getShareAccess());
- });
- $propFind->handle('{DAV:}invite', function () use ($node) {
- return new Property\Invite($node->getInvites());
- });
- $propFind->handle('{DAV:}share-resource-uri', function () use ($node) {
- return new Property\Href($node->getShareResourceUri());
- });
- }
- }
- /**
- * We intercept this to handle POST requests on shared resources.
- *
- * @return bool|null
- */
- public function httpPost(RequestInterface $request, ResponseInterface $response)
- {
- $path = $request->getPath();
- $contentType = $request->getHeader('Content-Type');
- if (null === $contentType) {
- return;
- }
- // We're only interested in the davsharing content type.
- if (false === strpos($contentType, 'application/davsharing+xml')) {
- return;
- }
- $message = $this->server->xml->parse(
- $request->getBody(),
- $request->getUrl(),
- $documentType
- );
- switch ($documentType) {
- case '{DAV:}share-resource':
- $this->shareResource($path, $message->sharees);
- $response->setStatus(200);
- // Adding this because sending a response body may cause issues,
- // and I wanted some type of indicator the response was handled.
- $response->setHeader('X-Sabre-Status', 'everything-went-well');
- // Breaking the event chain
- return false;
- default:
- throw new BadRequest('Unexpected document type: '.$documentType.' for this Content-Type');
- }
- }
- /**
- * This method is triggered whenever a subsystem reqeuests the privileges
- * hat are supported on a particular node.
- *
- * We need to add a number of privileges for scheduling purposes.
- */
- public function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet)
- {
- if ($node instanceof ISharedNode) {
- $supportedPrivilegeSet['{DAV:}share'] = [
- 'abstract' => false,
- 'aggregates' => [],
- ];
- }
- }
- /**
- * Returns a bunch of meta-data about the plugin.
- *
- * Providing this information is optional, and is mainly displayed by the
- * Browser plugin.
- *
- * The description key in the returned array may contain html and will not
- * be sanitized.
- *
- * @return array
- */
- public function getPluginInfo()
- {
- return [
- 'name' => $this->getPluginName(),
- 'description' => 'This plugin implements WebDAV resource sharing',
- 'link' => 'https://github.com/evert/webdav-sharing',
- ];
- }
- /**
- * This method is used to generate HTML output for the
- * DAV\Browser\Plugin.
- *
- * @param string $output
- * @param string $path
- *
- * @return bool|null
- */
- public function htmlActionsPanel(INode $node, &$output, $path)
- {
- if (!$node instanceof ISharedNode) {
- return;
- }
- $aclPlugin = $this->server->getPlugin('acl');
- if ($aclPlugin) {
- if (!$aclPlugin->checkPrivileges($path, '{DAV:}share', \Sabre\DAVACL\Plugin::R_PARENT, false)) {
- // Sharing is not permitted, we will not draw this interface.
- return;
- }
- }
- $output .= '<tr><td colspan="2"><form method="post" action="">
- <h3>Share this resource</h3>
- <input type="hidden" name="sabreAction" value="share" />
- <label>Share with (uri):</label> <input type="text" name="href" placeholder="mailto:user@example.org"/><br />
- <label>Access</label>
- <select name="access">
- <option value="readwrite">Read-write</option>
- <option value="read">Read-only</option>
- <option value="no-access">Revoke access</option>
- </select><br />
- <input type="submit" value="share" />
- </form>
- </td></tr>';
- }
- /**
- * This method is triggered for POST actions generated by the browser
- * plugin.
- *
- * @param string $path
- * @param string $action
- * @param array $postVars
- */
- public function browserPostAction($path, $action, $postVars)
- {
- if ('share' !== $action) {
- return;
- }
- if (empty($postVars['href'])) {
- throw new BadRequest('The "href" POST parameter is required');
- }
- if (empty($postVars['access'])) {
- throw new BadRequest('The "access" POST parameter is required');
- }
- $accessMap = [
- 'readwrite' => self::ACCESS_READWRITE,
- 'read' => self::ACCESS_READ,
- 'no-access' => self::ACCESS_NOACCESS,
- ];
- if (!isset($accessMap[$postVars['access']])) {
- throw new BadRequest('The "access" POST must be readwrite, read or no-access');
- }
- $sharee = new Sharee([
- 'href' => $postVars['href'],
- 'access' => $accessMap[$postVars['access']],
- ]);
- $this->shareResource(
- $path,
- [$sharee]
- );
- return false;
- }
- }
|