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