Completed
Push — master ( b1b976...74804b )
by
unknown
19:46
created
apps/dav/lib/Connector/Sabre/SharesPlugin.php 2 patches
Indentation   +239 added lines, -239 removed lines patch added patch discarded remove patch
@@ -27,243 +27,243 @@
 block discarded – undo
27 27
  * Sabre Plugin to provide share-related properties
28 28
  */
29 29
 class SharesPlugin extends \Sabre\DAV\ServerPlugin {
30
-	public const NS_OWNCLOUD = 'http://owncloud.org/ns';
31
-	public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
32
-	public const SHARETYPES_PROPERTYNAME = '{http://owncloud.org/ns}share-types';
33
-	public const SHAREES_PROPERTYNAME = '{http://nextcloud.org/ns}sharees';
34
-
35
-	/**
36
-	 * Reference to main server object
37
-	 *
38
-	 * @var \Sabre\DAV\Server
39
-	 */
40
-	private $server;
41
-	private string $userId;
42
-
43
-	/** @var IShare[][] */
44
-	private array $cachedShares = [];
45
-
46
-	/**
47
-	 * Tracks which folders have been cached.
48
-	 * When a folder is cached, it will appear with its path as key and true
49
-	 * as value.
50
-	 *
51
-	 * @var bool[]
52
-	 */
53
-	private array $cachedFolders = [];
54
-
55
-	public function __construct(
56
-		private Tree $tree,
57
-		private IUserSession $userSession,
58
-		private Folder $userFolder,
59
-		private IManager $shareManager,
60
-	) {
61
-		$this->userId = $userSession->getUser()->getUID();
62
-	}
63
-
64
-	/**
65
-	 * This initializes the plugin.
66
-	 *
67
-	 * This function is called by \Sabre\DAV\Server, after
68
-	 * addPlugin is called.
69
-	 *
70
-	 * This method should set up the required event subscriptions.
71
-	 *
72
-	 * @return void
73
-	 */
74
-	public function initialize(Server $server) {
75
-		$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
76
-		$server->xml->elementMap[self::SHARETYPES_PROPERTYNAME] = ShareTypeList::class;
77
-		$server->protectedProperties[] = self::SHARETYPES_PROPERTYNAME;
78
-		$server->protectedProperties[] = self::SHAREES_PROPERTYNAME;
79
-
80
-		$this->server = $server;
81
-		$this->server->on('preloadCollection', $this->preloadCollection(...));
82
-		$this->server->on('propFind', $this->handleGetProperties(...));
83
-		$this->server->on('beforeCopy', $this->validateMoveOrCopy(...));
84
-		$this->server->on('beforeMove', $this->validateMoveOrCopy(...));
85
-	}
86
-
87
-	/**
88
-	 * @param Node $node
89
-	 * @return IShare[]
90
-	 */
91
-	private function getShare(Node $node): array {
92
-		$result = [];
93
-		$requestedShareTypes = [
94
-			IShare::TYPE_USER,
95
-			IShare::TYPE_GROUP,
96
-			IShare::TYPE_LINK,
97
-			IShare::TYPE_REMOTE,
98
-			IShare::TYPE_EMAIL,
99
-			IShare::TYPE_ROOM,
100
-			IShare::TYPE_CIRCLE,
101
-			IShare::TYPE_DECK,
102
-			IShare::TYPE_SCIENCEMESH,
103
-		];
104
-
105
-		foreach ($requestedShareTypes as $requestedShareType) {
106
-			$result[] = $this->shareManager->getSharesBy(
107
-				$this->userId,
108
-				$requestedShareType,
109
-				$node,
110
-				false,
111
-				-1
112
-			);
113
-
114
-			// Also check for shares where the user is the recipient
115
-			try {
116
-				$result[] = $this->shareManager->getSharedWith(
117
-					$this->userId,
118
-					$requestedShareType,
119
-					$node,
120
-					-1
121
-				);
122
-			} catch (BackendError $e) {
123
-				// ignore
124
-			}
125
-		}
126
-
127
-		return array_merge(...$result);
128
-	}
129
-
130
-	/**
131
-	 * @param Folder $node
132
-	 * @return IShare[][]
133
-	 */
134
-	private function getSharesFolder(Folder $node): array {
135
-		return $this->shareManager->getSharesInFolder(
136
-			$this->userId,
137
-			$node,
138
-			true
139
-		);
140
-	}
141
-
142
-	/**
143
-	 * @param DavNode $sabreNode
144
-	 * @return IShare[]
145
-	 */
146
-	private function getShares(DavNode $sabreNode): array {
147
-		if (isset($this->cachedShares[$sabreNode->getId()])) {
148
-			return $this->cachedShares[$sabreNode->getId()];
149
-		}
150
-
151
-		[$parentPath,] = \Sabre\Uri\split($sabreNode->getPath());
152
-		if ($parentPath === '') {
153
-			$parentPath = '/';
154
-		}
155
-
156
-		// if we already cached the folder containing this file
157
-		// then we already know there are no shares here.
158
-		if (!isset($this->cachedFolders[$parentPath])) {
159
-			try {
160
-				$node = $sabreNode->getNode();
161
-			} catch (NotFoundException $e) {
162
-				return [];
163
-			}
164
-
165
-			$shares = $this->getShare($node);
166
-			$this->cachedShares[$sabreNode->getId()] = $shares;
167
-			return $shares;
168
-		}
169
-
170
-		return [];
171
-	}
172
-
173
-	private function preloadCollection(PropFind $propFind, ICollection $collection): void {
174
-		if (!$collection instanceof Directory
175
-			|| isset($this->cachedFolders[$collection->getPath()])
176
-			|| (
177
-				$propFind->getStatus(self::SHARETYPES_PROPERTYNAME) === null
178
-				&& $propFind->getStatus(self::SHAREES_PROPERTYNAME) === null
179
-			)
180
-		) {
181
-			return;
182
-		}
183
-
184
-		// If the node is a directory and we are requesting share types or sharees
185
-		// then we get all the shares in the folder and cache them.
186
-		// This is more performant than iterating each files afterwards.
187
-		$folderNode = $collection->getNode();
188
-		$this->cachedFolders[$collection->getPath()] = true;
189
-		foreach ($this->getSharesFolder($folderNode) as $id => $shares) {
190
-			$this->cachedShares[$id] = $shares;
191
-		}
192
-	}
193
-
194
-	/**
195
-	 * Adds shares to propfind response
196
-	 *
197
-	 * @param PropFind $propFind propfind object
198
-	 * @param \Sabre\DAV\INode $sabreNode sabre node
199
-	 */
200
-	public function handleGetProperties(
201
-		PropFind $propFind,
202
-		\Sabre\DAV\INode $sabreNode,
203
-	) {
204
-		if (!($sabreNode instanceof DavNode)) {
205
-			return;
206
-		}
207
-
208
-		$propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode): ShareTypeList {
209
-			$shares = $this->getShares($sabreNode);
210
-
211
-			$shareTypes = array_unique(array_map(function (IShare $share) {
212
-				return $share->getShareType();
213
-			}, $shares));
214
-
215
-			return new ShareTypeList($shareTypes);
216
-		});
217
-
218
-		$propFind->handle(self::SHAREES_PROPERTYNAME, function () use ($sabreNode): ShareeList {
219
-			$shares = $this->getShares($sabreNode);
220
-
221
-			return new ShareeList($shares);
222
-		});
223
-	}
224
-
225
-	/**
226
-	 * Ensure that when copying or moving a node it is not transferred from one share to another,
227
-	 * if the user is neither the owner nor has re-share permissions.
228
-	 * For share creation we already ensure this in the share manager.
229
-	 */
230
-	public function validateMoveOrCopy(string $source, string $target): bool {
231
-		try {
232
-			$targetNode = $this->tree->getNodeForPath($target);
233
-		} catch (NotFound) {
234
-			[$targetPath,] = \Sabre\Uri\split($target);
235
-			$targetNode = $this->tree->getNodeForPath($targetPath);
236
-		}
237
-
238
-		$sourceNode = $this->tree->getNodeForPath($source);
239
-		if ((!$sourceNode instanceof DavNode) || (!$targetNode instanceof DavNode)) {
240
-			return true;
241
-		}
242
-
243
-		$sourceNode = $sourceNode->getNode();
244
-		if ($sourceNode->isShareable()) {
245
-			return true;
246
-		}
247
-
248
-		$targetShares = $this->getShare($targetNode->getNode());
249
-		if (empty($targetShares)) {
250
-			// Target is not a share so no re-sharing inprogress
251
-			return true;
252
-		}
253
-
254
-		$sourceStorage = $sourceNode->getStorage();
255
-		if ($sourceStorage->instanceOfStorage(ISharedStorage::class)) {
256
-			// source is also a share - check if it is the same share
257
-
258
-			/** @var ISharedStorage $sourceStorage */
259
-			$sourceShare = $sourceStorage->getShare();
260
-			foreach ($targetShares as $targetShare) {
261
-				if ($targetShare->getId() === $sourceShare->getId()) {
262
-					return true;
263
-				}
264
-			}
265
-		}
266
-
267
-		throw new Forbidden('You cannot move a non-shareable node into a share');
268
-	}
30
+    public const NS_OWNCLOUD = 'http://owncloud.org/ns';
31
+    public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
32
+    public const SHARETYPES_PROPERTYNAME = '{http://owncloud.org/ns}share-types';
33
+    public const SHAREES_PROPERTYNAME = '{http://nextcloud.org/ns}sharees';
34
+
35
+    /**
36
+     * Reference to main server object
37
+     *
38
+     * @var \Sabre\DAV\Server
39
+     */
40
+    private $server;
41
+    private string $userId;
42
+
43
+    /** @var IShare[][] */
44
+    private array $cachedShares = [];
45
+
46
+    /**
47
+     * Tracks which folders have been cached.
48
+     * When a folder is cached, it will appear with its path as key and true
49
+     * as value.
50
+     *
51
+     * @var bool[]
52
+     */
53
+    private array $cachedFolders = [];
54
+
55
+    public function __construct(
56
+        private Tree $tree,
57
+        private IUserSession $userSession,
58
+        private Folder $userFolder,
59
+        private IManager $shareManager,
60
+    ) {
61
+        $this->userId = $userSession->getUser()->getUID();
62
+    }
63
+
64
+    /**
65
+     * This initializes the plugin.
66
+     *
67
+     * This function is called by \Sabre\DAV\Server, after
68
+     * addPlugin is called.
69
+     *
70
+     * This method should set up the required event subscriptions.
71
+     *
72
+     * @return void
73
+     */
74
+    public function initialize(Server $server) {
75
+        $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
76
+        $server->xml->elementMap[self::SHARETYPES_PROPERTYNAME] = ShareTypeList::class;
77
+        $server->protectedProperties[] = self::SHARETYPES_PROPERTYNAME;
78
+        $server->protectedProperties[] = self::SHAREES_PROPERTYNAME;
79
+
80
+        $this->server = $server;
81
+        $this->server->on('preloadCollection', $this->preloadCollection(...));
82
+        $this->server->on('propFind', $this->handleGetProperties(...));
83
+        $this->server->on('beforeCopy', $this->validateMoveOrCopy(...));
84
+        $this->server->on('beforeMove', $this->validateMoveOrCopy(...));
85
+    }
86
+
87
+    /**
88
+     * @param Node $node
89
+     * @return IShare[]
90
+     */
91
+    private function getShare(Node $node): array {
92
+        $result = [];
93
+        $requestedShareTypes = [
94
+            IShare::TYPE_USER,
95
+            IShare::TYPE_GROUP,
96
+            IShare::TYPE_LINK,
97
+            IShare::TYPE_REMOTE,
98
+            IShare::TYPE_EMAIL,
99
+            IShare::TYPE_ROOM,
100
+            IShare::TYPE_CIRCLE,
101
+            IShare::TYPE_DECK,
102
+            IShare::TYPE_SCIENCEMESH,
103
+        ];
104
+
105
+        foreach ($requestedShareTypes as $requestedShareType) {
106
+            $result[] = $this->shareManager->getSharesBy(
107
+                $this->userId,
108
+                $requestedShareType,
109
+                $node,
110
+                false,
111
+                -1
112
+            );
113
+
114
+            // Also check for shares where the user is the recipient
115
+            try {
116
+                $result[] = $this->shareManager->getSharedWith(
117
+                    $this->userId,
118
+                    $requestedShareType,
119
+                    $node,
120
+                    -1
121
+                );
122
+            } catch (BackendError $e) {
123
+                // ignore
124
+            }
125
+        }
126
+
127
+        return array_merge(...$result);
128
+    }
129
+
130
+    /**
131
+     * @param Folder $node
132
+     * @return IShare[][]
133
+     */
134
+    private function getSharesFolder(Folder $node): array {
135
+        return $this->shareManager->getSharesInFolder(
136
+            $this->userId,
137
+            $node,
138
+            true
139
+        );
140
+    }
141
+
142
+    /**
143
+     * @param DavNode $sabreNode
144
+     * @return IShare[]
145
+     */
146
+    private function getShares(DavNode $sabreNode): array {
147
+        if (isset($this->cachedShares[$sabreNode->getId()])) {
148
+            return $this->cachedShares[$sabreNode->getId()];
149
+        }
150
+
151
+        [$parentPath,] = \Sabre\Uri\split($sabreNode->getPath());
152
+        if ($parentPath === '') {
153
+            $parentPath = '/';
154
+        }
155
+
156
+        // if we already cached the folder containing this file
157
+        // then we already know there are no shares here.
158
+        if (!isset($this->cachedFolders[$parentPath])) {
159
+            try {
160
+                $node = $sabreNode->getNode();
161
+            } catch (NotFoundException $e) {
162
+                return [];
163
+            }
164
+
165
+            $shares = $this->getShare($node);
166
+            $this->cachedShares[$sabreNode->getId()] = $shares;
167
+            return $shares;
168
+        }
169
+
170
+        return [];
171
+    }
172
+
173
+    private function preloadCollection(PropFind $propFind, ICollection $collection): void {
174
+        if (!$collection instanceof Directory
175
+            || isset($this->cachedFolders[$collection->getPath()])
176
+            || (
177
+                $propFind->getStatus(self::SHARETYPES_PROPERTYNAME) === null
178
+                && $propFind->getStatus(self::SHAREES_PROPERTYNAME) === null
179
+            )
180
+        ) {
181
+            return;
182
+        }
183
+
184
+        // If the node is a directory and we are requesting share types or sharees
185
+        // then we get all the shares in the folder and cache them.
186
+        // This is more performant than iterating each files afterwards.
187
+        $folderNode = $collection->getNode();
188
+        $this->cachedFolders[$collection->getPath()] = true;
189
+        foreach ($this->getSharesFolder($folderNode) as $id => $shares) {
190
+            $this->cachedShares[$id] = $shares;
191
+        }
192
+    }
193
+
194
+    /**
195
+     * Adds shares to propfind response
196
+     *
197
+     * @param PropFind $propFind propfind object
198
+     * @param \Sabre\DAV\INode $sabreNode sabre node
199
+     */
200
+    public function handleGetProperties(
201
+        PropFind $propFind,
202
+        \Sabre\DAV\INode $sabreNode,
203
+    ) {
204
+        if (!($sabreNode instanceof DavNode)) {
205
+            return;
206
+        }
207
+
208
+        $propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode): ShareTypeList {
209
+            $shares = $this->getShares($sabreNode);
210
+
211
+            $shareTypes = array_unique(array_map(function (IShare $share) {
212
+                return $share->getShareType();
213
+            }, $shares));
214
+
215
+            return new ShareTypeList($shareTypes);
216
+        });
217
+
218
+        $propFind->handle(self::SHAREES_PROPERTYNAME, function () use ($sabreNode): ShareeList {
219
+            $shares = $this->getShares($sabreNode);
220
+
221
+            return new ShareeList($shares);
222
+        });
223
+    }
224
+
225
+    /**
226
+     * Ensure that when copying or moving a node it is not transferred from one share to another,
227
+     * if the user is neither the owner nor has re-share permissions.
228
+     * For share creation we already ensure this in the share manager.
229
+     */
230
+    public function validateMoveOrCopy(string $source, string $target): bool {
231
+        try {
232
+            $targetNode = $this->tree->getNodeForPath($target);
233
+        } catch (NotFound) {
234
+            [$targetPath,] = \Sabre\Uri\split($target);
235
+            $targetNode = $this->tree->getNodeForPath($targetPath);
236
+        }
237
+
238
+        $sourceNode = $this->tree->getNodeForPath($source);
239
+        if ((!$sourceNode instanceof DavNode) || (!$targetNode instanceof DavNode)) {
240
+            return true;
241
+        }
242
+
243
+        $sourceNode = $sourceNode->getNode();
244
+        if ($sourceNode->isShareable()) {
245
+            return true;
246
+        }
247
+
248
+        $targetShares = $this->getShare($targetNode->getNode());
249
+        if (empty($targetShares)) {
250
+            // Target is not a share so no re-sharing inprogress
251
+            return true;
252
+        }
253
+
254
+        $sourceStorage = $sourceNode->getStorage();
255
+        if ($sourceStorage->instanceOfStorage(ISharedStorage::class)) {
256
+            // source is also a share - check if it is the same share
257
+
258
+            /** @var ISharedStorage $sourceStorage */
259
+            $sourceShare = $sourceStorage->getShare();
260
+            foreach ($targetShares as $targetShare) {
261
+                if ($targetShare->getId() === $sourceShare->getId()) {
262
+                    return true;
263
+                }
264
+            }
265
+        }
266
+
267
+        throw new Forbidden('You cannot move a non-shareable node into a share');
268
+    }
269 269
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
 			return $this->cachedShares[$sabreNode->getId()];
149 149
 		}
150 150
 
151
-		[$parentPath,] = \Sabre\Uri\split($sabreNode->getPath());
151
+		[$parentPath, ] = \Sabre\Uri\split($sabreNode->getPath());
152 152
 		if ($parentPath === '') {
153 153
 			$parentPath = '/';
154 154
 		}
@@ -205,17 +205,17 @@  discard block
 block discarded – undo
205 205
 			return;
206 206
 		}
207 207
 
208
-		$propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode): ShareTypeList {
208
+		$propFind->handle(self::SHARETYPES_PROPERTYNAME, function() use ($sabreNode): ShareTypeList {
209 209
 			$shares = $this->getShares($sabreNode);
210 210
 
211
-			$shareTypes = array_unique(array_map(function (IShare $share) {
211
+			$shareTypes = array_unique(array_map(function(IShare $share) {
212 212
 				return $share->getShareType();
213 213
 			}, $shares));
214 214
 
215 215
 			return new ShareTypeList($shareTypes);
216 216
 		});
217 217
 
218
-		$propFind->handle(self::SHAREES_PROPERTYNAME, function () use ($sabreNode): ShareeList {
218
+		$propFind->handle(self::SHAREES_PROPERTYNAME, function() use ($sabreNode): ShareeList {
219 219
 			$shares = $this->getShares($sabreNode);
220 220
 
221 221
 			return new ShareeList($shares);
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
 		try {
232 232
 			$targetNode = $this->tree->getNodeForPath($target);
233 233
 		} catch (NotFound) {
234
-			[$targetPath,] = \Sabre\Uri\split($target);
234
+			[$targetPath, ] = \Sabre\Uri\split($target);
235 235
 			$targetNode = $this->tree->getNodeForPath($targetPath);
236 236
 		}
237 237
 
Please login to merge, or discard this patch.