The app which enables the users to edit office documents from Nextcloud using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Nextcloud
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
onlyoffice-nextcloud/lib/ExtraPermissions.php

500 lines
15 KiB

<?php
/**
*
* (c) Copyright Ascensio System SIA 2025
*
* This program is a free software product.
* You can redistribute it and/or modify it under the terms of the GNU Affero General Public License
* (AGPL) version 3 as published by the Free Software Foundation.
* In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* For details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions of the Program
* must display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product logo when distributing the program.
* Pursuant to Section 7(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as well as technical
* writing content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International.
* See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
namespace OCA\Onlyoffice;
use OCA\Talk\Manager as TalkManager;
use OCP\Constants;
use OCP\Files\File;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use OCP\Share\IShare;
use Psr\Log\LoggerInterface;
/**
* Class expands base permissions
*
* @package OCA\Onlyoffice
*/
class ExtraPermissions {
/**
* Application name
*
* @var string
*/
private $appName;
/**
* Logger
*
* @var LoggerInterface
*/
private $logger;
/**
* Share manager
*
* @var IManager
*/
private $shareManager;
/**
* Application configuration
*
* @var AppConfig
*/
private $config;
/**
* Talk manager
*
* @var TalkManager
*/
private $talkManager;
/**
* Table name
*/
private const TABLENAME_KEY = "onlyoffice_permissions";
/**
* Extra permission values
*
* @var integer
*/
public const NONE = 0;
public const REVIEW = 1;
public const COMMENT = 2;
public const FILLFORMS = 4;
public const MODIFYFILTER = 8;
/**
* @param string $AppName - application name
* @param LoggerInterface $logger - logger
* @param AppConfig $config - application configuration
* @param IManager $shareManager - Share manager
*/
public function __construct(
$AppName,
LoggerInterface $logger,
IManager $shareManager,
AppConfig $config
) {
$this->appName = $AppName;
$this->logger = $logger;
$this->shareManager = $shareManager;
$this->config = $config;
if (\OC::$server->getAppManager()->isInstalled("spreed")) {
try {
$this->talkManager = \OC::$server->query(TalkManager::class);
} catch (QueryException $e) {
$this->logger->error("TalkManager init error", ["exception" => $e]);
}
}
}
/**
* Get extra permissions by shareId
*
* @param integer $shareId - share identifier
*
* @return array
*/
public function getExtra($shareId) {
$share = $this->getShare($shareId);
if (empty($share)) {
return null;
}
$shareId = $share->getId();
$extra = self::get($shareId);
$wasInit = isset($extra["permissions"]);
$checkExtra = $wasInit ? (int)$extra["permissions"] : self::NONE;
list($availableExtra, $defaultPermissions) = $this->validation($share, $checkExtra, $wasInit);
if ($availableExtra === 0
|| ($availableExtra & $checkExtra) !== $checkExtra) {
if (!empty($extra)) {
self::delete($shareId);
}
$this->logger->debug("Share " . $shareId . " does not support extra permissions");
return null;
}
if (empty($extra)) {
$extra["id"] = -1;
$extra["share_id"] = $share->getId();
$extra["permissions"] = $defaultPermissions;
}
$extra["type"] = $share->getShareType();
$extra["shareWith"] = $share->getSharedWith();
$extra["shareWithName"] = $share->getSharedWithDisplayName();
$extra["available"] = $availableExtra;
return $extra;
}
/**
* Get list extra permissions by shares
*
* @param array $shares - array of shares
*
* @return array
*/
public function getExtras($shares) {
$result = [];
$shareIds = [];
foreach ($shares as $share) {
array_push($shareIds, $share->getId());
}
if (empty($shareIds)) {
return $result;
}
$extras = self::getList($shareIds);
$noActualList = [];
foreach ($shares as $share) {
$currentExtra = [];
foreach ($extras as $extra) {
if ($extra["share_id"] === $share->getId()) {
$currentExtra = $extra;
}
}
$wasInit = isset($currentExtra["permissions"]);
$checkExtra = $wasInit ? (int)$currentExtra["permissions"] : self::NONE;
list($availableExtra, $defaultPermissions) = $this->validation($share, $checkExtra, $wasInit);
if ($availableExtra === 0
|| ($availableExtra & $checkExtra) !== $checkExtra) {
if (!empty($currentExtra)) {
array_push($noActualList, $share->getId());
$currentExtra = [];
}
}
if ($availableExtra > 0) {
if (empty($currentExtra)) {
$currentExtra["id"] = -1;
$currentExtra["share_id"] = $share->getId();
$currentExtra["permissions"] = $defaultPermissions;
}
$currentExtra["type"] = $share->getShareType();
$currentExtra["shareWith"] = $share->getSharedWith();
$currentExtra["shareWithName"] = $share->getSharedWithDisplayName();
$currentExtra["available"] = $availableExtra;
if ($currentExtra["type"] === IShare::TYPE_ROOM && $this->talkManager !== null) {
$rooms = $this->talkManager->searchRoomsByToken($currentExtra["shareWith"]);
if (!empty($rooms)) {
$room = $rooms[0];
$currentExtra["shareWith"] = $room->getName();
$currentExtra["shareWithName"] = $room->getName();
}
}
array_push($result, $currentExtra);
}
}
if (!empty($noActualList)) {
self::deleteList($noActualList);
}
return $result;
}
/**
* Get extra permissions by share
*
* @param integer $shareId - share identifier
* @param integer $permissions - value extra permissions
* @param integer $extraId - extra permission identifier
*
* @return bool
*/
public function setExtra($shareId, $permissions, $extraId) {
$result = false;
$share = $this->getShare($shareId);
if (empty($share)) {
return $result;
}
list($availableExtra, $defaultPermissions) = $this->validation($share, $permissions);
if (($availableExtra & $permissions) !== $permissions) {
$this->logger->debug("Share " . $shareId . " does not available to extend permissions");
return $result;
}
if ($extraId > 0) {
$result = self::update($share->getId(), $permissions);
} else {
$result = self::insert($share->getId(), $permissions);
}
return $result;
}
/**
* Delete extra permissions for share
*
* @param integer $shareId - file identifier
*
* @return bool
*/
public static function delete($shareId) {
$connection = \OC::$server->getDatabaseConnection();
$delete = $connection->prepare("
DELETE FROM `*PREFIX*" . self::TABLENAME_KEY . "`
WHERE `share_id` = ?
");
return (bool)$delete->execute([$shareId]);
}
/**
* Delete list extra permissions
*
* @param array $shareIds - array of share identifiers
*
* @return bool
*/
public static function deleteList($shareIds) {
$connection = \OC::$server->getDatabaseConnection();
$condition = "";
if (count($shareIds) > 1) {
for ($i = 1; $i < count($shareIds); $i++) {
$condition = $condition . " OR `share_id` = ?";
}
}
$delete = $connection->prepare("
DELETE FROM `*PREFIX*" . self::TABLENAME_KEY . "`
WHERE `share_id` = ?
" . $condition);
return (bool)$delete->execute($shareIds);
}
/**
* Get extra permissions for share
*
* @param integer $shareId - share identifier
*
* @return array
*/
private static function get($shareId) {
$connection = \OC::$server->getDatabaseConnection();
$select = $connection->prepare("
SELECT id, share_id, permissions
FROM `*PREFIX*" . self::TABLENAME_KEY . "`
WHERE `share_id` = ?
");
$result = $select->execute([$shareId]);
$values = $result ? $select->fetch() : [];
$value = is_array($values) ? $values : [];
$result = [];
if (!empty($value)) {
$result = [
"id" => (int)$value["id"],
"share_id" => (string)$value["share_id"],
"permissions" => (int)$value["permissions"]
];
}
return $result;
}
/**
* Get list extra permissions
*
* @param array $shareIds - array of share identifiers
*
* @return array
*/
private static function getList($shareIds) {
$connection = \OC::$server->getDatabaseConnection();
$condition = "";
if (count($shareIds) > 1) {
for ($i = 1; $i < count($shareIds); $i++) {
$condition = $condition . " OR `share_id` = ?";
}
}
$select = $connection->prepare("
SELECT id, share_id, permissions
FROM `*PREFIX*" . self::TABLENAME_KEY . "`
WHERE `share_id` = ?
" . $condition);
$result = $select->execute($shareIds);
$values = $result ? $select->fetchAll() : [];
$result = [];
if (is_array($values)) {
foreach ($values as $value) {
array_push($result, [
"id" => (int)$value["id"],
"share_id" => (string)$value["share_id"],
"permissions" => (int)$value["permissions"]
]);
}
}
return $result;
}
/**
* Store extra permissions for share
*
* @param integer $shareId - share identifier
* @param integer $permissions - value permissions
*
* @return bool
*/
private static function insert($shareId, $permissions) {
$connection = \OC::$server->getDatabaseConnection();
$insert = $connection->prepare("
INSERT INTO `*PREFIX*" . self::TABLENAME_KEY . "`
(`share_id`, `permissions`)
VALUES (?, ?)
");
return (bool)$insert->execute([$shareId, $permissions]);
}
/**
* Update extra permissions for share
*
* @param integer $shareId - share identifier
* @param bool $permissions - value permissions
*
* @return bool
*/
private static function update($shareId, $permissions) {
$connection = \OC::$server->getDatabaseConnection();
$update = $connection->prepare("
UPDATE `*PREFIX*" . self::TABLENAME_KEY . "`
SET `permissions` = ?
WHERE `share_id` = ?
");
return (bool)$update->execute([$permissions, $shareId]);
}
/**
* Validation share on extend capability by extra permissions
*
* @param IShare $share - share
* @param int $checkExtra - checkable extra permissions
* @param bool $wasInit - was initialization extra
*
* @return array
*/
private function validation($share, $checkExtra, $wasInit = true) {
$availableExtra = self::NONE;
$defaultExtra = self::NONE;
if ($share->getShareType() !== IShare::TYPE_LINK
&& ($share->getPermissions() & Constants::PERMISSION_SHARE) === Constants::PERMISSION_SHARE) {
return [$availableExtra, $defaultExtra];
}
$node = $share->getNode();
$ext = strtolower(pathinfo($node->getName(), PATHINFO_EXTENSION));
$format = !empty($ext) && array_key_exists($ext, $this->config->formatsSetting()) ? $this->config->formatsSetting()[$ext] : null;
if (!isset($format)) {
return [$availableExtra, $defaultExtra];
}
if (($share->getPermissions() & Constants::PERMISSION_UPDATE) === Constants::PERMISSION_UPDATE) {
if (isset($format["modifyFilter"]) && $format["modifyFilter"]
&& ($checkExtra & self::COMMENT) !== self::COMMENT) {
$availableExtra |= self::MODIFYFILTER;
$defaultExtra |= self::MODIFYFILTER;
}
if (isset($format["review"]) && $format["review"]) {
$availableExtra |= self::REVIEW;
}
if (isset($format["comment"]) && $format["comment"]
&& ($checkExtra & self::REVIEW) !== self::REVIEW
&& (($checkExtra & self::MODIFYFILTER) !== self::MODIFYFILTER)) {
$availableExtra |= self::COMMENT;
}
if (isset($format["fillForms"]) && $format["fillForms"]
&& ($checkExtra & self::REVIEW) !== self::REVIEW) {
$availableExtra |= self::FILLFORMS;
}
if (!$wasInit) {
if (($defaultExtra & self::MODIFYFILTER) === self::MODIFYFILTER) {
$availableExtra ^= self::COMMENT;
}
}
}
return [$availableExtra, $defaultExtra];
}
/**
* Get origin share
*
* @param integer $shareId - share identifier
*
* @return IShare
*/
private function getShare($shareId) {
try {
$share = $this->shareManager->getShareById("ocinternal:" . $shareId);
return $share;
} catch (ShareNotFound $e) {}
try {
$share = $this->shareManager->getShareById("ocRoomShare:" . $shareId);
return $share;
} catch (ShareNotFound $e) {}
$this->logger->error("getShare: share not found: " . $shareId);
return null;
}
}