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.
403 lines
13 KiB
403 lines
13 KiB
<?php
|
|
/*
|
|
* Copyright (C) Ascensio System SIA, 2009-2026
|
|
*
|
|
* 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, together with the
|
|
* additional terms provided in the LICENSE file.
|
|
*
|
|
* 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: https://www.gnu.org/licenses/agpl-3.0.html
|
|
*
|
|
* You can contact Ascensio System SIA by email at info@onlyoffice.com
|
|
* or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga,
|
|
* LV-1050, Latvia, European Union.
|
|
*
|
|
* The interactive user interfaces in modified versions of the Program
|
|
* are required to display Appropriate Legal Notices in accordance with
|
|
* Section 5 of the GNU AGPL version 3.
|
|
*
|
|
* No trademark rights are granted under this License.
|
|
*
|
|
* All non-code elements of the Product, including illustrations,
|
|
* icon sets, and technical writing content, are licensed under the
|
|
* Creative Commons Attribution-ShareAlike 4.0 International License:
|
|
* https://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
*
|
|
* This license applies only to such non-code elements and does not
|
|
* modify or replace the licensing terms applicable to the Program's
|
|
* source code, which remains licensed under the GNU Affero General
|
|
* Public License v3.
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
namespace OCA\Onlyoffice;
|
|
|
|
use OCA\Talk\Manager as TalkManager;
|
|
use OCP\Constants;
|
|
use OCP\IAppConfig;
|
|
use OCP\IDBConnection;
|
|
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 {
|
|
|
|
/**
|
|
* 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;
|
|
|
|
public function __construct(
|
|
private readonly LoggerInterface $logger,
|
|
private readonly IManager $shareManager,
|
|
private readonly IAppConfig $config,
|
|
private readonly AppConfig $appConfig,
|
|
private readonly IDBConnection $connection,
|
|
private readonly ?TalkManager $talkManager,
|
|
) {}
|
|
|
|
/**
|
|
* Get extra permissions by shareId
|
|
*/
|
|
public function getExtra(string $shareId): ?array {
|
|
$share = $this->getShare($shareId);
|
|
if (!$share instanceof IShare) {
|
|
return null;
|
|
}
|
|
|
|
$shareId = $share->getId();
|
|
$extra = $this->get($shareId);
|
|
|
|
$wasInit = isset($extra["permissions"]);
|
|
$checkExtra = $wasInit ? (int)$extra["permissions"] : self::NONE;
|
|
[$availableExtra, $defaultPermissions] = $this->validation($share, $checkExtra, $wasInit);
|
|
|
|
if ($availableExtra === 0
|
|
|| ($availableExtra & $checkExtra) !== $checkExtra) {
|
|
if (!empty($extra)) {
|
|
$this->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
|
|
*/
|
|
public function getExtras(array $shares): array {
|
|
$result = [];
|
|
|
|
$shareIds = [];
|
|
foreach ($shares as $share) {
|
|
$shareIds[] = $share->getId();
|
|
}
|
|
|
|
if (empty($shareIds)) {
|
|
return $result;
|
|
}
|
|
|
|
$extras = $this->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;
|
|
[$availableExtra, $defaultPermissions] = $this->validation($share, $checkExtra, $wasInit);
|
|
|
|
if (($availableExtra === 0 || ($availableExtra & $checkExtra) !== $checkExtra) && !empty($currentExtra)) {
|
|
$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();
|
|
}
|
|
}
|
|
|
|
$result[] = $currentExtra;
|
|
}
|
|
}
|
|
|
|
if (!empty($noActualList)) {
|
|
$this->deleteList($noActualList);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get extra permissions by share
|
|
*
|
|
* @param string $shareId - share identifier
|
|
* @param integer $permissions - extra permissions bitmask
|
|
* @param integer $extraId - extra permission identifier
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function setExtra(string $shareId, int $permissions, int $extraId): bool {
|
|
$result = false;
|
|
|
|
$share = $this->getShare($shareId);
|
|
if (!$share instanceof IShare) {
|
|
return $result;
|
|
}
|
|
|
|
[$availableExtra, $defaultPermissions] = $this->validation($share, $permissions);
|
|
if (($availableExtra & $permissions) !== $permissions) {
|
|
$this->logger->debug("Share " . $shareId . " does not available to extend permissions");
|
|
return $result;
|
|
}
|
|
|
|
$result = $extraId > 0 ? $this->update($share->getId(), $permissions) : $this->insert($share->getId(), $permissions);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Delete extra permissions for share
|
|
*/
|
|
public function delete(string $shareId): bool {
|
|
$delete = $this->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
|
|
*/
|
|
public function deleteList(array $shareIds): bool {
|
|
$condition = "";
|
|
if (count($shareIds) > 1) {
|
|
$counter = count($shareIds);
|
|
for ($i = 1; $i < $counter; $i++) {
|
|
$condition .= " OR `share_id` = ?";
|
|
}
|
|
}
|
|
|
|
$delete = $this->connection->prepare("
|
|
DELETE FROM `*PREFIX*" . self::TABLENAME_KEY . "`
|
|
WHERE `share_id` = ?
|
|
" . $condition);
|
|
return (bool)$delete->execute($shareIds);
|
|
}
|
|
|
|
/**
|
|
* Get extra permissions for share
|
|
*/
|
|
private function get(string $shareId): array {
|
|
$select = $this->connection->prepare("
|
|
SELECT id, share_id, permissions
|
|
FROM `*PREFIX*" . self::TABLENAME_KEY . "`
|
|
WHERE `share_id` = ?
|
|
");
|
|
$result = $select->execute([$shareId]);
|
|
$values = $result->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
|
|
*/
|
|
private function getList(array $shareIds): array {
|
|
$condition = "";
|
|
if (count($shareIds) > 1) {
|
|
$counter = count($shareIds);
|
|
for ($i = 1; $i < $counter; $i++) {
|
|
$condition .= " OR `share_id` = ?";
|
|
}
|
|
}
|
|
|
|
$select = $this->connection->prepare("
|
|
SELECT id, share_id, permissions
|
|
FROM `*PREFIX*" . self::TABLENAME_KEY . "`
|
|
WHERE `share_id` = ?
|
|
" . $condition);
|
|
|
|
$result = $select->execute($shareIds);
|
|
$values = $result->fetchAll();
|
|
|
|
$result = [];
|
|
if (is_array($values)) {
|
|
foreach ($values as $value) {
|
|
$result[] = [
|
|
"id" => (int)$value["id"],
|
|
"share_id" => (string)$value["share_id"],
|
|
"permissions" => (int)$value["permissions"]
|
|
];
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Store extra permissions for share
|
|
*
|
|
* @param string $shareId - share identifier
|
|
* @param integer $permissions - permissions bitmask
|
|
*/
|
|
private function insert(string $shareId, int $permissions): bool {
|
|
$insert = $this->connection->prepare("
|
|
INSERT INTO `*PREFIX*" . self::TABLENAME_KEY . "`
|
|
(`share_id`, `permissions`)
|
|
VALUES (?, ?)
|
|
");
|
|
return (bool)$insert->execute([$shareId, $permissions]);
|
|
}
|
|
|
|
/**
|
|
* Update extra permissions for share
|
|
*
|
|
* @param string $shareId - share identifier
|
|
* @param int $permissions - permissions bitmask
|
|
*/
|
|
private function update(string $shareId, int $permissions): bool {
|
|
$update = $this->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 bitmask
|
|
* @param bool $wasInit - was initialization extra
|
|
*/
|
|
private function validation(IShare $share, int $checkExtra, bool $wasInit = true): array {
|
|
$availableExtra = self::NONE;
|
|
$defaultExtra = self::NONE;
|
|
|
|
if ($share->getShareType() !== IShare::TYPE_LINK
|
|
&& ($share->getPermissions() & Constants::PERMISSION_SHARE) === Constants::PERMISSION_SHARE
|
|
&& $this->config->getValueString('core', 'shareapi_allow_resharing', 'yes') === 'yes') {
|
|
return [$availableExtra, $defaultExtra];
|
|
}
|
|
|
|
$node = $share->getNode();
|
|
$ext = strtolower(pathinfo((string) $node->getName(), PATHINFO_EXTENSION));
|
|
$format = !empty($ext) && array_key_exists($ext, $this->appConfig->formatsSetting()) ? $this->appConfig->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 && ($defaultExtra & self::MODIFYFILTER) === self::MODIFYFILTER) {
|
|
$availableExtra ^= self::COMMENT;
|
|
}
|
|
}
|
|
|
|
return [$availableExtra, $defaultExtra];
|
|
}
|
|
|
|
/**
|
|
* Get origin share
|
|
*/
|
|
private function getShare(string $shareId): ?IShare {
|
|
foreach (["ocinternal", "ocRoomShare", "ocCircleShare"] as $provider) {
|
|
try {
|
|
return $this->shareManager->getShareById("$provider:$shareId");
|
|
} catch (ShareNotFound) {}
|
|
}
|
|
|
|
$this->logger->error("getShare: share not found: $shareId");
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|