Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/richdocuments.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJulius Härtl <jus@bitgrid.net>2021-06-29 15:42:10 +0300
committerJulius Härtl <jus@bitgrid.net>2021-06-29 19:24:23 +0300
commitbb8daed903274c4a2b92e5ed558c6e197238756d (patch)
tree5af7ea2ef65ce551462ee0cfbe5e85205c1c2eeb /lib
parent701d34a9bdf9a6267e7f9400e58bdb1c35b6d3d0 (diff)
Allow to limit wopi requests to specific source hosts
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'lib')
-rw-r--r--lib/AppInfo/Application.php2
-rw-r--r--lib/Controller/SettingsController.php6
-rw-r--r--lib/Middleware/WOPIMiddleware.php142
-rw-r--r--lib/Settings/Admin.php1
4 files changed, 151 insertions, 0 deletions
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 638842af..5701d50e 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -29,6 +29,7 @@ use OC\Files\Type\Detection;
use OC\Security\CSP\ContentSecurityPolicy;
use OCA\Richdocuments\AppConfig;
use OCA\Richdocuments\Capabilities;
+use OCA\Richdocuments\Middleware\WOPIMiddleware;
use OCA\Richdocuments\PermissionManager;
use OCA\Richdocuments\Preview\MSExcel;
use OCA\Richdocuments\Preview\MSWord;
@@ -64,6 +65,7 @@ class Application extends App implements IBootstrap {
public function register(IRegistrationContext $context): void {
$context->registerTemplateProvider(CollaboraTemplateProvider::class);
$context->registerCapability(Capabilities::class);
+ $context->registerMiddleWare(WOPIMiddleware::class);
}
public function boot(IBootContext $context): void {
diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php
index ce73dd1d..bec87a4b 100644
--- a/lib/Controller/SettingsController.php
+++ b/lib/Controller/SettingsController.php
@@ -111,6 +111,7 @@ class SettingsController extends Controller{
public function getSettings() {
return new JSONResponse([
'wopi_url' => $this->appConfig->getAppValue('wopi_url'),
+ 'wopi_allowlist' => $this->appConfig->getAppValue('wopi_allowlist'),
'public_wopi_url' => $this->appConfig->getAppValue('public_wopi_url'),
'disable_certificate_verification' => $this->appConfig->getAppValue('disable_certificate_verification') === 'yes',
'edit_groups' => $this->appConfig->getAppValue('edit_groups'),
@@ -130,6 +131,7 @@ class SettingsController extends Controller{
* @return JSONResponse
*/
public function setSettings($wopi_url,
+ $wopi_allowlist,
$disable_certificate_verification,
$edit_groups,
$use_groups,
@@ -142,6 +144,10 @@ class SettingsController extends Controller{
$this->appConfig->setAppValue('wopi_url', $wopi_url);
}
+ if ($wopi_allowlist !== null){
+ $this->appConfig->setAppValue('wopi_allowlist', $wopi_allowlist);
+ }
+
if ($disable_certificate_verification !== null) {
$this->appConfig->setAppValue(
'disable_certificate_verification',
diff --git a/lib/Middleware/WOPIMiddleware.php b/lib/Middleware/WOPIMiddleware.php
new file mode 100644
index 00000000..9f6420e9
--- /dev/null
+++ b/lib/Middleware/WOPIMiddleware.php
@@ -0,0 +1,142 @@
+<?php
+/*
+ * @copyright Copyright (c) 2021 Julius Härtl <jus@bitgrid.net>
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ * @author Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+declare(strict_types=1);
+
+namespace OCA\Richdocuments\Middleware;
+
+use OCA\Richdocuments\AppInfo\Application;
+use OCA\Richdocuments\Controller\WopiController;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\AppFramework\Http\Response;
+use OCP\AppFramework\Middleware;
+use OCP\Files\NotPermittedException;
+use OCP\IConfig;
+use OCP\IRequest;
+
+class WOPIMiddleware extends Middleware {
+
+ /** @var IConfig */
+ private $config;
+ /** @var IRequest */
+ private $request;
+
+ public function __construct(IConfig $config, IRequest $request) {
+ $this->config = $config;
+ $this->request = $request;
+ }
+
+ public function beforeController($controller, $methodName) {
+ parent::beforeController($controller, $methodName);
+
+ if ($controller instanceof WopiController && !$this->isWOPIAllowed()) {
+ throw new NotPermittedException();
+ }
+ }
+
+ public function afterException($controller, $methodName, \Exception $exception): Response {
+ if ($exception instanceof NotPermittedException && $controller instanceof WopiController) {
+ return new JSONResponse([], Http::STATUS_FORBIDDEN);
+ }
+
+ throw $exception;
+ }
+
+ public function isWOPIAllowed(): bool {
+ $allowedRanges = $this->config->getAppValue(Application::APPNAME, 'wopi_allowlist');
+ if($allowedRanges === '') {
+ return true;
+ }
+ $allowedRanges = explode(',', $allowedRanges);
+
+ $userIp = $this->request->getRemoteAddress();
+ foreach($allowedRanges as $range) {
+ if($this->matchCidr($userIp, $range)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @copyright https://stackoverflow.com/questions/594112/matching-an-ip-to-a-cidr-mask-in-php-5/594134#594134
+ * @copyright (IPv4) https://stackoverflow.com/questions/594112/matching-an-ip-to-a-cidr-mask-in-php-5/594134#594134
+ * @copyright (IPv6) MW. https://stackoverflow.com/questions/7951061/matching-ipv6-address-to-a-cidr-subnet via
+ */
+ private function matchCidr(string $ip, string $range): bool {
+ list($subnet, $bits) = array_pad(explode('/', $range), 2, null);
+ if ($bits === null) {
+ $bits = 32;
+ }
+ $bits = (int)$bits;
+
+ if ($this->isIpv4($ip) && $this->isIpv4($subnet)) {
+ $mask = -1 << (32 - $bits);
+
+ $ip = ip2long($ip);
+ $subnet = ip2long($subnet);
+ $subnet &= $mask;
+ return ($ip & $mask) === $subnet;
+ }
+
+ if ($this->isIpv6($ip) && $this->isIPv6($subnet)) {
+ $subnet = inet_pton($subnet);
+ $ip = inet_pton($ip);
+
+ $binMask = str_repeat("f", $bits / 4);
+ switch ($bits % 4) {
+ case 0:
+ break;
+ case 1:
+ $binMask .= "8";
+ break;
+ case 2:
+ $binMask .= "c";
+ break;
+ case 3:
+ $binMask .= "e";
+ break;
+ }
+
+ $binMask = str_pad($binMask, 32, '0');
+ $binMask = pack("H*", $binMask);
+
+ if ( ($ip & $binMask) === $subnet ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private function isIpv4($ip) {
+ return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
+ }
+
+ private function isIpv6($ip) {
+ return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
+ }
+}
diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php
index 8dc8c3bd..01fea6bd 100644
--- a/lib/Settings/Admin.php
+++ b/lib/Settings/Admin.php
@@ -80,6 +80,7 @@ class Admin implements ISettings {
[
'settings' => [
'wopi_url' => $this->config->getAppValue('richdocuments', 'wopi_url'),
+ 'wopi_allowlist' => $this->config->getAppValue('richdocuments', 'wopi_allowlist'),
'edit_groups' => $this->config->getAppValue('richdocuments', 'edit_groups'),
'use_groups' => $this->config->getAppValue('richdocuments', 'use_groups'),
'doc_format' => $this->config->getAppValue('richdocuments', 'doc_format'),