Completed
Push — master ( 93ac28...c773a2 )
by Joas
13s
created

Operation::translatePath()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
ccs 0
cts 14
cp 0
rs 8.6737
cc 5
eloc 13
nc 5
nop 2
crap 30
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Morris Jobke <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OCA\FilesAccessControl;
23
24
25
use OCP\Files\ForbiddenException;
26
use OCP\IL10N;
27
use OCP\WorkflowEngine\IManager;
28
use OCP\WorkflowEngine\IOperation;
29
30
class Operation implements IOperation{
31
	/** @var IManager */
32
	protected $manager;
33
34
	/** @var IL10N */
35
	protected $l;
36
37
	/**
38
	 * @param IManager $manager
39
	 * @param IL10N $l
40
	 */
41
	public function __construct(IManager $manager, IL10N $l) {
42
		$this->manager = $manager;
43
		$this->l = $l;
44
	}
45
46
	/**
47
	 * @param StorageWrapper $storage
48
	 * @param string $path
49
	 * @throws ForbiddenException
50
	 */
51
	public function checkFileAccess(StorageWrapper $storage, $path) {
52
		if (!$this->isBlockablePath($storage, $path) || $this->isCreatingSkeletonFiles()) {
53
			// Allow creating skeletons and theming
54
			// https://github.com/nextcloud/files_accesscontrol/issues/5
55
			// https://github.com/nextcloud/files_accesscontrol/issues/12
56
			return;
57
		}
58
59
		$filePath = $this->translatePath($storage, $path);
60
		$this->manager->setFileInfo($storage, $filePath);
61
		$match = $this->manager->getMatchingOperations('OCA\FilesAccessControl\Operation');
62
63
		if (!empty($match)) {
64
			// All Checks of one operation matched: prevent access
65
			throw new ForbiddenException('Access denied', true);
66
		}
67
	}
68
69
	/**
70
	 * @param StorageWrapper $storage
71
	 * @param string $path
72
	 * @return bool
73
	 */
74
	protected function isBlockablePath(StorageWrapper $storage, $path) {
75
		$fullPath = $storage->mountPoint . $path;
76
77
		if (substr_count($fullPath, '/') < 3) {
78
			return false;
79
		}
80
81
		// '', admin, 'files', 'path/to/file.txt'
82
		$segment = explode('/', $fullPath, 4);
83
84
		return isset($segment[2]) && in_array($segment[2], [
85
			'files',
86
			'thumbnails',
87
			'files_versions',
88
		]);
89
	}
90
91
	/**
92
	 * For thumbnails and versions we want to check the tags of the original file
93
	 *
94
	 * @param StorageWrapper $storage
95
	 * @param string $path
96
	 * @return bool
97
	 */
98
	protected function translatePath(StorageWrapper $storage, $path) {
99
		if (substr_count($path, '/') < 1) {
100
			return $path;
101
		}
102
103
		// 'files', 'path/to/file.txt'
104
		list($folder, $innerPath) = explode('/', $path, 2);
105
106
		if ($folder === 'files_versions') {
107
			$innerPath = substr($innerPath, 0, strrpos($innerPath, '.v'));
108
			return 'files/' . $innerPath;
109
		} else if ($folder === 'thumbnails') {
110
			list($fileId,) = explode('/', $innerPath, 2);
111
			$innerPath = $storage->getCache()->getPathById($fileId);
112
113
			if ($innerPath !== null) {
114
				return 'files/' . $innerPath;
115
			}
116
		}
117
118
		return $path;
119
	}
120
121
	/**
122
	 * Check if we are in the LoginController and if so, ignore the firewall
123
	 * @return bool
124
	 */
125
	protected function isCreatingSkeletonFiles() {
126
		$exception = new \Exception();
127
		$trace = $exception->getTrace();
128
129
		foreach ($trace as $step) {
130
			if (isset($step['class']) && $step['class'] === 'OC\Core\Controller\LoginController' &&
131
				isset($step['function']) && $step['function'] === 'tryLogin') {
132
				return true;
133
			}
134
		}
135
136
		return false;
137
	}
138
139
	/**
140
	 * @param string $name
141
	 * @param array[] $checks
142
	 * @param string $operation
143
	 * @throws \UnexpectedValueException
144
	 */
145
	public function validateOperation($name, array $checks, $operation) {
146
		if (empty($checks)) {
147
			throw new \UnexpectedValueException($this->l->t('No rule given'));
148
		}
149
	}
150
}
151