Passed
Push — master ( 4c6eb9...c449d5 )
by Morris
12:27 queued 10s
created
apps/files_sharing/lib/MountProvider.php 1 patch
Indentation   +200 added lines, -200 removed lines patch added patch discarded remove patch
@@ -40,204 +40,204 @@
 block discarded – undo
40 40
 use OCP\Share\IShare;
41 41
 
42 42
 class MountProvider implements IMountProvider {
43
-	/**
44
-	 * @var \OCP\IConfig
45
-	 */
46
-	protected $config;
47
-
48
-	/**
49
-	 * @var IManager
50
-	 */
51
-	protected $shareManager;
52
-
53
-	/**
54
-	 * @var ILogger
55
-	 */
56
-	protected $logger;
57
-
58
-	/**
59
-	 * @param \OCP\IConfig $config
60
-	 * @param IManager $shareManager
61
-	 * @param ILogger $logger
62
-	 */
63
-	public function __construct(IConfig $config, IManager $shareManager, ILogger $logger) {
64
-		$this->config = $config;
65
-		$this->shareManager = $shareManager;
66
-		$this->logger = $logger;
67
-	}
68
-
69
-
70
-	/**
71
-	 * Get all mountpoints applicable for the user and check for shares where we need to update the etags
72
-	 *
73
-	 * @param \OCP\IUser $user
74
-	 * @param \OCP\Files\Storage\IStorageFactory $loader
75
-	 * @return \OCP\Files\Mount\IMountPoint[]
76
-	 */
77
-	public function getMountsForUser(IUser $user, IStorageFactory $loader) {
78
-		$shares = $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_USER, null, -1);
79
-		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_GROUP, null, -1));
80
-		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_CIRCLE, null, -1));
81
-		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_ROOM, null, -1));
82
-
83
-		// filter out excluded shares and group shares that includes self
84
-		$shares = array_filter($shares, function (\OCP\Share\IShare $share) use ($user) {
85
-			return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID();
86
-		});
87
-
88
-		$superShares = $this->buildSuperShares($shares, $user);
89
-
90
-		$mounts = [];
91
-		$view = new View('/' . $user->getUID() . '/files');
92
-		$ownerViews = [];
93
-		$sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($user->getUID());
94
-		$foldersExistCache = new CappedMemoryCache();
95
-		foreach ($superShares as $share) {
96
-			try {
97
-				/** @var \OCP\Share\IShare $parentShare */
98
-				$parentShare = $share[0];
99
-
100
-				if ($parentShare->getStatus() !== IShare::STATUS_ACCEPTED &&
101
-					($parentShare->getShareType() === IShare::TYPE_GROUP ||
102
-						$parentShare->getShareType() === IShare::TYPE_USERGROUP ||
103
-						$parentShare->getShareType() === IShare::TYPE_USER)) {
104
-					continue;
105
-				}
106
-
107
-				$owner = $parentShare->getShareOwner();
108
-				if (!isset($ownerViews[$owner])) {
109
-					$ownerViews[$owner] = new View('/' . $parentShare->getShareOwner() . '/files');
110
-				}
111
-				$mount = new SharedMount(
112
-					'\OCA\Files_Sharing\SharedStorage',
113
-					$mounts,
114
-					[
115
-						'user' => $user->getUID(),
116
-						// parent share
117
-						'superShare' => $parentShare,
118
-						// children/component of the superShare
119
-						'groupedShares' => $share[1],
120
-						'ownerView' => $ownerViews[$owner],
121
-						'sharingDisabledForUser' => $sharingDisabledForUser
122
-					],
123
-					$loader,
124
-					$view,
125
-					$foldersExistCache
126
-				);
127
-				$mounts[$mount->getMountPoint()] = $mount;
128
-			} catch (\Exception $e) {
129
-				$this->logger->logException($e);
130
-				$this->logger->error('Error while trying to create shared mount');
131
-			}
132
-		}
133
-
134
-		// array_filter removes the null values from the array
135
-		return array_values(array_filter($mounts));
136
-	}
137
-
138
-	/**
139
-	 * Groups shares by path (nodeId) and target path
140
-	 *
141
-	 * @param \OCP\Share\IShare[] $shares
142
-	 * @return \OCP\Share\IShare[][] array of grouped shares, each element in the
143
-	 * array is a group which itself is an array of shares
144
-	 */
145
-	private function groupShares(array $shares) {
146
-		$tmp = [];
147
-
148
-		foreach ($shares as $share) {
149
-			if (!isset($tmp[$share->getNodeId()])) {
150
-				$tmp[$share->getNodeId()] = [];
151
-			}
152
-			$tmp[$share->getNodeId()][] = $share;
153
-		}
154
-
155
-		$result = [];
156
-		// sort by stime, the super share will be based on the least recent share
157
-		foreach ($tmp as &$tmp2) {
158
-			@usort($tmp2, function ($a, $b) {
159
-				$aTime = $a->getShareTime()->getTimestamp();
160
-				$bTime = $b->getShareTime()->getTimestamp();
161
-				if ($aTime === $bTime) {
162
-					return $a->getId() < $b->getId() ? -1 : 1;
163
-				}
164
-				return $aTime < $bTime ? -1 : 1;
165
-			});
166
-			$result[] = $tmp2;
167
-		}
168
-
169
-		return array_values($result);
170
-	}
171
-
172
-	/**
173
-	 * Build super shares (virtual share) by grouping them by node id and target,
174
-	 * then for each group compute the super share and return it along with the matching
175
-	 * grouped shares. The most permissive permissions are used based on the permissions
176
-	 * of all shares within the group.
177
-	 *
178
-	 * @param \OCP\Share\IShare[] $allShares
179
-	 * @param \OCP\IUser $user user
180
-	 * @return array Tuple of [superShare, groupedShares]
181
-	 */
182
-	private function buildSuperShares(array $allShares, \OCP\IUser $user) {
183
-		$result = [];
184
-
185
-		$groupedShares = $this->groupShares($allShares);
186
-
187
-		/** @var \OCP\Share\IShare[] $shares */
188
-		foreach ($groupedShares as $shares) {
189
-			if (count($shares) === 0) {
190
-				continue;
191
-			}
192
-
193
-			$superShare = $this->shareManager->newShare();
194
-
195
-			// compute super share based on first entry of the group
196
-			$superShare->setId($shares[0]->getId())
197
-				->setShareOwner($shares[0]->getShareOwner())
198
-				->setNodeId($shares[0]->getNodeId())
199
-				->setShareType($shares[0]->getShareType())
200
-				->setTarget($shares[0]->getTarget());
201
-
202
-			// use most permissive permissions
203
-			$permissions = 0;
204
-			$status = IShare::STATUS_PENDING;
205
-			foreach ($shares as $share) {
206
-				$permissions |= $share->getPermissions();
207
-				$status = max($status, $share->getStatus());
208
-
209
-				if ($share->getTarget() !== $superShare->getTarget()) {
210
-					// adjust target, for database consistency
211
-					$share->setTarget($superShare->getTarget());
212
-					try {
213
-						$this->shareManager->moveShare($share, $user->getUID());
214
-					} catch (\InvalidArgumentException $e) {
215
-						// ignore as it is not important and we don't want to
216
-						// block FS setup
217
-
218
-						// the subsequent code anyway only uses the target of the
219
-						// super share
220
-
221
-						// such issue can usually happen when dealing with
222
-						// null groups which usually appear with group backend
223
-						// caching inconsistencies
224
-						$this->logger->debug(
225
-							'Could not adjust share target for share ' . $share->getId() . ' to make it consistent: ' . $e->getMessage(),
226
-							['app' => 'files_sharing']
227
-						);
228
-					}
229
-				}
230
-				if (!is_null($share->getNodeCacheEntry())) {
231
-					$superShare->setNodeCacheEntry($share->getNodeCacheEntry());
232
-				}
233
-			}
234
-
235
-			$superShare->setPermissions($permissions)
236
-				->setStatus($status);
237
-
238
-			$result[] = [$superShare, $shares];
239
-		}
240
-
241
-		return $result;
242
-	}
43
+    /**
44
+     * @var \OCP\IConfig
45
+     */
46
+    protected $config;
47
+
48
+    /**
49
+     * @var IManager
50
+     */
51
+    protected $shareManager;
52
+
53
+    /**
54
+     * @var ILogger
55
+     */
56
+    protected $logger;
57
+
58
+    /**
59
+     * @param \OCP\IConfig $config
60
+     * @param IManager $shareManager
61
+     * @param ILogger $logger
62
+     */
63
+    public function __construct(IConfig $config, IManager $shareManager, ILogger $logger) {
64
+        $this->config = $config;
65
+        $this->shareManager = $shareManager;
66
+        $this->logger = $logger;
67
+    }
68
+
69
+
70
+    /**
71
+     * Get all mountpoints applicable for the user and check for shares where we need to update the etags
72
+     *
73
+     * @param \OCP\IUser $user
74
+     * @param \OCP\Files\Storage\IStorageFactory $loader
75
+     * @return \OCP\Files\Mount\IMountPoint[]
76
+     */
77
+    public function getMountsForUser(IUser $user, IStorageFactory $loader) {
78
+        $shares = $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_USER, null, -1);
79
+        $shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_GROUP, null, -1));
80
+        $shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_CIRCLE, null, -1));
81
+        $shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_ROOM, null, -1));
82
+
83
+        // filter out excluded shares and group shares that includes self
84
+        $shares = array_filter($shares, function (\OCP\Share\IShare $share) use ($user) {
85
+            return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID();
86
+        });
87
+
88
+        $superShares = $this->buildSuperShares($shares, $user);
89
+
90
+        $mounts = [];
91
+        $view = new View('/' . $user->getUID() . '/files');
92
+        $ownerViews = [];
93
+        $sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($user->getUID());
94
+        $foldersExistCache = new CappedMemoryCache();
95
+        foreach ($superShares as $share) {
96
+            try {
97
+                /** @var \OCP\Share\IShare $parentShare */
98
+                $parentShare = $share[0];
99
+
100
+                if ($parentShare->getStatus() !== IShare::STATUS_ACCEPTED &&
101
+                    ($parentShare->getShareType() === IShare::TYPE_GROUP ||
102
+                        $parentShare->getShareType() === IShare::TYPE_USERGROUP ||
103
+                        $parentShare->getShareType() === IShare::TYPE_USER)) {
104
+                    continue;
105
+                }
106
+
107
+                $owner = $parentShare->getShareOwner();
108
+                if (!isset($ownerViews[$owner])) {
109
+                    $ownerViews[$owner] = new View('/' . $parentShare->getShareOwner() . '/files');
110
+                }
111
+                $mount = new SharedMount(
112
+                    '\OCA\Files_Sharing\SharedStorage',
113
+                    $mounts,
114
+                    [
115
+                        'user' => $user->getUID(),
116
+                        // parent share
117
+                        'superShare' => $parentShare,
118
+                        // children/component of the superShare
119
+                        'groupedShares' => $share[1],
120
+                        'ownerView' => $ownerViews[$owner],
121
+                        'sharingDisabledForUser' => $sharingDisabledForUser
122
+                    ],
123
+                    $loader,
124
+                    $view,
125
+                    $foldersExistCache
126
+                );
127
+                $mounts[$mount->getMountPoint()] = $mount;
128
+            } catch (\Exception $e) {
129
+                $this->logger->logException($e);
130
+                $this->logger->error('Error while trying to create shared mount');
131
+            }
132
+        }
133
+
134
+        // array_filter removes the null values from the array
135
+        return array_values(array_filter($mounts));
136
+    }
137
+
138
+    /**
139
+     * Groups shares by path (nodeId) and target path
140
+     *
141
+     * @param \OCP\Share\IShare[] $shares
142
+     * @return \OCP\Share\IShare[][] array of grouped shares, each element in the
143
+     * array is a group which itself is an array of shares
144
+     */
145
+    private function groupShares(array $shares) {
146
+        $tmp = [];
147
+
148
+        foreach ($shares as $share) {
149
+            if (!isset($tmp[$share->getNodeId()])) {
150
+                $tmp[$share->getNodeId()] = [];
151
+            }
152
+            $tmp[$share->getNodeId()][] = $share;
153
+        }
154
+
155
+        $result = [];
156
+        // sort by stime, the super share will be based on the least recent share
157
+        foreach ($tmp as &$tmp2) {
158
+            @usort($tmp2, function ($a, $b) {
159
+                $aTime = $a->getShareTime()->getTimestamp();
160
+                $bTime = $b->getShareTime()->getTimestamp();
161
+                if ($aTime === $bTime) {
162
+                    return $a->getId() < $b->getId() ? -1 : 1;
163
+                }
164
+                return $aTime < $bTime ? -1 : 1;
165
+            });
166
+            $result[] = $tmp2;
167
+        }
168
+
169
+        return array_values($result);
170
+    }
171
+
172
+    /**
173
+     * Build super shares (virtual share) by grouping them by node id and target,
174
+     * then for each group compute the super share and return it along with the matching
175
+     * grouped shares. The most permissive permissions are used based on the permissions
176
+     * of all shares within the group.
177
+     *
178
+     * @param \OCP\Share\IShare[] $allShares
179
+     * @param \OCP\IUser $user user
180
+     * @return array Tuple of [superShare, groupedShares]
181
+     */
182
+    private function buildSuperShares(array $allShares, \OCP\IUser $user) {
183
+        $result = [];
184
+
185
+        $groupedShares = $this->groupShares($allShares);
186
+
187
+        /** @var \OCP\Share\IShare[] $shares */
188
+        foreach ($groupedShares as $shares) {
189
+            if (count($shares) === 0) {
190
+                continue;
191
+            }
192
+
193
+            $superShare = $this->shareManager->newShare();
194
+
195
+            // compute super share based on first entry of the group
196
+            $superShare->setId($shares[0]->getId())
197
+                ->setShareOwner($shares[0]->getShareOwner())
198
+                ->setNodeId($shares[0]->getNodeId())
199
+                ->setShareType($shares[0]->getShareType())
200
+                ->setTarget($shares[0]->getTarget());
201
+
202
+            // use most permissive permissions
203
+            $permissions = 0;
204
+            $status = IShare::STATUS_PENDING;
205
+            foreach ($shares as $share) {
206
+                $permissions |= $share->getPermissions();
207
+                $status = max($status, $share->getStatus());
208
+
209
+                if ($share->getTarget() !== $superShare->getTarget()) {
210
+                    // adjust target, for database consistency
211
+                    $share->setTarget($superShare->getTarget());
212
+                    try {
213
+                        $this->shareManager->moveShare($share, $user->getUID());
214
+                    } catch (\InvalidArgumentException $e) {
215
+                        // ignore as it is not important and we don't want to
216
+                        // block FS setup
217
+
218
+                        // the subsequent code anyway only uses the target of the
219
+                        // super share
220
+
221
+                        // such issue can usually happen when dealing with
222
+                        // null groups which usually appear with group backend
223
+                        // caching inconsistencies
224
+                        $this->logger->debug(
225
+                            'Could not adjust share target for share ' . $share->getId() . ' to make it consistent: ' . $e->getMessage(),
226
+                            ['app' => 'files_sharing']
227
+                        );
228
+                    }
229
+                }
230
+                if (!is_null($share->getNodeCacheEntry())) {
231
+                    $superShare->setNodeCacheEntry($share->getNodeCacheEntry());
232
+                }
233
+            }
234
+
235
+            $superShare->setPermissions($permissions)
236
+                ->setStatus($status);
237
+
238
+            $result[] = [$superShare, $shares];
239
+        }
240
+
241
+        return $result;
242
+    }
243 243
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Token/IProvider.php 1 patch
Indentation   +146 added lines, -146 removed lines patch added patch discarded remove patch
@@ -38,150 +38,150 @@
 block discarded – undo
38 38
 interface IProvider {
39 39
 
40 40
 
41
-	/**
42
-	 * Create and persist a new token
43
-	 *
44
-	 * @param string $token
45
-	 * @param string $uid
46
-	 * @param string $loginName
47
-	 * @param string|null $password
48
-	 * @param string $name
49
-	 * @param int $type token type
50
-	 * @param int $remember whether the session token should be used for remember-me
51
-	 * @return IToken
52
-	 * @throws \RuntimeException when OpenSSL reports a problem
53
-	 */
54
-	public function generateToken(string $token,
55
-								  string $uid,
56
-								  string $loginName,
57
-								  $password,
58
-								  string $name,
59
-								  int $type = IToken::TEMPORARY_TOKEN,
60
-								  int $remember = IToken::DO_NOT_REMEMBER): IToken;
61
-
62
-	/**
63
-	 * Get a token by token id
64
-	 *
65
-	 * @param string $tokenId
66
-	 * @throws InvalidTokenException
67
-	 * @throws ExpiredTokenException
68
-	 * @throws WipeTokenException
69
-	 * @return IToken
70
-	 */
71
-	public function getToken(string $tokenId): IToken;
72
-
73
-	/**
74
-	 * Get a token by token id
75
-	 *
76
-	 * @param int $tokenId
77
-	 * @throws InvalidTokenException
78
-	 * @throws ExpiredTokenException
79
-	 * @throws WipeTokenException
80
-	 * @return IToken
81
-	 */
82
-	public function getTokenById(int $tokenId): IToken;
83
-
84
-	/**
85
-	 * Duplicate an existing session token
86
-	 *
87
-	 * @param string $oldSessionId
88
-	 * @param string $sessionId
89
-	 * @throws InvalidTokenException
90
-	 * @throws \RuntimeException when OpenSSL reports a problem
91
-	 * @return IToken The new token
92
-	 */
93
-	public function renewSessionToken(string $oldSessionId, string $sessionId): IToken;
94
-
95
-	/**
96
-	 * Invalidate (delete) the given session token
97
-	 *
98
-	 * @param string $token
99
-	 */
100
-	public function invalidateToken(string $token);
101
-
102
-	/**
103
-	 * Invalidate (delete) the given token
104
-	 *
105
-	 * @param string $uid
106
-	 * @param int $id
107
-	 */
108
-	public function invalidateTokenById(string $uid, int $id);
109
-
110
-	/**
111
-	 * Invalidate (delete) old session tokens
112
-	 */
113
-	public function invalidateOldTokens();
114
-
115
-	/**
116
-	 * Save the updated token
117
-	 *
118
-	 * @param IToken $token
119
-	 */
120
-	public function updateToken(IToken $token);
121
-
122
-	/**
123
-	 * Update token activity timestamp
124
-	 *
125
-	 * @param IToken $token
126
-	 */
127
-	public function updateTokenActivity(IToken $token);
128
-
129
-	/**
130
-	 * Get all tokens of a user
131
-	 *
132
-	 * The provider may limit the number of result rows in case of an abuse
133
-	 * where a high number of (session) tokens is generated
134
-	 *
135
-	 * @param string $uid
136
-	 * @return IToken[]
137
-	 */
138
-	public function getTokenByUser(string $uid): array;
139
-
140
-	/**
141
-	 * Get the (unencrypted) password of the given token
142
-	 *
143
-	 * @param IToken $savedToken
144
-	 * @param string $tokenId
145
-	 * @throws InvalidTokenException
146
-	 * @throws PasswordlessTokenException
147
-	 * @return string
148
-	 */
149
-	public function getPassword(IToken $savedToken, string $tokenId): string;
150
-
151
-	/**
152
-	 * Encrypt and set the password of the given token
153
-	 *
154
-	 * @param IToken $token
155
-	 * @param string $tokenId
156
-	 * @param string $password
157
-	 * @throws InvalidTokenException
158
-	 */
159
-	public function setPassword(IToken $token, string $tokenId, string $password);
160
-
161
-	/**
162
-	 * Rotate the token. Usefull for for example oauth tokens
163
-	 *
164
-	 * @param IToken $token
165
-	 * @param string $oldTokenId
166
-	 * @param string $newTokenId
167
-	 * @return IToken
168
-	 * @throws \RuntimeException when OpenSSL reports a problem
169
-	 */
170
-	public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken;
171
-
172
-	/**
173
-	 * Marks a token as having an invalid password.
174
-	 *
175
-	 * @param IToken $token
176
-	 * @param string $tokenId
177
-	 */
178
-	public function markPasswordInvalid(IToken $token, string $tokenId);
179
-
180
-	/**
181
-	 * Update all the passwords of $uid if required
182
-	 *
183
-	 * @param string $uid
184
-	 * @param string $password
185
-	 */
186
-	public function updatePasswords(string $uid, string $password);
41
+    /**
42
+     * Create and persist a new token
43
+     *
44
+     * @param string $token
45
+     * @param string $uid
46
+     * @param string $loginName
47
+     * @param string|null $password
48
+     * @param string $name
49
+     * @param int $type token type
50
+     * @param int $remember whether the session token should be used for remember-me
51
+     * @return IToken
52
+     * @throws \RuntimeException when OpenSSL reports a problem
53
+     */
54
+    public function generateToken(string $token,
55
+                                    string $uid,
56
+                                    string $loginName,
57
+                                    $password,
58
+                                    string $name,
59
+                                    int $type = IToken::TEMPORARY_TOKEN,
60
+                                    int $remember = IToken::DO_NOT_REMEMBER): IToken;
61
+
62
+    /**
63
+     * Get a token by token id
64
+     *
65
+     * @param string $tokenId
66
+     * @throws InvalidTokenException
67
+     * @throws ExpiredTokenException
68
+     * @throws WipeTokenException
69
+     * @return IToken
70
+     */
71
+    public function getToken(string $tokenId): IToken;
72
+
73
+    /**
74
+     * Get a token by token id
75
+     *
76
+     * @param int $tokenId
77
+     * @throws InvalidTokenException
78
+     * @throws ExpiredTokenException
79
+     * @throws WipeTokenException
80
+     * @return IToken
81
+     */
82
+    public function getTokenById(int $tokenId): IToken;
83
+
84
+    /**
85
+     * Duplicate an existing session token
86
+     *
87
+     * @param string $oldSessionId
88
+     * @param string $sessionId
89
+     * @throws InvalidTokenException
90
+     * @throws \RuntimeException when OpenSSL reports a problem
91
+     * @return IToken The new token
92
+     */
93
+    public function renewSessionToken(string $oldSessionId, string $sessionId): IToken;
94
+
95
+    /**
96
+     * Invalidate (delete) the given session token
97
+     *
98
+     * @param string $token
99
+     */
100
+    public function invalidateToken(string $token);
101
+
102
+    /**
103
+     * Invalidate (delete) the given token
104
+     *
105
+     * @param string $uid
106
+     * @param int $id
107
+     */
108
+    public function invalidateTokenById(string $uid, int $id);
109
+
110
+    /**
111
+     * Invalidate (delete) old session tokens
112
+     */
113
+    public function invalidateOldTokens();
114
+
115
+    /**
116
+     * Save the updated token
117
+     *
118
+     * @param IToken $token
119
+     */
120
+    public function updateToken(IToken $token);
121
+
122
+    /**
123
+     * Update token activity timestamp
124
+     *
125
+     * @param IToken $token
126
+     */
127
+    public function updateTokenActivity(IToken $token);
128
+
129
+    /**
130
+     * Get all tokens of a user
131
+     *
132
+     * The provider may limit the number of result rows in case of an abuse
133
+     * where a high number of (session) tokens is generated
134
+     *
135
+     * @param string $uid
136
+     * @return IToken[]
137
+     */
138
+    public function getTokenByUser(string $uid): array;
139
+
140
+    /**
141
+     * Get the (unencrypted) password of the given token
142
+     *
143
+     * @param IToken $savedToken
144
+     * @param string $tokenId
145
+     * @throws InvalidTokenException
146
+     * @throws PasswordlessTokenException
147
+     * @return string
148
+     */
149
+    public function getPassword(IToken $savedToken, string $tokenId): string;
150
+
151
+    /**
152
+     * Encrypt and set the password of the given token
153
+     *
154
+     * @param IToken $token
155
+     * @param string $tokenId
156
+     * @param string $password
157
+     * @throws InvalidTokenException
158
+     */
159
+    public function setPassword(IToken $token, string $tokenId, string $password);
160
+
161
+    /**
162
+     * Rotate the token. Usefull for for example oauth tokens
163
+     *
164
+     * @param IToken $token
165
+     * @param string $oldTokenId
166
+     * @param string $newTokenId
167
+     * @return IToken
168
+     * @throws \RuntimeException when OpenSSL reports a problem
169
+     */
170
+    public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken;
171
+
172
+    /**
173
+     * Marks a token as having an invalid password.
174
+     *
175
+     * @param IToken $token
176
+     * @param string $tokenId
177
+     */
178
+    public function markPasswordInvalid(IToken $token, string $tokenId);
179
+
180
+    /**
181
+     * Update all the passwords of $uid if required
182
+     *
183
+     * @param string $uid
184
+     * @param string $password
185
+     */
186
+    public function updatePasswords(string $uid, string $password);
187 187
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Token/PublicKeyTokenProvider.php 1 patch
Indentation   +388 added lines, -388 removed lines patch added patch discarded remove patch
@@ -42,392 +42,392 @@
 block discarded – undo
42 42
 use OCP\Security\ICrypto;
43 43
 
44 44
 class PublicKeyTokenProvider implements IProvider {
45
-	/** @var PublicKeyTokenMapper */
46
-	private $mapper;
47
-
48
-	/** @var ICrypto */
49
-	private $crypto;
50
-
51
-	/** @var IConfig */
52
-	private $config;
53
-
54
-	/** @var ILogger $logger */
55
-	private $logger;
56
-
57
-	/** @var ITimeFactory $time */
58
-	private $time;
59
-
60
-	/** @var CappedMemoryCache */
61
-	private $cache;
62
-
63
-	public function __construct(PublicKeyTokenMapper $mapper,
64
-								ICrypto $crypto,
65
-								IConfig $config,
66
-								ILogger $logger,
67
-								ITimeFactory $time) {
68
-		$this->mapper = $mapper;
69
-		$this->crypto = $crypto;
70
-		$this->config = $config;
71
-		$this->logger = $logger;
72
-		$this->time = $time;
73
-
74
-		$this->cache = new CappedMemoryCache();
75
-	}
76
-
77
-	/**
78
-	 * {@inheritDoc}
79
-	 */
80
-	public function generateToken(string $token,
81
-								  string $uid,
82
-								  string $loginName,
83
-								  $password,
84
-								  string $name,
85
-								  int $type = IToken::TEMPORARY_TOKEN,
86
-								  int $remember = IToken::DO_NOT_REMEMBER): IToken {
87
-		$dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
88
-		$this->mapper->insert($dbToken);
89
-
90
-		// Add the token to the cache
91
-		$this->cache[$dbToken->getToken()] = $dbToken;
92
-
93
-		return $dbToken;
94
-	}
95
-
96
-	public function getToken(string $tokenId): IToken {
97
-		$tokenHash = $this->hashToken($tokenId);
98
-
99
-		if (isset($this->cache[$tokenHash])) {
100
-			$token = $this->cache[$tokenHash];
101
-		} else {
102
-			try {
103
-				$token = $this->mapper->getToken($this->hashToken($tokenId));
104
-				$this->cache[$token->getToken()] = $token;
105
-			} catch (DoesNotExistException $ex) {
106
-				throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
107
-			}
108
-		}
109
-
110
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
111
-			throw new ExpiredTokenException($token);
112
-		}
113
-
114
-		if ($token->getType() === IToken::WIPE_TOKEN) {
115
-			throw new WipeTokenException($token);
116
-		}
117
-
118
-		if ($token->getPasswordInvalid() === true) {
119
-			//The password is invalid we should throw an TokenPasswordExpiredException
120
-			throw new TokenPasswordExpiredException($token);
121
-		}
122
-
123
-		return $token;
124
-	}
125
-
126
-	public function getTokenById(int $tokenId): IToken {
127
-		try {
128
-			$token = $this->mapper->getTokenById($tokenId);
129
-		} catch (DoesNotExistException $ex) {
130
-			throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex);
131
-		}
132
-
133
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
134
-			throw new ExpiredTokenException($token);
135
-		}
136
-
137
-		if ($token->getType() === IToken::WIPE_TOKEN) {
138
-			throw new WipeTokenException($token);
139
-		}
140
-
141
-		if ($token->getPasswordInvalid() === true) {
142
-			//The password is invalid we should throw an TokenPasswordExpiredException
143
-			throw new TokenPasswordExpiredException($token);
144
-		}
145
-
146
-		return $token;
147
-	}
148
-
149
-	public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
150
-		$this->cache->clear();
151
-
152
-		$token = $this->getToken($oldSessionId);
153
-
154
-		if (!($token instanceof PublicKeyToken)) {
155
-			throw new InvalidTokenException("Invalid token type");
156
-		}
157
-
158
-		$password = null;
159
-		if (!is_null($token->getPassword())) {
160
-			$privateKey = $this->decrypt($token->getPrivateKey(), $oldSessionId);
161
-			$password = $this->decryptPassword($token->getPassword(), $privateKey);
162
-		}
163
-
164
-		$newToken = $this->generateToken(
165
-			$sessionId,
166
-			$token->getUID(),
167
-			$token->getLoginName(),
168
-			$password,
169
-			$token->getName(),
170
-			IToken::TEMPORARY_TOKEN,
171
-			$token->getRemember()
172
-		);
173
-
174
-		$this->mapper->delete($token);
175
-
176
-		return $newToken;
177
-	}
178
-
179
-	public function invalidateToken(string $token) {
180
-		$this->cache->clear();
181
-
182
-		$this->mapper->invalidate($this->hashToken($token));
183
-	}
184
-
185
-	public function invalidateTokenById(string $uid, int $id) {
186
-		$this->cache->clear();
187
-
188
-		$this->mapper->deleteById($uid, $id);
189
-	}
190
-
191
-	public function invalidateOldTokens() {
192
-		$this->cache->clear();
193
-
194
-		$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
195
-		$this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
196
-		$this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
197
-		$rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
198
-		$this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
199
-		$this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
200
-	}
201
-
202
-	public function updateToken(IToken $token) {
203
-		$this->cache->clear();
204
-
205
-		if (!($token instanceof PublicKeyToken)) {
206
-			throw new InvalidTokenException("Invalid token type");
207
-		}
208
-		$this->mapper->update($token);
209
-	}
210
-
211
-	public function updateTokenActivity(IToken $token) {
212
-		$this->cache->clear();
213
-
214
-		if (!($token instanceof PublicKeyToken)) {
215
-			throw new InvalidTokenException("Invalid token type");
216
-		}
217
-		/** @var DefaultToken $token */
218
-		$now = $this->time->getTime();
219
-		if ($token->getLastActivity() < ($now - 60)) {
220
-			// Update token only once per minute
221
-			$token->setLastActivity($now);
222
-			$this->mapper->update($token);
223
-		}
224
-	}
225
-
226
-	public function getTokenByUser(string $uid): array {
227
-		return $this->mapper->getTokenByUser($uid);
228
-	}
229
-
230
-	public function getPassword(IToken $savedToken, string $tokenId): string {
231
-		if (!($savedToken instanceof PublicKeyToken)) {
232
-			throw new InvalidTokenException("Invalid token type");
233
-		}
234
-
235
-		if ($savedToken->getPassword() === null) {
236
-			throw new PasswordlessTokenException();
237
-		}
238
-
239
-		// Decrypt private key with tokenId
240
-		$privateKey = $this->decrypt($savedToken->getPrivateKey(), $tokenId);
241
-
242
-		// Decrypt password with private key
243
-		return $this->decryptPassword($savedToken->getPassword(), $privateKey);
244
-	}
245
-
246
-	public function setPassword(IToken $token, string $tokenId, string $password) {
247
-		$this->cache->clear();
248
-
249
-		if (!($token instanceof PublicKeyToken)) {
250
-			throw new InvalidTokenException("Invalid token type");
251
-		}
252
-
253
-		// When changing passwords all temp tokens are deleted
254
-		$this->mapper->deleteTempToken($token);
255
-
256
-		// Update the password for all tokens
257
-		$tokens = $this->mapper->getTokenByUser($token->getUID());
258
-		foreach ($tokens as $t) {
259
-			$publicKey = $t->getPublicKey();
260
-			$t->setPassword($this->encryptPassword($password, $publicKey));
261
-			$this->updateToken($t);
262
-		}
263
-	}
264
-
265
-	public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
266
-		$this->cache->clear();
267
-
268
-		if (!($token instanceof PublicKeyToken)) {
269
-			throw new InvalidTokenException("Invalid token type");
270
-		}
271
-
272
-		// Decrypt private key with oldTokenId
273
-		$privateKey = $this->decrypt($token->getPrivateKey(), $oldTokenId);
274
-		// Encrypt with the new token
275
-		$token->setPrivateKey($this->encrypt($privateKey, $newTokenId));
276
-
277
-		$token->setToken($this->hashToken($newTokenId));
278
-		$this->updateToken($token);
279
-
280
-		return $token;
281
-	}
282
-
283
-	private function encrypt(string $plaintext, string $token): string {
284
-		$secret = $this->config->getSystemValue('secret');
285
-		return $this->crypto->encrypt($plaintext, $token . $secret);
286
-	}
287
-
288
-	/**
289
-	 * @throws InvalidTokenException
290
-	 */
291
-	private function decrypt(string $cipherText, string $token): string {
292
-		$secret = $this->config->getSystemValue('secret');
293
-		try {
294
-			return $this->crypto->decrypt($cipherText, $token . $secret);
295
-		} catch (\Exception $ex) {
296
-			// Delete the invalid token
297
-			$this->invalidateToken($token);
298
-			throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex);
299
-		}
300
-	}
301
-
302
-	private function encryptPassword(string $password, string $publicKey): string {
303
-		openssl_public_encrypt($password, $encryptedPassword, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
304
-		$encryptedPassword = base64_encode($encryptedPassword);
305
-
306
-		return $encryptedPassword;
307
-	}
308
-
309
-	private function decryptPassword(string $encryptedPassword, string $privateKey): string {
310
-		$encryptedPassword = base64_decode($encryptedPassword);
311
-		openssl_private_decrypt($encryptedPassword, $password, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
312
-
313
-		return $password;
314
-	}
315
-
316
-	private function hashToken(string $token): string {
317
-		$secret = $this->config->getSystemValue('secret');
318
-		return hash('sha512', $token . $secret);
319
-	}
320
-
321
-	/**
322
-	 * Convert a DefaultToken to a publicKeyToken
323
-	 * This will also be updated directly in the Database
324
-	 * @throws \RuntimeException when OpenSSL reports a problem
325
-	 */
326
-	public function convertToken(DefaultToken $defaultToken, string $token, $password): PublicKeyToken {
327
-		$this->cache->clear();
328
-
329
-		$pkToken = $this->newToken(
330
-			$token,
331
-			$defaultToken->getUID(),
332
-			$defaultToken->getLoginName(),
333
-			$password,
334
-			$defaultToken->getName(),
335
-			$defaultToken->getType(),
336
-			$defaultToken->getRemember()
337
-		);
338
-
339
-		$pkToken->setExpires($defaultToken->getExpires());
340
-		$pkToken->setId($defaultToken->getId());
341
-
342
-		return $this->mapper->update($pkToken);
343
-	}
344
-
345
-	/**
346
-	 * @throws \RuntimeException when OpenSSL reports a problem
347
-	 */
348
-	private function newToken(string $token,
349
-							  string $uid,
350
-							  string $loginName,
351
-							  $password,
352
-							  string $name,
353
-							  int $type,
354
-							  int $remember): PublicKeyToken {
355
-		$dbToken = new PublicKeyToken();
356
-		$dbToken->setUid($uid);
357
-		$dbToken->setLoginName($loginName);
358
-
359
-		$config = array_merge([
360
-			'digest_alg' => 'sha512',
361
-			'private_key_bits' => 2048,
362
-		], $this->config->getSystemValue('openssl', []));
363
-
364
-		// Generate new key
365
-		$res = openssl_pkey_new($config);
366
-		if ($res === false) {
367
-			$this->logOpensslError();
368
-			throw new \RuntimeException('OpenSSL reported a problem');
369
-		}
370
-
371
-		if (openssl_pkey_export($res, $privateKey, null, $config) === false) {
372
-			$this->logOpensslError();
373
-			throw new \RuntimeException('OpenSSL reported a problem');
374
-		}
375
-
376
-		// Extract the public key from $res to $pubKey
377
-		$publicKey = openssl_pkey_get_details($res);
378
-		$publicKey = $publicKey['key'];
379
-
380
-		$dbToken->setPublicKey($publicKey);
381
-		$dbToken->setPrivateKey($this->encrypt($privateKey, $token));
382
-
383
-		if (!is_null($password)) {
384
-			$dbToken->setPassword($this->encryptPassword($password, $publicKey));
385
-		}
386
-
387
-		$dbToken->setName($name);
388
-		$dbToken->setToken($this->hashToken($token));
389
-		$dbToken->setType($type);
390
-		$dbToken->setRemember($remember);
391
-		$dbToken->setLastActivity($this->time->getTime());
392
-		$dbToken->setLastCheck($this->time->getTime());
393
-		$dbToken->setVersion(PublicKeyToken::VERSION);
394
-
395
-		return $dbToken;
396
-	}
397
-
398
-	public function markPasswordInvalid(IToken $token, string $tokenId) {
399
-		$this->cache->clear();
400
-
401
-		if (!($token instanceof PublicKeyToken)) {
402
-			throw new InvalidTokenException("Invalid token type");
403
-		}
404
-
405
-		$token->setPasswordInvalid(true);
406
-		$this->mapper->update($token);
407
-	}
408
-
409
-	public function updatePasswords(string $uid, string $password) {
410
-		$this->cache->clear();
411
-
412
-		if (!$this->mapper->hasExpiredTokens($uid)) {
413
-			// Nothing to do here
414
-			return;
415
-		}
416
-
417
-		// Update the password for all tokens
418
-		$tokens = $this->mapper->getTokenByUser($uid);
419
-		foreach ($tokens as $t) {
420
-			$publicKey = $t->getPublicKey();
421
-			$t->setPassword($this->encryptPassword($password, $publicKey));
422
-			$this->updateToken($t);
423
-		}
424
-	}
425
-
426
-	private function logOpensslError() {
427
-		$errors = [];
428
-		while ($error = openssl_error_string()) {
429
-			$errors[] = $error;
430
-		}
431
-		$this->logger->critical('Something is wrong with your openssl setup: ' . implode(', ', $errors));
432
-	}
45
+    /** @var PublicKeyTokenMapper */
46
+    private $mapper;
47
+
48
+    /** @var ICrypto */
49
+    private $crypto;
50
+
51
+    /** @var IConfig */
52
+    private $config;
53
+
54
+    /** @var ILogger $logger */
55
+    private $logger;
56
+
57
+    /** @var ITimeFactory $time */
58
+    private $time;
59
+
60
+    /** @var CappedMemoryCache */
61
+    private $cache;
62
+
63
+    public function __construct(PublicKeyTokenMapper $mapper,
64
+                                ICrypto $crypto,
65
+                                IConfig $config,
66
+                                ILogger $logger,
67
+                                ITimeFactory $time) {
68
+        $this->mapper = $mapper;
69
+        $this->crypto = $crypto;
70
+        $this->config = $config;
71
+        $this->logger = $logger;
72
+        $this->time = $time;
73
+
74
+        $this->cache = new CappedMemoryCache();
75
+    }
76
+
77
+    /**
78
+     * {@inheritDoc}
79
+     */
80
+    public function generateToken(string $token,
81
+                                    string $uid,
82
+                                    string $loginName,
83
+                                    $password,
84
+                                    string $name,
85
+                                    int $type = IToken::TEMPORARY_TOKEN,
86
+                                    int $remember = IToken::DO_NOT_REMEMBER): IToken {
87
+        $dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
88
+        $this->mapper->insert($dbToken);
89
+
90
+        // Add the token to the cache
91
+        $this->cache[$dbToken->getToken()] = $dbToken;
92
+
93
+        return $dbToken;
94
+    }
95
+
96
+    public function getToken(string $tokenId): IToken {
97
+        $tokenHash = $this->hashToken($tokenId);
98
+
99
+        if (isset($this->cache[$tokenHash])) {
100
+            $token = $this->cache[$tokenHash];
101
+        } else {
102
+            try {
103
+                $token = $this->mapper->getToken($this->hashToken($tokenId));
104
+                $this->cache[$token->getToken()] = $token;
105
+            } catch (DoesNotExistException $ex) {
106
+                throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
107
+            }
108
+        }
109
+
110
+        if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
111
+            throw new ExpiredTokenException($token);
112
+        }
113
+
114
+        if ($token->getType() === IToken::WIPE_TOKEN) {
115
+            throw new WipeTokenException($token);
116
+        }
117
+
118
+        if ($token->getPasswordInvalid() === true) {
119
+            //The password is invalid we should throw an TokenPasswordExpiredException
120
+            throw new TokenPasswordExpiredException($token);
121
+        }
122
+
123
+        return $token;
124
+    }
125
+
126
+    public function getTokenById(int $tokenId): IToken {
127
+        try {
128
+            $token = $this->mapper->getTokenById($tokenId);
129
+        } catch (DoesNotExistException $ex) {
130
+            throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex);
131
+        }
132
+
133
+        if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
134
+            throw new ExpiredTokenException($token);
135
+        }
136
+
137
+        if ($token->getType() === IToken::WIPE_TOKEN) {
138
+            throw new WipeTokenException($token);
139
+        }
140
+
141
+        if ($token->getPasswordInvalid() === true) {
142
+            //The password is invalid we should throw an TokenPasswordExpiredException
143
+            throw new TokenPasswordExpiredException($token);
144
+        }
145
+
146
+        return $token;
147
+    }
148
+
149
+    public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
150
+        $this->cache->clear();
151
+
152
+        $token = $this->getToken($oldSessionId);
153
+
154
+        if (!($token instanceof PublicKeyToken)) {
155
+            throw new InvalidTokenException("Invalid token type");
156
+        }
157
+
158
+        $password = null;
159
+        if (!is_null($token->getPassword())) {
160
+            $privateKey = $this->decrypt($token->getPrivateKey(), $oldSessionId);
161
+            $password = $this->decryptPassword($token->getPassword(), $privateKey);
162
+        }
163
+
164
+        $newToken = $this->generateToken(
165
+            $sessionId,
166
+            $token->getUID(),
167
+            $token->getLoginName(),
168
+            $password,
169
+            $token->getName(),
170
+            IToken::TEMPORARY_TOKEN,
171
+            $token->getRemember()
172
+        );
173
+
174
+        $this->mapper->delete($token);
175
+
176
+        return $newToken;
177
+    }
178
+
179
+    public function invalidateToken(string $token) {
180
+        $this->cache->clear();
181
+
182
+        $this->mapper->invalidate($this->hashToken($token));
183
+    }
184
+
185
+    public function invalidateTokenById(string $uid, int $id) {
186
+        $this->cache->clear();
187
+
188
+        $this->mapper->deleteById($uid, $id);
189
+    }
190
+
191
+    public function invalidateOldTokens() {
192
+        $this->cache->clear();
193
+
194
+        $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
195
+        $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
196
+        $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
197
+        $rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
198
+        $this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
199
+        $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
200
+    }
201
+
202
+    public function updateToken(IToken $token) {
203
+        $this->cache->clear();
204
+
205
+        if (!($token instanceof PublicKeyToken)) {
206
+            throw new InvalidTokenException("Invalid token type");
207
+        }
208
+        $this->mapper->update($token);
209
+    }
210
+
211
+    public function updateTokenActivity(IToken $token) {
212
+        $this->cache->clear();
213
+
214
+        if (!($token instanceof PublicKeyToken)) {
215
+            throw new InvalidTokenException("Invalid token type");
216
+        }
217
+        /** @var DefaultToken $token */
218
+        $now = $this->time->getTime();
219
+        if ($token->getLastActivity() < ($now - 60)) {
220
+            // Update token only once per minute
221
+            $token->setLastActivity($now);
222
+            $this->mapper->update($token);
223
+        }
224
+    }
225
+
226
+    public function getTokenByUser(string $uid): array {
227
+        return $this->mapper->getTokenByUser($uid);
228
+    }
229
+
230
+    public function getPassword(IToken $savedToken, string $tokenId): string {
231
+        if (!($savedToken instanceof PublicKeyToken)) {
232
+            throw new InvalidTokenException("Invalid token type");
233
+        }
234
+
235
+        if ($savedToken->getPassword() === null) {
236
+            throw new PasswordlessTokenException();
237
+        }
238
+
239
+        // Decrypt private key with tokenId
240
+        $privateKey = $this->decrypt($savedToken->getPrivateKey(), $tokenId);
241
+
242
+        // Decrypt password with private key
243
+        return $this->decryptPassword($savedToken->getPassword(), $privateKey);
244
+    }
245
+
246
+    public function setPassword(IToken $token, string $tokenId, string $password) {
247
+        $this->cache->clear();
248
+
249
+        if (!($token instanceof PublicKeyToken)) {
250
+            throw new InvalidTokenException("Invalid token type");
251
+        }
252
+
253
+        // When changing passwords all temp tokens are deleted
254
+        $this->mapper->deleteTempToken($token);
255
+
256
+        // Update the password for all tokens
257
+        $tokens = $this->mapper->getTokenByUser($token->getUID());
258
+        foreach ($tokens as $t) {
259
+            $publicKey = $t->getPublicKey();
260
+            $t->setPassword($this->encryptPassword($password, $publicKey));
261
+            $this->updateToken($t);
262
+        }
263
+    }
264
+
265
+    public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
266
+        $this->cache->clear();
267
+
268
+        if (!($token instanceof PublicKeyToken)) {
269
+            throw new InvalidTokenException("Invalid token type");
270
+        }
271
+
272
+        // Decrypt private key with oldTokenId
273
+        $privateKey = $this->decrypt($token->getPrivateKey(), $oldTokenId);
274
+        // Encrypt with the new token
275
+        $token->setPrivateKey($this->encrypt($privateKey, $newTokenId));
276
+
277
+        $token->setToken($this->hashToken($newTokenId));
278
+        $this->updateToken($token);
279
+
280
+        return $token;
281
+    }
282
+
283
+    private function encrypt(string $plaintext, string $token): string {
284
+        $secret = $this->config->getSystemValue('secret');
285
+        return $this->crypto->encrypt($plaintext, $token . $secret);
286
+    }
287
+
288
+    /**
289
+     * @throws InvalidTokenException
290
+     */
291
+    private function decrypt(string $cipherText, string $token): string {
292
+        $secret = $this->config->getSystemValue('secret');
293
+        try {
294
+            return $this->crypto->decrypt($cipherText, $token . $secret);
295
+        } catch (\Exception $ex) {
296
+            // Delete the invalid token
297
+            $this->invalidateToken($token);
298
+            throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex);
299
+        }
300
+    }
301
+
302
+    private function encryptPassword(string $password, string $publicKey): string {
303
+        openssl_public_encrypt($password, $encryptedPassword, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
304
+        $encryptedPassword = base64_encode($encryptedPassword);
305
+
306
+        return $encryptedPassword;
307
+    }
308
+
309
+    private function decryptPassword(string $encryptedPassword, string $privateKey): string {
310
+        $encryptedPassword = base64_decode($encryptedPassword);
311
+        openssl_private_decrypt($encryptedPassword, $password, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
312
+
313
+        return $password;
314
+    }
315
+
316
+    private function hashToken(string $token): string {
317
+        $secret = $this->config->getSystemValue('secret');
318
+        return hash('sha512', $token . $secret);
319
+    }
320
+
321
+    /**
322
+     * Convert a DefaultToken to a publicKeyToken
323
+     * This will also be updated directly in the Database
324
+     * @throws \RuntimeException when OpenSSL reports a problem
325
+     */
326
+    public function convertToken(DefaultToken $defaultToken, string $token, $password): PublicKeyToken {
327
+        $this->cache->clear();
328
+
329
+        $pkToken = $this->newToken(
330
+            $token,
331
+            $defaultToken->getUID(),
332
+            $defaultToken->getLoginName(),
333
+            $password,
334
+            $defaultToken->getName(),
335
+            $defaultToken->getType(),
336
+            $defaultToken->getRemember()
337
+        );
338
+
339
+        $pkToken->setExpires($defaultToken->getExpires());
340
+        $pkToken->setId($defaultToken->getId());
341
+
342
+        return $this->mapper->update($pkToken);
343
+    }
344
+
345
+    /**
346
+     * @throws \RuntimeException when OpenSSL reports a problem
347
+     */
348
+    private function newToken(string $token,
349
+                                string $uid,
350
+                                string $loginName,
351
+                                $password,
352
+                                string $name,
353
+                                int $type,
354
+                                int $remember): PublicKeyToken {
355
+        $dbToken = new PublicKeyToken();
356
+        $dbToken->setUid($uid);
357
+        $dbToken->setLoginName($loginName);
358
+
359
+        $config = array_merge([
360
+            'digest_alg' => 'sha512',
361
+            'private_key_bits' => 2048,
362
+        ], $this->config->getSystemValue('openssl', []));
363
+
364
+        // Generate new key
365
+        $res = openssl_pkey_new($config);
366
+        if ($res === false) {
367
+            $this->logOpensslError();
368
+            throw new \RuntimeException('OpenSSL reported a problem');
369
+        }
370
+
371
+        if (openssl_pkey_export($res, $privateKey, null, $config) === false) {
372
+            $this->logOpensslError();
373
+            throw new \RuntimeException('OpenSSL reported a problem');
374
+        }
375
+
376
+        // Extract the public key from $res to $pubKey
377
+        $publicKey = openssl_pkey_get_details($res);
378
+        $publicKey = $publicKey['key'];
379
+
380
+        $dbToken->setPublicKey($publicKey);
381
+        $dbToken->setPrivateKey($this->encrypt($privateKey, $token));
382
+
383
+        if (!is_null($password)) {
384
+            $dbToken->setPassword($this->encryptPassword($password, $publicKey));
385
+        }
386
+
387
+        $dbToken->setName($name);
388
+        $dbToken->setToken($this->hashToken($token));
389
+        $dbToken->setType($type);
390
+        $dbToken->setRemember($remember);
391
+        $dbToken->setLastActivity($this->time->getTime());
392
+        $dbToken->setLastCheck($this->time->getTime());
393
+        $dbToken->setVersion(PublicKeyToken::VERSION);
394
+
395
+        return $dbToken;
396
+    }
397
+
398
+    public function markPasswordInvalid(IToken $token, string $tokenId) {
399
+        $this->cache->clear();
400
+
401
+        if (!($token instanceof PublicKeyToken)) {
402
+            throw new InvalidTokenException("Invalid token type");
403
+        }
404
+
405
+        $token->setPasswordInvalid(true);
406
+        $this->mapper->update($token);
407
+    }
408
+
409
+    public function updatePasswords(string $uid, string $password) {
410
+        $this->cache->clear();
411
+
412
+        if (!$this->mapper->hasExpiredTokens($uid)) {
413
+            // Nothing to do here
414
+            return;
415
+        }
416
+
417
+        // Update the password for all tokens
418
+        $tokens = $this->mapper->getTokenByUser($uid);
419
+        foreach ($tokens as $t) {
420
+            $publicKey = $t->getPublicKey();
421
+            $t->setPassword($this->encryptPassword($password, $publicKey));
422
+            $this->updateToken($t);
423
+        }
424
+    }
425
+
426
+    private function logOpensslError() {
427
+        $errors = [];
428
+        while ($error = openssl_error_string()) {
429
+            $errors[] = $error;
430
+        }
431
+        $this->logger->critical('Something is wrong with your openssl setup: ' . implode(', ', $errors));
432
+    }
433 433
 }
Please login to merge, or discard this patch.
lib/private/URLGenerator.php 1 patch
Indentation   +197 added lines, -197 removed lines patch added patch discarded remove patch
@@ -50,226 +50,226 @@
 block discarded – undo
50 50
  * Class to generate URLs
51 51
  */
52 52
 class URLGenerator implements IURLGenerator {
53
-	/** @var IConfig */
54
-	private $config;
55
-	/** @var ICacheFactory */
56
-	private $cacheFactory;
57
-	/** @var IRequest */
58
-	private $request;
53
+    /** @var IConfig */
54
+    private $config;
55
+    /** @var ICacheFactory */
56
+    private $cacheFactory;
57
+    /** @var IRequest */
58
+    private $request;
59 59
 
60
-	/**
61
-	 * @param IConfig $config
62
-	 * @param ICacheFactory $cacheFactory
63
-	 * @param IRequest $request
64
-	 */
65
-	public function __construct(IConfig $config,
66
-								ICacheFactory $cacheFactory,
67
-								IRequest $request) {
68
-		$this->config = $config;
69
-		$this->cacheFactory = $cacheFactory;
70
-		$this->request = $request;
71
-	}
60
+    /**
61
+     * @param IConfig $config
62
+     * @param ICacheFactory $cacheFactory
63
+     * @param IRequest $request
64
+     */
65
+    public function __construct(IConfig $config,
66
+                                ICacheFactory $cacheFactory,
67
+                                IRequest $request) {
68
+        $this->config = $config;
69
+        $this->cacheFactory = $cacheFactory;
70
+        $this->request = $request;
71
+    }
72 72
 
73
-	/**
74
-	 * Creates an url using a defined route
75
-	 *
76
-	 * @param string $routeName
77
-	 * @param array $arguments args with param=>value, will be appended to the returned url
78
-	 * @return string the url
79
-	 *
80
-	 * Returns a url to the given route.
81
-	 */
82
-	public function linkToRoute(string $routeName, array $arguments = []): string {
83
-		// TODO: mock router
84
-		return \OC::$server->getRouter()->generate($routeName, $arguments);
85
-	}
73
+    /**
74
+     * Creates an url using a defined route
75
+     *
76
+     * @param string $routeName
77
+     * @param array $arguments args with param=>value, will be appended to the returned url
78
+     * @return string the url
79
+     *
80
+     * Returns a url to the given route.
81
+     */
82
+    public function linkToRoute(string $routeName, array $arguments = []): string {
83
+        // TODO: mock router
84
+        return \OC::$server->getRouter()->generate($routeName, $arguments);
85
+    }
86 86
 
87
-	/**
88
-	 * Creates an absolute url using a defined route
89
-	 * @param string $routeName
90
-	 * @param array $arguments args with param=>value, will be appended to the returned url
91
-	 * @return string the url
92
-	 *
93
-	 * Returns an absolute url to the given route.
94
-	 */
95
-	public function linkToRouteAbsolute(string $routeName, array $arguments = []): string {
96
-		return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments));
97
-	}
87
+    /**
88
+     * Creates an absolute url using a defined route
89
+     * @param string $routeName
90
+     * @param array $arguments args with param=>value, will be appended to the returned url
91
+     * @return string the url
92
+     *
93
+     * Returns an absolute url to the given route.
94
+     */
95
+    public function linkToRouteAbsolute(string $routeName, array $arguments = []): string {
96
+        return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments));
97
+    }
98 98
 
99
-	public function linkToOCSRouteAbsolute(string $routeName, array $arguments = []): string {
100
-		$route = \OC::$server->getRouter()->generate('ocs.'.$routeName, $arguments, false);
99
+    public function linkToOCSRouteAbsolute(string $routeName, array $arguments = []): string {
100
+        $route = \OC::$server->getRouter()->generate('ocs.'.$routeName, $arguments, false);
101 101
 
102
-		$indexPhpPos = strpos($route, '/index.php/');
103
-		if ($indexPhpPos !== false) {
104
-			$route = substr($route, $indexPhpPos + 10);
105
-		}
102
+        $indexPhpPos = strpos($route, '/index.php/');
103
+        if ($indexPhpPos !== false) {
104
+            $route = substr($route, $indexPhpPos + 10);
105
+        }
106 106
 
107
-		$route = substr($route, 7);
108
-		$route = '/ocs/v2.php' . $route;
107
+        $route = substr($route, 7);
108
+        $route = '/ocs/v2.php' . $route;
109 109
 
110
-		return $this->getAbsoluteURL($route);
111
-	}
110
+        return $this->getAbsoluteURL($route);
111
+    }
112 112
 
113
-	/**
114
-	 * Creates an url
115
-	 *
116
-	 * @param string $appName app
117
-	 * @param string $file file
118
-	 * @param array $args array with param=>value, will be appended to the returned url
119
-	 *    The value of $args will be urlencoded
120
-	 * @return string the url
121
-	 *
122
-	 * Returns a url to the given app and file.
123
-	 */
124
-	public function linkTo(string $appName, string $file, array $args = []): string {
125
-		$frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
113
+    /**
114
+     * Creates an url
115
+     *
116
+     * @param string $appName app
117
+     * @param string $file file
118
+     * @param array $args array with param=>value, will be appended to the returned url
119
+     *    The value of $args will be urlencoded
120
+     * @return string the url
121
+     *
122
+     * Returns a url to the given app and file.
123
+     */
124
+    public function linkTo(string $appName, string $file, array $args = []): string {
125
+        $frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
126 126
 
127
-		if ($appName !== '') {
128
-			$app_path = \OC_App::getAppPath($appName);
129
-			// Check if the app is in the app folder
130
-			if ($app_path && file_exists($app_path . '/' . $file)) {
131
-				if (substr($file, -3) === 'php') {
132
-					$urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $appName;
133
-					if ($frontControllerActive) {
134
-						$urlLinkTo = \OC::$WEBROOT . '/apps/' . $appName;
135
-					}
136
-					$urlLinkTo .= ($file !== 'index.php') ? '/' . $file : '';
137
-				} else {
138
-					$urlLinkTo = \OC_App::getAppWebPath($appName) . '/' . $file;
139
-				}
140
-			} else {
141
-				$urlLinkTo = \OC::$WEBROOT . '/' . $appName . '/' . $file;
142
-			}
143
-		} else {
144
-			if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) {
145
-				$urlLinkTo = \OC::$WEBROOT . '/core/' . $file;
146
-			} else {
147
-				if ($frontControllerActive && $file === 'index.php') {
148
-					$urlLinkTo = \OC::$WEBROOT . '/';
149
-				} else {
150
-					$urlLinkTo = \OC::$WEBROOT . '/' . $file;
151
-				}
152
-			}
153
-		}
127
+        if ($appName !== '') {
128
+            $app_path = \OC_App::getAppPath($appName);
129
+            // Check if the app is in the app folder
130
+            if ($app_path && file_exists($app_path . '/' . $file)) {
131
+                if (substr($file, -3) === 'php') {
132
+                    $urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $appName;
133
+                    if ($frontControllerActive) {
134
+                        $urlLinkTo = \OC::$WEBROOT . '/apps/' . $appName;
135
+                    }
136
+                    $urlLinkTo .= ($file !== 'index.php') ? '/' . $file : '';
137
+                } else {
138
+                    $urlLinkTo = \OC_App::getAppWebPath($appName) . '/' . $file;
139
+                }
140
+            } else {
141
+                $urlLinkTo = \OC::$WEBROOT . '/' . $appName . '/' . $file;
142
+            }
143
+        } else {
144
+            if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) {
145
+                $urlLinkTo = \OC::$WEBROOT . '/core/' . $file;
146
+            } else {
147
+                if ($frontControllerActive && $file === 'index.php') {
148
+                    $urlLinkTo = \OC::$WEBROOT . '/';
149
+                } else {
150
+                    $urlLinkTo = \OC::$WEBROOT . '/' . $file;
151
+                }
152
+            }
153
+        }
154 154
 
155
-		if ($args && $query = http_build_query($args, '', '&')) {
156
-			$urlLinkTo .= '?' . $query;
157
-		}
155
+        if ($args && $query = http_build_query($args, '', '&')) {
156
+            $urlLinkTo .= '?' . $query;
157
+        }
158 158
 
159
-		return $urlLinkTo;
160
-	}
159
+        return $urlLinkTo;
160
+    }
161 161
 
162
-	/**
163
-	 * Creates path to an image
164
-	 *
165
-	 * @param string $appName app
166
-	 * @param string $file image name
167
-	 * @throws \RuntimeException If the image does not exist
168
-	 * @return string the url
169
-	 *
170
-	 * Returns the path to the image.
171
-	 */
172
-	public function imagePath(string $appName, string $file): string {
173
-		$cache = $this->cacheFactory->createDistributed('imagePath-'.md5($this->getBaseUrl()).'-');
174
-		$cacheKey = $appName.'-'.$file;
175
-		if ($key = $cache->get($cacheKey)) {
176
-			return $key;
177
-		}
162
+    /**
163
+     * Creates path to an image
164
+     *
165
+     * @param string $appName app
166
+     * @param string $file image name
167
+     * @throws \RuntimeException If the image does not exist
168
+     * @return string the url
169
+     *
170
+     * Returns the path to the image.
171
+     */
172
+    public function imagePath(string $appName, string $file): string {
173
+        $cache = $this->cacheFactory->createDistributed('imagePath-'.md5($this->getBaseUrl()).'-');
174
+        $cacheKey = $appName.'-'.$file;
175
+        if ($key = $cache->get($cacheKey)) {
176
+            return $key;
177
+        }
178 178
 
179
-		// Read the selected theme from the config file
180
-		$theme = \OC_Util::getTheme();
179
+        // Read the selected theme from the config file
180
+        $theme = \OC_Util::getTheme();
181 181
 
182
-		//if a theme has a png but not an svg always use the png
183
-		$basename = substr(basename($file),0,-4);
182
+        //if a theme has a png but not an svg always use the png
183
+        $basename = substr(basename($file),0,-4);
184 184
 
185
-		$appPath = \OC_App::getAppPath($appName);
185
+        $appPath = \OC_App::getAppPath($appName);
186 186
 
187
-		// Check if the app is in the app folder
188
-		$path = '';
189
-		$themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming');
190
-		$themingImagePath = false;
191
-		if ($themingEnabled) {
192
-			$themingDefaults = \OC::$server->getThemingDefaults();
193
-			if ($themingDefaults instanceof ThemingDefaults) {
194
-				$themingImagePath = $themingDefaults->replaceImagePath($appName, $file);
195
-			}
196
-		}
187
+        // Check if the app is in the app folder
188
+        $path = '';
189
+        $themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming');
190
+        $themingImagePath = false;
191
+        if ($themingEnabled) {
192
+            $themingDefaults = \OC::$server->getThemingDefaults();
193
+            if ($themingDefaults instanceof ThemingDefaults) {
194
+                $themingImagePath = $themingDefaults->replaceImagePath($appName, $file);
195
+            }
196
+        }
197 197
 
198
-		if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$file")) {
199
-			$path = \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$file";
200
-		} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.svg")
201
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.png")) {
202
-			$path =  \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$basename.png";
203
-		} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$file")) {
204
-			$path =  \OC::$WEBROOT . "/themes/$theme/$appName/img/$file";
205
-		} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.svg")
206
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.png"))) {
207
-			$path =  \OC::$WEBROOT . "/themes/$theme/$appName/img/$basename.png";
208
-		} elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$file")) {
209
-			$path =  \OC::$WEBROOT . "/themes/$theme/core/img/$file";
210
-		} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg")
211
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) {
212
-			$path =  \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
213
-		} elseif ($themingEnabled && $themingImagePath) {
214
-			$path = $themingImagePath;
215
-		} elseif ($appPath && file_exists($appPath . "/img/$file")) {
216
-			$path =  \OC_App::getAppWebPath($appName) . "/img/$file";
217
-		} elseif ($appPath && !file_exists($appPath . "/img/$basename.svg")
218
-			&& file_exists($appPath . "/img/$basename.png")) {
219
-			$path =  \OC_App::getAppWebPath($appName) . "/img/$basename.png";
220
-		} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/$appName/img/$file")) {
221
-			$path =  \OC::$WEBROOT . "/$appName/img/$file";
222
-		} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.svg")
223
-				&& file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.png"))) {
224
-			$path =  \OC::$WEBROOT . "/$appName/img/$basename.png";
225
-		} elseif (file_exists(\OC::$SERVERROOT . "/core/img/$file")) {
226
-			$path =  \OC::$WEBROOT . "/core/img/$file";
227
-		} elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg")
228
-			&& file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) {
229
-			$path =  \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
230
-		}
198
+        if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$file")) {
199
+            $path = \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$file";
200
+        } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.svg")
201
+            && file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.png")) {
202
+            $path =  \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$basename.png";
203
+        } elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$file")) {
204
+            $path =  \OC::$WEBROOT . "/themes/$theme/$appName/img/$file";
205
+        } elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.svg")
206
+            && file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.png"))) {
207
+            $path =  \OC::$WEBROOT . "/themes/$theme/$appName/img/$basename.png";
208
+        } elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$file")) {
209
+            $path =  \OC::$WEBROOT . "/themes/$theme/core/img/$file";
210
+        } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg")
211
+            && file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) {
212
+            $path =  \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
213
+        } elseif ($themingEnabled && $themingImagePath) {
214
+            $path = $themingImagePath;
215
+        } elseif ($appPath && file_exists($appPath . "/img/$file")) {
216
+            $path =  \OC_App::getAppWebPath($appName) . "/img/$file";
217
+        } elseif ($appPath && !file_exists($appPath . "/img/$basename.svg")
218
+            && file_exists($appPath . "/img/$basename.png")) {
219
+            $path =  \OC_App::getAppWebPath($appName) . "/img/$basename.png";
220
+        } elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/$appName/img/$file")) {
221
+            $path =  \OC::$WEBROOT . "/$appName/img/$file";
222
+        } elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.svg")
223
+                && file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.png"))) {
224
+            $path =  \OC::$WEBROOT . "/$appName/img/$basename.png";
225
+        } elseif (file_exists(\OC::$SERVERROOT . "/core/img/$file")) {
226
+            $path =  \OC::$WEBROOT . "/core/img/$file";
227
+        } elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg")
228
+            && file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) {
229
+            $path =  \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
230
+        }
231 231
 
232
-		if ($path !== '') {
233
-			$cache->set($cacheKey, $path);
234
-			return $path;
235
-		}
232
+        if ($path !== '') {
233
+            $cache->set($cacheKey, $path);
234
+            return $path;
235
+        }
236 236
 
237
-		throw new RuntimeException('image not found: image:' . $file . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT);
238
-	}
237
+        throw new RuntimeException('image not found: image:' . $file . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT);
238
+    }
239 239
 
240 240
 
241
-	/**
242
-	 * Makes an URL absolute
243
-	 * @param string $url the url in the ownCloud host
244
-	 * @return string the absolute version of the url
245
-	 */
246
-	public function getAbsoluteURL(string $url): string {
247
-		$separator = strpos($url, '/') === 0 ? '' : '/';
241
+    /**
242
+     * Makes an URL absolute
243
+     * @param string $url the url in the ownCloud host
244
+     * @return string the absolute version of the url
245
+     */
246
+    public function getAbsoluteURL(string $url): string {
247
+        $separator = strpos($url, '/') === 0 ? '' : '/';
248 248
 
249
-		if (\OC::$CLI && !\defined('PHPUNIT_RUN')) {
250
-			return rtrim($this->config->getSystemValue('overwrite.cli.url'), '/') . '/' . ltrim($url, '/');
251
-		}
252
-		// The ownCloud web root can already be prepended.
253
-		if (\OC::$WEBROOT !== '' && strpos($url, \OC::$WEBROOT) === 0) {
254
-			$url = substr($url, \strlen(\OC::$WEBROOT));
255
-		}
249
+        if (\OC::$CLI && !\defined('PHPUNIT_RUN')) {
250
+            return rtrim($this->config->getSystemValue('overwrite.cli.url'), '/') . '/' . ltrim($url, '/');
251
+        }
252
+        // The ownCloud web root can already be prepended.
253
+        if (\OC::$WEBROOT !== '' && strpos($url, \OC::$WEBROOT) === 0) {
254
+            $url = substr($url, \strlen(\OC::$WEBROOT));
255
+        }
256 256
 
257
-		return $this->getBaseUrl() . $separator . $url;
258
-	}
257
+        return $this->getBaseUrl() . $separator . $url;
258
+    }
259 259
 
260
-	/**
261
-	 * @param string $key
262
-	 * @return string url to the online documentation
263
-	 */
264
-	public function linkToDocs(string $key): string {
265
-		$theme = \OC::$server->getThemingDefaults();
266
-		return $theme->buildDocLinkToKey($key);
267
-	}
260
+    /**
261
+     * @param string $key
262
+     * @return string url to the online documentation
263
+     */
264
+    public function linkToDocs(string $key): string {
265
+        $theme = \OC::$server->getThemingDefaults();
266
+        return $theme->buildDocLinkToKey($key);
267
+    }
268 268
 
269
-	/**
270
-	 * @return string base url of the current request
271
-	 */
272
-	public function getBaseUrl(): string {
273
-		return $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT;
274
-	}
269
+    /**
270
+     * @return string base url of the current request
271
+     */
272
+    public function getBaseUrl(): string {
273
+        return $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT;
274
+    }
275 275
 }
Please login to merge, or discard this patch.
lib/private/User/Database.php 1 patch
Indentation   +418 added lines, -418 removed lines patch added patch discarded remove patch
@@ -77,422 +77,422 @@
 block discarded – undo
77 77
  * Class for user management in a SQL Database (e.g. MySQL, SQLite)
78 78
  */
79 79
 class Database extends ABackend implements
80
-	ICreateUserBackend,
81
-			   ISetPasswordBackend,
82
-			   ISetDisplayNameBackend,
83
-			   IGetDisplayNameBackend,
84
-			   ICheckPasswordBackend,
85
-			   IGetHomeBackend,
86
-			   ICountUsersBackend,
87
-			   IGetRealUIDBackend {
88
-	/** @var CappedMemoryCache */
89
-	private $cache;
90
-
91
-	/** @var IEventDispatcher */
92
-	private $eventDispatcher;
93
-
94
-	/** @var IDBConnection */
95
-	private $dbConn;
96
-
97
-	/** @var string */
98
-	private $table;
99
-
100
-	/**
101
-	 * \OC\User\Database constructor.
102
-	 *
103
-	 * @param IEventDispatcher $eventDispatcher
104
-	 * @param string $table
105
-	 */
106
-	public function __construct($eventDispatcher = null, $table = 'users') {
107
-		$this->cache = new CappedMemoryCache();
108
-		$this->table = $table;
109
-		$this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OC::$server->query(IEventDispatcher::class);
110
-	}
111
-
112
-	/**
113
-	 * FIXME: This function should not be required!
114
-	 */
115
-	private function fixDI() {
116
-		if ($this->dbConn === null) {
117
-			$this->dbConn = \OC::$server->getDatabaseConnection();
118
-		}
119
-	}
120
-
121
-	/**
122
-	 * Create a new user
123
-	 *
124
-	 * @param string $uid The username of the user to create
125
-	 * @param string $password The password of the new user
126
-	 * @return bool
127
-	 *
128
-	 * Creates a new user. Basic checking of username is done in OC_User
129
-	 * itself, not in its subclasses.
130
-	 */
131
-	public function createUser(string $uid, string $password): bool {
132
-		$this->fixDI();
133
-
134
-		if (!$this->userExists($uid)) {
135
-			$this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));
136
-
137
-			$qb = $this->dbConn->getQueryBuilder();
138
-			$qb->insert($this->table)
139
-				->values([
140
-					'uid' => $qb->createNamedParameter($uid),
141
-					'password' => $qb->createNamedParameter(\OC::$server->getHasher()->hash($password)),
142
-					'uid_lower' => $qb->createNamedParameter(mb_strtolower($uid)),
143
-				]);
144
-
145
-			$result = $qb->execute();
146
-
147
-			// Clear cache
148
-			unset($this->cache[$uid]);
149
-
150
-			return $result ? true : false;
151
-		}
152
-
153
-		return false;
154
-	}
155
-
156
-	/**
157
-	 * delete a user
158
-	 *
159
-	 * @param string $uid The username of the user to delete
160
-	 * @return bool
161
-	 *
162
-	 * Deletes a user
163
-	 */
164
-	public function deleteUser($uid) {
165
-		$this->fixDI();
166
-
167
-		// Delete user-group-relation
168
-		$query = $this->dbConn->getQueryBuilder();
169
-		$query->delete($this->table)
170
-			->where($query->expr()->eq('uid_lower', $query->createNamedParameter(mb_strtolower($uid))));
171
-		$result = $query->execute();
172
-
173
-		if (isset($this->cache[$uid])) {
174
-			unset($this->cache[$uid]);
175
-		}
176
-
177
-		return $result ? true : false;
178
-	}
179
-
180
-	private function updatePassword(string $uid, string $passwordHash): bool {
181
-		$query = $this->dbConn->getQueryBuilder();
182
-		$query->update($this->table)
183
-			->set('password', $query->createNamedParameter($passwordHash))
184
-			->where($query->expr()->eq('uid_lower', $query->createNamedParameter(mb_strtolower($uid))));
185
-		$result = $query->execute();
186
-
187
-		return $result ? true : false;
188
-	}
189
-
190
-	/**
191
-	 * Set password
192
-	 *
193
-	 * @param string $uid The username
194
-	 * @param string $password The new password
195
-	 * @return bool
196
-	 *
197
-	 * Change the password of a user
198
-	 */
199
-	public function setPassword(string $uid, string $password): bool {
200
-		$this->fixDI();
201
-
202
-		if ($this->userExists($uid)) {
203
-			$this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));
204
-
205
-			$hasher = \OC::$server->getHasher();
206
-			$hashedPassword = $hasher->hash($password);
207
-
208
-			return $this->updatePassword($uid, $hashedPassword);
209
-		}
210
-
211
-		return false;
212
-	}
213
-
214
-	/**
215
-	 * Set display name
216
-	 *
217
-	 * @param string $uid The username
218
-	 * @param string $displayName The new display name
219
-	 * @return bool
220
-	 *
221
-	 * Change the display name of a user
222
-	 */
223
-	public function setDisplayName(string $uid, string $displayName): bool {
224
-		$this->fixDI();
225
-
226
-		if ($this->userExists($uid)) {
227
-			$query = $this->dbConn->getQueryBuilder();
228
-			$query->update($this->table)
229
-				->set('displayname', $query->createNamedParameter($displayName))
230
-				->where($query->expr()->eq('uid_lower', $query->createNamedParameter(mb_strtolower($uid))));
231
-			$query->execute();
232
-
233
-			$this->cache[$uid]['displayname'] = $displayName;
234
-
235
-			return true;
236
-		}
237
-
238
-		return false;
239
-	}
240
-
241
-	/**
242
-	 * get display name of the user
243
-	 *
244
-	 * @param string $uid user ID of the user
245
-	 * @return string display name
246
-	 */
247
-	public function getDisplayName($uid): string {
248
-		$uid = (string)$uid;
249
-		$this->loadUser($uid);
250
-		return empty($this->cache[$uid]['displayname']) ? $uid : $this->cache[$uid]['displayname'];
251
-	}
252
-
253
-	/**
254
-	 * Get a list of all display names and user ids.
255
-	 *
256
-	 * @param string $search
257
-	 * @param string|null $limit
258
-	 * @param string|null $offset
259
-	 * @return array an array of all displayNames (value) and the corresponding uids (key)
260
-	 */
261
-	public function getDisplayNames($search = '', $limit = null, $offset = null) {
262
-		$limit = $this->fixLimit($limit);
263
-
264
-		$this->fixDI();
265
-
266
-		$query = $this->dbConn->getQueryBuilder();
267
-
268
-		$query->select('uid', 'displayname')
269
-			->from($this->table, 'u')
270
-			->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
271
-				$query->expr()->eq('userid', 'uid'),
272
-				$query->expr()->eq('appid', $query->expr()->literal('settings')),
273
-				$query->expr()->eq('configkey', $query->expr()->literal('email')))
274
-			)
275
-			// sqlite doesn't like re-using a single named parameter here
276
-			->where($query->expr()->iLike('uid', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')))
277
-			->orWhere($query->expr()->iLike('displayname', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')))
278
-			->orWhere($query->expr()->iLike('configvalue', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')))
279
-			->orderBy($query->func()->lower('displayname'), 'ASC')
280
-			->orderBy('uid_lower', 'ASC')
281
-			->setMaxResults($limit)
282
-			->setFirstResult($offset);
283
-
284
-		$result = $query->execute();
285
-		$displayNames = [];
286
-		while ($row = $result->fetch()) {
287
-			$displayNames[(string)$row['uid']] = (string)$row['displayname'];
288
-		}
289
-
290
-		return $displayNames;
291
-	}
292
-
293
-	/**
294
-	 * Check if the password is correct
295
-	 *
296
-	 * @param string $loginName The loginname
297
-	 * @param string $password The password
298
-	 * @return string
299
-	 *
300
-	 * Check if the password is correct without logging in the user
301
-	 * returns the user id or false
302
-	 */
303
-	public function checkPassword(string $loginName, string $password) {
304
-		$this->fixDI();
305
-
306
-		$qb = $this->dbConn->getQueryBuilder();
307
-		$qb->select('uid', 'password')
308
-			->from($this->table)
309
-			->where(
310
-				$qb->expr()->eq(
311
-					'uid_lower', $qb->createNamedParameter(mb_strtolower($loginName))
312
-				)
313
-			);
314
-		$result = $qb->execute();
315
-		$row = $result->fetch();
316
-		$result->closeCursor();
317
-
318
-		if ($row) {
319
-			$storedHash = $row['password'];
320
-			$newHash = '';
321
-			if (\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) {
322
-				if (!empty($newHash)) {
323
-					$this->updatePassword($loginName, $newHash);
324
-				}
325
-				return (string)$row['uid'];
326
-			}
327
-		}
328
-
329
-		return false;
330
-	}
331
-
332
-	/**
333
-	 * Load an user in the cache
334
-	 *
335
-	 * @param string $uid the username
336
-	 * @return boolean true if user was found, false otherwise
337
-	 */
338
-	private function loadUser($uid) {
339
-		$this->fixDI();
340
-
341
-		$uid = (string)$uid;
342
-		if (!isset($this->cache[$uid])) {
343
-			//guests $uid could be NULL or ''
344
-			if ($uid === '') {
345
-				$this->cache[$uid] = false;
346
-				return true;
347
-			}
348
-
349
-			$qb = $this->dbConn->getQueryBuilder();
350
-			$qb->select('uid', 'displayname')
351
-				->from($this->table)
352
-				->where(
353
-					$qb->expr()->eq(
354
-						'uid_lower', $qb->createNamedParameter(mb_strtolower($uid))
355
-					)
356
-				);
357
-			$result = $qb->execute();
358
-			$row = $result->fetch();
359
-			$result->closeCursor();
360
-
361
-			$this->cache[$uid] = false;
362
-
363
-			// "uid" is primary key, so there can only be a single result
364
-			if ($row !== false) {
365
-				$this->cache[$uid]['uid'] = (string)$row['uid'];
366
-				$this->cache[$uid]['displayname'] = (string)$row['displayname'];
367
-			} else {
368
-				return false;
369
-			}
370
-		}
371
-
372
-		return true;
373
-	}
374
-
375
-	/**
376
-	 * Get a list of all users
377
-	 *
378
-	 * @param string $search
379
-	 * @param null|int $limit
380
-	 * @param null|int $offset
381
-	 * @return string[] an array of all uids
382
-	 */
383
-	public function getUsers($search = '', $limit = null, $offset = null) {
384
-		$limit = $this->fixLimit($limit);
385
-
386
-		$users = $this->getDisplayNames($search, $limit, $offset);
387
-		$userIds = array_map(function ($uid) {
388
-			return (string)$uid;
389
-		}, array_keys($users));
390
-		sort($userIds, SORT_STRING | SORT_FLAG_CASE);
391
-		return $userIds;
392
-	}
393
-
394
-	/**
395
-	 * check if a user exists
396
-	 *
397
-	 * @param string $uid the username
398
-	 * @return boolean
399
-	 */
400
-	public function userExists($uid) {
401
-		$this->loadUser($uid);
402
-		return $this->cache[$uid] !== false;
403
-	}
404
-
405
-	/**
406
-	 * get the user's home directory
407
-	 *
408
-	 * @param string $uid the username
409
-	 * @return string|false
410
-	 */
411
-	public function getHome(string $uid) {
412
-		if ($this->userExists($uid)) {
413
-			return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $uid;
414
-		}
415
-
416
-		return false;
417
-	}
418
-
419
-	/**
420
-	 * @return bool
421
-	 */
422
-	public function hasUserListings() {
423
-		return true;
424
-	}
425
-
426
-	/**
427
-	 * counts the users in the database
428
-	 *
429
-	 * @return int|bool
430
-	 */
431
-	public function countUsers() {
432
-		$this->fixDI();
433
-
434
-		$query = $this->dbConn->getQueryBuilder();
435
-		$query->select($query->func()->count('uid'))
436
-			->from($this->table);
437
-		$result = $query->execute();
438
-
439
-		return $result->fetchColumn();
440
-	}
441
-
442
-	/**
443
-	 * returns the username for the given login name in the correct casing
444
-	 *
445
-	 * @param string $loginName
446
-	 * @return string|false
447
-	 */
448
-	public function loginName2UserName($loginName) {
449
-		if ($this->userExists($loginName)) {
450
-			return $this->cache[$loginName]['uid'];
451
-		}
452
-
453
-		return false;
454
-	}
455
-
456
-	/**
457
-	 * Backend name to be shown in user management
458
-	 *
459
-	 * @return string the name of the backend to be shown
460
-	 */
461
-	public function getBackendName() {
462
-		return 'Database';
463
-	}
464
-
465
-	public static function preLoginNameUsedAsUserName($param) {
466
-		if (!isset($param['uid'])) {
467
-			throw new \Exception('key uid is expected to be set in $param');
468
-		}
469
-
470
-		$backends = \OC::$server->getUserManager()->getBackends();
471
-		foreach ($backends as $backend) {
472
-			if ($backend instanceof Database) {
473
-				/** @var \OC\User\Database $backend */
474
-				$uid = $backend->loginName2UserName($param['uid']);
475
-				if ($uid !== false) {
476
-					$param['uid'] = $uid;
477
-					return;
478
-				}
479
-			}
480
-		}
481
-	}
482
-
483
-	public function getRealUID(string $uid): string {
484
-		if (!$this->userExists($uid)) {
485
-			throw new \RuntimeException($uid . ' does not exist');
486
-		}
487
-
488
-		return $this->cache[$uid]['uid'];
489
-	}
490
-
491
-	private function fixLimit($limit) {
492
-		if (is_int($limit) && $limit >= 0) {
493
-			return $limit;
494
-		}
495
-
496
-		return null;
497
-	}
80
+    ICreateUserBackend,
81
+                ISetPasswordBackend,
82
+                ISetDisplayNameBackend,
83
+                IGetDisplayNameBackend,
84
+                ICheckPasswordBackend,
85
+                IGetHomeBackend,
86
+                ICountUsersBackend,
87
+                IGetRealUIDBackend {
88
+    /** @var CappedMemoryCache */
89
+    private $cache;
90
+
91
+    /** @var IEventDispatcher */
92
+    private $eventDispatcher;
93
+
94
+    /** @var IDBConnection */
95
+    private $dbConn;
96
+
97
+    /** @var string */
98
+    private $table;
99
+
100
+    /**
101
+     * \OC\User\Database constructor.
102
+     *
103
+     * @param IEventDispatcher $eventDispatcher
104
+     * @param string $table
105
+     */
106
+    public function __construct($eventDispatcher = null, $table = 'users') {
107
+        $this->cache = new CappedMemoryCache();
108
+        $this->table = $table;
109
+        $this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OC::$server->query(IEventDispatcher::class);
110
+    }
111
+
112
+    /**
113
+     * FIXME: This function should not be required!
114
+     */
115
+    private function fixDI() {
116
+        if ($this->dbConn === null) {
117
+            $this->dbConn = \OC::$server->getDatabaseConnection();
118
+        }
119
+    }
120
+
121
+    /**
122
+     * Create a new user
123
+     *
124
+     * @param string $uid The username of the user to create
125
+     * @param string $password The password of the new user
126
+     * @return bool
127
+     *
128
+     * Creates a new user. Basic checking of username is done in OC_User
129
+     * itself, not in its subclasses.
130
+     */
131
+    public function createUser(string $uid, string $password): bool {
132
+        $this->fixDI();
133
+
134
+        if (!$this->userExists($uid)) {
135
+            $this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));
136
+
137
+            $qb = $this->dbConn->getQueryBuilder();
138
+            $qb->insert($this->table)
139
+                ->values([
140
+                    'uid' => $qb->createNamedParameter($uid),
141
+                    'password' => $qb->createNamedParameter(\OC::$server->getHasher()->hash($password)),
142
+                    'uid_lower' => $qb->createNamedParameter(mb_strtolower($uid)),
143
+                ]);
144
+
145
+            $result = $qb->execute();
146
+
147
+            // Clear cache
148
+            unset($this->cache[$uid]);
149
+
150
+            return $result ? true : false;
151
+        }
152
+
153
+        return false;
154
+    }
155
+
156
+    /**
157
+     * delete a user
158
+     *
159
+     * @param string $uid The username of the user to delete
160
+     * @return bool
161
+     *
162
+     * Deletes a user
163
+     */
164
+    public function deleteUser($uid) {
165
+        $this->fixDI();
166
+
167
+        // Delete user-group-relation
168
+        $query = $this->dbConn->getQueryBuilder();
169
+        $query->delete($this->table)
170
+            ->where($query->expr()->eq('uid_lower', $query->createNamedParameter(mb_strtolower($uid))));
171
+        $result = $query->execute();
172
+
173
+        if (isset($this->cache[$uid])) {
174
+            unset($this->cache[$uid]);
175
+        }
176
+
177
+        return $result ? true : false;
178
+    }
179
+
180
+    private function updatePassword(string $uid, string $passwordHash): bool {
181
+        $query = $this->dbConn->getQueryBuilder();
182
+        $query->update($this->table)
183
+            ->set('password', $query->createNamedParameter($passwordHash))
184
+            ->where($query->expr()->eq('uid_lower', $query->createNamedParameter(mb_strtolower($uid))));
185
+        $result = $query->execute();
186
+
187
+        return $result ? true : false;
188
+    }
189
+
190
+    /**
191
+     * Set password
192
+     *
193
+     * @param string $uid The username
194
+     * @param string $password The new password
195
+     * @return bool
196
+     *
197
+     * Change the password of a user
198
+     */
199
+    public function setPassword(string $uid, string $password): bool {
200
+        $this->fixDI();
201
+
202
+        if ($this->userExists($uid)) {
203
+            $this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));
204
+
205
+            $hasher = \OC::$server->getHasher();
206
+            $hashedPassword = $hasher->hash($password);
207
+
208
+            return $this->updatePassword($uid, $hashedPassword);
209
+        }
210
+
211
+        return false;
212
+    }
213
+
214
+    /**
215
+     * Set display name
216
+     *
217
+     * @param string $uid The username
218
+     * @param string $displayName The new display name
219
+     * @return bool
220
+     *
221
+     * Change the display name of a user
222
+     */
223
+    public function setDisplayName(string $uid, string $displayName): bool {
224
+        $this->fixDI();
225
+
226
+        if ($this->userExists($uid)) {
227
+            $query = $this->dbConn->getQueryBuilder();
228
+            $query->update($this->table)
229
+                ->set('displayname', $query->createNamedParameter($displayName))
230
+                ->where($query->expr()->eq('uid_lower', $query->createNamedParameter(mb_strtolower($uid))));
231
+            $query->execute();
232
+
233
+            $this->cache[$uid]['displayname'] = $displayName;
234
+
235
+            return true;
236
+        }
237
+
238
+        return false;
239
+    }
240
+
241
+    /**
242
+     * get display name of the user
243
+     *
244
+     * @param string $uid user ID of the user
245
+     * @return string display name
246
+     */
247
+    public function getDisplayName($uid): string {
248
+        $uid = (string)$uid;
249
+        $this->loadUser($uid);
250
+        return empty($this->cache[$uid]['displayname']) ? $uid : $this->cache[$uid]['displayname'];
251
+    }
252
+
253
+    /**
254
+     * Get a list of all display names and user ids.
255
+     *
256
+     * @param string $search
257
+     * @param string|null $limit
258
+     * @param string|null $offset
259
+     * @return array an array of all displayNames (value) and the corresponding uids (key)
260
+     */
261
+    public function getDisplayNames($search = '', $limit = null, $offset = null) {
262
+        $limit = $this->fixLimit($limit);
263
+
264
+        $this->fixDI();
265
+
266
+        $query = $this->dbConn->getQueryBuilder();
267
+
268
+        $query->select('uid', 'displayname')
269
+            ->from($this->table, 'u')
270
+            ->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
271
+                $query->expr()->eq('userid', 'uid'),
272
+                $query->expr()->eq('appid', $query->expr()->literal('settings')),
273
+                $query->expr()->eq('configkey', $query->expr()->literal('email')))
274
+            )
275
+            // sqlite doesn't like re-using a single named parameter here
276
+            ->where($query->expr()->iLike('uid', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')))
277
+            ->orWhere($query->expr()->iLike('displayname', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')))
278
+            ->orWhere($query->expr()->iLike('configvalue', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')))
279
+            ->orderBy($query->func()->lower('displayname'), 'ASC')
280
+            ->orderBy('uid_lower', 'ASC')
281
+            ->setMaxResults($limit)
282
+            ->setFirstResult($offset);
283
+
284
+        $result = $query->execute();
285
+        $displayNames = [];
286
+        while ($row = $result->fetch()) {
287
+            $displayNames[(string)$row['uid']] = (string)$row['displayname'];
288
+        }
289
+
290
+        return $displayNames;
291
+    }
292
+
293
+    /**
294
+     * Check if the password is correct
295
+     *
296
+     * @param string $loginName The loginname
297
+     * @param string $password The password
298
+     * @return string
299
+     *
300
+     * Check if the password is correct without logging in the user
301
+     * returns the user id or false
302
+     */
303
+    public function checkPassword(string $loginName, string $password) {
304
+        $this->fixDI();
305
+
306
+        $qb = $this->dbConn->getQueryBuilder();
307
+        $qb->select('uid', 'password')
308
+            ->from($this->table)
309
+            ->where(
310
+                $qb->expr()->eq(
311
+                    'uid_lower', $qb->createNamedParameter(mb_strtolower($loginName))
312
+                )
313
+            );
314
+        $result = $qb->execute();
315
+        $row = $result->fetch();
316
+        $result->closeCursor();
317
+
318
+        if ($row) {
319
+            $storedHash = $row['password'];
320
+            $newHash = '';
321
+            if (\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) {
322
+                if (!empty($newHash)) {
323
+                    $this->updatePassword($loginName, $newHash);
324
+                }
325
+                return (string)$row['uid'];
326
+            }
327
+        }
328
+
329
+        return false;
330
+    }
331
+
332
+    /**
333
+     * Load an user in the cache
334
+     *
335
+     * @param string $uid the username
336
+     * @return boolean true if user was found, false otherwise
337
+     */
338
+    private function loadUser($uid) {
339
+        $this->fixDI();
340
+
341
+        $uid = (string)$uid;
342
+        if (!isset($this->cache[$uid])) {
343
+            //guests $uid could be NULL or ''
344
+            if ($uid === '') {
345
+                $this->cache[$uid] = false;
346
+                return true;
347
+            }
348
+
349
+            $qb = $this->dbConn->getQueryBuilder();
350
+            $qb->select('uid', 'displayname')
351
+                ->from($this->table)
352
+                ->where(
353
+                    $qb->expr()->eq(
354
+                        'uid_lower', $qb->createNamedParameter(mb_strtolower($uid))
355
+                    )
356
+                );
357
+            $result = $qb->execute();
358
+            $row = $result->fetch();
359
+            $result->closeCursor();
360
+
361
+            $this->cache[$uid] = false;
362
+
363
+            // "uid" is primary key, so there can only be a single result
364
+            if ($row !== false) {
365
+                $this->cache[$uid]['uid'] = (string)$row['uid'];
366
+                $this->cache[$uid]['displayname'] = (string)$row['displayname'];
367
+            } else {
368
+                return false;
369
+            }
370
+        }
371
+
372
+        return true;
373
+    }
374
+
375
+    /**
376
+     * Get a list of all users
377
+     *
378
+     * @param string $search
379
+     * @param null|int $limit
380
+     * @param null|int $offset
381
+     * @return string[] an array of all uids
382
+     */
383
+    public function getUsers($search = '', $limit = null, $offset = null) {
384
+        $limit = $this->fixLimit($limit);
385
+
386
+        $users = $this->getDisplayNames($search, $limit, $offset);
387
+        $userIds = array_map(function ($uid) {
388
+            return (string)$uid;
389
+        }, array_keys($users));
390
+        sort($userIds, SORT_STRING | SORT_FLAG_CASE);
391
+        return $userIds;
392
+    }
393
+
394
+    /**
395
+     * check if a user exists
396
+     *
397
+     * @param string $uid the username
398
+     * @return boolean
399
+     */
400
+    public function userExists($uid) {
401
+        $this->loadUser($uid);
402
+        return $this->cache[$uid] !== false;
403
+    }
404
+
405
+    /**
406
+     * get the user's home directory
407
+     *
408
+     * @param string $uid the username
409
+     * @return string|false
410
+     */
411
+    public function getHome(string $uid) {
412
+        if ($this->userExists($uid)) {
413
+            return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $uid;
414
+        }
415
+
416
+        return false;
417
+    }
418
+
419
+    /**
420
+     * @return bool
421
+     */
422
+    public function hasUserListings() {
423
+        return true;
424
+    }
425
+
426
+    /**
427
+     * counts the users in the database
428
+     *
429
+     * @return int|bool
430
+     */
431
+    public function countUsers() {
432
+        $this->fixDI();
433
+
434
+        $query = $this->dbConn->getQueryBuilder();
435
+        $query->select($query->func()->count('uid'))
436
+            ->from($this->table);
437
+        $result = $query->execute();
438
+
439
+        return $result->fetchColumn();
440
+    }
441
+
442
+    /**
443
+     * returns the username for the given login name in the correct casing
444
+     *
445
+     * @param string $loginName
446
+     * @return string|false
447
+     */
448
+    public function loginName2UserName($loginName) {
449
+        if ($this->userExists($loginName)) {
450
+            return $this->cache[$loginName]['uid'];
451
+        }
452
+
453
+        return false;
454
+    }
455
+
456
+    /**
457
+     * Backend name to be shown in user management
458
+     *
459
+     * @return string the name of the backend to be shown
460
+     */
461
+    public function getBackendName() {
462
+        return 'Database';
463
+    }
464
+
465
+    public static function preLoginNameUsedAsUserName($param) {
466
+        if (!isset($param['uid'])) {
467
+            throw new \Exception('key uid is expected to be set in $param');
468
+        }
469
+
470
+        $backends = \OC::$server->getUserManager()->getBackends();
471
+        foreach ($backends as $backend) {
472
+            if ($backend instanceof Database) {
473
+                /** @var \OC\User\Database $backend */
474
+                $uid = $backend->loginName2UserName($param['uid']);
475
+                if ($uid !== false) {
476
+                    $param['uid'] = $uid;
477
+                    return;
478
+                }
479
+            }
480
+        }
481
+    }
482
+
483
+    public function getRealUID(string $uid): string {
484
+        if (!$this->userExists($uid)) {
485
+            throw new \RuntimeException($uid . ' does not exist');
486
+        }
487
+
488
+        return $this->cache[$uid]['uid'];
489
+    }
490
+
491
+    private function fixLimit($limit) {
492
+        if (is_int($limit) && $limit >= 0) {
493
+            return $limit;
494
+        }
495
+
496
+        return null;
497
+    }
498 498
 }
Please login to merge, or discard this patch.
lib/private/SystemTag/SystemTagManager.php 1 patch
Indentation   +385 added lines, -385 removed lines patch added patch discarded remove patch
@@ -45,389 +45,389 @@
 block discarded – undo
45 45
  * Manager class for system tags
46 46
  */
47 47
 class SystemTagManager implements ISystemTagManager {
48
-	public const TAG_TABLE = 'systemtag';
49
-	public const TAG_GROUP_TABLE = 'systemtag_group';
50
-
51
-	/** @var IDBConnection */
52
-	protected $connection;
53
-
54
-	/** @var EventDispatcherInterface */
55
-	protected $dispatcher;
56
-
57
-	/** @var IGroupManager */
58
-	protected $groupManager;
59
-
60
-	/**
61
-	 * Prepared query for selecting tags directly
62
-	 *
63
-	 * @var \OCP\DB\QueryBuilder\IQueryBuilder
64
-	 */
65
-	private $selectTagQuery;
66
-
67
-	/**
68
-	 * Constructor.
69
-	 *
70
-	 * @param IDBConnection $connection database connection
71
-	 * @param IGroupManager $groupManager
72
-	 * @param EventDispatcherInterface $dispatcher
73
-	 */
74
-	public function __construct(
75
-		IDBConnection $connection,
76
-		IGroupManager $groupManager,
77
-		EventDispatcherInterface $dispatcher
78
-	) {
79
-		$this->connection = $connection;
80
-		$this->groupManager = $groupManager;
81
-		$this->dispatcher = $dispatcher;
82
-
83
-		$query = $this->connection->getQueryBuilder();
84
-		$this->selectTagQuery = $query->select('*')
85
-			->from(self::TAG_TABLE)
86
-			->where($query->expr()->eq('name', $query->createParameter('name')))
87
-			->andWhere($query->expr()->eq('visibility', $query->createParameter('visibility')))
88
-			->andWhere($query->expr()->eq('editable', $query->createParameter('editable')));
89
-	}
90
-
91
-	/**
92
-	 * {@inheritdoc}
93
-	 */
94
-	public function getTagsByIds($tagIds): array {
95
-		if (!\is_array($tagIds)) {
96
-			$tagIds = [$tagIds];
97
-		}
98
-
99
-		$tags = [];
100
-
101
-		// note: not all databases will fail if it's a string or starts with a number
102
-		foreach ($tagIds as $tagId) {
103
-			if (!is_numeric($tagId)) {
104
-				throw new \InvalidArgumentException('Tag id must be integer');
105
-			}
106
-		}
107
-
108
-		$query = $this->connection->getQueryBuilder();
109
-		$query->select('*')
110
-			->from(self::TAG_TABLE)
111
-			->where($query->expr()->in('id', $query->createParameter('tagids')))
112
-			->addOrderBy('name', 'ASC')
113
-			->addOrderBy('visibility', 'ASC')
114
-			->addOrderBy('editable', 'ASC')
115
-			->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY);
116
-
117
-		$result = $query->execute();
118
-		while ($row = $result->fetch()) {
119
-			$tags[$row['id']] = $this->createSystemTagFromRow($row);
120
-		}
121
-
122
-		$result->closeCursor();
123
-
124
-		if (\count($tags) !== \count($tagIds)) {
125
-			throw new TagNotFoundException(
126
-				'Tag id(s) not found', 0, null, array_diff($tagIds, array_keys($tags))
127
-			);
128
-		}
129
-
130
-		return $tags;
131
-	}
132
-
133
-	/**
134
-	 * {@inheritdoc}
135
-	 */
136
-	public function getAllTags($visibilityFilter = null, $nameSearchPattern = null): array {
137
-		$tags = [];
138
-
139
-		$query = $this->connection->getQueryBuilder();
140
-		$query->select('*')
141
-			->from(self::TAG_TABLE);
142
-
143
-		if (!\is_null($visibilityFilter)) {
144
-			$query->andWhere($query->expr()->eq('visibility', $query->createNamedParameter((int)$visibilityFilter)));
145
-		}
146
-
147
-		if (!empty($nameSearchPattern)) {
148
-			$query->andWhere(
149
-				$query->expr()->like(
150
-					'name',
151
-					$query->createNamedParameter('%' . $this->connection->escapeLikeParameter($nameSearchPattern). '%')
152
-				)
153
-			);
154
-		}
155
-
156
-		$query
157
-			->addOrderBy('name', 'ASC')
158
-			->addOrderBy('visibility', 'ASC')
159
-			->addOrderBy('editable', 'ASC');
160
-
161
-		$result = $query->execute();
162
-		while ($row = $result->fetch()) {
163
-			$tags[$row['id']] = $this->createSystemTagFromRow($row);
164
-		}
165
-
166
-		$result->closeCursor();
167
-
168
-		return $tags;
169
-	}
170
-
171
-	/**
172
-	 * {@inheritdoc}
173
-	 */
174
-	public function getTag(string $tagName, bool $userVisible, bool $userAssignable): ISystemTag {
175
-		$result = $this->selectTagQuery
176
-			->setParameter('name', $tagName)
177
-			->setParameter('visibility', $userVisible ? 1 : 0)
178
-			->setParameter('editable', $userAssignable ? 1 : 0)
179
-			->execute();
180
-
181
-		$row = $result->fetch();
182
-		$result->closeCursor();
183
-		if (!$row) {
184
-			throw new TagNotFoundException(
185
-				'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') does not exist'
186
-			);
187
-		}
188
-
189
-		return $this->createSystemTagFromRow($row);
190
-	}
191
-
192
-	/**
193
-	 * {@inheritdoc}
194
-	 */
195
-	public function createTag(string $tagName, bool $userVisible, bool $userAssignable): ISystemTag {
196
-		$query = $this->connection->getQueryBuilder();
197
-		$query->insert(self::TAG_TABLE)
198
-			->values([
199
-				'name' => $query->createNamedParameter($tagName),
200
-				'visibility' => $query->createNamedParameter($userVisible ? 1 : 0),
201
-				'editable' => $query->createNamedParameter($userAssignable ? 1 : 0),
202
-			]);
203
-
204
-		try {
205
-			$query->execute();
206
-		} catch (UniqueConstraintViolationException $e) {
207
-			throw new TagAlreadyExistsException(
208
-				'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') already exists',
209
-				0,
210
-				$e
211
-			);
212
-		}
213
-
214
-		$tagId = $query->getLastInsertId();
215
-
216
-		$tag = new SystemTag(
217
-			(string)$tagId,
218
-			$tagName,
219
-			$userVisible,
220
-			$userAssignable
221
-		);
222
-
223
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_CREATE, new ManagerEvent(
224
-			ManagerEvent::EVENT_CREATE, $tag
225
-		));
226
-
227
-		return $tag;
228
-	}
229
-
230
-	/**
231
-	 * {@inheritdoc}
232
-	 */
233
-	public function updateTag(string $tagId, string $newName, bool $userVisible, bool $userAssignable) {
234
-		try {
235
-			$tags = $this->getTagsByIds($tagId);
236
-		} catch (TagNotFoundException $e) {
237
-			throw new TagNotFoundException(
238
-				'Tag does not exist', 0, null, [$tagId]
239
-			);
240
-		}
241
-
242
-		$beforeUpdate = array_shift($tags);
243
-		$afterUpdate = new SystemTag(
244
-			$tagId,
245
-			$newName,
246
-			$userVisible,
247
-			$userAssignable
248
-		);
249
-
250
-		$query = $this->connection->getQueryBuilder();
251
-		$query->update(self::TAG_TABLE)
252
-			->set('name', $query->createParameter('name'))
253
-			->set('visibility', $query->createParameter('visibility'))
254
-			->set('editable', $query->createParameter('editable'))
255
-			->where($query->expr()->eq('id', $query->createParameter('tagid')))
256
-			->setParameter('name', $newName)
257
-			->setParameter('visibility', $userVisible ? 1 : 0)
258
-			->setParameter('editable', $userAssignable ? 1 : 0)
259
-			->setParameter('tagid', $tagId);
260
-
261
-		try {
262
-			if ($query->execute() === 0) {
263
-				throw new TagNotFoundException(
264
-					'Tag does not exist', 0, null, [$tagId]
265
-				);
266
-			}
267
-		} catch (UniqueConstraintViolationException $e) {
268
-			throw new TagAlreadyExistsException(
269
-				'Tag ("' . $newName . '", '. $userVisible . ', ' . $userAssignable . ') already exists',
270
-				0,
271
-				$e
272
-			);
273
-		}
274
-
275
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_UPDATE, new ManagerEvent(
276
-			ManagerEvent::EVENT_UPDATE, $afterUpdate, $beforeUpdate
277
-		));
278
-	}
279
-
280
-	/**
281
-	 * {@inheritdoc}
282
-	 */
283
-	public function deleteTags($tagIds) {
284
-		if (!\is_array($tagIds)) {
285
-			$tagIds = [$tagIds];
286
-		}
287
-
288
-		$tagNotFoundException = null;
289
-		$tags = [];
290
-		try {
291
-			$tags = $this->getTagsByIds($tagIds);
292
-		} catch (TagNotFoundException $e) {
293
-			$tagNotFoundException = $e;
294
-
295
-			// Get existing tag objects for the hooks later
296
-			$existingTags = array_diff($tagIds, $tagNotFoundException->getMissingTags());
297
-			if (!empty($existingTags)) {
298
-				try {
299
-					$tags = $this->getTagsByIds($existingTags);
300
-				} catch (TagNotFoundException $e) {
301
-					// Ignore further errors...
302
-				}
303
-			}
304
-		}
305
-
306
-		// delete relationships first
307
-		$query = $this->connection->getQueryBuilder();
308
-		$query->delete(SystemTagObjectMapper::RELATION_TABLE)
309
-			->where($query->expr()->in('systemtagid', $query->createParameter('tagids')))
310
-			->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY)
311
-			->execute();
312
-
313
-		$query = $this->connection->getQueryBuilder();
314
-		$query->delete(self::TAG_TABLE)
315
-			->where($query->expr()->in('id', $query->createParameter('tagids')))
316
-			->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY)
317
-			->execute();
318
-
319
-		foreach ($tags as $tag) {
320
-			$this->dispatcher->dispatch(ManagerEvent::EVENT_DELETE, new ManagerEvent(
321
-				ManagerEvent::EVENT_DELETE, $tag
322
-			));
323
-		}
324
-
325
-		if ($tagNotFoundException !== null) {
326
-			throw new TagNotFoundException(
327
-				'Tag id(s) not found', 0, $tagNotFoundException, $tagNotFoundException->getMissingTags()
328
-			);
329
-		}
330
-	}
331
-
332
-	/**
333
-	 * {@inheritdoc}
334
-	 */
335
-	public function canUserAssignTag(ISystemTag $tag, IUser $user): bool {
336
-		// early check to avoid unneeded group lookups
337
-		if ($tag->isUserAssignable() && $tag->isUserVisible()) {
338
-			return true;
339
-		}
340
-
341
-		if ($this->groupManager->isAdmin($user->getUID())) {
342
-			return true;
343
-		}
344
-
345
-		if (!$tag->isUserVisible()) {
346
-			return false;
347
-		}
348
-
349
-		$groupIds = $this->groupManager->getUserGroupIds($user);
350
-		if (!empty($groupIds)) {
351
-			$matchingGroups = array_intersect($groupIds, $this->getTagGroups($tag));
352
-			if (!empty($matchingGroups)) {
353
-				return true;
354
-			}
355
-		}
356
-
357
-		return false;
358
-	}
359
-
360
-	/**
361
-	 * {@inheritdoc}
362
-	 */
363
-	public function canUserSeeTag(ISystemTag $tag, IUser $user): bool {
364
-		if ($tag->isUserVisible()) {
365
-			return true;
366
-		}
367
-
368
-		if ($this->groupManager->isAdmin($user->getUID())) {
369
-			return true;
370
-		}
371
-
372
-		return false;
373
-	}
374
-
375
-	private function createSystemTagFromRow($row) {
376
-		return new SystemTag((string)$row['id'], $row['name'], (bool)$row['visibility'], (bool)$row['editable']);
377
-	}
378
-
379
-	/**
380
-	 * {@inheritdoc}
381
-	 */
382
-	public function setTagGroups(ISystemTag $tag, array $groupIds) {
383
-		// delete relationships first
384
-		$this->connection->beginTransaction();
385
-		try {
386
-			$query = $this->connection->getQueryBuilder();
387
-			$query->delete(self::TAG_GROUP_TABLE)
388
-				->where($query->expr()->eq('systemtagid', $query->createNamedParameter($tag->getId())))
389
-				->execute();
390
-
391
-			// add each group id
392
-			$query = $this->connection->getQueryBuilder();
393
-			$query->insert(self::TAG_GROUP_TABLE)
394
-				->values([
395
-					'systemtagid' => $query->createNamedParameter($tag->getId()),
396
-					'gid' => $query->createParameter('gid'),
397
-				]);
398
-			foreach ($groupIds as $groupId) {
399
-				if ($groupId === '') {
400
-					continue;
401
-				}
402
-				$query->setParameter('gid', $groupId);
403
-				$query->execute();
404
-			}
405
-
406
-			$this->connection->commit();
407
-		} catch (\Exception $e) {
408
-			$this->connection->rollBack();
409
-			throw $e;
410
-		}
411
-	}
412
-
413
-	/**
414
-	 * {@inheritdoc}
415
-	 */
416
-	public function getTagGroups(ISystemTag $tag): array {
417
-		$groupIds = [];
418
-		$query = $this->connection->getQueryBuilder();
419
-		$query->select('gid')
420
-			->from(self::TAG_GROUP_TABLE)
421
-			->where($query->expr()->eq('systemtagid', $query->createNamedParameter($tag->getId())))
422
-			->orderBy('gid');
423
-
424
-		$result = $query->execute();
425
-		while ($row = $result->fetch()) {
426
-			$groupIds[] = $row['gid'];
427
-		}
428
-
429
-		$result->closeCursor();
430
-
431
-		return $groupIds;
432
-	}
48
+    public const TAG_TABLE = 'systemtag';
49
+    public const TAG_GROUP_TABLE = 'systemtag_group';
50
+
51
+    /** @var IDBConnection */
52
+    protected $connection;
53
+
54
+    /** @var EventDispatcherInterface */
55
+    protected $dispatcher;
56
+
57
+    /** @var IGroupManager */
58
+    protected $groupManager;
59
+
60
+    /**
61
+     * Prepared query for selecting tags directly
62
+     *
63
+     * @var \OCP\DB\QueryBuilder\IQueryBuilder
64
+     */
65
+    private $selectTagQuery;
66
+
67
+    /**
68
+     * Constructor.
69
+     *
70
+     * @param IDBConnection $connection database connection
71
+     * @param IGroupManager $groupManager
72
+     * @param EventDispatcherInterface $dispatcher
73
+     */
74
+    public function __construct(
75
+        IDBConnection $connection,
76
+        IGroupManager $groupManager,
77
+        EventDispatcherInterface $dispatcher
78
+    ) {
79
+        $this->connection = $connection;
80
+        $this->groupManager = $groupManager;
81
+        $this->dispatcher = $dispatcher;
82
+
83
+        $query = $this->connection->getQueryBuilder();
84
+        $this->selectTagQuery = $query->select('*')
85
+            ->from(self::TAG_TABLE)
86
+            ->where($query->expr()->eq('name', $query->createParameter('name')))
87
+            ->andWhere($query->expr()->eq('visibility', $query->createParameter('visibility')))
88
+            ->andWhere($query->expr()->eq('editable', $query->createParameter('editable')));
89
+    }
90
+
91
+    /**
92
+     * {@inheritdoc}
93
+     */
94
+    public function getTagsByIds($tagIds): array {
95
+        if (!\is_array($tagIds)) {
96
+            $tagIds = [$tagIds];
97
+        }
98
+
99
+        $tags = [];
100
+
101
+        // note: not all databases will fail if it's a string or starts with a number
102
+        foreach ($tagIds as $tagId) {
103
+            if (!is_numeric($tagId)) {
104
+                throw new \InvalidArgumentException('Tag id must be integer');
105
+            }
106
+        }
107
+
108
+        $query = $this->connection->getQueryBuilder();
109
+        $query->select('*')
110
+            ->from(self::TAG_TABLE)
111
+            ->where($query->expr()->in('id', $query->createParameter('tagids')))
112
+            ->addOrderBy('name', 'ASC')
113
+            ->addOrderBy('visibility', 'ASC')
114
+            ->addOrderBy('editable', 'ASC')
115
+            ->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY);
116
+
117
+        $result = $query->execute();
118
+        while ($row = $result->fetch()) {
119
+            $tags[$row['id']] = $this->createSystemTagFromRow($row);
120
+        }
121
+
122
+        $result->closeCursor();
123
+
124
+        if (\count($tags) !== \count($tagIds)) {
125
+            throw new TagNotFoundException(
126
+                'Tag id(s) not found', 0, null, array_diff($tagIds, array_keys($tags))
127
+            );
128
+        }
129
+
130
+        return $tags;
131
+    }
132
+
133
+    /**
134
+     * {@inheritdoc}
135
+     */
136
+    public function getAllTags($visibilityFilter = null, $nameSearchPattern = null): array {
137
+        $tags = [];
138
+
139
+        $query = $this->connection->getQueryBuilder();
140
+        $query->select('*')
141
+            ->from(self::TAG_TABLE);
142
+
143
+        if (!\is_null($visibilityFilter)) {
144
+            $query->andWhere($query->expr()->eq('visibility', $query->createNamedParameter((int)$visibilityFilter)));
145
+        }
146
+
147
+        if (!empty($nameSearchPattern)) {
148
+            $query->andWhere(
149
+                $query->expr()->like(
150
+                    'name',
151
+                    $query->createNamedParameter('%' . $this->connection->escapeLikeParameter($nameSearchPattern). '%')
152
+                )
153
+            );
154
+        }
155
+
156
+        $query
157
+            ->addOrderBy('name', 'ASC')
158
+            ->addOrderBy('visibility', 'ASC')
159
+            ->addOrderBy('editable', 'ASC');
160
+
161
+        $result = $query->execute();
162
+        while ($row = $result->fetch()) {
163
+            $tags[$row['id']] = $this->createSystemTagFromRow($row);
164
+        }
165
+
166
+        $result->closeCursor();
167
+
168
+        return $tags;
169
+    }
170
+
171
+    /**
172
+     * {@inheritdoc}
173
+     */
174
+    public function getTag(string $tagName, bool $userVisible, bool $userAssignable): ISystemTag {
175
+        $result = $this->selectTagQuery
176
+            ->setParameter('name', $tagName)
177
+            ->setParameter('visibility', $userVisible ? 1 : 0)
178
+            ->setParameter('editable', $userAssignable ? 1 : 0)
179
+            ->execute();
180
+
181
+        $row = $result->fetch();
182
+        $result->closeCursor();
183
+        if (!$row) {
184
+            throw new TagNotFoundException(
185
+                'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') does not exist'
186
+            );
187
+        }
188
+
189
+        return $this->createSystemTagFromRow($row);
190
+    }
191
+
192
+    /**
193
+     * {@inheritdoc}
194
+     */
195
+    public function createTag(string $tagName, bool $userVisible, bool $userAssignable): ISystemTag {
196
+        $query = $this->connection->getQueryBuilder();
197
+        $query->insert(self::TAG_TABLE)
198
+            ->values([
199
+                'name' => $query->createNamedParameter($tagName),
200
+                'visibility' => $query->createNamedParameter($userVisible ? 1 : 0),
201
+                'editable' => $query->createNamedParameter($userAssignable ? 1 : 0),
202
+            ]);
203
+
204
+        try {
205
+            $query->execute();
206
+        } catch (UniqueConstraintViolationException $e) {
207
+            throw new TagAlreadyExistsException(
208
+                'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') already exists',
209
+                0,
210
+                $e
211
+            );
212
+        }
213
+
214
+        $tagId = $query->getLastInsertId();
215
+
216
+        $tag = new SystemTag(
217
+            (string)$tagId,
218
+            $tagName,
219
+            $userVisible,
220
+            $userAssignable
221
+        );
222
+
223
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_CREATE, new ManagerEvent(
224
+            ManagerEvent::EVENT_CREATE, $tag
225
+        ));
226
+
227
+        return $tag;
228
+    }
229
+
230
+    /**
231
+     * {@inheritdoc}
232
+     */
233
+    public function updateTag(string $tagId, string $newName, bool $userVisible, bool $userAssignable) {
234
+        try {
235
+            $tags = $this->getTagsByIds($tagId);
236
+        } catch (TagNotFoundException $e) {
237
+            throw new TagNotFoundException(
238
+                'Tag does not exist', 0, null, [$tagId]
239
+            );
240
+        }
241
+
242
+        $beforeUpdate = array_shift($tags);
243
+        $afterUpdate = new SystemTag(
244
+            $tagId,
245
+            $newName,
246
+            $userVisible,
247
+            $userAssignable
248
+        );
249
+
250
+        $query = $this->connection->getQueryBuilder();
251
+        $query->update(self::TAG_TABLE)
252
+            ->set('name', $query->createParameter('name'))
253
+            ->set('visibility', $query->createParameter('visibility'))
254
+            ->set('editable', $query->createParameter('editable'))
255
+            ->where($query->expr()->eq('id', $query->createParameter('tagid')))
256
+            ->setParameter('name', $newName)
257
+            ->setParameter('visibility', $userVisible ? 1 : 0)
258
+            ->setParameter('editable', $userAssignable ? 1 : 0)
259
+            ->setParameter('tagid', $tagId);
260
+
261
+        try {
262
+            if ($query->execute() === 0) {
263
+                throw new TagNotFoundException(
264
+                    'Tag does not exist', 0, null, [$tagId]
265
+                );
266
+            }
267
+        } catch (UniqueConstraintViolationException $e) {
268
+            throw new TagAlreadyExistsException(
269
+                'Tag ("' . $newName . '", '. $userVisible . ', ' . $userAssignable . ') already exists',
270
+                0,
271
+                $e
272
+            );
273
+        }
274
+
275
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_UPDATE, new ManagerEvent(
276
+            ManagerEvent::EVENT_UPDATE, $afterUpdate, $beforeUpdate
277
+        ));
278
+    }
279
+
280
+    /**
281
+     * {@inheritdoc}
282
+     */
283
+    public function deleteTags($tagIds) {
284
+        if (!\is_array($tagIds)) {
285
+            $tagIds = [$tagIds];
286
+        }
287
+
288
+        $tagNotFoundException = null;
289
+        $tags = [];
290
+        try {
291
+            $tags = $this->getTagsByIds($tagIds);
292
+        } catch (TagNotFoundException $e) {
293
+            $tagNotFoundException = $e;
294
+
295
+            // Get existing tag objects for the hooks later
296
+            $existingTags = array_diff($tagIds, $tagNotFoundException->getMissingTags());
297
+            if (!empty($existingTags)) {
298
+                try {
299
+                    $tags = $this->getTagsByIds($existingTags);
300
+                } catch (TagNotFoundException $e) {
301
+                    // Ignore further errors...
302
+                }
303
+            }
304
+        }
305
+
306
+        // delete relationships first
307
+        $query = $this->connection->getQueryBuilder();
308
+        $query->delete(SystemTagObjectMapper::RELATION_TABLE)
309
+            ->where($query->expr()->in('systemtagid', $query->createParameter('tagids')))
310
+            ->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY)
311
+            ->execute();
312
+
313
+        $query = $this->connection->getQueryBuilder();
314
+        $query->delete(self::TAG_TABLE)
315
+            ->where($query->expr()->in('id', $query->createParameter('tagids')))
316
+            ->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY)
317
+            ->execute();
318
+
319
+        foreach ($tags as $tag) {
320
+            $this->dispatcher->dispatch(ManagerEvent::EVENT_DELETE, new ManagerEvent(
321
+                ManagerEvent::EVENT_DELETE, $tag
322
+            ));
323
+        }
324
+
325
+        if ($tagNotFoundException !== null) {
326
+            throw new TagNotFoundException(
327
+                'Tag id(s) not found', 0, $tagNotFoundException, $tagNotFoundException->getMissingTags()
328
+            );
329
+        }
330
+    }
331
+
332
+    /**
333
+     * {@inheritdoc}
334
+     */
335
+    public function canUserAssignTag(ISystemTag $tag, IUser $user): bool {
336
+        // early check to avoid unneeded group lookups
337
+        if ($tag->isUserAssignable() && $tag->isUserVisible()) {
338
+            return true;
339
+        }
340
+
341
+        if ($this->groupManager->isAdmin($user->getUID())) {
342
+            return true;
343
+        }
344
+
345
+        if (!$tag->isUserVisible()) {
346
+            return false;
347
+        }
348
+
349
+        $groupIds = $this->groupManager->getUserGroupIds($user);
350
+        if (!empty($groupIds)) {
351
+            $matchingGroups = array_intersect($groupIds, $this->getTagGroups($tag));
352
+            if (!empty($matchingGroups)) {
353
+                return true;
354
+            }
355
+        }
356
+
357
+        return false;
358
+    }
359
+
360
+    /**
361
+     * {@inheritdoc}
362
+     */
363
+    public function canUserSeeTag(ISystemTag $tag, IUser $user): bool {
364
+        if ($tag->isUserVisible()) {
365
+            return true;
366
+        }
367
+
368
+        if ($this->groupManager->isAdmin($user->getUID())) {
369
+            return true;
370
+        }
371
+
372
+        return false;
373
+    }
374
+
375
+    private function createSystemTagFromRow($row) {
376
+        return new SystemTag((string)$row['id'], $row['name'], (bool)$row['visibility'], (bool)$row['editable']);
377
+    }
378
+
379
+    /**
380
+     * {@inheritdoc}
381
+     */
382
+    public function setTagGroups(ISystemTag $tag, array $groupIds) {
383
+        // delete relationships first
384
+        $this->connection->beginTransaction();
385
+        try {
386
+            $query = $this->connection->getQueryBuilder();
387
+            $query->delete(self::TAG_GROUP_TABLE)
388
+                ->where($query->expr()->eq('systemtagid', $query->createNamedParameter($tag->getId())))
389
+                ->execute();
390
+
391
+            // add each group id
392
+            $query = $this->connection->getQueryBuilder();
393
+            $query->insert(self::TAG_GROUP_TABLE)
394
+                ->values([
395
+                    'systemtagid' => $query->createNamedParameter($tag->getId()),
396
+                    'gid' => $query->createParameter('gid'),
397
+                ]);
398
+            foreach ($groupIds as $groupId) {
399
+                if ($groupId === '') {
400
+                    continue;
401
+                }
402
+                $query->setParameter('gid', $groupId);
403
+                $query->execute();
404
+            }
405
+
406
+            $this->connection->commit();
407
+        } catch (\Exception $e) {
408
+            $this->connection->rollBack();
409
+            throw $e;
410
+        }
411
+    }
412
+
413
+    /**
414
+     * {@inheritdoc}
415
+     */
416
+    public function getTagGroups(ISystemTag $tag): array {
417
+        $groupIds = [];
418
+        $query = $this->connection->getQueryBuilder();
419
+        $query->select('gid')
420
+            ->from(self::TAG_GROUP_TABLE)
421
+            ->where($query->expr()->eq('systemtagid', $query->createNamedParameter($tag->getId())))
422
+            ->orderBy('gid');
423
+
424
+        $result = $query->execute();
425
+        while ($row = $result->fetch()) {
426
+            $groupIds[] = $row['gid'];
427
+        }
428
+
429
+        $result->closeCursor();
430
+
431
+        return $groupIds;
432
+    }
433 433
 }
Please login to merge, or discard this patch.
lib/private/Files/ObjectStore/NoopScanner.php 1 patch
Indentation   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -31,52 +31,52 @@
 block discarded – undo
31 31
 use OC\Files\Storage\Storage;
32 32
 
33 33
 class NoopScanner extends Scanner {
34
-	public function __construct(Storage $storage) {
35
-		//we don't need the storage, so do nothing here
36
-	}
34
+    public function __construct(Storage $storage) {
35
+        //we don't need the storage, so do nothing here
36
+    }
37 37
 
38
-	/**
39
-	 * scan a single file and store it in the cache
40
-	 *
41
-	 * @param string $file
42
-	 * @param int $reuseExisting
43
-	 * @param int $parentId
44
-	 * @param array|null $cacheData existing data in the cache for the file to be scanned
45
-	 * @return array an array of metadata of the scanned file
46
-	 */
47
-	public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true, $data = null) {
48
-		return [];
49
-	}
38
+    /**
39
+     * scan a single file and store it in the cache
40
+     *
41
+     * @param string $file
42
+     * @param int $reuseExisting
43
+     * @param int $parentId
44
+     * @param array|null $cacheData existing data in the cache for the file to be scanned
45
+     * @return array an array of metadata of the scanned file
46
+     */
47
+    public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true, $data = null) {
48
+        return [];
49
+    }
50 50
 
51
-	/**
52
-	 * scan a folder and all it's children
53
-	 *
54
-	 * @param string $path
55
-	 * @param bool $recursive
56
-	 * @param int $reuse
57
-	 * @return array with the meta data of the scanned file or folder
58
-	 */
59
-	public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) {
60
-		return [];
61
-	}
51
+    /**
52
+     * scan a folder and all it's children
53
+     *
54
+     * @param string $path
55
+     * @param bool $recursive
56
+     * @param int $reuse
57
+     * @return array with the meta data of the scanned file or folder
58
+     */
59
+    public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) {
60
+        return [];
61
+    }
62 62
 
63
-	/**
64
-	 * scan all the files and folders in a folder
65
-	 *
66
-	 * @param string $path
67
-	 * @param bool $recursive
68
-	 * @param int $reuse
69
-	 * @param array $folderData existing cache data for the folder to be scanned
70
-	 * @return int the size of the scanned folder or -1 if the size is unknown at this stage
71
-	 */
72
-	protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true) {
73
-		return 0;
74
-	}
63
+    /**
64
+     * scan all the files and folders in a folder
65
+     *
66
+     * @param string $path
67
+     * @param bool $recursive
68
+     * @param int $reuse
69
+     * @param array $folderData existing cache data for the folder to be scanned
70
+     * @return int the size of the scanned folder or -1 if the size is unknown at this stage
71
+     */
72
+    protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true) {
73
+        return 0;
74
+    }
75 75
 
76
-	/**
77
-	 * walk over any folders that are not fully scanned yet and scan them
78
-	 */
79
-	public function backgroundScan() {
80
-		//noop
81
-	}
76
+    /**
77
+     * walk over any folders that are not fully scanned yet and scan them
78
+     */
79
+    public function backgroundScan() {
80
+        //noop
81
+    }
82 82
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/Wrapper/CacheJail.php 1 patch
Indentation   +291 added lines, -291 removed lines patch added patch discarded remove patch
@@ -38,295 +38,295 @@
 block discarded – undo
38 38
  * Jail to a subdirectory of the wrapped cache
39 39
  */
40 40
 class CacheJail extends CacheWrapper {
41
-	/**
42
-	 * @var string
43
-	 */
44
-	protected $root;
45
-
46
-	/**
47
-	 * @param \OCP\Files\Cache\ICache $cache
48
-	 * @param string $root
49
-	 */
50
-	public function __construct($cache, $root) {
51
-		parent::__construct($cache);
52
-		$this->root = $root;
53
-	}
54
-
55
-	protected function getRoot() {
56
-		return $this->root;
57
-	}
58
-
59
-	protected function getSourcePath($path) {
60
-		if ($path === '') {
61
-			return $this->getRoot();
62
-		} else {
63
-			return $this->getRoot() . '/' . ltrim($path, '/');
64
-		}
65
-	}
66
-
67
-	/**
68
-	 * @param string $path
69
-	 * @return null|string the jailed path or null if the path is outside the jail
70
-	 */
71
-	protected function getJailedPath($path) {
72
-		if ($this->getRoot() === '') {
73
-			return $path;
74
-		}
75
-		$rootLength = strlen($this->getRoot()) + 1;
76
-		if ($path === $this->getRoot()) {
77
-			return '';
78
-		} elseif (substr($path, 0, $rootLength) === $this->getRoot() . '/') {
79
-			return substr($path, $rootLength);
80
-		} else {
81
-			return null;
82
-		}
83
-	}
84
-
85
-	/**
86
-	 * @param ICacheEntry|array $entry
87
-	 * @return array
88
-	 */
89
-	protected function formatCacheEntry($entry) {
90
-		if (isset($entry['path'])) {
91
-			$entry['path'] = $this->getJailedPath($entry['path']);
92
-		}
93
-		return $entry;
94
-	}
95
-
96
-	protected function filterCacheEntry($entry) {
97
-		$rootLength = strlen($this->getRoot()) + 1;
98
-		return $rootLength === 1 || ($entry['path'] === $this->getRoot()) || (substr($entry['path'], 0, $rootLength) === $this->getRoot() . '/');
99
-	}
100
-
101
-	/**
102
-	 * get the stored metadata of a file or folder
103
-	 *
104
-	 * @param string /int $file
105
-	 * @return ICacheEntry|false
106
-	 */
107
-	public function get($file) {
108
-		if (is_string($file) or $file == '') {
109
-			$file = $this->getSourcePath($file);
110
-		}
111
-		return parent::get($file);
112
-	}
113
-
114
-	/**
115
-	 * insert meta data for a new file or folder
116
-	 *
117
-	 * @param string $file
118
-	 * @param array $data
119
-	 *
120
-	 * @return int file id
121
-	 * @throws \RuntimeException
122
-	 */
123
-	public function insert($file, array $data) {
124
-		return $this->getCache()->insert($this->getSourcePath($file), $data);
125
-	}
126
-
127
-	/**
128
-	 * update the metadata in the cache
129
-	 *
130
-	 * @param int $id
131
-	 * @param array $data
132
-	 */
133
-	public function update($id, array $data) {
134
-		$this->getCache()->update($id, $data);
135
-	}
136
-
137
-	/**
138
-	 * get the file id for a file
139
-	 *
140
-	 * @param string $file
141
-	 * @return int
142
-	 */
143
-	public function getId($file) {
144
-		return $this->getCache()->getId($this->getSourcePath($file));
145
-	}
146
-
147
-	/**
148
-	 * get the id of the parent folder of a file
149
-	 *
150
-	 * @param string $file
151
-	 * @return int
152
-	 */
153
-	public function getParentId($file) {
154
-		return $this->getCache()->getParentId($this->getSourcePath($file));
155
-	}
156
-
157
-	/**
158
-	 * check if a file is available in the cache
159
-	 *
160
-	 * @param string $file
161
-	 * @return bool
162
-	 */
163
-	public function inCache($file) {
164
-		return $this->getCache()->inCache($this->getSourcePath($file));
165
-	}
166
-
167
-	/**
168
-	 * remove a file or folder from the cache
169
-	 *
170
-	 * @param string $file
171
-	 */
172
-	public function remove($file) {
173
-		$this->getCache()->remove($this->getSourcePath($file));
174
-	}
175
-
176
-	/**
177
-	 * Move a file or folder in the cache
178
-	 *
179
-	 * @param string $source
180
-	 * @param string $target
181
-	 */
182
-	public function move($source, $target) {
183
-		$this->getCache()->move($this->getSourcePath($source), $this->getSourcePath($target));
184
-	}
185
-
186
-	/**
187
-	 * Get the storage id and path needed for a move
188
-	 *
189
-	 * @param string $path
190
-	 * @return array [$storageId, $internalPath]
191
-	 */
192
-	protected function getMoveInfo($path) {
193
-		return [$this->getNumericStorageId(), $this->getSourcePath($path)];
194
-	}
195
-
196
-	/**
197
-	 * remove all entries for files that are stored on the storage from the cache
198
-	 */
199
-	public function clear() {
200
-		$this->getCache()->remove($this->getRoot());
201
-	}
202
-
203
-	/**
204
-	 * @param string $file
205
-	 *
206
-	 * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
207
-	 */
208
-	public function getStatus($file) {
209
-		return $this->getCache()->getStatus($this->getSourcePath($file));
210
-	}
211
-
212
-	private function formatSearchResults($results) {
213
-		$results = array_filter($results, [$this, 'filterCacheEntry']);
214
-		$results = array_values($results);
215
-		return array_map([$this, 'formatCacheEntry'], $results);
216
-	}
217
-
218
-	/**
219
-	 * search for files matching $pattern
220
-	 *
221
-	 * @param string $pattern
222
-	 * @return array an array of file data
223
-	 */
224
-	public function search($pattern) {
225
-		$results = $this->getCache()->search($pattern);
226
-		return $this->formatSearchResults($results);
227
-	}
228
-
229
-	/**
230
-	 * search for files by mimetype
231
-	 *
232
-	 * @param string $mimetype
233
-	 * @return array
234
-	 */
235
-	public function searchByMime($mimetype) {
236
-		$results = $this->getCache()->searchByMime($mimetype);
237
-		return $this->formatSearchResults($results);
238
-	}
239
-
240
-	public function searchQuery(ISearchQuery $query) {
241
-		$simpleQuery = new SearchQuery($query->getSearchOperation(), 0, 0, $query->getOrder(), $query->getUser());
242
-		$results = $this->getCache()->searchQuery($simpleQuery);
243
-		$results = $this->formatSearchResults($results);
244
-
245
-		$limit = $query->getLimit() === 0 ? null : $query->getLimit();
246
-		$results = array_slice($results, $query->getOffset(), $limit);
247
-
248
-		return $results;
249
-	}
250
-
251
-	/**
252
-	 * update the folder size and the size of all parent folders
253
-	 *
254
-	 * @param string|boolean $path
255
-	 * @param array $data (optional) meta data of the folder
256
-	 */
257
-	public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
258
-		if ($this->getCache() instanceof Cache) {
259
-			$this->getCache()->correctFolderSize($this->getSourcePath($path), $data, $isBackgroundScan);
260
-		}
261
-	}
262
-
263
-	/**
264
-	 * get the size of a folder and set it in the cache
265
-	 *
266
-	 * @param string $path
267
-	 * @param array $entry (optional) meta data of the folder
268
-	 * @return int
269
-	 */
270
-	public function calculateFolderSize($path, $entry = null) {
271
-		if ($this->getCache() instanceof Cache) {
272
-			return $this->getCache()->calculateFolderSize($this->getSourcePath($path), $entry);
273
-		} else {
274
-			return 0;
275
-		}
276
-	}
277
-
278
-	/**
279
-	 * get all file ids on the files on the storage
280
-	 *
281
-	 * @return int[]
282
-	 */
283
-	public function getAll() {
284
-		// not supported
285
-		return [];
286
-	}
287
-
288
-	/**
289
-	 * find a folder in the cache which has not been fully scanned
290
-	 *
291
-	 * If multiply incomplete folders are in the cache, the one with the highest id will be returned,
292
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
293
-	 * likely the folder where we stopped scanning previously
294
-	 *
295
-	 * @return string|bool the path of the folder or false when no folder matched
296
-	 */
297
-	public function getIncomplete() {
298
-		// not supported
299
-		return false;
300
-	}
301
-
302
-	/**
303
-	 * get the path of a file on this storage by it's id
304
-	 *
305
-	 * @param int $id
306
-	 * @return string|null
307
-	 */
308
-	public function getPathById($id) {
309
-		$path = $this->getCache()->getPathById($id);
310
-		if ($path === null) {
311
-			return null;
312
-		}
313
-
314
-		return $this->getJailedPath($path);
315
-	}
316
-
317
-	/**
318
-	 * Move a file or folder in the cache
319
-	 *
320
-	 * Note that this should make sure the entries are removed from the source cache
321
-	 *
322
-	 * @param \OCP\Files\Cache\ICache $sourceCache
323
-	 * @param string $sourcePath
324
-	 * @param string $targetPath
325
-	 */
326
-	public function moveFromCache(\OCP\Files\Cache\ICache $sourceCache, $sourcePath, $targetPath) {
327
-		if ($sourceCache === $this) {
328
-			return $this->move($sourcePath, $targetPath);
329
-		}
330
-		return $this->getCache()->moveFromCache($sourceCache, $sourcePath, $this->getSourcePath($targetPath));
331
-	}
41
+    /**
42
+     * @var string
43
+     */
44
+    protected $root;
45
+
46
+    /**
47
+     * @param \OCP\Files\Cache\ICache $cache
48
+     * @param string $root
49
+     */
50
+    public function __construct($cache, $root) {
51
+        parent::__construct($cache);
52
+        $this->root = $root;
53
+    }
54
+
55
+    protected function getRoot() {
56
+        return $this->root;
57
+    }
58
+
59
+    protected function getSourcePath($path) {
60
+        if ($path === '') {
61
+            return $this->getRoot();
62
+        } else {
63
+            return $this->getRoot() . '/' . ltrim($path, '/');
64
+        }
65
+    }
66
+
67
+    /**
68
+     * @param string $path
69
+     * @return null|string the jailed path or null if the path is outside the jail
70
+     */
71
+    protected function getJailedPath($path) {
72
+        if ($this->getRoot() === '') {
73
+            return $path;
74
+        }
75
+        $rootLength = strlen($this->getRoot()) + 1;
76
+        if ($path === $this->getRoot()) {
77
+            return '';
78
+        } elseif (substr($path, 0, $rootLength) === $this->getRoot() . '/') {
79
+            return substr($path, $rootLength);
80
+        } else {
81
+            return null;
82
+        }
83
+    }
84
+
85
+    /**
86
+     * @param ICacheEntry|array $entry
87
+     * @return array
88
+     */
89
+    protected function formatCacheEntry($entry) {
90
+        if (isset($entry['path'])) {
91
+            $entry['path'] = $this->getJailedPath($entry['path']);
92
+        }
93
+        return $entry;
94
+    }
95
+
96
+    protected function filterCacheEntry($entry) {
97
+        $rootLength = strlen($this->getRoot()) + 1;
98
+        return $rootLength === 1 || ($entry['path'] === $this->getRoot()) || (substr($entry['path'], 0, $rootLength) === $this->getRoot() . '/');
99
+    }
100
+
101
+    /**
102
+     * get the stored metadata of a file or folder
103
+     *
104
+     * @param string /int $file
105
+     * @return ICacheEntry|false
106
+     */
107
+    public function get($file) {
108
+        if (is_string($file) or $file == '') {
109
+            $file = $this->getSourcePath($file);
110
+        }
111
+        return parent::get($file);
112
+    }
113
+
114
+    /**
115
+     * insert meta data for a new file or folder
116
+     *
117
+     * @param string $file
118
+     * @param array $data
119
+     *
120
+     * @return int file id
121
+     * @throws \RuntimeException
122
+     */
123
+    public function insert($file, array $data) {
124
+        return $this->getCache()->insert($this->getSourcePath($file), $data);
125
+    }
126
+
127
+    /**
128
+     * update the metadata in the cache
129
+     *
130
+     * @param int $id
131
+     * @param array $data
132
+     */
133
+    public function update($id, array $data) {
134
+        $this->getCache()->update($id, $data);
135
+    }
136
+
137
+    /**
138
+     * get the file id for a file
139
+     *
140
+     * @param string $file
141
+     * @return int
142
+     */
143
+    public function getId($file) {
144
+        return $this->getCache()->getId($this->getSourcePath($file));
145
+    }
146
+
147
+    /**
148
+     * get the id of the parent folder of a file
149
+     *
150
+     * @param string $file
151
+     * @return int
152
+     */
153
+    public function getParentId($file) {
154
+        return $this->getCache()->getParentId($this->getSourcePath($file));
155
+    }
156
+
157
+    /**
158
+     * check if a file is available in the cache
159
+     *
160
+     * @param string $file
161
+     * @return bool
162
+     */
163
+    public function inCache($file) {
164
+        return $this->getCache()->inCache($this->getSourcePath($file));
165
+    }
166
+
167
+    /**
168
+     * remove a file or folder from the cache
169
+     *
170
+     * @param string $file
171
+     */
172
+    public function remove($file) {
173
+        $this->getCache()->remove($this->getSourcePath($file));
174
+    }
175
+
176
+    /**
177
+     * Move a file or folder in the cache
178
+     *
179
+     * @param string $source
180
+     * @param string $target
181
+     */
182
+    public function move($source, $target) {
183
+        $this->getCache()->move($this->getSourcePath($source), $this->getSourcePath($target));
184
+    }
185
+
186
+    /**
187
+     * Get the storage id and path needed for a move
188
+     *
189
+     * @param string $path
190
+     * @return array [$storageId, $internalPath]
191
+     */
192
+    protected function getMoveInfo($path) {
193
+        return [$this->getNumericStorageId(), $this->getSourcePath($path)];
194
+    }
195
+
196
+    /**
197
+     * remove all entries for files that are stored on the storage from the cache
198
+     */
199
+    public function clear() {
200
+        $this->getCache()->remove($this->getRoot());
201
+    }
202
+
203
+    /**
204
+     * @param string $file
205
+     *
206
+     * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
207
+     */
208
+    public function getStatus($file) {
209
+        return $this->getCache()->getStatus($this->getSourcePath($file));
210
+    }
211
+
212
+    private function formatSearchResults($results) {
213
+        $results = array_filter($results, [$this, 'filterCacheEntry']);
214
+        $results = array_values($results);
215
+        return array_map([$this, 'formatCacheEntry'], $results);
216
+    }
217
+
218
+    /**
219
+     * search for files matching $pattern
220
+     *
221
+     * @param string $pattern
222
+     * @return array an array of file data
223
+     */
224
+    public function search($pattern) {
225
+        $results = $this->getCache()->search($pattern);
226
+        return $this->formatSearchResults($results);
227
+    }
228
+
229
+    /**
230
+     * search for files by mimetype
231
+     *
232
+     * @param string $mimetype
233
+     * @return array
234
+     */
235
+    public function searchByMime($mimetype) {
236
+        $results = $this->getCache()->searchByMime($mimetype);
237
+        return $this->formatSearchResults($results);
238
+    }
239
+
240
+    public function searchQuery(ISearchQuery $query) {
241
+        $simpleQuery = new SearchQuery($query->getSearchOperation(), 0, 0, $query->getOrder(), $query->getUser());
242
+        $results = $this->getCache()->searchQuery($simpleQuery);
243
+        $results = $this->formatSearchResults($results);
244
+
245
+        $limit = $query->getLimit() === 0 ? null : $query->getLimit();
246
+        $results = array_slice($results, $query->getOffset(), $limit);
247
+
248
+        return $results;
249
+    }
250
+
251
+    /**
252
+     * update the folder size and the size of all parent folders
253
+     *
254
+     * @param string|boolean $path
255
+     * @param array $data (optional) meta data of the folder
256
+     */
257
+    public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
258
+        if ($this->getCache() instanceof Cache) {
259
+            $this->getCache()->correctFolderSize($this->getSourcePath($path), $data, $isBackgroundScan);
260
+        }
261
+    }
262
+
263
+    /**
264
+     * get the size of a folder and set it in the cache
265
+     *
266
+     * @param string $path
267
+     * @param array $entry (optional) meta data of the folder
268
+     * @return int
269
+     */
270
+    public function calculateFolderSize($path, $entry = null) {
271
+        if ($this->getCache() instanceof Cache) {
272
+            return $this->getCache()->calculateFolderSize($this->getSourcePath($path), $entry);
273
+        } else {
274
+            return 0;
275
+        }
276
+    }
277
+
278
+    /**
279
+     * get all file ids on the files on the storage
280
+     *
281
+     * @return int[]
282
+     */
283
+    public function getAll() {
284
+        // not supported
285
+        return [];
286
+    }
287
+
288
+    /**
289
+     * find a folder in the cache which has not been fully scanned
290
+     *
291
+     * If multiply incomplete folders are in the cache, the one with the highest id will be returned,
292
+     * use the one with the highest id gives the best result with the background scanner, since that is most
293
+     * likely the folder where we stopped scanning previously
294
+     *
295
+     * @return string|bool the path of the folder or false when no folder matched
296
+     */
297
+    public function getIncomplete() {
298
+        // not supported
299
+        return false;
300
+    }
301
+
302
+    /**
303
+     * get the path of a file on this storage by it's id
304
+     *
305
+     * @param int $id
306
+     * @return string|null
307
+     */
308
+    public function getPathById($id) {
309
+        $path = $this->getCache()->getPathById($id);
310
+        if ($path === null) {
311
+            return null;
312
+        }
313
+
314
+        return $this->getJailedPath($path);
315
+    }
316
+
317
+    /**
318
+     * Move a file or folder in the cache
319
+     *
320
+     * Note that this should make sure the entries are removed from the source cache
321
+     *
322
+     * @param \OCP\Files\Cache\ICache $sourceCache
323
+     * @param string $sourcePath
324
+     * @param string $targetPath
325
+     */
326
+    public function moveFromCache(\OCP\Files\Cache\ICache $sourceCache, $sourcePath, $targetPath) {
327
+        if ($sourceCache === $this) {
328
+            return $this->move($sourcePath, $targetPath);
329
+        }
330
+        return $this->getCache()->moveFromCache($sourceCache, $sourcePath, $this->getSourcePath($targetPath));
331
+    }
332 332
 }
Please login to merge, or discard this patch.
lib/private/ContactsManager.php 1 patch
Indentation   +173 added lines, -173 removed lines patch added patch discarded remove patch
@@ -37,177 +37,177 @@
 block discarded – undo
37 37
 
38 38
 class ContactsManager implements IManager {
39 39
 
40
-	/**
41
-	 * This function is used to search and find contacts within the users address books.
42
-	 * In case $pattern is empty all contacts will be returned.
43
-	 *
44
-	 * @param string $pattern which should match within the $searchProperties
45
-	 * @param array $searchProperties defines the properties within the query pattern should match
46
-	 * @param array $options = array() to define the search behavior
47
-	 * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
48
-	 * 	- 'limit' - Set a numeric limit for the search results
49
-	 * 	- 'offset' - Set the offset for the limited search results
50
-	 * @return array an array of contacts which are arrays of key-value-pairs
51
-	 */
52
-	public function search($pattern, $searchProperties = [], $options = []) {
53
-		$this->loadAddressBooks();
54
-		$result = [];
55
-		foreach ($this->addressBooks as $addressBook) {
56
-			$r = $addressBook->search($pattern, $searchProperties, $options);
57
-			$contacts = [];
58
-			foreach ($r as $c) {
59
-				$c['addressbook-key'] = $addressBook->getKey();
60
-				$contacts[] = $c;
61
-			}
62
-			$result = array_merge($result, $contacts);
63
-		}
64
-
65
-		return $result;
66
-	}
67
-
68
-	/**
69
-	 * This function can be used to delete the contact identified by the given id
70
-	 *
71
-	 * @param object $id the unique identifier to a contact
72
-	 * @param string $address_book_key identifier of the address book in which the contact shall be deleted
73
-	 * @return bool successful or not
74
-	 */
75
-	public function delete($id, $address_book_key) {
76
-		$addressBook = $this->getAddressBook($address_book_key);
77
-		if (!$addressBook) {
78
-			return null;
79
-		}
80
-
81
-		if ($addressBook->getPermissions() & Constants::PERMISSION_DELETE) {
82
-			return $addressBook->delete($id);
83
-		}
84
-
85
-		return null;
86
-	}
87
-
88
-	/**
89
-	 * This function is used to create a new contact if 'id' is not given or not present.
90
-	 * Otherwise the contact will be updated by replacing the entire data set.
91
-	 *
92
-	 * @param array $properties this array if key-value-pairs defines a contact
93
-	 * @param string $address_book_key identifier of the address book in which the contact shall be created or updated
94
-	 * @return array representing the contact just created or updated
95
-	 */
96
-	public function createOrUpdate($properties, $address_book_key) {
97
-		$addressBook = $this->getAddressBook($address_book_key);
98
-		if (!$addressBook) {
99
-			return null;
100
-		}
101
-
102
-		if ($addressBook->getPermissions() & Constants::PERMISSION_CREATE) {
103
-			return $addressBook->createOrUpdate($properties);
104
-		}
105
-
106
-		return null;
107
-	}
108
-
109
-	/**
110
-	 * Check if contacts are available (e.g. contacts app enabled)
111
-	 *
112
-	 * @return bool true if enabled, false if not
113
-	 */
114
-	public function isEnabled() {
115
-		return !empty($this->addressBooks) || !empty($this->addressBookLoaders);
116
-	}
117
-
118
-	/**
119
-	 * @param IAddressBook $addressBook
120
-	 */
121
-	public function registerAddressBook(IAddressBook $addressBook) {
122
-		$this->addressBooks[$addressBook->getKey()] = $addressBook;
123
-	}
124
-
125
-	/**
126
-	 * @param IAddressBook $addressBook
127
-	 */
128
-	public function unregisterAddressBook(IAddressBook $addressBook) {
129
-		unset($this->addressBooks[$addressBook->getKey()]);
130
-	}
131
-
132
-	/**
133
-	 * Return a list of the user's addressbooks display names
134
-	 * ! The addressBook displayName are not unique, please use getUserAddressBooks
135
-	 *
136
-	 * @return IAddressBook[]
137
-	 * @since 6.0.0
138
-	 * @deprecated 16.0.0 - Use `$this->getUserAddressBooks()` instead
139
-	 */
140
-	public function getAddressBooks() {
141
-		$this->loadAddressBooks();
142
-		$result = [];
143
-		foreach ($this->addressBooks as $addressBook) {
144
-			$result[$addressBook->getKey()] = $addressBook->getDisplayName();
145
-		}
146
-
147
-		return $result;
148
-	}
149
-
150
-	/**
151
-	 * Return a list of the user's addressbooks
152
-	 *
153
-	 * @return IAddressBook[]
154
-	 * @since 16.0.0
155
-	 */
156
-	public function getUserAddressBooks(): array {
157
-		$this->loadAddressBooks();
158
-		return $this->addressBooks;
159
-	}
160
-
161
-	/**
162
-	 * removes all registered address book instances
163
-	 */
164
-	public function clear() {
165
-		$this->addressBooks = [];
166
-		$this->addressBookLoaders = [];
167
-	}
168
-
169
-	/**
170
-	 * @var IAddressBook[] which holds all registered address books
171
-	 */
172
-	private $addressBooks = [];
173
-
174
-	/**
175
-	 * @var \Closure[] to call to load/register address books
176
-	 */
177
-	private $addressBookLoaders = [];
178
-
179
-	/**
180
-	 * In order to improve lazy loading a closure can be registered which will be called in case
181
-	 * address books are actually requested
182
-	 *
183
-	 * @param \Closure $callable
184
-	 */
185
-	public function register(\Closure $callable) {
186
-		$this->addressBookLoaders[] = $callable;
187
-	}
188
-
189
-	/**
190
-	 * Get (and load when needed) the address book for $key
191
-	 *
192
-	 * @param string $addressBookKey
193
-	 * @return IAddressBook
194
-	 */
195
-	protected function getAddressBook($addressBookKey) {
196
-		$this->loadAddressBooks();
197
-		if (!array_key_exists($addressBookKey, $this->addressBooks)) {
198
-			return null;
199
-		}
200
-
201
-		return $this->addressBooks[$addressBookKey];
202
-	}
203
-
204
-	/**
205
-	 * Load all address books registered with 'register'
206
-	 */
207
-	protected function loadAddressBooks() {
208
-		foreach ($this->addressBookLoaders as $callable) {
209
-			$callable($this);
210
-		}
211
-		$this->addressBookLoaders = [];
212
-	}
40
+    /**
41
+     * This function is used to search and find contacts within the users address books.
42
+     * In case $pattern is empty all contacts will be returned.
43
+     *
44
+     * @param string $pattern which should match within the $searchProperties
45
+     * @param array $searchProperties defines the properties within the query pattern should match
46
+     * @param array $options = array() to define the search behavior
47
+     * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
48
+     * 	- 'limit' - Set a numeric limit for the search results
49
+     * 	- 'offset' - Set the offset for the limited search results
50
+     * @return array an array of contacts which are arrays of key-value-pairs
51
+     */
52
+    public function search($pattern, $searchProperties = [], $options = []) {
53
+        $this->loadAddressBooks();
54
+        $result = [];
55
+        foreach ($this->addressBooks as $addressBook) {
56
+            $r = $addressBook->search($pattern, $searchProperties, $options);
57
+            $contacts = [];
58
+            foreach ($r as $c) {
59
+                $c['addressbook-key'] = $addressBook->getKey();
60
+                $contacts[] = $c;
61
+            }
62
+            $result = array_merge($result, $contacts);
63
+        }
64
+
65
+        return $result;
66
+    }
67
+
68
+    /**
69
+     * This function can be used to delete the contact identified by the given id
70
+     *
71
+     * @param object $id the unique identifier to a contact
72
+     * @param string $address_book_key identifier of the address book in which the contact shall be deleted
73
+     * @return bool successful or not
74
+     */
75
+    public function delete($id, $address_book_key) {
76
+        $addressBook = $this->getAddressBook($address_book_key);
77
+        if (!$addressBook) {
78
+            return null;
79
+        }
80
+
81
+        if ($addressBook->getPermissions() & Constants::PERMISSION_DELETE) {
82
+            return $addressBook->delete($id);
83
+        }
84
+
85
+        return null;
86
+    }
87
+
88
+    /**
89
+     * This function is used to create a new contact if 'id' is not given or not present.
90
+     * Otherwise the contact will be updated by replacing the entire data set.
91
+     *
92
+     * @param array $properties this array if key-value-pairs defines a contact
93
+     * @param string $address_book_key identifier of the address book in which the contact shall be created or updated
94
+     * @return array representing the contact just created or updated
95
+     */
96
+    public function createOrUpdate($properties, $address_book_key) {
97
+        $addressBook = $this->getAddressBook($address_book_key);
98
+        if (!$addressBook) {
99
+            return null;
100
+        }
101
+
102
+        if ($addressBook->getPermissions() & Constants::PERMISSION_CREATE) {
103
+            return $addressBook->createOrUpdate($properties);
104
+        }
105
+
106
+        return null;
107
+    }
108
+
109
+    /**
110
+     * Check if contacts are available (e.g. contacts app enabled)
111
+     *
112
+     * @return bool true if enabled, false if not
113
+     */
114
+    public function isEnabled() {
115
+        return !empty($this->addressBooks) || !empty($this->addressBookLoaders);
116
+    }
117
+
118
+    /**
119
+     * @param IAddressBook $addressBook
120
+     */
121
+    public function registerAddressBook(IAddressBook $addressBook) {
122
+        $this->addressBooks[$addressBook->getKey()] = $addressBook;
123
+    }
124
+
125
+    /**
126
+     * @param IAddressBook $addressBook
127
+     */
128
+    public function unregisterAddressBook(IAddressBook $addressBook) {
129
+        unset($this->addressBooks[$addressBook->getKey()]);
130
+    }
131
+
132
+    /**
133
+     * Return a list of the user's addressbooks display names
134
+     * ! The addressBook displayName are not unique, please use getUserAddressBooks
135
+     *
136
+     * @return IAddressBook[]
137
+     * @since 6.0.0
138
+     * @deprecated 16.0.0 - Use `$this->getUserAddressBooks()` instead
139
+     */
140
+    public function getAddressBooks() {
141
+        $this->loadAddressBooks();
142
+        $result = [];
143
+        foreach ($this->addressBooks as $addressBook) {
144
+            $result[$addressBook->getKey()] = $addressBook->getDisplayName();
145
+        }
146
+
147
+        return $result;
148
+    }
149
+
150
+    /**
151
+     * Return a list of the user's addressbooks
152
+     *
153
+     * @return IAddressBook[]
154
+     * @since 16.0.0
155
+     */
156
+    public function getUserAddressBooks(): array {
157
+        $this->loadAddressBooks();
158
+        return $this->addressBooks;
159
+    }
160
+
161
+    /**
162
+     * removes all registered address book instances
163
+     */
164
+    public function clear() {
165
+        $this->addressBooks = [];
166
+        $this->addressBookLoaders = [];
167
+    }
168
+
169
+    /**
170
+     * @var IAddressBook[] which holds all registered address books
171
+     */
172
+    private $addressBooks = [];
173
+
174
+    /**
175
+     * @var \Closure[] to call to load/register address books
176
+     */
177
+    private $addressBookLoaders = [];
178
+
179
+    /**
180
+     * In order to improve lazy loading a closure can be registered which will be called in case
181
+     * address books are actually requested
182
+     *
183
+     * @param \Closure $callable
184
+     */
185
+    public function register(\Closure $callable) {
186
+        $this->addressBookLoaders[] = $callable;
187
+    }
188
+
189
+    /**
190
+     * Get (and load when needed) the address book for $key
191
+     *
192
+     * @param string $addressBookKey
193
+     * @return IAddressBook
194
+     */
195
+    protected function getAddressBook($addressBookKey) {
196
+        $this->loadAddressBooks();
197
+        if (!array_key_exists($addressBookKey, $this->addressBooks)) {
198
+            return null;
199
+        }
200
+
201
+        return $this->addressBooks[$addressBookKey];
202
+    }
203
+
204
+    /**
205
+     * Load all address books registered with 'register'
206
+     */
207
+    protected function loadAddressBooks() {
208
+        foreach ($this->addressBookLoaders as $callable) {
209
+            $callable($this);
210
+        }
211
+        $this->addressBookLoaders = [];
212
+    }
213 213
 }
Please login to merge, or discard this patch.