1<?php 2 3declare(strict_types=1); 4/** 5 * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> 6 * 7 * @license GNU AGPL version 3 or any later version 8 * 9 * This program is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU Affero General Public License as 11 * published by the Free Software Foundation, either version 3 of the 12 * License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU Affero General Public License for more details. 18 * 19 * You should have received a copy of the GNU Affero General Public License 20 * along with this program. If not, see <http://www.gnu.org/licenses/>. 21 * 22 */ 23 24 25namespace OCA\EndToEndEncryption; 26 27use OCP\Files\Folder; 28use OCP\Files\IRootFolder; 29use OCP\Files\Node; 30use OCP\Files\NotFoundException; 31use OCP\IUserSession; 32use OCP\Share\IManager; 33use OCP\Share\IShare; 34 35/** 36 * Class EncryptionManager 37 * 38 * Manage encryption state in the file cache 39 * 40 * @package OCA\EndToEndEncryption 41 */ 42class EncryptionManager { 43 44 /** @var IRootFolder */ 45 private $rootFolder; 46 47 /** @var IUserSession */ 48 private $userSession; 49 50 /** @var IManager */ 51 private $shareManager; 52 53 /** 54 * EncryptionManager constructor. 55 * 56 * @param IRootFolder $rootFolder 57 * @param IUserSession $userSession 58 * @param IManager $shareManager 59 */ 60 public function __construct(IRootFolder $rootFolder, 61 IUserSession $userSession, 62 IManager $shareManager) { 63 $this->rootFolder = $rootFolder; 64 $this->userSession = $userSession; 65 $this->shareManager = $shareManager; 66 } 67 68 /** 69 * mark folder as encrypted 70 * 71 * @param int $id 72 * @throws NotFoundException 73 */ 74 public function setEncryptionFlag(int $id): void { 75 $this->isValidFolder($id); 76 $userRoot = $this->getUserRoot(); 77 $userRoot->getStorage()->getCache()->update($id, ['encrypted' => '1']); 78 } 79 80 /** 81 * mark folder as un-encrypted 82 * 83 * @param int $id 84 * @throws NotFoundException 85 */ 86 public function removeEncryptionFlag(int $id): void { 87 $this->isValidFolder($id); 88 $userRoot = $this->getUserRoot(); 89 $userRoot->getStorage()->getCache()->update($id, ['encrypted' => '0']); 90 } 91 92 /** 93 * check if a file is in a folder marked as encrypted 94 * 95 * @param Node $node 96 * @return bool 97 */ 98 public function isEncryptedFile(Node $node): bool { 99 do { 100 if ($node->isEncrypted()) { 101 return true; 102 } 103 $node = $node->getParent(); 104 } while ($node->getPath() !== '/'); 105 106 return false; 107 } 108 109 /** 110 * get root folder of the currently logged in user 111 * 112 * @return Folder 113 */ 114 protected function getUserRoot(): Folder { 115 $uid = $this->userSession->getUser()->getUID(); 116 $userRoot = $this->rootFolder->getUserFolder($uid); 117 return $userRoot; 118 } 119 120 121 /** 122 * check if file ID points to a valid folder 123 * 124 * @param int $id folder id 125 * 126 * @throws NotFoundException 127 */ 128 protected function isValidFolder(int $id):void { 129 $node = $this->rootFolder->getById($id); 130 131 if (!isset($node[0])) { 132 throw new NotFoundException('No folder with ID ' . $id); 133 } 134 135 $firstNode = $node[0]; 136 if (!($firstNode instanceof Folder)) { 137 throw new NotFoundException('No folder with ID ' . $id); 138 } 139 140 if (!empty($firstNode->getDirectoryListing())) { 141 throw new NotFoundException('Folder with ID ' . $id . ' not empty'); 142 } 143 144 $user = $this->userSession->getUser(); 145 if ($user === null) { 146 throw new NotFoundException('No active user-session'); 147 } 148 149 $userId = $user->getUID(); 150 if ($this->isNodeShared($userId, $firstNode)) { 151 throw new NotFoundException('Folder with ID ' . $id . ' is shared'); 152 } 153 } 154 155 /** 156 * Check if a node is shared 157 * 158 * @param string $userId 159 * @param Node $node 160 * @return bool 161 */ 162 private function isNodeShared(string $userId, Node $node):bool { 163 $shareTypesToCheck = [ 164 IShare::TYPE_USER, 165 IShare::TYPE_GROUP, 166 IShare::TYPE_USERGROUP, 167 IShare::TYPE_LINK, 168 IShare::TYPE_EMAIL, 169 IShare::TYPE_REMOTE, 170 IShare::TYPE_CIRCLE, 171 IShare::TYPE_GUEST, 172 IShare::TYPE_REMOTE_GROUP, 173 IShare::TYPE_ROOM, 174 ]; 175 176 foreach ($shareTypesToCheck as $shareType) { 177 $shares = $this->shareManager->getSharesBy( 178 $userId, 179 $shareType, 180 $node, 181 false, 182 1 // Limit 1, because we only care whether there is a share or not 183 ); 184 185 if (!empty($shares)) { 186 return true; 187 } 188 } 189 190 return false; 191 } 192} 193