Completed
Push — stable13 ( 9c0a9a...065ec7 )
by Morris
19:50 queued 05:17
created

MountProvider::buildSuperShares()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 9
nop 2
dl 0
loc 56
rs 8.0266
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
80
		// filter out excluded shares and group shares that includes self
81
		$shares = array_filter($shares, function (\OCP\Share\IShare $share) use ($user) {
82
			return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID();
83
		});
84
85
		$superShares = $this->buildSuperShares($shares, $user);
86
87
		$mounts = [];
88
		$view = new View('/' . $user->getUID() . '/files');
89
		$ownerViews = [];
90
		$sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($user->getUID());
91
		$foldersExistCache = new CappedMemoryCache();
92
		foreach ($superShares as $share) {
93
			try {
94
				/** @var \OCP\Share\IShare $parentShare */
95
				$parentShare = $share[0];
96
				$owner = $parentShare->getShareOwner();
97
				if (!isset($ownerViews[$owner])) {
98
					$ownerViews[$owner] = new View('/' . $parentShare->getShareOwner() . '/files');
99
				}
100
				$mount = new SharedMount(
101
					'\OCA\Files_Sharing\SharedStorage',
102
					$mounts,
103
					[
104
						'user' => $user->getUID(),
105
						// parent share
106
						'superShare' => $parentShare,
107
						// children/component of the superShare
108
						'groupedShares' => $share[1],
109
						'ownerView' => $ownerViews[$owner],
110
						'sharingDisabledForUser' => $sharingDisabledForUser
111
					],
112
					$storageFactory,
113
					$view,
114
					$foldersExistCache
115
				);
116
				$mounts[$mount->getMountPoint()] = $mount;
117
			} catch (\Exception $e) {
118
				$this->logger->logException($e);
119
				$this->logger->error('Error while trying to create shared mount');
120
			}
121
		}
122
123
		// array_filter removes the null values from the array
124
		return array_values(array_filter($mounts));
125
	}
126
127
	/**
128
	 * Groups shares by path (nodeId) and target path
129
	 *
130
	 * @param \OCP\Share\IShare[] $shares
131
	 * @return \OCP\Share\IShare[][] array of grouped shares, each element in the
132
	 * array is a group which itself is an array of shares
133
	 */
134
	private function groupShares(array $shares) {
135
		$tmp = [];
136
137
		foreach ($shares as $share) {
138
			if (!isset($tmp[$share->getNodeId()])) {
139
				$tmp[$share->getNodeId()] = [];
140
			}
141
			$tmp[$share->getNodeId()][] = $share;
142
		}
143
144
		$result = [];
145
		// sort by stime, the super share will be based on the least recent share
146
		foreach ($tmp as &$tmp2) {
147
			@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...
148
				if ($a->getShareTime() <= $b->getShareTime()) {
149
					return -1;
150
				}
151
				return 1;
152
			});
153
			$result[] = $tmp2;
154
		}
155
156
		return array_values($result);
157
	}
158
159
	/**
160
	 * Build super shares (virtual share) by grouping them by node id and target,
161
	 * then for each group compute the super share and return it along with the matching
162
	 * grouped shares. The most permissive permissions are used based on the permissions
163
	 * of all shares within the group.
164
	 *
165
	 * @param \OCP\Share\IShare[] $allShares
166
	 * @param \OCP\IUser $user user
167
	 * @return array Tuple of [superShare, groupedShares]
168
	 */
169
	private function buildSuperShares(array $allShares, \OCP\IUser $user) {
170
		$result = [];
171
172
		$groupedShares = $this->groupShares($allShares);
173
174
		/** @var \OCP\Share\IShare[] $shares */
175
		foreach ($groupedShares as $shares) {
176
			if (count($shares) === 0) {
177
				continue;
178
			}
179
180
			$superShare = $this->shareManager->newShare();
181
182
			// compute super share based on first entry of the group
183
			$superShare->setId($shares[0]->getId())
184
				->setShareOwner($shares[0]->getShareOwner())
185
				->setNodeId($shares[0]->getNodeId())
186
				->setTarget($shares[0]->getTarget());
187
188
			// use most permissive permissions
189
			$permissions = 0;
190
			foreach ($shares as $share) {
191
				$permissions |= $share->getPermissions();
192
				if ($share->getTarget() !== $superShare->getTarget()) {
193
					// adjust target, for database consistency
194
					$share->setTarget($superShare->getTarget());
195
					try {
196
						$this->shareManager->moveShare($share, $user->getUID());
197
					} catch (\InvalidArgumentException $e) {
198
						// ignore as it is not important and we don't want to
199
						// block FS setup
200
201
						// the subsequent code anyway only uses the target of the
202
						// super share
203
204
						// such issue can usually happen when dealing with
205
						// null groups which usually appear with group backend
206
						// caching inconsistencies
207
						$this->logger->debug(
208
							'Could not adjust share target for share ' . $share->getId() . ' to make it consistent: ' . $e->getMessage(),
209
							['app' => 'files_sharing']
210
						);
211
					}
212
				}
213
				if (!is_null($share->getNodeCacheEntry())) {
214
					$superShare->setNodeCacheEntry($share->getNodeCacheEntry());
215
				}
216
			}
217
218
			$superShare->setPermissions($permissions);
219
220
			$result[] = [$superShare, $shares];
221
		}
222
223
		return $result;
224
	}
225
}
226