mirror of
https://github.com/kd2org/picodav.git
synced 2024-06-08 00:42:18 +02:00
Fix implementation of per-directory restrictions
This commit is contained in:
parent
3adcc56962
commit
456a14f21e
52
README.md
52
README.md
|
@ -6,6 +6,21 @@ If you drop the [`index.php`](./index.php) file in a directory of your web-serve
|
||||||
|
|
||||||
![Web UI screenshot](https://raw.githubusercontent.com/kd2org/webdav-manager.js/main/scr_desktop.png)
|
![Web UI screenshot](https://raw.githubusercontent.com/kd2org/webdav-manager.js/main/scr_desktop.png)
|
||||||
|
|
||||||
|
* Single-file WebDAV server!
|
||||||
|
* No database!
|
||||||
|
* Very fast and lightweight!
|
||||||
|
* Compatible with tons of apps!
|
||||||
|
* Manage files and directories from a web browser:
|
||||||
|
* Upload directly from browser, using paste or drag and drop
|
||||||
|
* Rename
|
||||||
|
* Delete
|
||||||
|
* Create and edit text files
|
||||||
|
* Create directories
|
||||||
|
* MarkDown live preview
|
||||||
|
* Preview of images, text, MarkDown and PDF
|
||||||
|
* Manage users and password with only a text file!
|
||||||
|
* Restrict users to some directories, control where they can write!
|
||||||
|
|
||||||
## WebDAV clients
|
## WebDAV clients
|
||||||
|
|
||||||
You can use any WebDAV client, but we recommend these:
|
You can use any WebDAV client, but we recommend these:
|
||||||
|
@ -70,19 +85,38 @@ All users have read access to everything by default.
|
||||||
|
|
||||||
#### Restricting users to some directories
|
#### Restricting users to some directories
|
||||||
|
|
||||||
You can also limit users in which directories and files they can access by using the `restrict` and `restrict_write` configuration directives:
|
If you want something more detailed, you can also limit users in which directories and files they can access by using the `restrict[]` and `restrict_write[]` configuration directives.
|
||||||
|
|
||||||
|
These are tables, so you can have more than one directory restriction, don't forget the `[]`!
|
||||||
|
|
||||||
|
In the following example, the user will only be able to read the `constitution` directory and not write anything:
|
||||||
|
|
||||||
```
|
```
|
||||||
[emusk]
|
[olympe]
|
||||||
password = youSuck
|
password = abcd
|
||||||
write = false
|
write = false
|
||||||
restrict[] = 'kill-twitter/'
|
restrict[] = 'constitution/'
|
||||||
|
```
|
||||||
|
|
||||||
[pouyane]
|
Here the user will be able to only read and write in the `constitution` and `images` directories:
|
||||||
password = youArePaidWayTooMuch
|
|
||||||
write = false
|
```
|
||||||
restrict[] = 'total/'
|
[olympe]
|
||||||
restrict_write[] = 'total/kill-the-planet/'
|
password = abcd
|
||||||
|
write = true
|
||||||
|
restrict[] = 'constitution/'
|
||||||
|
restrict[] = 'images/'
|
||||||
|
```
|
||||||
|
|
||||||
|
And here, she will be able to only read from the `constitution` directory and write in the `constitution/book` and `constitution/summary` directories:
|
||||||
|
|
||||||
|
```
|
||||||
|
[olympe]
|
||||||
|
password = abcd
|
||||||
|
write = true
|
||||||
|
restrict[] = 'constitution/'
|
||||||
|
restrict_write[] = 'constitution/book/'
|
||||||
|
restrict_write[] = 'constitution/summary/'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Allow unrestricted access to everyone
|
### Allow unrestricted access to everyone
|
||||||
|
|
89
index.php
89
index.php
|
@ -725,7 +725,7 @@ namespace KD2\WebDAV
|
||||||
$uri = trim(rtrim($this->base_uri, '/') . '/' . ltrim($uri, '/'), '/');
|
$uri = trim(rtrim($this->base_uri, '/') . '/' . ltrim($uri, '/'), '/');
|
||||||
$path = '/' . str_replace('%2F', '/', rawurlencode($uri));
|
$path = '/' . str_replace('%2F', '/', rawurlencode($uri));
|
||||||
|
|
||||||
if (($item['DAV::resourcetype'] ?? null) == 'collection') {
|
if (($item['DAV::resourcetype'] ?? null) == 'collection' && $path != '/') {
|
||||||
$path .= '/';
|
$path .= '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1353,16 +1353,28 @@ namespace PicoDAV
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->auth()) {
|
if (!$this->auth()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$restrict = $this->users[$this->user]['restrict'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($restrict) || empty($restrict)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($restrict as $match) {
|
||||||
|
if (0 === strpos($uri, $match)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canWrite(string $uri): bool
|
public function canWrite(string $uri): bool
|
||||||
{
|
{
|
||||||
if (!$this->user && !ANONYMOUS_WRITE) {
|
if (!$this->auth() && !ANONYMOUS_WRITE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1374,7 +1386,36 @@ namespace PicoDAV
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($this->users[$this->user]['write'])) {
|
if (!$this->auth() || empty($this->users[$this->user]['write'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$restrict = $this->users[$this->user]['restrict_write'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($restrict) || empty($restrict)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($restrict as $match) {
|
||||||
|
if (0 === strpos($uri, $match)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canOnlyCreate(string $uri): bool
|
||||||
|
{
|
||||||
|
$restrict = $this->users[$this->user]['restrict_write'] ?? [];
|
||||||
|
|
||||||
|
if (in_array($uri, $restrict, true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$restrict = $this->users[$this->user]['restrict'] ?? [];
|
||||||
|
|
||||||
|
if (in_array($uri, $restrict, true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1383,13 +1424,13 @@ namespace PicoDAV
|
||||||
|
|
||||||
public function list(string $uri, ?array $properties): iterable
|
public function list(string $uri, ?array $properties): iterable
|
||||||
{
|
{
|
||||||
if (!$this->canRead($uri)) {
|
if (!$this->canRead($uri . '/')) {
|
||||||
throw new WebDAV_Exception('Access forbidden', 403);
|
//throw new WebDAV_Exception('Access forbidden', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dirs = self::glob($this->path . $uri, '/*', \GLOB_ONLYDIR);
|
$dirs = self::glob($this->path . $uri, '/*', \GLOB_ONLYDIR);
|
||||||
$dirs = array_map('basename', $dirs);
|
$dirs = array_map('basename', $dirs);
|
||||||
$dirs = array_filter($dirs, fn($a) => $this->canRead(ltrim($uri . '/' . $a, '/')));
|
$dirs = array_filter($dirs, fn($a) => $this->canRead(ltrim($uri . '/' . $a, '/') . '/'));
|
||||||
natcasesort($dirs);
|
natcasesort($dirs);
|
||||||
|
|
||||||
$files = self::glob($this->path . $uri, '/*');
|
$files = self::glob($this->path . $uri, '/*');
|
||||||
|
@ -1403,6 +1444,7 @@ namespace PicoDAV
|
||||||
|
|
||||||
$files = array_flip(array_merge($dirs, $files));
|
$files = array_flip(array_merge($dirs, $files));
|
||||||
$files = array_map(fn($a) => null, $files);
|
$files = array_map(fn($a) => null, $files);
|
||||||
|
|
||||||
return $files;
|
return $files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1452,8 +1494,6 @@ namespace PicoDAV
|
||||||
}
|
}
|
||||||
|
|
||||||
return new \DateTime('@' . $mtime);
|
return new \DateTime('@' . $mtime);
|
||||||
case 'DAV::displayname':
|
|
||||||
return basename($target);
|
|
||||||
case 'DAV::ishidden':
|
case 'DAV::ishidden':
|
||||||
return basename($target)[0] == '.';
|
return basename($target)[0] == '.';
|
||||||
case 'DAV::getetag':
|
case 'DAV::getetag':
|
||||||
|
@ -1466,12 +1506,23 @@ namespace PicoDAV
|
||||||
case 'http://owncloud.org/ns:permissions':
|
case 'http://owncloud.org/ns:permissions':
|
||||||
$permissions = 'G';
|
$permissions = 'G';
|
||||||
|
|
||||||
|
if (is_dir($target)) {
|
||||||
|
$uri .= '/';
|
||||||
|
}
|
||||||
|
|
||||||
if (is_writeable($target) && $this->canWrite($uri)) {
|
if (is_writeable($target) && $this->canWrite($uri)) {
|
||||||
$permissions .= 'DNVWCK';
|
// If the directory is one of the restricted paths,
|
||||||
|
// then we can only do stuff INSIDE, and not delete/rename the directory itself
|
||||||
|
if ($this->canOnlyCreate($uri)) {
|
||||||
|
$permissions .= 'CK';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$permissions .= 'DNVWCK';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $permissions;
|
return $permissions;
|
||||||
case WebDAV::PROP_DIGEST_MD5:
|
case Server::PROP_DIGEST_MD5:
|
||||||
if (!is_file($target)) {
|
if (!is_file($target)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1578,6 +1629,10 @@ namespace PicoDAV
|
||||||
throw new WebDAV_Exception('Access forbidden', 403);
|
throw new WebDAV_Exception('Access forbidden', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->canOnlyCreate($uri)) {
|
||||||
|
throw new WebDAV_Exception('Access forbidden', 403);
|
||||||
|
}
|
||||||
|
|
||||||
$target = $this->path . $uri;
|
$target = $this->path . $uri;
|
||||||
|
|
||||||
if (!file_exists($target)) {
|
if (!file_exists($target)) {
|
||||||
|
@ -1602,11 +1657,9 @@ namespace PicoDAV
|
||||||
|
|
||||||
public function copymove(bool $move, string $uri, string $destination): bool
|
public function copymove(bool $move, string $uri, string $destination): bool
|
||||||
{
|
{
|
||||||
if (!$this->canWrite($uri)) {
|
if (!$this->canWrite($uri)
|
||||||
throw new WebDAV_Exception('Access forbidden', 403);
|
|| !$this->canWrite($destination)
|
||||||
}
|
|| $this->canOnlyCreate($uri)) {
|
||||||
|
|
||||||
if (!$this->canWrite($destination)) {
|
|
||||||
throw new WebDAV_Exception('Access forbidden', 403);
|
throw new WebDAV_Exception('Access forbidden', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1794,11 +1847,11 @@ RewriteRule ^.*$ /index.php [END]
|
||||||
$fp = fopen(__FILE__, 'r');
|
$fp = fopen(__FILE__, 'r');
|
||||||
|
|
||||||
if ($relative_uri == 'webdav.js') {
|
if ($relative_uri == 'webdav.js') {
|
||||||
fseek($fp, 48399, SEEK_SET);
|
fseek($fp, 49575, SEEK_SET);
|
||||||
echo fread($fp, 25889);
|
echo fread($fp, 25889);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fseek($fp, 48399 + 25889, SEEK_SET);
|
fseek($fp, 49575 + 25889, SEEK_SET);
|
||||||
echo fread($fp, 6760);
|
echo fread($fp, 6760);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
83
server.php
83
server.php
|
@ -77,16 +77,28 @@ namespace PicoDAV
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->auth()) {
|
if (!$this->auth()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$restrict = $this->users[$this->user]['restrict'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($restrict) || empty($restrict)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($restrict as $match) {
|
||||||
|
if (0 === strpos($uri, $match)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canWrite(string $uri): bool
|
public function canWrite(string $uri): bool
|
||||||
{
|
{
|
||||||
if (!$this->user && !ANONYMOUS_WRITE) {
|
if (!$this->auth() && !ANONYMOUS_WRITE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +110,36 @@ namespace PicoDAV
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($this->users[$this->user]['write'])) {
|
if (!$this->auth() || empty($this->users[$this->user]['write'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$restrict = $this->users[$this->user]['restrict_write'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($restrict) || empty($restrict)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($restrict as $match) {
|
||||||
|
if (0 === strpos($uri, $match)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canOnlyCreate(string $uri): bool
|
||||||
|
{
|
||||||
|
$restrict = $this->users[$this->user]['restrict_write'] ?? [];
|
||||||
|
|
||||||
|
if (in_array($uri, $restrict, true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$restrict = $this->users[$this->user]['restrict'] ?? [];
|
||||||
|
|
||||||
|
if (in_array($uri, $restrict, true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,13 +148,13 @@ namespace PicoDAV
|
||||||
|
|
||||||
public function list(string $uri, ?array $properties): iterable
|
public function list(string $uri, ?array $properties): iterable
|
||||||
{
|
{
|
||||||
if (!$this->canRead($uri)) {
|
if (!$this->canRead($uri . '/')) {
|
||||||
throw new WebDAV_Exception('Access forbidden', 403);
|
//throw new WebDAV_Exception('Access forbidden', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dirs = self::glob($this->path . $uri, '/*', \GLOB_ONLYDIR);
|
$dirs = self::glob($this->path . $uri, '/*', \GLOB_ONLYDIR);
|
||||||
$dirs = array_map('basename', $dirs);
|
$dirs = array_map('basename', $dirs);
|
||||||
$dirs = array_filter($dirs, fn($a) => $this->canRead(ltrim($uri . '/' . $a, '/')));
|
$dirs = array_filter($dirs, fn($a) => $this->canRead(ltrim($uri . '/' . $a, '/') . '/'));
|
||||||
natcasesort($dirs);
|
natcasesort($dirs);
|
||||||
|
|
||||||
$files = self::glob($this->path . $uri, '/*');
|
$files = self::glob($this->path . $uri, '/*');
|
||||||
|
@ -127,6 +168,7 @@ namespace PicoDAV
|
||||||
|
|
||||||
$files = array_flip(array_merge($dirs, $files));
|
$files = array_flip(array_merge($dirs, $files));
|
||||||
$files = array_map(fn($a) => null, $files);
|
$files = array_map(fn($a) => null, $files);
|
||||||
|
|
||||||
return $files;
|
return $files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,8 +218,6 @@ namespace PicoDAV
|
||||||
}
|
}
|
||||||
|
|
||||||
return new \DateTime('@' . $mtime);
|
return new \DateTime('@' . $mtime);
|
||||||
case 'DAV::displayname':
|
|
||||||
return basename($target);
|
|
||||||
case 'DAV::ishidden':
|
case 'DAV::ishidden':
|
||||||
return basename($target)[0] == '.';
|
return basename($target)[0] == '.';
|
||||||
case 'DAV::getetag':
|
case 'DAV::getetag':
|
||||||
|
@ -190,12 +230,23 @@ namespace PicoDAV
|
||||||
case 'http://owncloud.org/ns:permissions':
|
case 'http://owncloud.org/ns:permissions':
|
||||||
$permissions = 'G';
|
$permissions = 'G';
|
||||||
|
|
||||||
|
if (is_dir($target)) {
|
||||||
|
$uri .= '/';
|
||||||
|
}
|
||||||
|
|
||||||
if (is_writeable($target) && $this->canWrite($uri)) {
|
if (is_writeable($target) && $this->canWrite($uri)) {
|
||||||
$permissions .= 'DNVWCK';
|
// If the directory is one of the restricted paths,
|
||||||
|
// then we can only do stuff INSIDE, and not delete/rename the directory itself
|
||||||
|
if ($this->canOnlyCreate($uri)) {
|
||||||
|
$permissions .= 'CK';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$permissions .= 'DNVWCK';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $permissions;
|
return $permissions;
|
||||||
case WebDAV::PROP_DIGEST_MD5:
|
case Server::PROP_DIGEST_MD5:
|
||||||
if (!is_file($target)) {
|
if (!is_file($target)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -302,6 +353,10 @@ namespace PicoDAV
|
||||||
throw new WebDAV_Exception('Access forbidden', 403);
|
throw new WebDAV_Exception('Access forbidden', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->canOnlyCreate($uri)) {
|
||||||
|
throw new WebDAV_Exception('Access forbidden', 403);
|
||||||
|
}
|
||||||
|
|
||||||
$target = $this->path . $uri;
|
$target = $this->path . $uri;
|
||||||
|
|
||||||
if (!file_exists($target)) {
|
if (!file_exists($target)) {
|
||||||
|
@ -326,11 +381,9 @@ namespace PicoDAV
|
||||||
|
|
||||||
public function copymove(bool $move, string $uri, string $destination): bool
|
public function copymove(bool $move, string $uri, string $destination): bool
|
||||||
{
|
{
|
||||||
if (!$this->canWrite($uri)) {
|
if (!$this->canWrite($uri)
|
||||||
throw new WebDAV_Exception('Access forbidden', 403);
|
|| !$this->canWrite($destination)
|
||||||
}
|
|| $this->canOnlyCreate($uri)) {
|
||||||
|
|
||||||
if (!$this->canWrite($destination)) {
|
|
||||||
throw new WebDAV_Exception('Access forbidden', 403);
|
throw new WebDAV_Exception('Access forbidden', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue