Skip to content

Commit e2cfcd9

Browse files
committed
Allow storage wrappers to through a forbidden exception with retry information
1 parent 705d208 commit e2cfcd9

File tree

12 files changed

+270
-5
lines changed

12 files changed

+270
-5
lines changed

apps/dav/lib/connector/sabre/directory.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
*/
2929
namespace OCA\DAV\Connector\Sabre;
3030

31+
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
3132
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
3233
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
34+
use OCP\Files\ForbiddenException;
3335
use OCP\Lock\ILockingProvider;
3436
use OCP\Lock\LockedException;
3537
use Sabre\DAV\Exception\Locked;
@@ -117,6 +119,8 @@ public function createFile($name, $data = null) {
117119
throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
118120
} catch (\OCP\Files\InvalidPathException $ex) {
119121
throw new InvalidPath($ex->getMessage());
122+
} catch (ForbiddenException $ex) {
123+
throw new Forbidden($ex->getMessage(), $ex->getRetry());
120124
} catch (LockedException $e) {
121125
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
122126
}
@@ -146,6 +150,8 @@ public function createDirectory($name) {
146150
throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
147151
} catch (\OCP\Files\InvalidPathException $ex) {
148152
throw new InvalidPath($ex->getMessage());
153+
} catch (ForbiddenException $ex) {
154+
throw new Forbidden($ex->getMessage(), $ex->getRetry());
149155
} catch (LockedException $e) {
150156
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
151157
}
@@ -247,6 +253,8 @@ public function delete() {
247253
// assume it wasn't possible to remove due to permission issue
248254
throw new \Sabre\DAV\Exception\Forbidden();
249255
}
256+
} catch (ForbiddenException $ex) {
257+
throw new Forbidden($ex->getMessage(), $ex->getRetry());
250258
} catch (LockedException $e) {
251259
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
252260
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* @author Joas Schilling <nickvergessen@owncloud.com>
4+
*
5+
* @copyright Copyright (c) 2015, ownCloud, Inc.
6+
* @license AGPL-3.0
7+
*
8+
* This code is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License, version 3,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License, version 3,
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>
19+
*
20+
*/
21+
22+
namespace OCA\DAV\Connector\Sabre\Exception;
23+
24+
class Forbidden extends \Sabre\DAV\Exception\Forbidden {
25+
26+
const NS_OWNCLOUD = 'http://owncloud.org/ns';
27+
28+
/**
29+
* @var bool
30+
*/
31+
private $retry;
32+
33+
/**
34+
* @param string $message
35+
* @param bool $retry
36+
* @param \Exception $previous
37+
*/
38+
public function __construct($message, $retry = false, \Exception $previous = null) {
39+
parent::__construct($message, 0, $previous);
40+
$this->retry = $retry;
41+
}
42+
43+
/**
44+
* This method allows the exception to include additional information
45+
* into the WebDAV error response
46+
*
47+
* @param \Sabre\DAV\Server $server
48+
* @param \DOMElement $errorNode
49+
* @return void
50+
*/
51+
public function serialize(\Sabre\DAV\Server $server,\DOMElement $errorNode) {
52+
53+
// set ownCloud namespace
54+
$errorNode->setAttribute('xmlns:o', self::NS_OWNCLOUD);
55+
56+
// adding the retry node
57+
$error = $errorNode->ownerDocument->createElementNS('o:','o:retry', var_export($this->retry, true));
58+
$errorNode->appendChild($error);
59+
60+
// adding the message node
61+
$error = $errorNode->ownerDocument->createElementNS('o:','o:reason', $this->getMessage());
62+
$errorNode->appendChild($error);
63+
}
64+
}

apps/dav/lib/connector/sabre/file.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@
3535
use OC\Files\Filesystem;
3636
use OCA\DAV\Connector\Sabre\Exception\EntityTooLarge;
3737
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
38+
use OCA\DAV\Connector\Sabre\Exception\Forbidden as DAVForbiddenException;
3839
use OCA\DAV\Connector\Sabre\Exception\UnsupportedMediaType;
3940
use OCP\Encryption\Exceptions\GenericEncryptionException;
4041
use OCP\Files\EntityTooLargeException;
42+
use OCP\Files\ForbiddenException;
4143
use OCP\Files\InvalidContentException;
4244
use OCP\Files\InvalidPathException;
4345
use OCP\Files\LockNotAcquiredException;
@@ -175,6 +177,8 @@ public function put($data) {
175177
\OCP\Util::writeLog('webdav', 'renaming part file to final file failed', \OCP\Util::ERROR);
176178
throw new Exception('Could not rename part file to final file');
177179
}
180+
} catch (ForbiddenException $ex) {
181+
throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
178182
} catch (\Exception $e) {
179183
$partStorage->unlink($internalPartPath);
180184
$this->convertToSabreException($e);
@@ -273,6 +277,8 @@ public function get() {
273277
throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
274278
} catch (StorageNotAvailableException $e) {
275279
throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
280+
} catch (ForbiddenException $ex) {
281+
throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
276282
} catch (LockedException $e) {
277283
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
278284
}
@@ -296,6 +302,8 @@ public function delete() {
296302
}
297303
} catch (StorageNotAvailableException $e) {
298304
throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
305+
} catch (ForbiddenException $ex) {
306+
throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
299307
} catch (LockedException $e) {
300308
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
301309
}
@@ -474,6 +482,10 @@ private function convertToSabreException(\Exception $e) {
474482
// a more general case - due to whatever reason the content could not be written
475483
throw new Forbidden($e->getMessage(), 0, $e);
476484
}
485+
if ($e instanceof ForbiddenException) {
486+
// the path for the file was forbidden
487+
throw new DAVForbiddenException($e->getMessage(), $e->getRetry(), $e);
488+
}
477489
if ($e instanceof EntityTooLargeException) {
478490
// the file is too big to be stored
479491
throw new EntityTooLarge($e->getMessage(), 0, $e);

apps/dav/lib/connector/sabre/objecttree.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525

2626
namespace OCA\DAV\Connector\Sabre;
2727

28+
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
2829
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
2930
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
3031
use OC\Files\FileInfo;
3132
use OC\Files\Mount\MoveableMount;
33+
use OCP\Files\ForbiddenException;
3234
use OCP\Files\StorageInvalidException;
3335
use OCP\Files\StorageNotAvailableException;
3436
use OCP\Lock\LockedException;
@@ -235,6 +237,8 @@ public function move($sourcePath, $destinationPath) {
235237
}
236238
} catch (StorageNotAvailableException $e) {
237239
throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
240+
} catch (ForbiddenException $ex) {
241+
throw new Forbidden($ex->getMessage(), $ex->getRetry());
238242
} catch (LockedException $e) {
239243
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
240244
}
@@ -274,6 +278,8 @@ public function copy($source, $destination) {
274278
$this->fileView->copy($source, $destination);
275279
} catch (StorageNotAvailableException $e) {
276280
throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
281+
} catch (ForbiddenException $ex) {
282+
throw new Forbidden($ex->getMessage(), $ex->getRetry());
277283
} catch (LockedException $e) {
278284
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
279285
}

apps/dav/tests/unit/connector/sabre/directory.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace OCA\DAV\Tests\Unit\Connector\Sabre;
1111

12+
use OCP\Files\ForbiddenException;
13+
1214
class Directory extends \Test\TestCase {
1315

1416
/** @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject */
@@ -48,6 +50,25 @@ public function testDeleteRootFolderFails() {
4850
$dir->delete();
4951
}
5052

53+
/**
54+
* @expectedException \OCA\DAV\Connector\Sabre\Exception\Forbidden
55+
*/
56+
public function testDeleteForbidden() {
57+
// deletion allowed
58+
$this->info->expects($this->once())
59+
->method('isDeletable')
60+
->will($this->returnValue(true));
61+
62+
// but fails
63+
$this->view->expects($this->once())
64+
->method('rmdir')
65+
->with('sub')
66+
->willThrowException(new ForbiddenException('', true));
67+
68+
$dir = $this->getDir('sub');
69+
$dir->delete();
70+
}
71+
5172
/**
5273
*
5374
*/
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
/**
3+
* Copyright (c) 2015 Thomas Müller <deepdiver@owncloud.com>
4+
* This file is licensed under the Affero General Public License version 3 or
5+
* later.
6+
* See the COPYING-README file.
7+
*/
8+
9+
namespace OCA\DAV\Tests\Unit\Connector\Sabre\Exception;
10+
11+
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
12+
13+
class ForbiddenTest extends \Test\TestCase {
14+
15+
public function testSerialization() {
16+
17+
// create xml doc
18+
$DOM = new \DOMDocument('1.0','utf-8');
19+
$DOM->formatOutput = true;
20+
$error = $DOM->createElementNS('DAV:','d:error');
21+
$error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV);
22+
$DOM->appendChild($error);
23+
24+
// serialize the exception
25+
$message = "1234567890";
26+
$retry = false;
27+
$expectedXml = <<<EOD
28+
<?xml version="1.0" encoding="utf-8"?>
29+
<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:o="http://owncloud.org/ns">
30+
<o:retry xmlns:o="o:">false</o:retry>
31+
<o:reason xmlns:o="o:">1234567890</o:reason>
32+
</d:error>
33+
34+
EOD;
35+
36+
$ex = new Forbidden($message, $retry);
37+
$server = $this->getMock('Sabre\DAV\Server');
38+
$ex->serialize($server, $error);
39+
40+
// assert
41+
$xml = $DOM->saveXML();
42+
$this->assertEquals($expectedXml, $xml);
43+
}
44+
}

apps/dav/tests/unit/connector/sabre/exception/invalidpathtest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
<?php
2-
3-
namespace OCA\DAV\Tests\Unit\Connector\Sabre\Exception;
4-
5-
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
6-
72
/**
83
* Copyright (c) 2015 Thomas Müller <deepdiver@owncloud.com>
94
* This file is licensed under the Affero General Public License version 3 or
105
* later.
116
* See the COPYING-README file.
127
*/
8+
9+
namespace OCA\DAV\Tests\Unit\Connector\Sabre\Exception;
10+
11+
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
12+
1313
class InvalidPathTest extends \Test\TestCase {
1414

1515
public function testSerialization() {

apps/dav/tests/unit/connector/sabre/file.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace OCA\DAV\Tests\Unit\Connector\Sabre;
1010

1111
use OC\Files\Storage\Local;
12+
use OCP\Files\ForbiddenException;
1213
use Test\HookHelper;
1314
use OC\Files\Filesystem;
1415
use OCP\Lock\ILockingProvider;
@@ -72,6 +73,10 @@ public function fopenFailuresProvider() {
7273
new \OCP\Files\InvalidPathException(),
7374
'Sabre\DAV\Exception\Forbidden'
7475
],
76+
[
77+
new \OCP\Files\ForbiddenException('', true),
78+
'OCA\DAV\Connector\Sabre\Exception\Forbidden'
79+
],
7580
[
7681
new \OCP\Files\LockNotAcquiredException('/test.txt', 1),
7782
'OCA\DAV\Connector\Sabre\Exception\FileLocked'
@@ -690,6 +695,29 @@ public function testDeleteThrowsWhenDeletionFailed() {
690695
$file->delete();
691696
}
692697

698+
/**
699+
* @expectedException \OCA\DAV\Connector\Sabre\Exception\Forbidden
700+
*/
701+
public function testDeleteThrowsWhenDeletionThrows() {
702+
// setup
703+
$view = $this->getMock('\OC\Files\View',
704+
array());
705+
706+
// but fails
707+
$view->expects($this->once())
708+
->method('unlink')
709+
->willThrowException(new ForbiddenException('', true));
710+
711+
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
712+
'permissions' => \OCP\Constants::PERMISSION_ALL
713+
), null);
714+
715+
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
716+
717+
// action
718+
$file->delete();
719+
}
720+
693721
/**
694722
* Asserts hook call
695723
*
@@ -835,4 +863,22 @@ public function testGetFopenFails() {
835863

836864
$file->get();
837865
}
866+
867+
/**
868+
* @expectedException \OCA\DAV\Connector\Sabre\Exception\Forbidden
869+
*/
870+
public function testGetFopenThrows() {
871+
$view = $this->getMock('\OC\Files\View', ['fopen'], array());
872+
$view->expects($this->atLeastOnce())
873+
->method('fopen')
874+
->willThrowException(new ForbiddenException('', true));
875+
876+
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
877+
'permissions' => \OCP\Constants::PERMISSION_ALL
878+
), null);
879+
880+
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
881+
882+
$file->get();
883+
}
838884
}

lib/private/cache/file.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ public function gc() {
185185
} catch (\OCP\Lock\LockedException $e) {
186186
// ignore locked chunks
187187
\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
188+
} catch (\OCP\Files\ForbiddenException $e) {
189+
\OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', array('app' => 'core'));
188190
} catch (\OCP\Files\LockNotAcquiredException $e) {
189191
\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
190192
}

lib/private/files.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ public static function get($dir, $files, $onlyHeader = false) {
142142
$l = \OC::$server->getL10N('core');
143143
$hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
144144
\OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint);
145+
} catch (\OCP\Files\ForbiddenException $ex) {
146+
self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
147+
OC::$server->getLogger()->logException($ex);
148+
$l = \OC::$server->getL10N('core');
149+
\OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage());
145150
} catch (\Exception $ex) {
146151
self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
147152
OC::$server->getLogger()->logException($ex);

0 commit comments

Comments
 (0)