Completed
Push — master ( 609978...e8817e )
by Morris
18:30
created

MountProvider   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 188
rs 10
c 0
b 0
f 0
wmc 18
lcom 1
cbo 7

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B getMountsForUser() 0 53 5
A groupShares() 0 24 5
B buildSuperShares() 0 56 7
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Joas Schilling <[email protected]>
6
 * @author Maxence Lange <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Roeland Jago Douma <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OCA\Files_Sharing;
29
30
use OC\Cache\CappedMemoryCache;
31
use OC\Files\View;
32
use OCP\Files\Config\IMountProvider;
33
use OCP\Files\Storage\IStorageFactory;
34
use OCP\IConfig;
35
use OCP\ILogger;
36
use OCP\IUser;
37
use OCP\Share\IManager;
38
39
class MountProvider implements IMountProvider {
40
	/**
41
	 * @var \OCP\IConfig
42
	 */
43
	protected $config;
44
45
	/**
46
	 * @var IManager
47
	 */
48
	protected $shareManager;
49
50
	/**
51
	 * @var ILogger
52
	 */
53
	protected $logger;
54
55
	/**
56
	 * @param \OCP\IConfig $config
57
	 * @param IManager $shareManager
58
	 * @param ILogger $logger
59
	 */
60
	public function __construct(IConfig $config, IManager $shareManager, ILogger $logger) {
61
		$this->config = $config;
62
		$this->shareManager = $shareManager;
63
		$this->logger = $logger;
64
	}
65
66
67
	/**
68
	 * Get all mountpoints applicable for the user and check for shares where we need to update the etags
69
	 *
70
	 * @param \OCP\IUser $user
71
	 * @param \OCP\Files\Storage\IStorageFactory $storageFactory
72
	 * @return \OCP\Files\Mount\IMountPoint[]
73
	 */
74
	public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) {
75
76
		$shares = $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_USER, null, -1);
77
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_GROUP, null, -1));
78
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_CIRCLE, null, -1));
79
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_ROOM, null, -1));
80
81
		// filter out excluded shares and group shares that includes self
82
		$shares = array_filter($shares, function (\OCP\Share\IShare $share) use ($user) {
83
			return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID();
84
		});
85
86
		$superShares = $this->buildSuperShares($shares, $user);
87
88
		$mounts = [];
89
		$view = new View('/' . $user->getUID() . '/files');
90
		$ownerViews = [];
91
		$sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($user->getUID());
92
		$foldersExistCache = new CappedMemoryCache();
93
		foreach ($superShares as $share) {
94
			try {
95
				/** @var \OCP\Share\IShare $parentShare */
96
				$parentShare = $share[0];
97
				$owner = $parentShare->getShareOwner();
98
				if (!isset($ownerViews[$owner])) {
99
					$ownerViews[$owner] = new View('/' . $parentShare->getShareOwner() . '/files');
100
				}
101
				$mount = new SharedMount(
102
					'\OCA\Files_Sharing\SharedStorage',
103
					$mounts,
104
					[
105
						'user' => $user->getUID(),
106
						// parent share
107
						'superShare' => $parentShare,
108
						// children/component of the superShare
109
						'groupedShares' => $share[1],
110
						'ownerView' => $ownerViews[$owner],
111
						'sharingDisabledForUser' => $sharingDisabledForUser
112
					],
113
					$storageFactory,
114
					$view,
115
					$foldersExistCache
116
				);
117
				$mounts[$mount->getMountPoint()] = $mount;
118
			} catch (\Exception $e) {
119
				$this->logger->logException($e);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
120
				$this->logger->error('Error while trying to create shared mount');
121
			}
122
		}
123
124
		// array_filter removes the null values from the array
125
		return array_values(array_filter($mounts));
126
	}
127
128
	/**
129
	 * Groups shares by path (nodeId) and target path
130
	 *
131
	 * @param \OCP\Share\IShare[] $shares
132
	 * @return \OCP\Share\IShare[][] array of grouped shares, each element in the
133
	 * array is a group which itself is an array of shares
134
	 */
135
	private function groupShares(array $shares) {
136
		$tmp = [];
137
138
		foreach ($shares as $share) {
139
			if (!isset($tmp[$share->getNodeId()])) {
140
				$tmp[$share->getNodeId()] = [];
141
			}
142
			$tmp[$share->getNodeId()][] = $share;
143
		}
144
145
		$result = [];
146
		// sort by stime, the super share will be based on the least recent share
147
		foreach ($tmp as &$tmp2) {
148
			@usort($tmp2, function($a, $b) {
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
149
				if ($a->getShareTime() <= $b->getShareTime()) {
150
					return -1;
151
				}
152
				return 1;
153
			});
154
			$result[] = $tmp2;
155
		}
156
157
		return array_values($result);
158
	}
159
160
	/**
161
	 * Build super shares (virtual share) by grouping them by node id and target,
162
	 * then for each group compute the super share and return it along with the matching
163
	 * grouped shares. The most permissive permissions are used based on the permissions
164
	 * of all shares within the group.
165
	 *
166
	 * @param \OCP\Share\IShare[] $allShares
167
	 * @param \OCP\IUser $user user
168
	 * @return array Tuple of [superShare, groupedShares]
169
	 */
170
	private function buildSuperShares(array $allShares, \OCP\IUser $user) {
171
		$result = [];
172
173
		$groupedShares = $this->groupShares($allShares);
174
175
		/** @var \OCP\Share\IShare[] $shares */
176
		foreach ($groupedShares as $shares) {
177
			if (count($shares) === 0) {
178
				continue;
179
			}
180
181
			$superShare = $this->shareManager->newShare();
182
183
			// compute super share based on first entry of the group
184
			$superShare->setId($shares[0]->getId())
185
				->setShareOwner($shares[0]->getShareOwner())
186
				->setNodeId($shares[0]->getNodeId())
187
				->setTarget($shares[0]->getTarget());
188
189
			// use most permissive permissions
190
			$permissions = 0;
191
			foreach ($shares as $share) {
192
				$permissions |= $share->getPermissions();
193
				if ($share->getTarget() !== $superShare->getTarget()) {
194
					// adjust target, for database consistency
195
					$share->setTarget($superShare->getTarget());
196
					try {
197
						$this->shareManager->moveShare($share, $user->getUID());
198
					} catch (\InvalidArgumentException $e) {
199
						// ignore as it is not important and we don't want to
200
						// block FS setup
201
202
						// the subsequent code anyway only uses the target of the
203
						// super share
204
205
						// such issue can usually happen when dealing with
206
						// null groups which usually appear with group backend
207
						// caching inconsistencies
208
						$this->logger->debug(
209
							'Could not adjust share target for share ' . $share->getId() . ' to make it consistent: ' . $e->getMessage(),
210
							['app' => 'files_sharing']
211
						);
212
					}
213
				}
214
				if (!is_null($share->getNodeCacheEntry())) {
215
					$superShare->setNodeCacheEntry($share->getNodeCacheEntry());
216
				}
217
			}
218
219
			$superShare->setPermissions($permissions);
220
221
			$result[] = [$superShare, $shares];
222
		}
223
224
		return $result;
225
	}
226
}
227