@@ -47,655 +47,655 @@ |
||
47 | 47 | * represents an LDAP user, gets and holds user-specific information from LDAP |
48 | 48 | */ |
49 | 49 | class User { |
50 | - /** |
|
51 | - * @var IUserTools |
|
52 | - */ |
|
53 | - protected $access; |
|
54 | - /** |
|
55 | - * @var Connection |
|
56 | - */ |
|
57 | - protected $connection; |
|
58 | - /** |
|
59 | - * @var IConfig |
|
60 | - */ |
|
61 | - protected $config; |
|
62 | - /** |
|
63 | - * @var FilesystemHelper |
|
64 | - */ |
|
65 | - protected $fs; |
|
66 | - /** |
|
67 | - * @var Image |
|
68 | - */ |
|
69 | - protected $image; |
|
70 | - /** |
|
71 | - * @var LogWrapper |
|
72 | - */ |
|
73 | - protected $log; |
|
74 | - /** |
|
75 | - * @var IAvatarManager |
|
76 | - */ |
|
77 | - protected $avatarManager; |
|
78 | - /** |
|
79 | - * @var IUserManager |
|
80 | - */ |
|
81 | - protected $userManager; |
|
82 | - /** |
|
83 | - * @var INotificationManager |
|
84 | - */ |
|
85 | - protected $notificationManager; |
|
86 | - /** |
|
87 | - * @var string |
|
88 | - */ |
|
89 | - protected $dn; |
|
90 | - /** |
|
91 | - * @var string |
|
92 | - */ |
|
93 | - protected $uid; |
|
94 | - /** |
|
95 | - * @var string[] |
|
96 | - */ |
|
97 | - protected $refreshedFeatures = array(); |
|
98 | - /** |
|
99 | - * @var string |
|
100 | - */ |
|
101 | - protected $avatarImage; |
|
102 | - |
|
103 | - /** |
|
104 | - * DB config keys for user preferences |
|
105 | - */ |
|
106 | - const USER_PREFKEY_FIRSTLOGIN = 'firstLoginAccomplished'; |
|
107 | - const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh'; |
|
108 | - |
|
109 | - /** |
|
110 | - * @brief constructor, make sure the subclasses call this one! |
|
111 | - * @param string $username the internal username |
|
112 | - * @param string $dn the LDAP DN |
|
113 | - * @param IUserTools $access an instance that implements IUserTools for |
|
114 | - * LDAP interaction |
|
115 | - * @param IConfig $config |
|
116 | - * @param FilesystemHelper $fs |
|
117 | - * @param Image $image any empty instance |
|
118 | - * @param LogWrapper $log |
|
119 | - * @param IAvatarManager $avatarManager |
|
120 | - * @param IUserManager $userManager |
|
121 | - * @param INotificationManager $notificationManager |
|
122 | - */ |
|
123 | - public function __construct($username, $dn, IUserTools $access, |
|
124 | - IConfig $config, FilesystemHelper $fs, Image $image, |
|
125 | - LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager, |
|
126 | - INotificationManager $notificationManager) { |
|
50 | + /** |
|
51 | + * @var IUserTools |
|
52 | + */ |
|
53 | + protected $access; |
|
54 | + /** |
|
55 | + * @var Connection |
|
56 | + */ |
|
57 | + protected $connection; |
|
58 | + /** |
|
59 | + * @var IConfig |
|
60 | + */ |
|
61 | + protected $config; |
|
62 | + /** |
|
63 | + * @var FilesystemHelper |
|
64 | + */ |
|
65 | + protected $fs; |
|
66 | + /** |
|
67 | + * @var Image |
|
68 | + */ |
|
69 | + protected $image; |
|
70 | + /** |
|
71 | + * @var LogWrapper |
|
72 | + */ |
|
73 | + protected $log; |
|
74 | + /** |
|
75 | + * @var IAvatarManager |
|
76 | + */ |
|
77 | + protected $avatarManager; |
|
78 | + /** |
|
79 | + * @var IUserManager |
|
80 | + */ |
|
81 | + protected $userManager; |
|
82 | + /** |
|
83 | + * @var INotificationManager |
|
84 | + */ |
|
85 | + protected $notificationManager; |
|
86 | + /** |
|
87 | + * @var string |
|
88 | + */ |
|
89 | + protected $dn; |
|
90 | + /** |
|
91 | + * @var string |
|
92 | + */ |
|
93 | + protected $uid; |
|
94 | + /** |
|
95 | + * @var string[] |
|
96 | + */ |
|
97 | + protected $refreshedFeatures = array(); |
|
98 | + /** |
|
99 | + * @var string |
|
100 | + */ |
|
101 | + protected $avatarImage; |
|
102 | + |
|
103 | + /** |
|
104 | + * DB config keys for user preferences |
|
105 | + */ |
|
106 | + const USER_PREFKEY_FIRSTLOGIN = 'firstLoginAccomplished'; |
|
107 | + const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh'; |
|
108 | + |
|
109 | + /** |
|
110 | + * @brief constructor, make sure the subclasses call this one! |
|
111 | + * @param string $username the internal username |
|
112 | + * @param string $dn the LDAP DN |
|
113 | + * @param IUserTools $access an instance that implements IUserTools for |
|
114 | + * LDAP interaction |
|
115 | + * @param IConfig $config |
|
116 | + * @param FilesystemHelper $fs |
|
117 | + * @param Image $image any empty instance |
|
118 | + * @param LogWrapper $log |
|
119 | + * @param IAvatarManager $avatarManager |
|
120 | + * @param IUserManager $userManager |
|
121 | + * @param INotificationManager $notificationManager |
|
122 | + */ |
|
123 | + public function __construct($username, $dn, IUserTools $access, |
|
124 | + IConfig $config, FilesystemHelper $fs, Image $image, |
|
125 | + LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager, |
|
126 | + INotificationManager $notificationManager) { |
|
127 | 127 | |
128 | - if ($username === null) { |
|
129 | - $log->log("uid for '$dn' must not be null!", ILogger::ERROR); |
|
130 | - throw new \InvalidArgumentException('uid must not be null!'); |
|
131 | - } else if ($username === '') { |
|
132 | - $log->log("uid for '$dn' must not be an empty string", ILogger::ERROR); |
|
133 | - throw new \InvalidArgumentException('uid must not be an empty string!'); |
|
134 | - } |
|
135 | - |
|
136 | - $this->access = $access; |
|
137 | - $this->connection = $access->getConnection(); |
|
138 | - $this->config = $config; |
|
139 | - $this->fs = $fs; |
|
140 | - $this->dn = $dn; |
|
141 | - $this->uid = $username; |
|
142 | - $this->image = $image; |
|
143 | - $this->log = $log; |
|
144 | - $this->avatarManager = $avatarManager; |
|
145 | - $this->userManager = $userManager; |
|
146 | - $this->notificationManager = $notificationManager; |
|
147 | - |
|
148 | - \OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry'); |
|
149 | - } |
|
150 | - |
|
151 | - /** |
|
152 | - * @brief updates properties like email, quota or avatar provided by LDAP |
|
153 | - * @return null |
|
154 | - */ |
|
155 | - public function update() { |
|
156 | - if(is_null($this->dn)) { |
|
157 | - return null; |
|
158 | - } |
|
159 | - |
|
160 | - $hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap', |
|
161 | - self::USER_PREFKEY_FIRSTLOGIN, 0); |
|
162 | - |
|
163 | - if($this->needsRefresh()) { |
|
164 | - $this->updateEmail(); |
|
165 | - $this->updateQuota(); |
|
166 | - if($hasLoggedIn !== 0) { |
|
167 | - //we do not need to try it, when the user has not been logged in |
|
168 | - //before, because the file system will not be ready. |
|
169 | - $this->updateAvatar(); |
|
170 | - //in order to get an avatar as soon as possible, mark the user |
|
171 | - //as refreshed only when updating the avatar did happen |
|
172 | - $this->markRefreshTime(); |
|
173 | - } |
|
174 | - } |
|
175 | - } |
|
176 | - |
|
177 | - /** |
|
178 | - * processes results from LDAP for attributes as returned by getAttributesToRead() |
|
179 | - * @param array $ldapEntry the user entry as retrieved from LDAP |
|
180 | - */ |
|
181 | - public function processAttributes($ldapEntry) { |
|
182 | - $this->markRefreshTime(); |
|
183 | - //Quota |
|
184 | - $attr = strtolower($this->connection->ldapQuotaAttribute); |
|
185 | - if(isset($ldapEntry[$attr])) { |
|
186 | - $this->updateQuota($ldapEntry[$attr][0]); |
|
187 | - } else { |
|
188 | - if ($this->connection->ldapQuotaDefault !== '') { |
|
189 | - $this->updateQuota(); |
|
190 | - } |
|
191 | - } |
|
192 | - unset($attr); |
|
193 | - |
|
194 | - //displayName |
|
195 | - $displayName = $displayName2 = ''; |
|
196 | - $attr = strtolower($this->connection->ldapUserDisplayName); |
|
197 | - if(isset($ldapEntry[$attr])) { |
|
198 | - $displayName = (string)$ldapEntry[$attr][0]; |
|
199 | - } |
|
200 | - $attr = strtolower($this->connection->ldapUserDisplayName2); |
|
201 | - if(isset($ldapEntry[$attr])) { |
|
202 | - $displayName2 = (string)$ldapEntry[$attr][0]; |
|
203 | - } |
|
204 | - if ($displayName !== '') { |
|
205 | - $this->composeAndStoreDisplayName($displayName); |
|
206 | - $this->access->cacheUserDisplayName( |
|
207 | - $this->getUsername(), |
|
208 | - $displayName, |
|
209 | - $displayName2 |
|
210 | - ); |
|
211 | - } |
|
212 | - unset($attr); |
|
213 | - |
|
214 | ||
215 | - //email must be stored after displayname, because it would cause a user |
|
216 | - //change event that will trigger fetching the display name again |
|
217 | - $attr = strtolower($this->connection->ldapEmailAttribute); |
|
218 | - if(isset($ldapEntry[$attr])) { |
|
219 | - $this->updateEmail($ldapEntry[$attr][0]); |
|
220 | - } |
|
221 | - unset($attr); |
|
222 | - |
|
223 | - // LDAP Username, needed for s2s sharing |
|
224 | - if(isset($ldapEntry['uid'])) { |
|
225 | - $this->storeLDAPUserName($ldapEntry['uid'][0]); |
|
226 | - } else if(isset($ldapEntry['samaccountname'])) { |
|
227 | - $this->storeLDAPUserName($ldapEntry['samaccountname'][0]); |
|
228 | - } |
|
229 | - |
|
230 | - //homePath |
|
231 | - if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) { |
|
232 | - $attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:'))); |
|
233 | - if(isset($ldapEntry[$attr])) { |
|
234 | - $this->access->cacheUserHome( |
|
235 | - $this->getUsername(), $this->getHomePath($ldapEntry[$attr][0])); |
|
236 | - } |
|
237 | - } |
|
238 | - |
|
239 | - //memberOf groups |
|
240 | - $cacheKey = 'getMemberOf'.$this->getUsername(); |
|
241 | - $groups = false; |
|
242 | - if(isset($ldapEntry['memberof'])) { |
|
243 | - $groups = $ldapEntry['memberof']; |
|
244 | - } |
|
245 | - $this->connection->writeToCache($cacheKey, $groups); |
|
246 | - |
|
247 | - //Avatar |
|
248 | - $attrs = array('jpegphoto', 'thumbnailphoto'); |
|
249 | - foreach ($attrs as $attr) { |
|
250 | - if(isset($ldapEntry[$attr])) { |
|
251 | - $this->avatarImage = $ldapEntry[$attr][0]; |
|
252 | - // the call to the method that saves the avatar in the file |
|
253 | - // system must be postponed after the login. It is to ensure |
|
254 | - // external mounts are mounted properly (e.g. with login |
|
255 | - // credentials from the session). |
|
256 | - \OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin'); |
|
257 | - break; |
|
258 | - } |
|
259 | - } |
|
260 | - } |
|
261 | - |
|
262 | - /** |
|
263 | - * @brief returns the LDAP DN of the user |
|
264 | - * @return string |
|
265 | - */ |
|
266 | - public function getDN() { |
|
267 | - return $this->dn; |
|
268 | - } |
|
269 | - |
|
270 | - /** |
|
271 | - * @brief returns the Nextcloud internal username of the user |
|
272 | - * @return string |
|
273 | - */ |
|
274 | - public function getUsername() { |
|
275 | - return $this->uid; |
|
276 | - } |
|
277 | - |
|
278 | - /** |
|
279 | - * returns the home directory of the user if specified by LDAP settings |
|
280 | - * @param string $valueFromLDAP |
|
281 | - * @return bool|string |
|
282 | - * @throws \Exception |
|
283 | - */ |
|
284 | - public function getHomePath($valueFromLDAP = null) { |
|
285 | - $path = (string)$valueFromLDAP; |
|
286 | - $attr = null; |
|
287 | - |
|
288 | - if (is_null($valueFromLDAP) |
|
289 | - && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0 |
|
290 | - && $this->access->connection->homeFolderNamingRule !== 'attr:') |
|
291 | - { |
|
292 | - $attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:')); |
|
293 | - $homedir = $this->access->readAttribute( |
|
294 | - $this->access->username2dn($this->getUsername()), $attr); |
|
295 | - if ($homedir && isset($homedir[0])) { |
|
296 | - $path = $homedir[0]; |
|
297 | - } |
|
298 | - } |
|
299 | - |
|
300 | - if ($path !== '') { |
|
301 | - //if attribute's value is an absolute path take this, otherwise append it to data dir |
|
302 | - //check for / at the beginning or pattern c:\ resp. c:/ |
|
303 | - if( '/' !== $path[0] |
|
304 | - && !(3 < strlen($path) && ctype_alpha($path[0]) |
|
305 | - && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2])) |
|
306 | - ) { |
|
307 | - $path = $this->config->getSystemValue('datadirectory', |
|
308 | - \OC::$SERVERROOT.'/data' ) . '/' . $path; |
|
309 | - } |
|
310 | - //we need it to store it in the DB as well in case a user gets |
|
311 | - //deleted so we can clean up afterwards |
|
312 | - $this->config->setUserValue( |
|
313 | - $this->getUsername(), 'user_ldap', 'homePath', $path |
|
314 | - ); |
|
315 | - return $path; |
|
316 | - } |
|
317 | - |
|
318 | - if( !is_null($attr) |
|
319 | - && $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true) |
|
320 | - ) { |
|
321 | - // a naming rule attribute is defined, but it doesn't exist for that LDAP user |
|
322 | - throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername()); |
|
323 | - } |
|
324 | - |
|
325 | - //false will apply default behaviour as defined and done by OC_User |
|
326 | - $this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', ''); |
|
327 | - return false; |
|
328 | - } |
|
329 | - |
|
330 | - public function getMemberOfGroups() { |
|
331 | - $cacheKey = 'getMemberOf'.$this->getUsername(); |
|
332 | - $memberOfGroups = $this->connection->getFromCache($cacheKey); |
|
333 | - if(!is_null($memberOfGroups)) { |
|
334 | - return $memberOfGroups; |
|
335 | - } |
|
336 | - $groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf'); |
|
337 | - $this->connection->writeToCache($cacheKey, $groupDNs); |
|
338 | - return $groupDNs; |
|
339 | - } |
|
340 | - |
|
341 | - /** |
|
342 | - * @brief reads the image from LDAP that shall be used as Avatar |
|
343 | - * @return string data (provided by LDAP) | false |
|
344 | - */ |
|
345 | - public function getAvatarImage() { |
|
346 | - if(!is_null($this->avatarImage)) { |
|
347 | - return $this->avatarImage; |
|
348 | - } |
|
349 | - |
|
350 | - $this->avatarImage = false; |
|
351 | - $attributes = array('jpegPhoto', 'thumbnailPhoto'); |
|
352 | - foreach($attributes as $attribute) { |
|
353 | - $result = $this->access->readAttribute($this->dn, $attribute); |
|
354 | - if($result !== false && is_array($result) && isset($result[0])) { |
|
355 | - $this->avatarImage = $result[0]; |
|
356 | - break; |
|
357 | - } |
|
358 | - } |
|
359 | - |
|
360 | - return $this->avatarImage; |
|
361 | - } |
|
362 | - |
|
363 | - /** |
|
364 | - * @brief marks the user as having logged in at least once |
|
365 | - * @return null |
|
366 | - */ |
|
367 | - public function markLogin() { |
|
368 | - $this->config->setUserValue( |
|
369 | - $this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1); |
|
370 | - } |
|
371 | - |
|
372 | - /** |
|
373 | - * @brief marks the time when user features like email have been updated |
|
374 | - * @return null |
|
375 | - */ |
|
376 | - public function markRefreshTime() { |
|
377 | - $this->config->setUserValue( |
|
378 | - $this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time()); |
|
379 | - } |
|
380 | - |
|
381 | - /** |
|
382 | - * @brief checks whether user features needs to be updated again by |
|
383 | - * comparing the difference of time of the last refresh to now with the |
|
384 | - * desired interval |
|
385 | - * @return bool |
|
386 | - */ |
|
387 | - private function needsRefresh() { |
|
388 | - $lastChecked = $this->config->getUserValue($this->uid, 'user_ldap', |
|
389 | - self::USER_PREFKEY_LASTREFRESH, 0); |
|
390 | - |
|
391 | - if((time() - (int)$lastChecked) < (int)$this->config->getAppValue('user_ldap', 'updateAttributesInterval', 86400)) { |
|
392 | - return false; |
|
393 | - } |
|
394 | - return true; |
|
395 | - } |
|
396 | - |
|
397 | - /** |
|
398 | - * Stores a key-value pair in relation to this user |
|
399 | - * |
|
400 | - * @param string $key |
|
401 | - * @param string $value |
|
402 | - */ |
|
403 | - private function store($key, $value) { |
|
404 | - $this->config->setUserValue($this->uid, 'user_ldap', $key, $value); |
|
405 | - } |
|
406 | - |
|
407 | - /** |
|
408 | - * Composes the display name and stores it in the database. The final |
|
409 | - * display name is returned. |
|
410 | - * |
|
411 | - * @param string $displayName |
|
412 | - * @param string $displayName2 |
|
413 | - * @returns string the effective display name |
|
414 | - */ |
|
415 | - public function composeAndStoreDisplayName($displayName, $displayName2 = '') { |
|
416 | - $displayName2 = (string)$displayName2; |
|
417 | - if($displayName2 !== '') { |
|
418 | - $displayName .= ' (' . $displayName2 . ')'; |
|
419 | - } |
|
420 | - $this->store('displayName', $displayName); |
|
421 | - return $displayName; |
|
422 | - } |
|
423 | - |
|
424 | - /** |
|
425 | - * Stores the LDAP Username in the Database |
|
426 | - * @param string $userName |
|
427 | - */ |
|
428 | - public function storeLDAPUserName($userName) { |
|
429 | - $this->store('uid', $userName); |
|
430 | - } |
|
431 | - |
|
432 | - /** |
|
433 | - * @brief checks whether an update method specified by feature was run |
|
434 | - * already. If not, it will marked like this, because it is expected that |
|
435 | - * the method will be run, when false is returned. |
|
436 | - * @param string $feature email | quota | avatar (can be extended) |
|
437 | - * @return bool |
|
438 | - */ |
|
439 | - private function wasRefreshed($feature) { |
|
440 | - if(isset($this->refreshedFeatures[$feature])) { |
|
441 | - return true; |
|
442 | - } |
|
443 | - $this->refreshedFeatures[$feature] = 1; |
|
444 | - return false; |
|
445 | - } |
|
446 | - |
|
447 | - /** |
|
448 | - * fetches the email from LDAP and stores it as Nextcloud user value |
|
449 | - * @param string $valueFromLDAP if known, to save an LDAP read request |
|
450 | - * @return null |
|
451 | - */ |
|
452 | - public function updateEmail($valueFromLDAP = null) { |
|
453 | - if($this->wasRefreshed('email')) { |
|
454 | - return; |
|
455 | - } |
|
456 | - $email = (string)$valueFromLDAP; |
|
457 | - if(is_null($valueFromLDAP)) { |
|
458 | - $emailAttribute = $this->connection->ldapEmailAttribute; |
|
459 | - if ($emailAttribute !== '') { |
|
460 | - $aEmail = $this->access->readAttribute($this->dn, $emailAttribute); |
|
461 | - if(is_array($aEmail) && (count($aEmail) > 0)) { |
|
462 | - $email = (string)$aEmail[0]; |
|
463 | - } |
|
464 | - } |
|
465 | - } |
|
466 | - if ($email !== '') { |
|
467 | - $user = $this->userManager->get($this->uid); |
|
468 | - if (!is_null($user)) { |
|
469 | - $currentEmail = (string)$user->getEMailAddress(); |
|
470 | - if ($currentEmail !== $email) { |
|
471 | - $user->setEMailAddress($email); |
|
472 | - } |
|
473 | - } |
|
474 | - } |
|
475 | - } |
|
476 | - |
|
477 | - /** |
|
478 | - * Overall process goes as follow: |
|
479 | - * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function |
|
480 | - * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota |
|
481 | - * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default') |
|
482 | - * 4. check if the target user exists and set the quota for the user. |
|
483 | - * |
|
484 | - * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP |
|
485 | - * parameter can be passed with the value of the attribute. This value will be considered as the |
|
486 | - * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to |
|
487 | - * fetch all the user's attributes in one call and use the fetched values in this function. |
|
488 | - * The expected value for that parameter is a string describing the quota for the user. Valid |
|
489 | - * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in |
|
490 | - * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info) |
|
491 | - * |
|
492 | - * fetches the quota from LDAP and stores it as Nextcloud user value |
|
493 | - * @param string $valueFromLDAP the quota attribute's value can be passed, |
|
494 | - * to save the readAttribute request |
|
495 | - * @return null |
|
496 | - */ |
|
497 | - public function updateQuota($valueFromLDAP = null) { |
|
498 | - if($this->wasRefreshed('quota')) { |
|
499 | - return; |
|
500 | - } |
|
501 | - |
|
502 | - $quota = false; |
|
503 | - if(is_null($valueFromLDAP)) { |
|
504 | - $quotaAttribute = $this->connection->ldapQuotaAttribute; |
|
505 | - if ($quotaAttribute !== '') { |
|
506 | - $aQuota = $this->access->readAttribute($this->dn, $quotaAttribute); |
|
507 | - if($aQuota && (count($aQuota) > 0)) { |
|
508 | - if ($this->verifyQuotaValue($aQuota[0])) { |
|
509 | - $quota = $aQuota[0]; |
|
510 | - } else { |
|
511 | - $this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', ILogger::WARN); |
|
512 | - } |
|
513 | - } |
|
514 | - } |
|
515 | - } else { |
|
516 | - if ($this->verifyQuotaValue($valueFromLDAP)) { |
|
517 | - $quota = $valueFromLDAP; |
|
518 | - } else { |
|
519 | - $this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', ILogger::WARN); |
|
520 | - } |
|
521 | - } |
|
522 | - |
|
523 | - if ($quota === false) { |
|
524 | - // quota not found using the LDAP attribute (or not parseable). Try the default quota |
|
525 | - $defaultQuota = $this->connection->ldapQuotaDefault; |
|
526 | - if ($this->verifyQuotaValue($defaultQuota)) { |
|
527 | - $quota = $defaultQuota; |
|
528 | - } |
|
529 | - } |
|
530 | - |
|
531 | - $targetUser = $this->userManager->get($this->uid); |
|
532 | - if ($targetUser) { |
|
533 | - if($quota !== false) { |
|
534 | - $targetUser->setQuota($quota); |
|
535 | - } else { |
|
536 | - $this->log->log('not suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', ILogger::WARN); |
|
537 | - } |
|
538 | - } else { |
|
539 | - $this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', ILogger::ERROR); |
|
540 | - } |
|
541 | - } |
|
542 | - |
|
543 | - private function verifyQuotaValue($quotaValue) { |
|
544 | - return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false; |
|
545 | - } |
|
546 | - |
|
547 | - /** |
|
548 | - * called by a post_login hook to save the avatar picture |
|
549 | - * |
|
550 | - * @param array $params |
|
551 | - */ |
|
552 | - public function updateAvatarPostLogin($params) { |
|
553 | - if(isset($params['uid']) && $params['uid'] === $this->getUsername()) { |
|
554 | - $this->updateAvatar(); |
|
555 | - } |
|
556 | - } |
|
557 | - |
|
558 | - /** |
|
559 | - * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar |
|
560 | - * @return null |
|
561 | - */ |
|
562 | - public function updateAvatar() { |
|
563 | - if($this->wasRefreshed('avatar')) { |
|
564 | - return; |
|
565 | - } |
|
566 | - $avatarImage = $this->getAvatarImage(); |
|
567 | - if($avatarImage === false) { |
|
568 | - //not set, nothing left to do; |
|
569 | - return; |
|
570 | - } |
|
571 | - $this->image->loadFromBase64(base64_encode($avatarImage)); |
|
572 | - $this->setOwnCloudAvatar(); |
|
573 | - } |
|
574 | - |
|
575 | - /** |
|
576 | - * @brief sets an image as Nextcloud avatar |
|
577 | - * @return null |
|
578 | - */ |
|
579 | - private function setOwnCloudAvatar() { |
|
580 | - if(!$this->image->valid()) { |
|
581 | - $this->log->log('jpegPhoto data invalid for '.$this->dn, ILogger::ERROR); |
|
582 | - return; |
|
583 | - } |
|
584 | - //make sure it is a square and not bigger than 128x128 |
|
585 | - $size = min(array($this->image->width(), $this->image->height(), 128)); |
|
586 | - if(!$this->image->centerCrop($size)) { |
|
587 | - $this->log->log('croping image for avatar failed for '.$this->dn, ILogger::ERROR); |
|
588 | - return; |
|
589 | - } |
|
590 | - |
|
591 | - if(!$this->fs->isLoaded()) { |
|
592 | - $this->fs->setup($this->uid); |
|
593 | - } |
|
594 | - |
|
595 | - try { |
|
596 | - $avatar = $this->avatarManager->getAvatar($this->uid); |
|
597 | - $avatar->set($this->image); |
|
598 | - } catch (\Exception $e) { |
|
599 | - \OC::$server->getLogger()->logException($e, [ |
|
600 | - 'message' => 'Could not set avatar for ' . $this->dn, |
|
601 | - 'level' => ILogger::INFO, |
|
602 | - 'app' => 'user_ldap', |
|
603 | - ]); |
|
604 | - } |
|
605 | - } |
|
606 | - |
|
607 | - /** |
|
608 | - * called by a post_login hook to handle password expiry |
|
609 | - * |
|
610 | - * @param array $params |
|
611 | - */ |
|
612 | - public function handlePasswordExpiry($params) { |
|
613 | - $ppolicyDN = $this->connection->ldapDefaultPPolicyDN; |
|
614 | - if (empty($ppolicyDN) || ((int)$this->connection->turnOnPasswordChange !== 1)) { |
|
615 | - return;//password expiry handling disabled |
|
616 | - } |
|
617 | - $uid = $params['uid']; |
|
618 | - if(isset($uid) && $uid === $this->getUsername()) { |
|
619 | - //retrieve relevant user attributes |
|
620 | - $result = $this->access->search('objectclass=*', $this->dn, ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']); |
|
128 | + if ($username === null) { |
|
129 | + $log->log("uid for '$dn' must not be null!", ILogger::ERROR); |
|
130 | + throw new \InvalidArgumentException('uid must not be null!'); |
|
131 | + } else if ($username === '') { |
|
132 | + $log->log("uid for '$dn' must not be an empty string", ILogger::ERROR); |
|
133 | + throw new \InvalidArgumentException('uid must not be an empty string!'); |
|
134 | + } |
|
135 | + |
|
136 | + $this->access = $access; |
|
137 | + $this->connection = $access->getConnection(); |
|
138 | + $this->config = $config; |
|
139 | + $this->fs = $fs; |
|
140 | + $this->dn = $dn; |
|
141 | + $this->uid = $username; |
|
142 | + $this->image = $image; |
|
143 | + $this->log = $log; |
|
144 | + $this->avatarManager = $avatarManager; |
|
145 | + $this->userManager = $userManager; |
|
146 | + $this->notificationManager = $notificationManager; |
|
147 | + |
|
148 | + \OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry'); |
|
149 | + } |
|
150 | + |
|
151 | + /** |
|
152 | + * @brief updates properties like email, quota or avatar provided by LDAP |
|
153 | + * @return null |
|
154 | + */ |
|
155 | + public function update() { |
|
156 | + if(is_null($this->dn)) { |
|
157 | + return null; |
|
158 | + } |
|
159 | + |
|
160 | + $hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap', |
|
161 | + self::USER_PREFKEY_FIRSTLOGIN, 0); |
|
162 | + |
|
163 | + if($this->needsRefresh()) { |
|
164 | + $this->updateEmail(); |
|
165 | + $this->updateQuota(); |
|
166 | + if($hasLoggedIn !== 0) { |
|
167 | + //we do not need to try it, when the user has not been logged in |
|
168 | + //before, because the file system will not be ready. |
|
169 | + $this->updateAvatar(); |
|
170 | + //in order to get an avatar as soon as possible, mark the user |
|
171 | + //as refreshed only when updating the avatar did happen |
|
172 | + $this->markRefreshTime(); |
|
173 | + } |
|
174 | + } |
|
175 | + } |
|
176 | + |
|
177 | + /** |
|
178 | + * processes results from LDAP for attributes as returned by getAttributesToRead() |
|
179 | + * @param array $ldapEntry the user entry as retrieved from LDAP |
|
180 | + */ |
|
181 | + public function processAttributes($ldapEntry) { |
|
182 | + $this->markRefreshTime(); |
|
183 | + //Quota |
|
184 | + $attr = strtolower($this->connection->ldapQuotaAttribute); |
|
185 | + if(isset($ldapEntry[$attr])) { |
|
186 | + $this->updateQuota($ldapEntry[$attr][0]); |
|
187 | + } else { |
|
188 | + if ($this->connection->ldapQuotaDefault !== '') { |
|
189 | + $this->updateQuota(); |
|
190 | + } |
|
191 | + } |
|
192 | + unset($attr); |
|
193 | + |
|
194 | + //displayName |
|
195 | + $displayName = $displayName2 = ''; |
|
196 | + $attr = strtolower($this->connection->ldapUserDisplayName); |
|
197 | + if(isset($ldapEntry[$attr])) { |
|
198 | + $displayName = (string)$ldapEntry[$attr][0]; |
|
199 | + } |
|
200 | + $attr = strtolower($this->connection->ldapUserDisplayName2); |
|
201 | + if(isset($ldapEntry[$attr])) { |
|
202 | + $displayName2 = (string)$ldapEntry[$attr][0]; |
|
203 | + } |
|
204 | + if ($displayName !== '') { |
|
205 | + $this->composeAndStoreDisplayName($displayName); |
|
206 | + $this->access->cacheUserDisplayName( |
|
207 | + $this->getUsername(), |
|
208 | + $displayName, |
|
209 | + $displayName2 |
|
210 | + ); |
|
211 | + } |
|
212 | + unset($attr); |
|
213 | + |
|
214 | ||
215 | + //email must be stored after displayname, because it would cause a user |
|
216 | + //change event that will trigger fetching the display name again |
|
217 | + $attr = strtolower($this->connection->ldapEmailAttribute); |
|
218 | + if(isset($ldapEntry[$attr])) { |
|
219 | + $this->updateEmail($ldapEntry[$attr][0]); |
|
220 | + } |
|
221 | + unset($attr); |
|
222 | + |
|
223 | + // LDAP Username, needed for s2s sharing |
|
224 | + if(isset($ldapEntry['uid'])) { |
|
225 | + $this->storeLDAPUserName($ldapEntry['uid'][0]); |
|
226 | + } else if(isset($ldapEntry['samaccountname'])) { |
|
227 | + $this->storeLDAPUserName($ldapEntry['samaccountname'][0]); |
|
228 | + } |
|
229 | + |
|
230 | + //homePath |
|
231 | + if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) { |
|
232 | + $attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:'))); |
|
233 | + if(isset($ldapEntry[$attr])) { |
|
234 | + $this->access->cacheUserHome( |
|
235 | + $this->getUsername(), $this->getHomePath($ldapEntry[$attr][0])); |
|
236 | + } |
|
237 | + } |
|
238 | + |
|
239 | + //memberOf groups |
|
240 | + $cacheKey = 'getMemberOf'.$this->getUsername(); |
|
241 | + $groups = false; |
|
242 | + if(isset($ldapEntry['memberof'])) { |
|
243 | + $groups = $ldapEntry['memberof']; |
|
244 | + } |
|
245 | + $this->connection->writeToCache($cacheKey, $groups); |
|
246 | + |
|
247 | + //Avatar |
|
248 | + $attrs = array('jpegphoto', 'thumbnailphoto'); |
|
249 | + foreach ($attrs as $attr) { |
|
250 | + if(isset($ldapEntry[$attr])) { |
|
251 | + $this->avatarImage = $ldapEntry[$attr][0]; |
|
252 | + // the call to the method that saves the avatar in the file |
|
253 | + // system must be postponed after the login. It is to ensure |
|
254 | + // external mounts are mounted properly (e.g. with login |
|
255 | + // credentials from the session). |
|
256 | + \OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin'); |
|
257 | + break; |
|
258 | + } |
|
259 | + } |
|
260 | + } |
|
261 | + |
|
262 | + /** |
|
263 | + * @brief returns the LDAP DN of the user |
|
264 | + * @return string |
|
265 | + */ |
|
266 | + public function getDN() { |
|
267 | + return $this->dn; |
|
268 | + } |
|
269 | + |
|
270 | + /** |
|
271 | + * @brief returns the Nextcloud internal username of the user |
|
272 | + * @return string |
|
273 | + */ |
|
274 | + public function getUsername() { |
|
275 | + return $this->uid; |
|
276 | + } |
|
277 | + |
|
278 | + /** |
|
279 | + * returns the home directory of the user if specified by LDAP settings |
|
280 | + * @param string $valueFromLDAP |
|
281 | + * @return bool|string |
|
282 | + * @throws \Exception |
|
283 | + */ |
|
284 | + public function getHomePath($valueFromLDAP = null) { |
|
285 | + $path = (string)$valueFromLDAP; |
|
286 | + $attr = null; |
|
287 | + |
|
288 | + if (is_null($valueFromLDAP) |
|
289 | + && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0 |
|
290 | + && $this->access->connection->homeFolderNamingRule !== 'attr:') |
|
291 | + { |
|
292 | + $attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:')); |
|
293 | + $homedir = $this->access->readAttribute( |
|
294 | + $this->access->username2dn($this->getUsername()), $attr); |
|
295 | + if ($homedir && isset($homedir[0])) { |
|
296 | + $path = $homedir[0]; |
|
297 | + } |
|
298 | + } |
|
299 | + |
|
300 | + if ($path !== '') { |
|
301 | + //if attribute's value is an absolute path take this, otherwise append it to data dir |
|
302 | + //check for / at the beginning or pattern c:\ resp. c:/ |
|
303 | + if( '/' !== $path[0] |
|
304 | + && !(3 < strlen($path) && ctype_alpha($path[0]) |
|
305 | + && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2])) |
|
306 | + ) { |
|
307 | + $path = $this->config->getSystemValue('datadirectory', |
|
308 | + \OC::$SERVERROOT.'/data' ) . '/' . $path; |
|
309 | + } |
|
310 | + //we need it to store it in the DB as well in case a user gets |
|
311 | + //deleted so we can clean up afterwards |
|
312 | + $this->config->setUserValue( |
|
313 | + $this->getUsername(), 'user_ldap', 'homePath', $path |
|
314 | + ); |
|
315 | + return $path; |
|
316 | + } |
|
317 | + |
|
318 | + if( !is_null($attr) |
|
319 | + && $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true) |
|
320 | + ) { |
|
321 | + // a naming rule attribute is defined, but it doesn't exist for that LDAP user |
|
322 | + throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername()); |
|
323 | + } |
|
324 | + |
|
325 | + //false will apply default behaviour as defined and done by OC_User |
|
326 | + $this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', ''); |
|
327 | + return false; |
|
328 | + } |
|
329 | + |
|
330 | + public function getMemberOfGroups() { |
|
331 | + $cacheKey = 'getMemberOf'.$this->getUsername(); |
|
332 | + $memberOfGroups = $this->connection->getFromCache($cacheKey); |
|
333 | + if(!is_null($memberOfGroups)) { |
|
334 | + return $memberOfGroups; |
|
335 | + } |
|
336 | + $groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf'); |
|
337 | + $this->connection->writeToCache($cacheKey, $groupDNs); |
|
338 | + return $groupDNs; |
|
339 | + } |
|
340 | + |
|
341 | + /** |
|
342 | + * @brief reads the image from LDAP that shall be used as Avatar |
|
343 | + * @return string data (provided by LDAP) | false |
|
344 | + */ |
|
345 | + public function getAvatarImage() { |
|
346 | + if(!is_null($this->avatarImage)) { |
|
347 | + return $this->avatarImage; |
|
348 | + } |
|
349 | + |
|
350 | + $this->avatarImage = false; |
|
351 | + $attributes = array('jpegPhoto', 'thumbnailPhoto'); |
|
352 | + foreach($attributes as $attribute) { |
|
353 | + $result = $this->access->readAttribute($this->dn, $attribute); |
|
354 | + if($result !== false && is_array($result) && isset($result[0])) { |
|
355 | + $this->avatarImage = $result[0]; |
|
356 | + break; |
|
357 | + } |
|
358 | + } |
|
359 | + |
|
360 | + return $this->avatarImage; |
|
361 | + } |
|
362 | + |
|
363 | + /** |
|
364 | + * @brief marks the user as having logged in at least once |
|
365 | + * @return null |
|
366 | + */ |
|
367 | + public function markLogin() { |
|
368 | + $this->config->setUserValue( |
|
369 | + $this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1); |
|
370 | + } |
|
371 | + |
|
372 | + /** |
|
373 | + * @brief marks the time when user features like email have been updated |
|
374 | + * @return null |
|
375 | + */ |
|
376 | + public function markRefreshTime() { |
|
377 | + $this->config->setUserValue( |
|
378 | + $this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time()); |
|
379 | + } |
|
380 | + |
|
381 | + /** |
|
382 | + * @brief checks whether user features needs to be updated again by |
|
383 | + * comparing the difference of time of the last refresh to now with the |
|
384 | + * desired interval |
|
385 | + * @return bool |
|
386 | + */ |
|
387 | + private function needsRefresh() { |
|
388 | + $lastChecked = $this->config->getUserValue($this->uid, 'user_ldap', |
|
389 | + self::USER_PREFKEY_LASTREFRESH, 0); |
|
390 | + |
|
391 | + if((time() - (int)$lastChecked) < (int)$this->config->getAppValue('user_ldap', 'updateAttributesInterval', 86400)) { |
|
392 | + return false; |
|
393 | + } |
|
394 | + return true; |
|
395 | + } |
|
396 | + |
|
397 | + /** |
|
398 | + * Stores a key-value pair in relation to this user |
|
399 | + * |
|
400 | + * @param string $key |
|
401 | + * @param string $value |
|
402 | + */ |
|
403 | + private function store($key, $value) { |
|
404 | + $this->config->setUserValue($this->uid, 'user_ldap', $key, $value); |
|
405 | + } |
|
406 | + |
|
407 | + /** |
|
408 | + * Composes the display name and stores it in the database. The final |
|
409 | + * display name is returned. |
|
410 | + * |
|
411 | + * @param string $displayName |
|
412 | + * @param string $displayName2 |
|
413 | + * @returns string the effective display name |
|
414 | + */ |
|
415 | + public function composeAndStoreDisplayName($displayName, $displayName2 = '') { |
|
416 | + $displayName2 = (string)$displayName2; |
|
417 | + if($displayName2 !== '') { |
|
418 | + $displayName .= ' (' . $displayName2 . ')'; |
|
419 | + } |
|
420 | + $this->store('displayName', $displayName); |
|
421 | + return $displayName; |
|
422 | + } |
|
423 | + |
|
424 | + /** |
|
425 | + * Stores the LDAP Username in the Database |
|
426 | + * @param string $userName |
|
427 | + */ |
|
428 | + public function storeLDAPUserName($userName) { |
|
429 | + $this->store('uid', $userName); |
|
430 | + } |
|
431 | + |
|
432 | + /** |
|
433 | + * @brief checks whether an update method specified by feature was run |
|
434 | + * already. If not, it will marked like this, because it is expected that |
|
435 | + * the method will be run, when false is returned. |
|
436 | + * @param string $feature email | quota | avatar (can be extended) |
|
437 | + * @return bool |
|
438 | + */ |
|
439 | + private function wasRefreshed($feature) { |
|
440 | + if(isset($this->refreshedFeatures[$feature])) { |
|
441 | + return true; |
|
442 | + } |
|
443 | + $this->refreshedFeatures[$feature] = 1; |
|
444 | + return false; |
|
445 | + } |
|
446 | + |
|
447 | + /** |
|
448 | + * fetches the email from LDAP and stores it as Nextcloud user value |
|
449 | + * @param string $valueFromLDAP if known, to save an LDAP read request |
|
450 | + * @return null |
|
451 | + */ |
|
452 | + public function updateEmail($valueFromLDAP = null) { |
|
453 | + if($this->wasRefreshed('email')) { |
|
454 | + return; |
|
455 | + } |
|
456 | + $email = (string)$valueFromLDAP; |
|
457 | + if(is_null($valueFromLDAP)) { |
|
458 | + $emailAttribute = $this->connection->ldapEmailAttribute; |
|
459 | + if ($emailAttribute !== '') { |
|
460 | + $aEmail = $this->access->readAttribute($this->dn, $emailAttribute); |
|
461 | + if(is_array($aEmail) && (count($aEmail) > 0)) { |
|
462 | + $email = (string)$aEmail[0]; |
|
463 | + } |
|
464 | + } |
|
465 | + } |
|
466 | + if ($email !== '') { |
|
467 | + $user = $this->userManager->get($this->uid); |
|
468 | + if (!is_null($user)) { |
|
469 | + $currentEmail = (string)$user->getEMailAddress(); |
|
470 | + if ($currentEmail !== $email) { |
|
471 | + $user->setEMailAddress($email); |
|
472 | + } |
|
473 | + } |
|
474 | + } |
|
475 | + } |
|
476 | + |
|
477 | + /** |
|
478 | + * Overall process goes as follow: |
|
479 | + * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function |
|
480 | + * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota |
|
481 | + * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default') |
|
482 | + * 4. check if the target user exists and set the quota for the user. |
|
483 | + * |
|
484 | + * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP |
|
485 | + * parameter can be passed with the value of the attribute. This value will be considered as the |
|
486 | + * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to |
|
487 | + * fetch all the user's attributes in one call and use the fetched values in this function. |
|
488 | + * The expected value for that parameter is a string describing the quota for the user. Valid |
|
489 | + * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in |
|
490 | + * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info) |
|
491 | + * |
|
492 | + * fetches the quota from LDAP and stores it as Nextcloud user value |
|
493 | + * @param string $valueFromLDAP the quota attribute's value can be passed, |
|
494 | + * to save the readAttribute request |
|
495 | + * @return null |
|
496 | + */ |
|
497 | + public function updateQuota($valueFromLDAP = null) { |
|
498 | + if($this->wasRefreshed('quota')) { |
|
499 | + return; |
|
500 | + } |
|
501 | + |
|
502 | + $quota = false; |
|
503 | + if(is_null($valueFromLDAP)) { |
|
504 | + $quotaAttribute = $this->connection->ldapQuotaAttribute; |
|
505 | + if ($quotaAttribute !== '') { |
|
506 | + $aQuota = $this->access->readAttribute($this->dn, $quotaAttribute); |
|
507 | + if($aQuota && (count($aQuota) > 0)) { |
|
508 | + if ($this->verifyQuotaValue($aQuota[0])) { |
|
509 | + $quota = $aQuota[0]; |
|
510 | + } else { |
|
511 | + $this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', ILogger::WARN); |
|
512 | + } |
|
513 | + } |
|
514 | + } |
|
515 | + } else { |
|
516 | + if ($this->verifyQuotaValue($valueFromLDAP)) { |
|
517 | + $quota = $valueFromLDAP; |
|
518 | + } else { |
|
519 | + $this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', ILogger::WARN); |
|
520 | + } |
|
521 | + } |
|
522 | + |
|
523 | + if ($quota === false) { |
|
524 | + // quota not found using the LDAP attribute (or not parseable). Try the default quota |
|
525 | + $defaultQuota = $this->connection->ldapQuotaDefault; |
|
526 | + if ($this->verifyQuotaValue($defaultQuota)) { |
|
527 | + $quota = $defaultQuota; |
|
528 | + } |
|
529 | + } |
|
530 | + |
|
531 | + $targetUser = $this->userManager->get($this->uid); |
|
532 | + if ($targetUser) { |
|
533 | + if($quota !== false) { |
|
534 | + $targetUser->setQuota($quota); |
|
535 | + } else { |
|
536 | + $this->log->log('not suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', ILogger::WARN); |
|
537 | + } |
|
538 | + } else { |
|
539 | + $this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', ILogger::ERROR); |
|
540 | + } |
|
541 | + } |
|
542 | + |
|
543 | + private function verifyQuotaValue($quotaValue) { |
|
544 | + return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false; |
|
545 | + } |
|
546 | + |
|
547 | + /** |
|
548 | + * called by a post_login hook to save the avatar picture |
|
549 | + * |
|
550 | + * @param array $params |
|
551 | + */ |
|
552 | + public function updateAvatarPostLogin($params) { |
|
553 | + if(isset($params['uid']) && $params['uid'] === $this->getUsername()) { |
|
554 | + $this->updateAvatar(); |
|
555 | + } |
|
556 | + } |
|
557 | + |
|
558 | + /** |
|
559 | + * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar |
|
560 | + * @return null |
|
561 | + */ |
|
562 | + public function updateAvatar() { |
|
563 | + if($this->wasRefreshed('avatar')) { |
|
564 | + return; |
|
565 | + } |
|
566 | + $avatarImage = $this->getAvatarImage(); |
|
567 | + if($avatarImage === false) { |
|
568 | + //not set, nothing left to do; |
|
569 | + return; |
|
570 | + } |
|
571 | + $this->image->loadFromBase64(base64_encode($avatarImage)); |
|
572 | + $this->setOwnCloudAvatar(); |
|
573 | + } |
|
574 | + |
|
575 | + /** |
|
576 | + * @brief sets an image as Nextcloud avatar |
|
577 | + * @return null |
|
578 | + */ |
|
579 | + private function setOwnCloudAvatar() { |
|
580 | + if(!$this->image->valid()) { |
|
581 | + $this->log->log('jpegPhoto data invalid for '.$this->dn, ILogger::ERROR); |
|
582 | + return; |
|
583 | + } |
|
584 | + //make sure it is a square and not bigger than 128x128 |
|
585 | + $size = min(array($this->image->width(), $this->image->height(), 128)); |
|
586 | + if(!$this->image->centerCrop($size)) { |
|
587 | + $this->log->log('croping image for avatar failed for '.$this->dn, ILogger::ERROR); |
|
588 | + return; |
|
589 | + } |
|
590 | + |
|
591 | + if(!$this->fs->isLoaded()) { |
|
592 | + $this->fs->setup($this->uid); |
|
593 | + } |
|
594 | + |
|
595 | + try { |
|
596 | + $avatar = $this->avatarManager->getAvatar($this->uid); |
|
597 | + $avatar->set($this->image); |
|
598 | + } catch (\Exception $e) { |
|
599 | + \OC::$server->getLogger()->logException($e, [ |
|
600 | + 'message' => 'Could not set avatar for ' . $this->dn, |
|
601 | + 'level' => ILogger::INFO, |
|
602 | + 'app' => 'user_ldap', |
|
603 | + ]); |
|
604 | + } |
|
605 | + } |
|
606 | + |
|
607 | + /** |
|
608 | + * called by a post_login hook to handle password expiry |
|
609 | + * |
|
610 | + * @param array $params |
|
611 | + */ |
|
612 | + public function handlePasswordExpiry($params) { |
|
613 | + $ppolicyDN = $this->connection->ldapDefaultPPolicyDN; |
|
614 | + if (empty($ppolicyDN) || ((int)$this->connection->turnOnPasswordChange !== 1)) { |
|
615 | + return;//password expiry handling disabled |
|
616 | + } |
|
617 | + $uid = $params['uid']; |
|
618 | + if(isset($uid) && $uid === $this->getUsername()) { |
|
619 | + //retrieve relevant user attributes |
|
620 | + $result = $this->access->search('objectclass=*', $this->dn, ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']); |
|
621 | 621 | |
622 | - if(array_key_exists('pwdpolicysubentry', $result[0])) { |
|
623 | - $pwdPolicySubentry = $result[0]['pwdpolicysubentry']; |
|
624 | - if($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){ |
|
625 | - $ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN |
|
626 | - } |
|
627 | - } |
|
622 | + if(array_key_exists('pwdpolicysubentry', $result[0])) { |
|
623 | + $pwdPolicySubentry = $result[0]['pwdpolicysubentry']; |
|
624 | + if($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){ |
|
625 | + $ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN |
|
626 | + } |
|
627 | + } |
|
628 | 628 | |
629 | - $pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : null; |
|
630 | - $pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : null; |
|
631 | - $pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : null; |
|
629 | + $pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : null; |
|
630 | + $pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : null; |
|
631 | + $pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : null; |
|
632 | 632 | |
633 | - //retrieve relevant password policy attributes |
|
634 | - $cacheKey = 'ppolicyAttributes' . $ppolicyDN; |
|
635 | - $result = $this->connection->getFromCache($cacheKey); |
|
636 | - if(is_null($result)) { |
|
637 | - $result = $this->access->search('objectclass=*', $ppolicyDN, ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']); |
|
638 | - $this->connection->writeToCache($cacheKey, $result); |
|
639 | - } |
|
633 | + //retrieve relevant password policy attributes |
|
634 | + $cacheKey = 'ppolicyAttributes' . $ppolicyDN; |
|
635 | + $result = $this->connection->getFromCache($cacheKey); |
|
636 | + if(is_null($result)) { |
|
637 | + $result = $this->access->search('objectclass=*', $ppolicyDN, ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']); |
|
638 | + $this->connection->writeToCache($cacheKey, $result); |
|
639 | + } |
|
640 | 640 | |
641 | - $pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : null; |
|
642 | - $pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : null; |
|
643 | - $pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : null; |
|
641 | + $pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : null; |
|
642 | + $pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : null; |
|
643 | + $pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : null; |
|
644 | 644 | |
645 | - //handle grace login |
|
646 | - $pwdGraceUseTimeCount = count($pwdGraceUseTime); |
|
647 | - if($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login? |
|
648 | - if($pwdGraceAuthNLimit |
|
649 | - && (count($pwdGraceAuthNLimit) > 0) |
|
650 | - &&($pwdGraceUseTimeCount < (int)$pwdGraceAuthNLimit[0])) { //at least one more grace login available? |
|
651 | - $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true'); |
|
652 | - header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute( |
|
653 | - 'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid))); |
|
654 | - } else { //no more grace login available |
|
655 | - header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute( |
|
656 | - 'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid))); |
|
657 | - } |
|
658 | - exit(); |
|
659 | - } |
|
660 | - //handle pwdReset attribute |
|
661 | - if($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password |
|
662 | - $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true'); |
|
663 | - header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute( |
|
664 | - 'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid))); |
|
665 | - exit(); |
|
666 | - } |
|
667 | - //handle password expiry warning |
|
668 | - if($pwdChangedTime && (count($pwdChangedTime) > 0)) { |
|
669 | - if($pwdMaxAge && (count($pwdMaxAge) > 0) |
|
670 | - && $pwdExpireWarning && (count($pwdExpireWarning) > 0)) { |
|
671 | - $pwdMaxAgeInt = (int)$pwdMaxAge[0]; |
|
672 | - $pwdExpireWarningInt = (int)$pwdExpireWarning[0]; |
|
673 | - if($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){ |
|
674 | - $pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]); |
|
675 | - $pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S')); |
|
676 | - $currentDateTime = new \DateTime(); |
|
677 | - $secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp(); |
|
678 | - if($secondsToExpiry <= $pwdExpireWarningInt) { |
|
679 | - //remove last password expiry warning if any |
|
680 | - $notification = $this->notificationManager->createNotification(); |
|
681 | - $notification->setApp('user_ldap') |
|
682 | - ->setUser($uid) |
|
683 | - ->setObject('pwd_exp_warn', $uid) |
|
684 | - ; |
|
685 | - $this->notificationManager->markProcessed($notification); |
|
686 | - //create new password expiry warning |
|
687 | - $notification = $this->notificationManager->createNotification(); |
|
688 | - $notification->setApp('user_ldap') |
|
689 | - ->setUser($uid) |
|
690 | - ->setDateTime($currentDateTime) |
|
691 | - ->setObject('pwd_exp_warn', $uid) |
|
692 | - ->setSubject('pwd_exp_warn_days', [(int) ceil($secondsToExpiry / 60 / 60 / 24)]) |
|
693 | - ; |
|
694 | - $this->notificationManager->notify($notification); |
|
695 | - } |
|
696 | - } |
|
697 | - } |
|
698 | - } |
|
699 | - } |
|
700 | - } |
|
645 | + //handle grace login |
|
646 | + $pwdGraceUseTimeCount = count($pwdGraceUseTime); |
|
647 | + if($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login? |
|
648 | + if($pwdGraceAuthNLimit |
|
649 | + && (count($pwdGraceAuthNLimit) > 0) |
|
650 | + &&($pwdGraceUseTimeCount < (int)$pwdGraceAuthNLimit[0])) { //at least one more grace login available? |
|
651 | + $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true'); |
|
652 | + header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute( |
|
653 | + 'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid))); |
|
654 | + } else { //no more grace login available |
|
655 | + header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute( |
|
656 | + 'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid))); |
|
657 | + } |
|
658 | + exit(); |
|
659 | + } |
|
660 | + //handle pwdReset attribute |
|
661 | + if($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password |
|
662 | + $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true'); |
|
663 | + header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute( |
|
664 | + 'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid))); |
|
665 | + exit(); |
|
666 | + } |
|
667 | + //handle password expiry warning |
|
668 | + if($pwdChangedTime && (count($pwdChangedTime) > 0)) { |
|
669 | + if($pwdMaxAge && (count($pwdMaxAge) > 0) |
|
670 | + && $pwdExpireWarning && (count($pwdExpireWarning) > 0)) { |
|
671 | + $pwdMaxAgeInt = (int)$pwdMaxAge[0]; |
|
672 | + $pwdExpireWarningInt = (int)$pwdExpireWarning[0]; |
|
673 | + if($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){ |
|
674 | + $pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]); |
|
675 | + $pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S')); |
|
676 | + $currentDateTime = new \DateTime(); |
|
677 | + $secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp(); |
|
678 | + if($secondsToExpiry <= $pwdExpireWarningInt) { |
|
679 | + //remove last password expiry warning if any |
|
680 | + $notification = $this->notificationManager->createNotification(); |
|
681 | + $notification->setApp('user_ldap') |
|
682 | + ->setUser($uid) |
|
683 | + ->setObject('pwd_exp_warn', $uid) |
|
684 | + ; |
|
685 | + $this->notificationManager->markProcessed($notification); |
|
686 | + //create new password expiry warning |
|
687 | + $notification = $this->notificationManager->createNotification(); |
|
688 | + $notification->setApp('user_ldap') |
|
689 | + ->setUser($uid) |
|
690 | + ->setDateTime($currentDateTime) |
|
691 | + ->setObject('pwd_exp_warn', $uid) |
|
692 | + ->setSubject('pwd_exp_warn_days', [(int) ceil($secondsToExpiry / 60 / 60 / 24)]) |
|
693 | + ; |
|
694 | + $this->notificationManager->notify($notification); |
|
695 | + } |
|
696 | + } |
|
697 | + } |
|
698 | + } |
|
699 | + } |
|
700 | + } |
|
701 | 701 | } |
@@ -40,87 +40,87 @@ |
||
40 | 40 | use Sabre\DAV\Exception\ServiceUnavailable; |
41 | 41 | |
42 | 42 | class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { |
43 | - protected $nonFatalExceptions = [ |
|
44 | - NotAuthenticated::class => true, |
|
45 | - // If tokenauth can throw this exception (which is basically as |
|
46 | - // NotAuthenticated. So not fatal. |
|
47 | - PasswordLoginForbidden::class => true, |
|
48 | - // basically a NotAuthenticated |
|
49 | - InvalidSyncToken::class => true, |
|
50 | - // the sync client uses this to find out whether files exist, |
|
51 | - // so it is not always an error, log it as debug |
|
52 | - NotFound::class => true, |
|
53 | - // this one mostly happens when the same file is uploaded at |
|
54 | - // exactly the same time from two clients, only one client |
|
55 | - // wins, the second one gets "Precondition failed" |
|
56 | - PreconditionFailed::class => true, |
|
57 | - // forbidden can be expected when trying to upload to |
|
58 | - // read-only folders for example |
|
59 | - Forbidden::class => true, |
|
60 | - // Happens when an external storage or federated share is temporarily |
|
61 | - // not available |
|
62 | - StorageNotAvailableException::class => true, |
|
63 | - // happens if some a client uses the wrong method for a given URL |
|
64 | - // the error message itself is visible on the client side anyways |
|
65 | - NotImplemented::class => true, |
|
66 | - // happens when the parent directory is not present (for example when a |
|
67 | - // move is done to a non-existent directory) |
|
68 | - Conflict::class => true, |
|
69 | - // happens when a certain method is not allowed to be called |
|
70 | - // for example creating a folder that already exists |
|
71 | - MethodNotAllowed::class => true, |
|
72 | - ]; |
|
43 | + protected $nonFatalExceptions = [ |
|
44 | + NotAuthenticated::class => true, |
|
45 | + // If tokenauth can throw this exception (which is basically as |
|
46 | + // NotAuthenticated. So not fatal. |
|
47 | + PasswordLoginForbidden::class => true, |
|
48 | + // basically a NotAuthenticated |
|
49 | + InvalidSyncToken::class => true, |
|
50 | + // the sync client uses this to find out whether files exist, |
|
51 | + // so it is not always an error, log it as debug |
|
52 | + NotFound::class => true, |
|
53 | + // this one mostly happens when the same file is uploaded at |
|
54 | + // exactly the same time from two clients, only one client |
|
55 | + // wins, the second one gets "Precondition failed" |
|
56 | + PreconditionFailed::class => true, |
|
57 | + // forbidden can be expected when trying to upload to |
|
58 | + // read-only folders for example |
|
59 | + Forbidden::class => true, |
|
60 | + // Happens when an external storage or federated share is temporarily |
|
61 | + // not available |
|
62 | + StorageNotAvailableException::class => true, |
|
63 | + // happens if some a client uses the wrong method for a given URL |
|
64 | + // the error message itself is visible on the client side anyways |
|
65 | + NotImplemented::class => true, |
|
66 | + // happens when the parent directory is not present (for example when a |
|
67 | + // move is done to a non-existent directory) |
|
68 | + Conflict::class => true, |
|
69 | + // happens when a certain method is not allowed to be called |
|
70 | + // for example creating a folder that already exists |
|
71 | + MethodNotAllowed::class => true, |
|
72 | + ]; |
|
73 | 73 | |
74 | - /** @var string */ |
|
75 | - private $appName; |
|
74 | + /** @var string */ |
|
75 | + private $appName; |
|
76 | 76 | |
77 | - /** @var ILogger */ |
|
78 | - private $logger; |
|
77 | + /** @var ILogger */ |
|
78 | + private $logger; |
|
79 | 79 | |
80 | - /** |
|
81 | - * @param string $loggerAppName app name to use when logging |
|
82 | - * @param ILogger $logger |
|
83 | - */ |
|
84 | - public function __construct($loggerAppName, $logger) { |
|
85 | - $this->appName = $loggerAppName; |
|
86 | - $this->logger = $logger; |
|
87 | - } |
|
80 | + /** |
|
81 | + * @param string $loggerAppName app name to use when logging |
|
82 | + * @param ILogger $logger |
|
83 | + */ |
|
84 | + public function __construct($loggerAppName, $logger) { |
|
85 | + $this->appName = $loggerAppName; |
|
86 | + $this->logger = $logger; |
|
87 | + } |
|
88 | 88 | |
89 | - /** |
|
90 | - * This initializes the plugin. |
|
91 | - * |
|
92 | - * This function is called by \Sabre\DAV\Server, after |
|
93 | - * addPlugin is called. |
|
94 | - * |
|
95 | - * This method should set up the required event subscriptions. |
|
96 | - * |
|
97 | - * @param \Sabre\DAV\Server $server |
|
98 | - * @return void |
|
99 | - */ |
|
100 | - public function initialize(\Sabre\DAV\Server $server) { |
|
89 | + /** |
|
90 | + * This initializes the plugin. |
|
91 | + * |
|
92 | + * This function is called by \Sabre\DAV\Server, after |
|
93 | + * addPlugin is called. |
|
94 | + * |
|
95 | + * This method should set up the required event subscriptions. |
|
96 | + * |
|
97 | + * @param \Sabre\DAV\Server $server |
|
98 | + * @return void |
|
99 | + */ |
|
100 | + public function initialize(\Sabre\DAV\Server $server) { |
|
101 | 101 | |
102 | - $server->on('exception', array($this, 'logException'), 10); |
|
103 | - } |
|
102 | + $server->on('exception', array($this, 'logException'), 10); |
|
103 | + } |
|
104 | 104 | |
105 | - /** |
|
106 | - * Log exception |
|
107 | - * |
|
108 | - */ |
|
109 | - public function logException(\Exception $ex) { |
|
110 | - $exceptionClass = get_class($ex); |
|
111 | - $level = ILogger::FATAL; |
|
112 | - if (isset($this->nonFatalExceptions[$exceptionClass]) || |
|
113 | - ( |
|
114 | - $exceptionClass === ServiceUnavailable::class && |
|
115 | - $ex->getMessage() === 'System in maintenance mode.' |
|
116 | - ) |
|
117 | - ) { |
|
118 | - $level = ILogger::DEBUG; |
|
119 | - } |
|
105 | + /** |
|
106 | + * Log exception |
|
107 | + * |
|
108 | + */ |
|
109 | + public function logException(\Exception $ex) { |
|
110 | + $exceptionClass = get_class($ex); |
|
111 | + $level = ILogger::FATAL; |
|
112 | + if (isset($this->nonFatalExceptions[$exceptionClass]) || |
|
113 | + ( |
|
114 | + $exceptionClass === ServiceUnavailable::class && |
|
115 | + $ex->getMessage() === 'System in maintenance mode.' |
|
116 | + ) |
|
117 | + ) { |
|
118 | + $level = ILogger::DEBUG; |
|
119 | + } |
|
120 | 120 | |
121 | - $this->logger->logException($ex, [ |
|
122 | - 'app' => $this->appName, |
|
123 | - 'level' => $level, |
|
124 | - ]); |
|
125 | - } |
|
121 | + $this->logger->logException($ex, [ |
|
122 | + 'app' => $this->appName, |
|
123 | + 'level' => $level, |
|
124 | + ]); |
|
125 | + } |
|
126 | 126 | } |
@@ -53,989 +53,989 @@ |
||
53 | 53 | */ |
54 | 54 | class FederatedShareProvider implements IShareProvider { |
55 | 55 | |
56 | - const SHARE_TYPE_REMOTE = 6; |
|
57 | - |
|
58 | - /** @var IDBConnection */ |
|
59 | - private $dbConnection; |
|
60 | - |
|
61 | - /** @var AddressHandler */ |
|
62 | - private $addressHandler; |
|
63 | - |
|
64 | - /** @var Notifications */ |
|
65 | - private $notifications; |
|
66 | - |
|
67 | - /** @var TokenHandler */ |
|
68 | - private $tokenHandler; |
|
69 | - |
|
70 | - /** @var IL10N */ |
|
71 | - private $l; |
|
72 | - |
|
73 | - /** @var ILogger */ |
|
74 | - private $logger; |
|
75 | - |
|
76 | - /** @var IRootFolder */ |
|
77 | - private $rootFolder; |
|
78 | - |
|
79 | - /** @var IConfig */ |
|
80 | - private $config; |
|
81 | - |
|
82 | - /** @var string */ |
|
83 | - private $externalShareTable = 'share_external'; |
|
84 | - |
|
85 | - /** @var IUserManager */ |
|
86 | - private $userManager; |
|
87 | - |
|
88 | - /** @var ICloudIdManager */ |
|
89 | - private $cloudIdManager; |
|
90 | - |
|
91 | - /** @var \OCP\GlobalScale\IConfig */ |
|
92 | - private $gsConfig; |
|
93 | - |
|
94 | - /** |
|
95 | - * DefaultShareProvider constructor. |
|
96 | - * |
|
97 | - * @param IDBConnection $connection |
|
98 | - * @param AddressHandler $addressHandler |
|
99 | - * @param Notifications $notifications |
|
100 | - * @param TokenHandler $tokenHandler |
|
101 | - * @param IL10N $l10n |
|
102 | - * @param ILogger $logger |
|
103 | - * @param IRootFolder $rootFolder |
|
104 | - * @param IConfig $config |
|
105 | - * @param IUserManager $userManager |
|
106 | - * @param ICloudIdManager $cloudIdManager |
|
107 | - * @param \OCP\GlobalScale\IConfig $globalScaleConfig |
|
108 | - */ |
|
109 | - public function __construct( |
|
110 | - IDBConnection $connection, |
|
111 | - AddressHandler $addressHandler, |
|
112 | - Notifications $notifications, |
|
113 | - TokenHandler $tokenHandler, |
|
114 | - IL10N $l10n, |
|
115 | - ILogger $logger, |
|
116 | - IRootFolder $rootFolder, |
|
117 | - IConfig $config, |
|
118 | - IUserManager $userManager, |
|
119 | - ICloudIdManager $cloudIdManager, |
|
120 | - \OCP\GlobalScale\IConfig $globalScaleConfig |
|
121 | - ) { |
|
122 | - $this->dbConnection = $connection; |
|
123 | - $this->addressHandler = $addressHandler; |
|
124 | - $this->notifications = $notifications; |
|
125 | - $this->tokenHandler = $tokenHandler; |
|
126 | - $this->l = $l10n; |
|
127 | - $this->logger = $logger; |
|
128 | - $this->rootFolder = $rootFolder; |
|
129 | - $this->config = $config; |
|
130 | - $this->userManager = $userManager; |
|
131 | - $this->cloudIdManager = $cloudIdManager; |
|
132 | - $this->gsConfig = $globalScaleConfig; |
|
133 | - } |
|
134 | - |
|
135 | - /** |
|
136 | - * Return the identifier of this provider. |
|
137 | - * |
|
138 | - * @return string Containing only [a-zA-Z0-9] |
|
139 | - */ |
|
140 | - public function identifier() { |
|
141 | - return 'ocFederatedSharing'; |
|
142 | - } |
|
143 | - |
|
144 | - /** |
|
145 | - * Share a path |
|
146 | - * |
|
147 | - * @param IShare $share |
|
148 | - * @return IShare The share object |
|
149 | - * @throws ShareNotFound |
|
150 | - * @throws \Exception |
|
151 | - */ |
|
152 | - public function create(IShare $share) { |
|
153 | - |
|
154 | - $shareWith = $share->getSharedWith(); |
|
155 | - $itemSource = $share->getNodeId(); |
|
156 | - $itemType = $share->getNodeType(); |
|
157 | - $permissions = $share->getPermissions(); |
|
158 | - $sharedBy = $share->getSharedBy(); |
|
159 | - |
|
160 | - /* |
|
56 | + const SHARE_TYPE_REMOTE = 6; |
|
57 | + |
|
58 | + /** @var IDBConnection */ |
|
59 | + private $dbConnection; |
|
60 | + |
|
61 | + /** @var AddressHandler */ |
|
62 | + private $addressHandler; |
|
63 | + |
|
64 | + /** @var Notifications */ |
|
65 | + private $notifications; |
|
66 | + |
|
67 | + /** @var TokenHandler */ |
|
68 | + private $tokenHandler; |
|
69 | + |
|
70 | + /** @var IL10N */ |
|
71 | + private $l; |
|
72 | + |
|
73 | + /** @var ILogger */ |
|
74 | + private $logger; |
|
75 | + |
|
76 | + /** @var IRootFolder */ |
|
77 | + private $rootFolder; |
|
78 | + |
|
79 | + /** @var IConfig */ |
|
80 | + private $config; |
|
81 | + |
|
82 | + /** @var string */ |
|
83 | + private $externalShareTable = 'share_external'; |
|
84 | + |
|
85 | + /** @var IUserManager */ |
|
86 | + private $userManager; |
|
87 | + |
|
88 | + /** @var ICloudIdManager */ |
|
89 | + private $cloudIdManager; |
|
90 | + |
|
91 | + /** @var \OCP\GlobalScale\IConfig */ |
|
92 | + private $gsConfig; |
|
93 | + |
|
94 | + /** |
|
95 | + * DefaultShareProvider constructor. |
|
96 | + * |
|
97 | + * @param IDBConnection $connection |
|
98 | + * @param AddressHandler $addressHandler |
|
99 | + * @param Notifications $notifications |
|
100 | + * @param TokenHandler $tokenHandler |
|
101 | + * @param IL10N $l10n |
|
102 | + * @param ILogger $logger |
|
103 | + * @param IRootFolder $rootFolder |
|
104 | + * @param IConfig $config |
|
105 | + * @param IUserManager $userManager |
|
106 | + * @param ICloudIdManager $cloudIdManager |
|
107 | + * @param \OCP\GlobalScale\IConfig $globalScaleConfig |
|
108 | + */ |
|
109 | + public function __construct( |
|
110 | + IDBConnection $connection, |
|
111 | + AddressHandler $addressHandler, |
|
112 | + Notifications $notifications, |
|
113 | + TokenHandler $tokenHandler, |
|
114 | + IL10N $l10n, |
|
115 | + ILogger $logger, |
|
116 | + IRootFolder $rootFolder, |
|
117 | + IConfig $config, |
|
118 | + IUserManager $userManager, |
|
119 | + ICloudIdManager $cloudIdManager, |
|
120 | + \OCP\GlobalScale\IConfig $globalScaleConfig |
|
121 | + ) { |
|
122 | + $this->dbConnection = $connection; |
|
123 | + $this->addressHandler = $addressHandler; |
|
124 | + $this->notifications = $notifications; |
|
125 | + $this->tokenHandler = $tokenHandler; |
|
126 | + $this->l = $l10n; |
|
127 | + $this->logger = $logger; |
|
128 | + $this->rootFolder = $rootFolder; |
|
129 | + $this->config = $config; |
|
130 | + $this->userManager = $userManager; |
|
131 | + $this->cloudIdManager = $cloudIdManager; |
|
132 | + $this->gsConfig = $globalScaleConfig; |
|
133 | + } |
|
134 | + |
|
135 | + /** |
|
136 | + * Return the identifier of this provider. |
|
137 | + * |
|
138 | + * @return string Containing only [a-zA-Z0-9] |
|
139 | + */ |
|
140 | + public function identifier() { |
|
141 | + return 'ocFederatedSharing'; |
|
142 | + } |
|
143 | + |
|
144 | + /** |
|
145 | + * Share a path |
|
146 | + * |
|
147 | + * @param IShare $share |
|
148 | + * @return IShare The share object |
|
149 | + * @throws ShareNotFound |
|
150 | + * @throws \Exception |
|
151 | + */ |
|
152 | + public function create(IShare $share) { |
|
153 | + |
|
154 | + $shareWith = $share->getSharedWith(); |
|
155 | + $itemSource = $share->getNodeId(); |
|
156 | + $itemType = $share->getNodeType(); |
|
157 | + $permissions = $share->getPermissions(); |
|
158 | + $sharedBy = $share->getSharedBy(); |
|
159 | + |
|
160 | + /* |
|
161 | 161 | * Check if file is not already shared with the remote user |
162 | 162 | */ |
163 | - $alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0); |
|
164 | - if (!empty($alreadyShared)) { |
|
165 | - $message = 'Sharing %s failed, because this item is already shared with %s'; |
|
166 | - $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
167 | - $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
168 | - throw new \Exception($message_t); |
|
169 | - } |
|
170 | - |
|
171 | - |
|
172 | - // don't allow federated shares if source and target server are the same |
|
173 | - $cloudId = $this->cloudIdManager->resolveCloudId($shareWith); |
|
174 | - $currentServer = $this->addressHandler->generateRemoteURL(); |
|
175 | - $currentUser = $sharedBy; |
|
176 | - if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) { |
|
177 | - $message = 'Not allowed to create a federated share with the same user.'; |
|
178 | - $message_t = $this->l->t('Not allowed to create a federated share with the same user'); |
|
179 | - $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
180 | - throw new \Exception($message_t); |
|
181 | - } |
|
182 | - |
|
183 | - |
|
184 | - $share->setSharedWith($cloudId->getId()); |
|
185 | - |
|
186 | - try { |
|
187 | - $remoteShare = $this->getShareFromExternalShareTable($share); |
|
188 | - } catch (ShareNotFound $e) { |
|
189 | - $remoteShare = null; |
|
190 | - } |
|
191 | - |
|
192 | - if ($remoteShare) { |
|
193 | - try { |
|
194 | - $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); |
|
195 | - $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time()); |
|
196 | - $share->setId($shareId); |
|
197 | - list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId); |
|
198 | - // remote share was create successfully if we get a valid token as return |
|
199 | - $send = is_string($token) && $token !== ''; |
|
200 | - } catch (\Exception $e) { |
|
201 | - // fall back to old re-share behavior if the remote server |
|
202 | - // doesn't support flat re-shares (was introduced with Nextcloud 9.1) |
|
203 | - $this->removeShareFromTable($share); |
|
204 | - $shareId = $this->createFederatedShare($share); |
|
205 | - } |
|
206 | - if ($send) { |
|
207 | - $this->updateSuccessfulReshare($shareId, $token); |
|
208 | - $this->storeRemoteId($shareId, $remoteId); |
|
209 | - } else { |
|
210 | - $this->removeShareFromTable($share); |
|
211 | - $message_t = $this->l->t('File is already shared with %s', [$shareWith]); |
|
212 | - throw new \Exception($message_t); |
|
213 | - } |
|
214 | - |
|
215 | - } else { |
|
216 | - $shareId = $this->createFederatedShare($share); |
|
217 | - } |
|
218 | - |
|
219 | - $data = $this->getRawShare($shareId); |
|
220 | - return $this->createShareObject($data); |
|
221 | - } |
|
222 | - |
|
223 | - /** |
|
224 | - * create federated share and inform the recipient |
|
225 | - * |
|
226 | - * @param IShare $share |
|
227 | - * @return int |
|
228 | - * @throws ShareNotFound |
|
229 | - * @throws \Exception |
|
230 | - */ |
|
231 | - protected function createFederatedShare(IShare $share) { |
|
232 | - $token = $this->tokenHandler->generateToken(); |
|
233 | - $shareId = $this->addShareToDB( |
|
234 | - $share->getNodeId(), |
|
235 | - $share->getNodeType(), |
|
236 | - $share->getSharedWith(), |
|
237 | - $share->getSharedBy(), |
|
238 | - $share->getShareOwner(), |
|
239 | - $share->getPermissions(), |
|
240 | - $token |
|
241 | - ); |
|
242 | - |
|
243 | - $failure = false; |
|
244 | - |
|
245 | - try { |
|
246 | - $sharedByFederatedId = $share->getSharedBy(); |
|
247 | - if ($this->userManager->userExists($sharedByFederatedId)) { |
|
248 | - $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL()); |
|
249 | - $sharedByFederatedId = $cloudId->getId(); |
|
250 | - } |
|
251 | - $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL()); |
|
252 | - $send = $this->notifications->sendRemoteShare( |
|
253 | - $token, |
|
254 | - $share->getSharedWith(), |
|
255 | - $share->getNode()->getName(), |
|
256 | - $shareId, |
|
257 | - $share->getShareOwner(), |
|
258 | - $ownerCloudId->getId(), |
|
259 | - $share->getSharedBy(), |
|
260 | - $sharedByFederatedId |
|
261 | - ); |
|
262 | - |
|
263 | - if ($send === false) { |
|
264 | - $failure = true; |
|
265 | - } |
|
266 | - } catch (\Exception $e) { |
|
267 | - $this->logger->logException($e, [ |
|
268 | - 'message' => 'Failed to notify remote server of federated share, removing share.', |
|
269 | - 'level' => ILogger::ERROR, |
|
270 | - 'app' => 'federatedfilesharing', |
|
271 | - ]); |
|
272 | - $failure = true; |
|
273 | - } |
|
274 | - |
|
275 | - if($failure) { |
|
276 | - $this->removeShareFromTableById($shareId); |
|
277 | - $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.', |
|
278 | - [$share->getNode()->getName(), $share->getSharedWith()]); |
|
279 | - throw new \Exception($message_t); |
|
280 | - } |
|
281 | - |
|
282 | - return $shareId; |
|
283 | - |
|
284 | - } |
|
285 | - |
|
286 | - /** |
|
287 | - * @param string $shareWith |
|
288 | - * @param IShare $share |
|
289 | - * @param string $shareId internal share Id |
|
290 | - * @return array |
|
291 | - * @throws \Exception |
|
292 | - */ |
|
293 | - protected function askOwnerToReShare($shareWith, IShare $share, $shareId) { |
|
294 | - |
|
295 | - $remoteShare = $this->getShareFromExternalShareTable($share); |
|
296 | - $token = $remoteShare['share_token']; |
|
297 | - $remoteId = $remoteShare['remote_id']; |
|
298 | - $remote = $remoteShare['remote']; |
|
299 | - |
|
300 | - list($token, $remoteId) = $this->notifications->requestReShare( |
|
301 | - $token, |
|
302 | - $remoteId, |
|
303 | - $shareId, |
|
304 | - $remote, |
|
305 | - $shareWith, |
|
306 | - $share->getPermissions() |
|
307 | - ); |
|
308 | - |
|
309 | - return [$token, $remoteId]; |
|
310 | - } |
|
311 | - |
|
312 | - /** |
|
313 | - * get federated share from the share_external table but exclude mounted link shares |
|
314 | - * |
|
315 | - * @param IShare $share |
|
316 | - * @return array |
|
317 | - * @throws ShareNotFound |
|
318 | - */ |
|
319 | - protected function getShareFromExternalShareTable(IShare $share) { |
|
320 | - $query = $this->dbConnection->getQueryBuilder(); |
|
321 | - $query->select('*')->from($this->externalShareTable) |
|
322 | - ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) |
|
323 | - ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); |
|
324 | - $result = $query->execute()->fetchAll(); |
|
325 | - |
|
326 | - if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) { |
|
327 | - return $result[0]; |
|
328 | - } |
|
329 | - |
|
330 | - throw new ShareNotFound('share not found in share_external table'); |
|
331 | - } |
|
332 | - |
|
333 | - /** |
|
334 | - * add share to the database and return the ID |
|
335 | - * |
|
336 | - * @param int $itemSource |
|
337 | - * @param string $itemType |
|
338 | - * @param string $shareWith |
|
339 | - * @param string $sharedBy |
|
340 | - * @param string $uidOwner |
|
341 | - * @param int $permissions |
|
342 | - * @param string $token |
|
343 | - * @return int |
|
344 | - */ |
|
345 | - private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) { |
|
346 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
347 | - $qb->insert('share') |
|
348 | - ->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)) |
|
349 | - ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
350 | - ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
351 | - ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
352 | - ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
353 | - ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
354 | - ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
355 | - ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
356 | - ->setValue('token', $qb->createNamedParameter($token)) |
|
357 | - ->setValue('stime', $qb->createNamedParameter(time())); |
|
358 | - |
|
359 | - /* |
|
163 | + $alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0); |
|
164 | + if (!empty($alreadyShared)) { |
|
165 | + $message = 'Sharing %s failed, because this item is already shared with %s'; |
|
166 | + $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
167 | + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
168 | + throw new \Exception($message_t); |
|
169 | + } |
|
170 | + |
|
171 | + |
|
172 | + // don't allow federated shares if source and target server are the same |
|
173 | + $cloudId = $this->cloudIdManager->resolveCloudId($shareWith); |
|
174 | + $currentServer = $this->addressHandler->generateRemoteURL(); |
|
175 | + $currentUser = $sharedBy; |
|
176 | + if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) { |
|
177 | + $message = 'Not allowed to create a federated share with the same user.'; |
|
178 | + $message_t = $this->l->t('Not allowed to create a federated share with the same user'); |
|
179 | + $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
180 | + throw new \Exception($message_t); |
|
181 | + } |
|
182 | + |
|
183 | + |
|
184 | + $share->setSharedWith($cloudId->getId()); |
|
185 | + |
|
186 | + try { |
|
187 | + $remoteShare = $this->getShareFromExternalShareTable($share); |
|
188 | + } catch (ShareNotFound $e) { |
|
189 | + $remoteShare = null; |
|
190 | + } |
|
191 | + |
|
192 | + if ($remoteShare) { |
|
193 | + try { |
|
194 | + $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); |
|
195 | + $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time()); |
|
196 | + $share->setId($shareId); |
|
197 | + list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId); |
|
198 | + // remote share was create successfully if we get a valid token as return |
|
199 | + $send = is_string($token) && $token !== ''; |
|
200 | + } catch (\Exception $e) { |
|
201 | + // fall back to old re-share behavior if the remote server |
|
202 | + // doesn't support flat re-shares (was introduced with Nextcloud 9.1) |
|
203 | + $this->removeShareFromTable($share); |
|
204 | + $shareId = $this->createFederatedShare($share); |
|
205 | + } |
|
206 | + if ($send) { |
|
207 | + $this->updateSuccessfulReshare($shareId, $token); |
|
208 | + $this->storeRemoteId($shareId, $remoteId); |
|
209 | + } else { |
|
210 | + $this->removeShareFromTable($share); |
|
211 | + $message_t = $this->l->t('File is already shared with %s', [$shareWith]); |
|
212 | + throw new \Exception($message_t); |
|
213 | + } |
|
214 | + |
|
215 | + } else { |
|
216 | + $shareId = $this->createFederatedShare($share); |
|
217 | + } |
|
218 | + |
|
219 | + $data = $this->getRawShare($shareId); |
|
220 | + return $this->createShareObject($data); |
|
221 | + } |
|
222 | + |
|
223 | + /** |
|
224 | + * create federated share and inform the recipient |
|
225 | + * |
|
226 | + * @param IShare $share |
|
227 | + * @return int |
|
228 | + * @throws ShareNotFound |
|
229 | + * @throws \Exception |
|
230 | + */ |
|
231 | + protected function createFederatedShare(IShare $share) { |
|
232 | + $token = $this->tokenHandler->generateToken(); |
|
233 | + $shareId = $this->addShareToDB( |
|
234 | + $share->getNodeId(), |
|
235 | + $share->getNodeType(), |
|
236 | + $share->getSharedWith(), |
|
237 | + $share->getSharedBy(), |
|
238 | + $share->getShareOwner(), |
|
239 | + $share->getPermissions(), |
|
240 | + $token |
|
241 | + ); |
|
242 | + |
|
243 | + $failure = false; |
|
244 | + |
|
245 | + try { |
|
246 | + $sharedByFederatedId = $share->getSharedBy(); |
|
247 | + if ($this->userManager->userExists($sharedByFederatedId)) { |
|
248 | + $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL()); |
|
249 | + $sharedByFederatedId = $cloudId->getId(); |
|
250 | + } |
|
251 | + $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL()); |
|
252 | + $send = $this->notifications->sendRemoteShare( |
|
253 | + $token, |
|
254 | + $share->getSharedWith(), |
|
255 | + $share->getNode()->getName(), |
|
256 | + $shareId, |
|
257 | + $share->getShareOwner(), |
|
258 | + $ownerCloudId->getId(), |
|
259 | + $share->getSharedBy(), |
|
260 | + $sharedByFederatedId |
|
261 | + ); |
|
262 | + |
|
263 | + if ($send === false) { |
|
264 | + $failure = true; |
|
265 | + } |
|
266 | + } catch (\Exception $e) { |
|
267 | + $this->logger->logException($e, [ |
|
268 | + 'message' => 'Failed to notify remote server of federated share, removing share.', |
|
269 | + 'level' => ILogger::ERROR, |
|
270 | + 'app' => 'federatedfilesharing', |
|
271 | + ]); |
|
272 | + $failure = true; |
|
273 | + } |
|
274 | + |
|
275 | + if($failure) { |
|
276 | + $this->removeShareFromTableById($shareId); |
|
277 | + $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.', |
|
278 | + [$share->getNode()->getName(), $share->getSharedWith()]); |
|
279 | + throw new \Exception($message_t); |
|
280 | + } |
|
281 | + |
|
282 | + return $shareId; |
|
283 | + |
|
284 | + } |
|
285 | + |
|
286 | + /** |
|
287 | + * @param string $shareWith |
|
288 | + * @param IShare $share |
|
289 | + * @param string $shareId internal share Id |
|
290 | + * @return array |
|
291 | + * @throws \Exception |
|
292 | + */ |
|
293 | + protected function askOwnerToReShare($shareWith, IShare $share, $shareId) { |
|
294 | + |
|
295 | + $remoteShare = $this->getShareFromExternalShareTable($share); |
|
296 | + $token = $remoteShare['share_token']; |
|
297 | + $remoteId = $remoteShare['remote_id']; |
|
298 | + $remote = $remoteShare['remote']; |
|
299 | + |
|
300 | + list($token, $remoteId) = $this->notifications->requestReShare( |
|
301 | + $token, |
|
302 | + $remoteId, |
|
303 | + $shareId, |
|
304 | + $remote, |
|
305 | + $shareWith, |
|
306 | + $share->getPermissions() |
|
307 | + ); |
|
308 | + |
|
309 | + return [$token, $remoteId]; |
|
310 | + } |
|
311 | + |
|
312 | + /** |
|
313 | + * get federated share from the share_external table but exclude mounted link shares |
|
314 | + * |
|
315 | + * @param IShare $share |
|
316 | + * @return array |
|
317 | + * @throws ShareNotFound |
|
318 | + */ |
|
319 | + protected function getShareFromExternalShareTable(IShare $share) { |
|
320 | + $query = $this->dbConnection->getQueryBuilder(); |
|
321 | + $query->select('*')->from($this->externalShareTable) |
|
322 | + ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) |
|
323 | + ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); |
|
324 | + $result = $query->execute()->fetchAll(); |
|
325 | + |
|
326 | + if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) { |
|
327 | + return $result[0]; |
|
328 | + } |
|
329 | + |
|
330 | + throw new ShareNotFound('share not found in share_external table'); |
|
331 | + } |
|
332 | + |
|
333 | + /** |
|
334 | + * add share to the database and return the ID |
|
335 | + * |
|
336 | + * @param int $itemSource |
|
337 | + * @param string $itemType |
|
338 | + * @param string $shareWith |
|
339 | + * @param string $sharedBy |
|
340 | + * @param string $uidOwner |
|
341 | + * @param int $permissions |
|
342 | + * @param string $token |
|
343 | + * @return int |
|
344 | + */ |
|
345 | + private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) { |
|
346 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
347 | + $qb->insert('share') |
|
348 | + ->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)) |
|
349 | + ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
350 | + ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
351 | + ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
352 | + ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
353 | + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
354 | + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
355 | + ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
356 | + ->setValue('token', $qb->createNamedParameter($token)) |
|
357 | + ->setValue('stime', $qb->createNamedParameter(time())); |
|
358 | + |
|
359 | + /* |
|
360 | 360 | * Added to fix https://github.com/owncloud/core/issues/22215 |
361 | 361 | * Can be removed once we get rid of ajax/share.php |
362 | 362 | */ |
363 | - $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
364 | - |
|
365 | - $qb->execute(); |
|
366 | - $id = $qb->getLastInsertId(); |
|
367 | - |
|
368 | - return (int)$id; |
|
369 | - } |
|
370 | - |
|
371 | - /** |
|
372 | - * Update a share |
|
373 | - * |
|
374 | - * @param IShare $share |
|
375 | - * @return IShare The share object |
|
376 | - */ |
|
377 | - public function update(IShare $share) { |
|
378 | - /* |
|
363 | + $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
364 | + |
|
365 | + $qb->execute(); |
|
366 | + $id = $qb->getLastInsertId(); |
|
367 | + |
|
368 | + return (int)$id; |
|
369 | + } |
|
370 | + |
|
371 | + /** |
|
372 | + * Update a share |
|
373 | + * |
|
374 | + * @param IShare $share |
|
375 | + * @return IShare The share object |
|
376 | + */ |
|
377 | + public function update(IShare $share) { |
|
378 | + /* |
|
379 | 379 | * We allow updating the permissions of federated shares |
380 | 380 | */ |
381 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
382 | - $qb->update('share') |
|
383 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
384 | - ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
385 | - ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
386 | - ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
387 | - ->execute(); |
|
388 | - |
|
389 | - // send the updated permission to the owner/initiator, if they are not the same |
|
390 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
391 | - $this->sendPermissionUpdate($share); |
|
392 | - } |
|
393 | - |
|
394 | - return $share; |
|
395 | - } |
|
396 | - |
|
397 | - /** |
|
398 | - * send the updated permission to the owner/initiator, if they are not the same |
|
399 | - * |
|
400 | - * @param IShare $share |
|
401 | - * @throws ShareNotFound |
|
402 | - * @throws \OC\HintException |
|
403 | - */ |
|
404 | - protected function sendPermissionUpdate(IShare $share) { |
|
405 | - $remoteId = $this->getRemoteId($share); |
|
406 | - // if the local user is the owner we send the permission change to the initiator |
|
407 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
408 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
409 | - } else { // ... if not we send the permission change to the owner |
|
410 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
411 | - } |
|
412 | - $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions()); |
|
413 | - } |
|
414 | - |
|
415 | - |
|
416 | - /** |
|
417 | - * update successful reShare with the correct token |
|
418 | - * |
|
419 | - * @param int $shareId |
|
420 | - * @param string $token |
|
421 | - */ |
|
422 | - protected function updateSuccessfulReShare($shareId, $token) { |
|
423 | - $query = $this->dbConnection->getQueryBuilder(); |
|
424 | - $query->update('share') |
|
425 | - ->where($query->expr()->eq('id', $query->createNamedParameter($shareId))) |
|
426 | - ->set('token', $query->createNamedParameter($token)) |
|
427 | - ->execute(); |
|
428 | - } |
|
429 | - |
|
430 | - /** |
|
431 | - * store remote ID in federated reShare table |
|
432 | - * |
|
433 | - * @param $shareId |
|
434 | - * @param $remoteId |
|
435 | - */ |
|
436 | - public function storeRemoteId($shareId, $remoteId) { |
|
437 | - $query = $this->dbConnection->getQueryBuilder(); |
|
438 | - $query->insert('federated_reshares') |
|
439 | - ->values( |
|
440 | - [ |
|
441 | - 'share_id' => $query->createNamedParameter($shareId), |
|
442 | - 'remote_id' => $query->createNamedParameter($remoteId), |
|
443 | - ] |
|
444 | - ); |
|
445 | - $query->execute(); |
|
446 | - } |
|
447 | - |
|
448 | - /** |
|
449 | - * get share ID on remote server for federated re-shares |
|
450 | - * |
|
451 | - * @param IShare $share |
|
452 | - * @return int |
|
453 | - * @throws ShareNotFound |
|
454 | - */ |
|
455 | - public function getRemoteId(IShare $share) { |
|
456 | - $query = $this->dbConnection->getQueryBuilder(); |
|
457 | - $query->select('remote_id')->from('federated_reshares') |
|
458 | - ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); |
|
459 | - $data = $query->execute()->fetch(); |
|
460 | - |
|
461 | - if (!is_array($data) || !isset($data['remote_id'])) { |
|
462 | - throw new ShareNotFound(); |
|
463 | - } |
|
464 | - |
|
465 | - return (int)$data['remote_id']; |
|
466 | - } |
|
467 | - |
|
468 | - /** |
|
469 | - * @inheritdoc |
|
470 | - */ |
|
471 | - public function move(IShare $share, $recipient) { |
|
472 | - /* |
|
381 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
382 | + $qb->update('share') |
|
383 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
384 | + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
385 | + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
386 | + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
387 | + ->execute(); |
|
388 | + |
|
389 | + // send the updated permission to the owner/initiator, if they are not the same |
|
390 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
391 | + $this->sendPermissionUpdate($share); |
|
392 | + } |
|
393 | + |
|
394 | + return $share; |
|
395 | + } |
|
396 | + |
|
397 | + /** |
|
398 | + * send the updated permission to the owner/initiator, if they are not the same |
|
399 | + * |
|
400 | + * @param IShare $share |
|
401 | + * @throws ShareNotFound |
|
402 | + * @throws \OC\HintException |
|
403 | + */ |
|
404 | + protected function sendPermissionUpdate(IShare $share) { |
|
405 | + $remoteId = $this->getRemoteId($share); |
|
406 | + // if the local user is the owner we send the permission change to the initiator |
|
407 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
408 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
409 | + } else { // ... if not we send the permission change to the owner |
|
410 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
411 | + } |
|
412 | + $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions()); |
|
413 | + } |
|
414 | + |
|
415 | + |
|
416 | + /** |
|
417 | + * update successful reShare with the correct token |
|
418 | + * |
|
419 | + * @param int $shareId |
|
420 | + * @param string $token |
|
421 | + */ |
|
422 | + protected function updateSuccessfulReShare($shareId, $token) { |
|
423 | + $query = $this->dbConnection->getQueryBuilder(); |
|
424 | + $query->update('share') |
|
425 | + ->where($query->expr()->eq('id', $query->createNamedParameter($shareId))) |
|
426 | + ->set('token', $query->createNamedParameter($token)) |
|
427 | + ->execute(); |
|
428 | + } |
|
429 | + |
|
430 | + /** |
|
431 | + * store remote ID in federated reShare table |
|
432 | + * |
|
433 | + * @param $shareId |
|
434 | + * @param $remoteId |
|
435 | + */ |
|
436 | + public function storeRemoteId($shareId, $remoteId) { |
|
437 | + $query = $this->dbConnection->getQueryBuilder(); |
|
438 | + $query->insert('federated_reshares') |
|
439 | + ->values( |
|
440 | + [ |
|
441 | + 'share_id' => $query->createNamedParameter($shareId), |
|
442 | + 'remote_id' => $query->createNamedParameter($remoteId), |
|
443 | + ] |
|
444 | + ); |
|
445 | + $query->execute(); |
|
446 | + } |
|
447 | + |
|
448 | + /** |
|
449 | + * get share ID on remote server for federated re-shares |
|
450 | + * |
|
451 | + * @param IShare $share |
|
452 | + * @return int |
|
453 | + * @throws ShareNotFound |
|
454 | + */ |
|
455 | + public function getRemoteId(IShare $share) { |
|
456 | + $query = $this->dbConnection->getQueryBuilder(); |
|
457 | + $query->select('remote_id')->from('federated_reshares') |
|
458 | + ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); |
|
459 | + $data = $query->execute()->fetch(); |
|
460 | + |
|
461 | + if (!is_array($data) || !isset($data['remote_id'])) { |
|
462 | + throw new ShareNotFound(); |
|
463 | + } |
|
464 | + |
|
465 | + return (int)$data['remote_id']; |
|
466 | + } |
|
467 | + |
|
468 | + /** |
|
469 | + * @inheritdoc |
|
470 | + */ |
|
471 | + public function move(IShare $share, $recipient) { |
|
472 | + /* |
|
473 | 473 | * This function does nothing yet as it is just for outgoing |
474 | 474 | * federated shares. |
475 | 475 | */ |
476 | - return $share; |
|
477 | - } |
|
478 | - |
|
479 | - /** |
|
480 | - * Get all children of this share |
|
481 | - * |
|
482 | - * @param IShare $parent |
|
483 | - * @return IShare[] |
|
484 | - */ |
|
485 | - public function getChildren(IShare $parent) { |
|
486 | - $children = []; |
|
487 | - |
|
488 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
489 | - $qb->select('*') |
|
490 | - ->from('share') |
|
491 | - ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
492 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
493 | - ->orderBy('id'); |
|
494 | - |
|
495 | - $cursor = $qb->execute(); |
|
496 | - while($data = $cursor->fetch()) { |
|
497 | - $children[] = $this->createShareObject($data); |
|
498 | - } |
|
499 | - $cursor->closeCursor(); |
|
500 | - |
|
501 | - return $children; |
|
502 | - } |
|
503 | - |
|
504 | - /** |
|
505 | - * Delete a share (owner unShares the file) |
|
506 | - * |
|
507 | - * @param IShare $share |
|
508 | - */ |
|
509 | - public function delete(IShare $share) { |
|
510 | - |
|
511 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith()); |
|
512 | - |
|
513 | - $isOwner = false; |
|
514 | - |
|
515 | - $this->removeShareFromTable($share); |
|
516 | - |
|
517 | - // if the local user is the owner we can send the unShare request directly... |
|
518 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
519 | - $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); |
|
520 | - $this->revokeShare($share, true); |
|
521 | - $isOwner = true; |
|
522 | - } else { // ... if not we need to correct ID for the unShare request |
|
523 | - $remoteId = $this->getRemoteId($share); |
|
524 | - $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken()); |
|
525 | - $this->revokeShare($share, false); |
|
526 | - } |
|
527 | - |
|
528 | - // send revoke notification to the other user, if initiator and owner are not the same user |
|
529 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
530 | - $remoteId = $this->getRemoteId($share); |
|
531 | - if ($isOwner) { |
|
532 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
533 | - } else { |
|
534 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
535 | - } |
|
536 | - $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
537 | - } |
|
538 | - } |
|
539 | - |
|
540 | - /** |
|
541 | - * in case of a re-share we need to send the other use (initiator or owner) |
|
542 | - * a message that the file was unshared |
|
543 | - * |
|
544 | - * @param IShare $share |
|
545 | - * @param bool $isOwner the user can either be the owner or the user who re-sahred it |
|
546 | - * @throws ShareNotFound |
|
547 | - * @throws \OC\HintException |
|
548 | - */ |
|
549 | - protected function revokeShare($share, $isOwner) { |
|
550 | - // also send a unShare request to the initiator, if this is a different user than the owner |
|
551 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
552 | - if ($isOwner) { |
|
553 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
554 | - } else { |
|
555 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
556 | - } |
|
557 | - $remoteId = $this->getRemoteId($share); |
|
558 | - $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
559 | - } |
|
560 | - } |
|
561 | - |
|
562 | - /** |
|
563 | - * remove share from table |
|
564 | - * |
|
565 | - * @param IShare $share |
|
566 | - */ |
|
567 | - public function removeShareFromTable(IShare $share) { |
|
568 | - $this->removeShareFromTableById($share->getId()); |
|
569 | - } |
|
570 | - |
|
571 | - /** |
|
572 | - * remove share from table |
|
573 | - * |
|
574 | - * @param string $shareId |
|
575 | - */ |
|
576 | - private function removeShareFromTableById($shareId) { |
|
577 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
578 | - $qb->delete('share') |
|
579 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
580 | - $qb->execute(); |
|
581 | - |
|
582 | - $qb->delete('federated_reshares') |
|
583 | - ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId))); |
|
584 | - $qb->execute(); |
|
585 | - } |
|
586 | - |
|
587 | - /** |
|
588 | - * @inheritdoc |
|
589 | - */ |
|
590 | - public function deleteFromSelf(IShare $share, $recipient) { |
|
591 | - // nothing to do here. Technically deleteFromSelf in the context of federated |
|
592 | - // shares is a umount of a external storage. This is handled here |
|
593 | - // apps/files_sharing/lib/external/manager.php |
|
594 | - // TODO move this code over to this app |
|
595 | - } |
|
596 | - |
|
597 | - |
|
598 | - public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
599 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
600 | - $qb->select('*') |
|
601 | - ->from('share', 's') |
|
602 | - ->andWhere($qb->expr()->orX( |
|
603 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
604 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
605 | - )) |
|
606 | - ->andWhere( |
|
607 | - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)) |
|
608 | - ); |
|
609 | - |
|
610 | - /** |
|
611 | - * Reshares for this user are shares where they are the owner. |
|
612 | - */ |
|
613 | - if ($reshares === false) { |
|
614 | - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
615 | - } else { |
|
616 | - $qb->andWhere( |
|
617 | - $qb->expr()->orX( |
|
618 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
619 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
620 | - ) |
|
621 | - ); |
|
622 | - } |
|
623 | - |
|
624 | - $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
625 | - $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
626 | - |
|
627 | - $qb->orderBy('id'); |
|
628 | - |
|
629 | - $cursor = $qb->execute(); |
|
630 | - $shares = []; |
|
631 | - while ($data = $cursor->fetch()) { |
|
632 | - $shares[$data['fileid']][] = $this->createShareObject($data); |
|
633 | - } |
|
634 | - $cursor->closeCursor(); |
|
635 | - |
|
636 | - return $shares; |
|
637 | - } |
|
638 | - |
|
639 | - /** |
|
640 | - * @inheritdoc |
|
641 | - */ |
|
642 | - public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
643 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
644 | - $qb->select('*') |
|
645 | - ->from('share'); |
|
646 | - |
|
647 | - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
648 | - |
|
649 | - /** |
|
650 | - * Reshares for this user are shares where they are the owner. |
|
651 | - */ |
|
652 | - if ($reshares === false) { |
|
653 | - //Special case for old shares created via the web UI |
|
654 | - $or1 = $qb->expr()->andX( |
|
655 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
656 | - $qb->expr()->isNull('uid_initiator') |
|
657 | - ); |
|
658 | - |
|
659 | - $qb->andWhere( |
|
660 | - $qb->expr()->orX( |
|
661 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
662 | - $or1 |
|
663 | - ) |
|
664 | - ); |
|
665 | - } else { |
|
666 | - $qb->andWhere( |
|
667 | - $qb->expr()->orX( |
|
668 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
669 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
670 | - ) |
|
671 | - ); |
|
672 | - } |
|
673 | - |
|
674 | - if ($node !== null) { |
|
675 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
676 | - } |
|
677 | - |
|
678 | - if ($limit !== -1) { |
|
679 | - $qb->setMaxResults($limit); |
|
680 | - } |
|
681 | - |
|
682 | - $qb->setFirstResult($offset); |
|
683 | - $qb->orderBy('id'); |
|
684 | - |
|
685 | - $cursor = $qb->execute(); |
|
686 | - $shares = []; |
|
687 | - while($data = $cursor->fetch()) { |
|
688 | - $shares[] = $this->createShareObject($data); |
|
689 | - } |
|
690 | - $cursor->closeCursor(); |
|
691 | - |
|
692 | - return $shares; |
|
693 | - } |
|
694 | - |
|
695 | - /** |
|
696 | - * @inheritdoc |
|
697 | - */ |
|
698 | - public function getShareById($id, $recipientId = null) { |
|
699 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
700 | - |
|
701 | - $qb->select('*') |
|
702 | - ->from('share') |
|
703 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
704 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
705 | - |
|
706 | - $cursor = $qb->execute(); |
|
707 | - $data = $cursor->fetch(); |
|
708 | - $cursor->closeCursor(); |
|
709 | - |
|
710 | - if ($data === false) { |
|
711 | - throw new ShareNotFound(); |
|
712 | - } |
|
713 | - |
|
714 | - try { |
|
715 | - $share = $this->createShareObject($data); |
|
716 | - } catch (InvalidShare $e) { |
|
717 | - throw new ShareNotFound(); |
|
718 | - } |
|
719 | - |
|
720 | - return $share; |
|
721 | - } |
|
722 | - |
|
723 | - /** |
|
724 | - * Get shares for a given path |
|
725 | - * |
|
726 | - * @param \OCP\Files\Node $path |
|
727 | - * @return IShare[] |
|
728 | - */ |
|
729 | - public function getSharesByPath(Node $path) { |
|
730 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
731 | - |
|
732 | - $cursor = $qb->select('*') |
|
733 | - ->from('share') |
|
734 | - ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
735 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
736 | - ->execute(); |
|
737 | - |
|
738 | - $shares = []; |
|
739 | - while($data = $cursor->fetch()) { |
|
740 | - $shares[] = $this->createShareObject($data); |
|
741 | - } |
|
742 | - $cursor->closeCursor(); |
|
743 | - |
|
744 | - return $shares; |
|
745 | - } |
|
746 | - |
|
747 | - /** |
|
748 | - * @inheritdoc |
|
749 | - */ |
|
750 | - public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
751 | - /** @var IShare[] $shares */ |
|
752 | - $shares = []; |
|
753 | - |
|
754 | - //Get shares directly with this user |
|
755 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
756 | - $qb->select('*') |
|
757 | - ->from('share'); |
|
758 | - |
|
759 | - // Order by id |
|
760 | - $qb->orderBy('id'); |
|
761 | - |
|
762 | - // Set limit and offset |
|
763 | - if ($limit !== -1) { |
|
764 | - $qb->setMaxResults($limit); |
|
765 | - } |
|
766 | - $qb->setFirstResult($offset); |
|
767 | - |
|
768 | - $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
769 | - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
770 | - |
|
771 | - // Filter by node if provided |
|
772 | - if ($node !== null) { |
|
773 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
774 | - } |
|
775 | - |
|
776 | - $cursor = $qb->execute(); |
|
777 | - |
|
778 | - while($data = $cursor->fetch()) { |
|
779 | - $shares[] = $this->createShareObject($data); |
|
780 | - } |
|
781 | - $cursor->closeCursor(); |
|
782 | - |
|
783 | - |
|
784 | - return $shares; |
|
785 | - } |
|
786 | - |
|
787 | - /** |
|
788 | - * Get a share by token |
|
789 | - * |
|
790 | - * @param string $token |
|
791 | - * @return IShare |
|
792 | - * @throws ShareNotFound |
|
793 | - */ |
|
794 | - public function getShareByToken($token) { |
|
795 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
796 | - |
|
797 | - $cursor = $qb->select('*') |
|
798 | - ->from('share') |
|
799 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
800 | - ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
801 | - ->execute(); |
|
802 | - |
|
803 | - $data = $cursor->fetch(); |
|
804 | - |
|
805 | - if ($data === false) { |
|
806 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
807 | - } |
|
808 | - |
|
809 | - try { |
|
810 | - $share = $this->createShareObject($data); |
|
811 | - } catch (InvalidShare $e) { |
|
812 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
813 | - } |
|
814 | - |
|
815 | - return $share; |
|
816 | - } |
|
817 | - |
|
818 | - /** |
|
819 | - * get database row of a give share |
|
820 | - * |
|
821 | - * @param $id |
|
822 | - * @return array |
|
823 | - * @throws ShareNotFound |
|
824 | - */ |
|
825 | - private function getRawShare($id) { |
|
826 | - |
|
827 | - // Now fetch the inserted share and create a complete share object |
|
828 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
829 | - $qb->select('*') |
|
830 | - ->from('share') |
|
831 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
832 | - |
|
833 | - $cursor = $qb->execute(); |
|
834 | - $data = $cursor->fetch(); |
|
835 | - $cursor->closeCursor(); |
|
836 | - |
|
837 | - if ($data === false) { |
|
838 | - throw new ShareNotFound; |
|
839 | - } |
|
840 | - |
|
841 | - return $data; |
|
842 | - } |
|
843 | - |
|
844 | - /** |
|
845 | - * Create a share object from an database row |
|
846 | - * |
|
847 | - * @param array $data |
|
848 | - * @return IShare |
|
849 | - * @throws InvalidShare |
|
850 | - * @throws ShareNotFound |
|
851 | - */ |
|
852 | - private function createShareObject($data) { |
|
853 | - |
|
854 | - $share = new Share($this->rootFolder, $this->userManager); |
|
855 | - $share->setId((int)$data['id']) |
|
856 | - ->setShareType((int)$data['share_type']) |
|
857 | - ->setPermissions((int)$data['permissions']) |
|
858 | - ->setTarget($data['file_target']) |
|
859 | - ->setMailSend((bool)$data['mail_send']) |
|
860 | - ->setToken($data['token']); |
|
861 | - |
|
862 | - $shareTime = new \DateTime(); |
|
863 | - $shareTime->setTimestamp((int)$data['stime']); |
|
864 | - $share->setShareTime($shareTime); |
|
865 | - $share->setSharedWith($data['share_with']); |
|
866 | - |
|
867 | - if ($data['uid_initiator'] !== null) { |
|
868 | - $share->setShareOwner($data['uid_owner']); |
|
869 | - $share->setSharedBy($data['uid_initiator']); |
|
870 | - } else { |
|
871 | - //OLD SHARE |
|
872 | - $share->setSharedBy($data['uid_owner']); |
|
873 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
874 | - |
|
875 | - $owner = $path->getOwner(); |
|
876 | - $share->setShareOwner($owner->getUID()); |
|
877 | - } |
|
878 | - |
|
879 | - $share->setNodeId((int)$data['file_source']); |
|
880 | - $share->setNodeType($data['item_type']); |
|
881 | - |
|
882 | - $share->setProviderId($this->identifier()); |
|
883 | - |
|
884 | - return $share; |
|
885 | - } |
|
886 | - |
|
887 | - /** |
|
888 | - * Get the node with file $id for $user |
|
889 | - * |
|
890 | - * @param string $userId |
|
891 | - * @param int $id |
|
892 | - * @return \OCP\Files\File|\OCP\Files\Folder |
|
893 | - * @throws InvalidShare |
|
894 | - */ |
|
895 | - private function getNode($userId, $id) { |
|
896 | - try { |
|
897 | - $userFolder = $this->rootFolder->getUserFolder($userId); |
|
898 | - } catch (NotFoundException $e) { |
|
899 | - throw new InvalidShare(); |
|
900 | - } |
|
901 | - |
|
902 | - $nodes = $userFolder->getById($id); |
|
903 | - |
|
904 | - if (empty($nodes)) { |
|
905 | - throw new InvalidShare(); |
|
906 | - } |
|
907 | - |
|
908 | - return $nodes[0]; |
|
909 | - } |
|
910 | - |
|
911 | - /** |
|
912 | - * A user is deleted from the system |
|
913 | - * So clean up the relevant shares. |
|
914 | - * |
|
915 | - * @param string $uid |
|
916 | - * @param int $shareType |
|
917 | - */ |
|
918 | - public function userDeleted($uid, $shareType) { |
|
919 | - //TODO: probabaly a good idea to send unshare info to remote servers |
|
920 | - |
|
921 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
922 | - |
|
923 | - $qb->delete('share') |
|
924 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) |
|
925 | - ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
926 | - ->execute(); |
|
927 | - } |
|
928 | - |
|
929 | - /** |
|
930 | - * This provider does not handle groups |
|
931 | - * |
|
932 | - * @param string $gid |
|
933 | - */ |
|
934 | - public function groupDeleted($gid) { |
|
935 | - // We don't handle groups here |
|
936 | - } |
|
937 | - |
|
938 | - /** |
|
939 | - * This provider does not handle groups |
|
940 | - * |
|
941 | - * @param string $uid |
|
942 | - * @param string $gid |
|
943 | - */ |
|
944 | - public function userDeletedFromGroup($uid, $gid) { |
|
945 | - // We don't handle groups here |
|
946 | - } |
|
947 | - |
|
948 | - /** |
|
949 | - * check if users from other Nextcloud instances are allowed to mount public links share by this instance |
|
950 | - * |
|
951 | - * @return bool |
|
952 | - */ |
|
953 | - public function isOutgoingServer2serverShareEnabled() { |
|
954 | - if ($this->gsConfig->onlyInternalFederation()) { |
|
955 | - return false; |
|
956 | - } |
|
957 | - $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); |
|
958 | - return ($result === 'yes'); |
|
959 | - } |
|
960 | - |
|
961 | - /** |
|
962 | - * check if users are allowed to mount public links from other Nextclouds |
|
963 | - * |
|
964 | - * @return bool |
|
965 | - */ |
|
966 | - public function isIncomingServer2serverShareEnabled() { |
|
967 | - if ($this->gsConfig->onlyInternalFederation()) { |
|
968 | - return false; |
|
969 | - } |
|
970 | - $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); |
|
971 | - return ($result === 'yes'); |
|
972 | - } |
|
973 | - |
|
974 | - /** |
|
975 | - * Check if querying sharees on the lookup server is enabled |
|
976 | - * |
|
977 | - * @return bool |
|
978 | - */ |
|
979 | - public function isLookupServerQueriesEnabled() { |
|
980 | - // in a global scale setup we should always query the lookup server |
|
981 | - if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
982 | - return true; |
|
983 | - } |
|
984 | - $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); |
|
985 | - return ($result === 'yes'); |
|
986 | - } |
|
987 | - |
|
988 | - |
|
989 | - /** |
|
990 | - * Check if it is allowed to publish user specific data to the lookup server |
|
991 | - * |
|
992 | - * @return bool |
|
993 | - */ |
|
994 | - public function isLookupServerUploadEnabled() { |
|
995 | - // in a global scale setup the admin is responsible to keep the lookup server up-to-date |
|
996 | - if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
997 | - return false; |
|
998 | - } |
|
999 | - $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes'); |
|
1000 | - return ($result === 'yes'); |
|
1001 | - } |
|
1002 | - |
|
1003 | - /** |
|
1004 | - * @inheritdoc |
|
1005 | - */ |
|
1006 | - public function getAccessList($nodes, $currentAccess) { |
|
1007 | - $ids = []; |
|
1008 | - foreach ($nodes as $node) { |
|
1009 | - $ids[] = $node->getId(); |
|
1010 | - } |
|
1011 | - |
|
1012 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
1013 | - $qb->select('share_with', 'token', 'file_source') |
|
1014 | - ->from('share') |
|
1015 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) |
|
1016 | - ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
1017 | - ->andWhere($qb->expr()->orX( |
|
1018 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1019 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1020 | - )); |
|
1021 | - $cursor = $qb->execute(); |
|
1022 | - |
|
1023 | - if ($currentAccess === false) { |
|
1024 | - $remote = $cursor->fetch() !== false; |
|
1025 | - $cursor->closeCursor(); |
|
1026 | - |
|
1027 | - return ['remote' => $remote]; |
|
1028 | - } |
|
1029 | - |
|
1030 | - $remote = []; |
|
1031 | - while ($row = $cursor->fetch()) { |
|
1032 | - $remote[$row['share_with']] = [ |
|
1033 | - 'node_id' => $row['file_source'], |
|
1034 | - 'token' => $row['token'], |
|
1035 | - ]; |
|
1036 | - } |
|
1037 | - $cursor->closeCursor(); |
|
1038 | - |
|
1039 | - return ['remote' => $remote]; |
|
1040 | - } |
|
476 | + return $share; |
|
477 | + } |
|
478 | + |
|
479 | + /** |
|
480 | + * Get all children of this share |
|
481 | + * |
|
482 | + * @param IShare $parent |
|
483 | + * @return IShare[] |
|
484 | + */ |
|
485 | + public function getChildren(IShare $parent) { |
|
486 | + $children = []; |
|
487 | + |
|
488 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
489 | + $qb->select('*') |
|
490 | + ->from('share') |
|
491 | + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
492 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
493 | + ->orderBy('id'); |
|
494 | + |
|
495 | + $cursor = $qb->execute(); |
|
496 | + while($data = $cursor->fetch()) { |
|
497 | + $children[] = $this->createShareObject($data); |
|
498 | + } |
|
499 | + $cursor->closeCursor(); |
|
500 | + |
|
501 | + return $children; |
|
502 | + } |
|
503 | + |
|
504 | + /** |
|
505 | + * Delete a share (owner unShares the file) |
|
506 | + * |
|
507 | + * @param IShare $share |
|
508 | + */ |
|
509 | + public function delete(IShare $share) { |
|
510 | + |
|
511 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith()); |
|
512 | + |
|
513 | + $isOwner = false; |
|
514 | + |
|
515 | + $this->removeShareFromTable($share); |
|
516 | + |
|
517 | + // if the local user is the owner we can send the unShare request directly... |
|
518 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
519 | + $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); |
|
520 | + $this->revokeShare($share, true); |
|
521 | + $isOwner = true; |
|
522 | + } else { // ... if not we need to correct ID for the unShare request |
|
523 | + $remoteId = $this->getRemoteId($share); |
|
524 | + $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken()); |
|
525 | + $this->revokeShare($share, false); |
|
526 | + } |
|
527 | + |
|
528 | + // send revoke notification to the other user, if initiator and owner are not the same user |
|
529 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
530 | + $remoteId = $this->getRemoteId($share); |
|
531 | + if ($isOwner) { |
|
532 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
533 | + } else { |
|
534 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
535 | + } |
|
536 | + $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
537 | + } |
|
538 | + } |
|
539 | + |
|
540 | + /** |
|
541 | + * in case of a re-share we need to send the other use (initiator or owner) |
|
542 | + * a message that the file was unshared |
|
543 | + * |
|
544 | + * @param IShare $share |
|
545 | + * @param bool $isOwner the user can either be the owner or the user who re-sahred it |
|
546 | + * @throws ShareNotFound |
|
547 | + * @throws \OC\HintException |
|
548 | + */ |
|
549 | + protected function revokeShare($share, $isOwner) { |
|
550 | + // also send a unShare request to the initiator, if this is a different user than the owner |
|
551 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
552 | + if ($isOwner) { |
|
553 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
554 | + } else { |
|
555 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
556 | + } |
|
557 | + $remoteId = $this->getRemoteId($share); |
|
558 | + $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
559 | + } |
|
560 | + } |
|
561 | + |
|
562 | + /** |
|
563 | + * remove share from table |
|
564 | + * |
|
565 | + * @param IShare $share |
|
566 | + */ |
|
567 | + public function removeShareFromTable(IShare $share) { |
|
568 | + $this->removeShareFromTableById($share->getId()); |
|
569 | + } |
|
570 | + |
|
571 | + /** |
|
572 | + * remove share from table |
|
573 | + * |
|
574 | + * @param string $shareId |
|
575 | + */ |
|
576 | + private function removeShareFromTableById($shareId) { |
|
577 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
578 | + $qb->delete('share') |
|
579 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
580 | + $qb->execute(); |
|
581 | + |
|
582 | + $qb->delete('federated_reshares') |
|
583 | + ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId))); |
|
584 | + $qb->execute(); |
|
585 | + } |
|
586 | + |
|
587 | + /** |
|
588 | + * @inheritdoc |
|
589 | + */ |
|
590 | + public function deleteFromSelf(IShare $share, $recipient) { |
|
591 | + // nothing to do here. Technically deleteFromSelf in the context of federated |
|
592 | + // shares is a umount of a external storage. This is handled here |
|
593 | + // apps/files_sharing/lib/external/manager.php |
|
594 | + // TODO move this code over to this app |
|
595 | + } |
|
596 | + |
|
597 | + |
|
598 | + public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
599 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
600 | + $qb->select('*') |
|
601 | + ->from('share', 's') |
|
602 | + ->andWhere($qb->expr()->orX( |
|
603 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
604 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
605 | + )) |
|
606 | + ->andWhere( |
|
607 | + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)) |
|
608 | + ); |
|
609 | + |
|
610 | + /** |
|
611 | + * Reshares for this user are shares where they are the owner. |
|
612 | + */ |
|
613 | + if ($reshares === false) { |
|
614 | + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
615 | + } else { |
|
616 | + $qb->andWhere( |
|
617 | + $qb->expr()->orX( |
|
618 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
619 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
620 | + ) |
|
621 | + ); |
|
622 | + } |
|
623 | + |
|
624 | + $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
625 | + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
626 | + |
|
627 | + $qb->orderBy('id'); |
|
628 | + |
|
629 | + $cursor = $qb->execute(); |
|
630 | + $shares = []; |
|
631 | + while ($data = $cursor->fetch()) { |
|
632 | + $shares[$data['fileid']][] = $this->createShareObject($data); |
|
633 | + } |
|
634 | + $cursor->closeCursor(); |
|
635 | + |
|
636 | + return $shares; |
|
637 | + } |
|
638 | + |
|
639 | + /** |
|
640 | + * @inheritdoc |
|
641 | + */ |
|
642 | + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
643 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
644 | + $qb->select('*') |
|
645 | + ->from('share'); |
|
646 | + |
|
647 | + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
648 | + |
|
649 | + /** |
|
650 | + * Reshares for this user are shares where they are the owner. |
|
651 | + */ |
|
652 | + if ($reshares === false) { |
|
653 | + //Special case for old shares created via the web UI |
|
654 | + $or1 = $qb->expr()->andX( |
|
655 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
656 | + $qb->expr()->isNull('uid_initiator') |
|
657 | + ); |
|
658 | + |
|
659 | + $qb->andWhere( |
|
660 | + $qb->expr()->orX( |
|
661 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
662 | + $or1 |
|
663 | + ) |
|
664 | + ); |
|
665 | + } else { |
|
666 | + $qb->andWhere( |
|
667 | + $qb->expr()->orX( |
|
668 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
669 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
670 | + ) |
|
671 | + ); |
|
672 | + } |
|
673 | + |
|
674 | + if ($node !== null) { |
|
675 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
676 | + } |
|
677 | + |
|
678 | + if ($limit !== -1) { |
|
679 | + $qb->setMaxResults($limit); |
|
680 | + } |
|
681 | + |
|
682 | + $qb->setFirstResult($offset); |
|
683 | + $qb->orderBy('id'); |
|
684 | + |
|
685 | + $cursor = $qb->execute(); |
|
686 | + $shares = []; |
|
687 | + while($data = $cursor->fetch()) { |
|
688 | + $shares[] = $this->createShareObject($data); |
|
689 | + } |
|
690 | + $cursor->closeCursor(); |
|
691 | + |
|
692 | + return $shares; |
|
693 | + } |
|
694 | + |
|
695 | + /** |
|
696 | + * @inheritdoc |
|
697 | + */ |
|
698 | + public function getShareById($id, $recipientId = null) { |
|
699 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
700 | + |
|
701 | + $qb->select('*') |
|
702 | + ->from('share') |
|
703 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
704 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
705 | + |
|
706 | + $cursor = $qb->execute(); |
|
707 | + $data = $cursor->fetch(); |
|
708 | + $cursor->closeCursor(); |
|
709 | + |
|
710 | + if ($data === false) { |
|
711 | + throw new ShareNotFound(); |
|
712 | + } |
|
713 | + |
|
714 | + try { |
|
715 | + $share = $this->createShareObject($data); |
|
716 | + } catch (InvalidShare $e) { |
|
717 | + throw new ShareNotFound(); |
|
718 | + } |
|
719 | + |
|
720 | + return $share; |
|
721 | + } |
|
722 | + |
|
723 | + /** |
|
724 | + * Get shares for a given path |
|
725 | + * |
|
726 | + * @param \OCP\Files\Node $path |
|
727 | + * @return IShare[] |
|
728 | + */ |
|
729 | + public function getSharesByPath(Node $path) { |
|
730 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
731 | + |
|
732 | + $cursor = $qb->select('*') |
|
733 | + ->from('share') |
|
734 | + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
735 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
736 | + ->execute(); |
|
737 | + |
|
738 | + $shares = []; |
|
739 | + while($data = $cursor->fetch()) { |
|
740 | + $shares[] = $this->createShareObject($data); |
|
741 | + } |
|
742 | + $cursor->closeCursor(); |
|
743 | + |
|
744 | + return $shares; |
|
745 | + } |
|
746 | + |
|
747 | + /** |
|
748 | + * @inheritdoc |
|
749 | + */ |
|
750 | + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
751 | + /** @var IShare[] $shares */ |
|
752 | + $shares = []; |
|
753 | + |
|
754 | + //Get shares directly with this user |
|
755 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
756 | + $qb->select('*') |
|
757 | + ->from('share'); |
|
758 | + |
|
759 | + // Order by id |
|
760 | + $qb->orderBy('id'); |
|
761 | + |
|
762 | + // Set limit and offset |
|
763 | + if ($limit !== -1) { |
|
764 | + $qb->setMaxResults($limit); |
|
765 | + } |
|
766 | + $qb->setFirstResult($offset); |
|
767 | + |
|
768 | + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
769 | + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
770 | + |
|
771 | + // Filter by node if provided |
|
772 | + if ($node !== null) { |
|
773 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
774 | + } |
|
775 | + |
|
776 | + $cursor = $qb->execute(); |
|
777 | + |
|
778 | + while($data = $cursor->fetch()) { |
|
779 | + $shares[] = $this->createShareObject($data); |
|
780 | + } |
|
781 | + $cursor->closeCursor(); |
|
782 | + |
|
783 | + |
|
784 | + return $shares; |
|
785 | + } |
|
786 | + |
|
787 | + /** |
|
788 | + * Get a share by token |
|
789 | + * |
|
790 | + * @param string $token |
|
791 | + * @return IShare |
|
792 | + * @throws ShareNotFound |
|
793 | + */ |
|
794 | + public function getShareByToken($token) { |
|
795 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
796 | + |
|
797 | + $cursor = $qb->select('*') |
|
798 | + ->from('share') |
|
799 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
800 | + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
801 | + ->execute(); |
|
802 | + |
|
803 | + $data = $cursor->fetch(); |
|
804 | + |
|
805 | + if ($data === false) { |
|
806 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
807 | + } |
|
808 | + |
|
809 | + try { |
|
810 | + $share = $this->createShareObject($data); |
|
811 | + } catch (InvalidShare $e) { |
|
812 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
813 | + } |
|
814 | + |
|
815 | + return $share; |
|
816 | + } |
|
817 | + |
|
818 | + /** |
|
819 | + * get database row of a give share |
|
820 | + * |
|
821 | + * @param $id |
|
822 | + * @return array |
|
823 | + * @throws ShareNotFound |
|
824 | + */ |
|
825 | + private function getRawShare($id) { |
|
826 | + |
|
827 | + // Now fetch the inserted share and create a complete share object |
|
828 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
829 | + $qb->select('*') |
|
830 | + ->from('share') |
|
831 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
832 | + |
|
833 | + $cursor = $qb->execute(); |
|
834 | + $data = $cursor->fetch(); |
|
835 | + $cursor->closeCursor(); |
|
836 | + |
|
837 | + if ($data === false) { |
|
838 | + throw new ShareNotFound; |
|
839 | + } |
|
840 | + |
|
841 | + return $data; |
|
842 | + } |
|
843 | + |
|
844 | + /** |
|
845 | + * Create a share object from an database row |
|
846 | + * |
|
847 | + * @param array $data |
|
848 | + * @return IShare |
|
849 | + * @throws InvalidShare |
|
850 | + * @throws ShareNotFound |
|
851 | + */ |
|
852 | + private function createShareObject($data) { |
|
853 | + |
|
854 | + $share = new Share($this->rootFolder, $this->userManager); |
|
855 | + $share->setId((int)$data['id']) |
|
856 | + ->setShareType((int)$data['share_type']) |
|
857 | + ->setPermissions((int)$data['permissions']) |
|
858 | + ->setTarget($data['file_target']) |
|
859 | + ->setMailSend((bool)$data['mail_send']) |
|
860 | + ->setToken($data['token']); |
|
861 | + |
|
862 | + $shareTime = new \DateTime(); |
|
863 | + $shareTime->setTimestamp((int)$data['stime']); |
|
864 | + $share->setShareTime($shareTime); |
|
865 | + $share->setSharedWith($data['share_with']); |
|
866 | + |
|
867 | + if ($data['uid_initiator'] !== null) { |
|
868 | + $share->setShareOwner($data['uid_owner']); |
|
869 | + $share->setSharedBy($data['uid_initiator']); |
|
870 | + } else { |
|
871 | + //OLD SHARE |
|
872 | + $share->setSharedBy($data['uid_owner']); |
|
873 | + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
874 | + |
|
875 | + $owner = $path->getOwner(); |
|
876 | + $share->setShareOwner($owner->getUID()); |
|
877 | + } |
|
878 | + |
|
879 | + $share->setNodeId((int)$data['file_source']); |
|
880 | + $share->setNodeType($data['item_type']); |
|
881 | + |
|
882 | + $share->setProviderId($this->identifier()); |
|
883 | + |
|
884 | + return $share; |
|
885 | + } |
|
886 | + |
|
887 | + /** |
|
888 | + * Get the node with file $id for $user |
|
889 | + * |
|
890 | + * @param string $userId |
|
891 | + * @param int $id |
|
892 | + * @return \OCP\Files\File|\OCP\Files\Folder |
|
893 | + * @throws InvalidShare |
|
894 | + */ |
|
895 | + private function getNode($userId, $id) { |
|
896 | + try { |
|
897 | + $userFolder = $this->rootFolder->getUserFolder($userId); |
|
898 | + } catch (NotFoundException $e) { |
|
899 | + throw new InvalidShare(); |
|
900 | + } |
|
901 | + |
|
902 | + $nodes = $userFolder->getById($id); |
|
903 | + |
|
904 | + if (empty($nodes)) { |
|
905 | + throw new InvalidShare(); |
|
906 | + } |
|
907 | + |
|
908 | + return $nodes[0]; |
|
909 | + } |
|
910 | + |
|
911 | + /** |
|
912 | + * A user is deleted from the system |
|
913 | + * So clean up the relevant shares. |
|
914 | + * |
|
915 | + * @param string $uid |
|
916 | + * @param int $shareType |
|
917 | + */ |
|
918 | + public function userDeleted($uid, $shareType) { |
|
919 | + //TODO: probabaly a good idea to send unshare info to remote servers |
|
920 | + |
|
921 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
922 | + |
|
923 | + $qb->delete('share') |
|
924 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) |
|
925 | + ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
926 | + ->execute(); |
|
927 | + } |
|
928 | + |
|
929 | + /** |
|
930 | + * This provider does not handle groups |
|
931 | + * |
|
932 | + * @param string $gid |
|
933 | + */ |
|
934 | + public function groupDeleted($gid) { |
|
935 | + // We don't handle groups here |
|
936 | + } |
|
937 | + |
|
938 | + /** |
|
939 | + * This provider does not handle groups |
|
940 | + * |
|
941 | + * @param string $uid |
|
942 | + * @param string $gid |
|
943 | + */ |
|
944 | + public function userDeletedFromGroup($uid, $gid) { |
|
945 | + // We don't handle groups here |
|
946 | + } |
|
947 | + |
|
948 | + /** |
|
949 | + * check if users from other Nextcloud instances are allowed to mount public links share by this instance |
|
950 | + * |
|
951 | + * @return bool |
|
952 | + */ |
|
953 | + public function isOutgoingServer2serverShareEnabled() { |
|
954 | + if ($this->gsConfig->onlyInternalFederation()) { |
|
955 | + return false; |
|
956 | + } |
|
957 | + $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); |
|
958 | + return ($result === 'yes'); |
|
959 | + } |
|
960 | + |
|
961 | + /** |
|
962 | + * check if users are allowed to mount public links from other Nextclouds |
|
963 | + * |
|
964 | + * @return bool |
|
965 | + */ |
|
966 | + public function isIncomingServer2serverShareEnabled() { |
|
967 | + if ($this->gsConfig->onlyInternalFederation()) { |
|
968 | + return false; |
|
969 | + } |
|
970 | + $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); |
|
971 | + return ($result === 'yes'); |
|
972 | + } |
|
973 | + |
|
974 | + /** |
|
975 | + * Check if querying sharees on the lookup server is enabled |
|
976 | + * |
|
977 | + * @return bool |
|
978 | + */ |
|
979 | + public function isLookupServerQueriesEnabled() { |
|
980 | + // in a global scale setup we should always query the lookup server |
|
981 | + if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
982 | + return true; |
|
983 | + } |
|
984 | + $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); |
|
985 | + return ($result === 'yes'); |
|
986 | + } |
|
987 | + |
|
988 | + |
|
989 | + /** |
|
990 | + * Check if it is allowed to publish user specific data to the lookup server |
|
991 | + * |
|
992 | + * @return bool |
|
993 | + */ |
|
994 | + public function isLookupServerUploadEnabled() { |
|
995 | + // in a global scale setup the admin is responsible to keep the lookup server up-to-date |
|
996 | + if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
997 | + return false; |
|
998 | + } |
|
999 | + $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes'); |
|
1000 | + return ($result === 'yes'); |
|
1001 | + } |
|
1002 | + |
|
1003 | + /** |
|
1004 | + * @inheritdoc |
|
1005 | + */ |
|
1006 | + public function getAccessList($nodes, $currentAccess) { |
|
1007 | + $ids = []; |
|
1008 | + foreach ($nodes as $node) { |
|
1009 | + $ids[] = $node->getId(); |
|
1010 | + } |
|
1011 | + |
|
1012 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
1013 | + $qb->select('share_with', 'token', 'file_source') |
|
1014 | + ->from('share') |
|
1015 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) |
|
1016 | + ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
1017 | + ->andWhere($qb->expr()->orX( |
|
1018 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1019 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1020 | + )); |
|
1021 | + $cursor = $qb->execute(); |
|
1022 | + |
|
1023 | + if ($currentAccess === false) { |
|
1024 | + $remote = $cursor->fetch() !== false; |
|
1025 | + $cursor->closeCursor(); |
|
1026 | + |
|
1027 | + return ['remote' => $remote]; |
|
1028 | + } |
|
1029 | + |
|
1030 | + $remote = []; |
|
1031 | + while ($row = $cursor->fetch()) { |
|
1032 | + $remote[$row['share_with']] = [ |
|
1033 | + 'node_id' => $row['file_source'], |
|
1034 | + 'token' => $row['token'], |
|
1035 | + ]; |
|
1036 | + } |
|
1037 | + $cursor->closeCursor(); |
|
1038 | + |
|
1039 | + return ['remote' => $remote]; |
|
1040 | + } |
|
1041 | 1041 | } |
@@ -51,639 +51,639 @@ |
||
51 | 51 | |
52 | 52 | class RequestHandlerController extends OCSController { |
53 | 53 | |
54 | - /** @var FederatedShareProvider */ |
|
55 | - private $federatedShareProvider; |
|
56 | - |
|
57 | - /** @var IDBConnection */ |
|
58 | - private $connection; |
|
59 | - |
|
60 | - /** @var Share\IManager */ |
|
61 | - private $shareManager; |
|
62 | - |
|
63 | - /** @var Notifications */ |
|
64 | - private $notifications; |
|
65 | - |
|
66 | - /** @var AddressHandler */ |
|
67 | - private $addressHandler; |
|
68 | - |
|
69 | - /** @var IUserManager */ |
|
70 | - private $userManager; |
|
71 | - |
|
72 | - /** @var string */ |
|
73 | - private $shareTable = 'share'; |
|
74 | - |
|
75 | - /** @var ICloudIdManager */ |
|
76 | - private $cloudIdManager; |
|
77 | - |
|
78 | - /** @var ILogger */ |
|
79 | - private $logger; |
|
80 | - |
|
81 | - /** |
|
82 | - * Server2Server constructor. |
|
83 | - * |
|
84 | - * @param string $appName |
|
85 | - * @param IRequest $request |
|
86 | - * @param FederatedShareProvider $federatedShareProvider |
|
87 | - * @param IDBConnection $connection |
|
88 | - * @param Share\IManager $shareManager |
|
89 | - * @param Notifications $notifications |
|
90 | - * @param AddressHandler $addressHandler |
|
91 | - * @param IUserManager $userManager |
|
92 | - * @param ICloudIdManager $cloudIdManager |
|
93 | - */ |
|
94 | - public function __construct($appName, |
|
95 | - IRequest $request, |
|
96 | - FederatedShareProvider $federatedShareProvider, |
|
97 | - IDBConnection $connection, |
|
98 | - Share\IManager $shareManager, |
|
99 | - Notifications $notifications, |
|
100 | - AddressHandler $addressHandler, |
|
101 | - IUserManager $userManager, |
|
102 | - ICloudIdManager $cloudIdManager, |
|
103 | - ILogger $logger |
|
104 | - ) { |
|
105 | - parent::__construct($appName, $request); |
|
106 | - |
|
107 | - $this->federatedShareProvider = $federatedShareProvider; |
|
108 | - $this->connection = $connection; |
|
109 | - $this->shareManager = $shareManager; |
|
110 | - $this->notifications = $notifications; |
|
111 | - $this->addressHandler = $addressHandler; |
|
112 | - $this->userManager = $userManager; |
|
113 | - $this->cloudIdManager = $cloudIdManager; |
|
114 | - $this->logger = $logger; |
|
115 | - } |
|
116 | - |
|
117 | - /** |
|
118 | - * @NoCSRFRequired |
|
119 | - * @PublicPage |
|
120 | - * |
|
121 | - * create a new share |
|
122 | - * |
|
123 | - * @return Http\DataResponse |
|
124 | - * @throws OCSException |
|
125 | - */ |
|
126 | - public function createShare() { |
|
127 | - |
|
128 | - if (!$this->isS2SEnabled(true)) { |
|
129 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
130 | - } |
|
131 | - |
|
132 | - $remote = isset($_POST['remote']) ? $_POST['remote'] : null; |
|
133 | - $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
134 | - $name = isset($_POST['name']) ? $_POST['name'] : null; |
|
135 | - $owner = isset($_POST['owner']) ? $_POST['owner'] : null; |
|
136 | - $sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null; |
|
137 | - $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null; |
|
138 | - $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null; |
|
139 | - $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null; |
|
140 | - $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null; |
|
141 | - |
|
142 | - if ($remote && $token && $name && $owner && $remoteId && $shareWith) { |
|
143 | - |
|
144 | - if (!\OCP\Util::isValidFileName($name)) { |
|
145 | - throw new OCSException('The mountpoint name contains invalid characters.', 400); |
|
146 | - } |
|
147 | - |
|
148 | - // FIXME this should be a method in the user management instead |
|
149 | - $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']); |
|
150 | - \OCP\Util::emitHook( |
|
151 | - '\OCA\Files_Sharing\API\Server2Server', |
|
152 | - 'preLoginNameUsedAsUserName', |
|
153 | - array('uid' => &$shareWith) |
|
154 | - ); |
|
155 | - $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']); |
|
156 | - |
|
157 | - if (!\OC::$server->getUserManager()->userExists($shareWith)) { |
|
158 | - throw new OCSException('User does not exists', 400); |
|
159 | - } |
|
160 | - |
|
161 | - \OC_Util::setupFS($shareWith); |
|
162 | - |
|
163 | - $externalManager = new \OCA\Files_Sharing\External\Manager( |
|
164 | - \OC::$server->getDatabaseConnection(), |
|
165 | - \OC\Files\Filesystem::getMountManager(), |
|
166 | - \OC\Files\Filesystem::getLoader(), |
|
167 | - \OC::$server->getHTTPClientService(), |
|
168 | - \OC::$server->getNotificationManager(), |
|
169 | - \OC::$server->query(\OCP\OCS\IDiscoveryService::class), |
|
170 | - $shareWith |
|
171 | - ); |
|
172 | - |
|
173 | - try { |
|
174 | - $externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId); |
|
175 | - $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external'); |
|
176 | - |
|
177 | - if ($ownerFederatedId === null) { |
|
178 | - $ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId(); |
|
179 | - } |
|
180 | - // if the owner of the share and the initiator are the same user |
|
181 | - // we also complete the federated share ID for the initiator |
|
182 | - if ($sharedByFederatedId === null && $owner === $sharedBy) { |
|
183 | - $sharedByFederatedId = $ownerFederatedId; |
|
184 | - } |
|
185 | - |
|
186 | - $event = \OC::$server->getActivityManager()->generateEvent(); |
|
187 | - $event->setApp('files_sharing') |
|
188 | - ->setType('remote_share') |
|
189 | - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')]) |
|
190 | - ->setAffectedUser($shareWith) |
|
191 | - ->setObject('remote_share', (int)$shareId, $name); |
|
192 | - \OC::$server->getActivityManager()->publish($event); |
|
193 | - |
|
194 | - $urlGenerator = \OC::$server->getURLGenerator(); |
|
195 | - |
|
196 | - $notificationManager = \OC::$server->getNotificationManager(); |
|
197 | - $notification = $notificationManager->createNotification(); |
|
198 | - $notification->setApp('files_sharing') |
|
199 | - ->setUser($shareWith) |
|
200 | - ->setDateTime(new \DateTime()) |
|
201 | - ->setObject('remote_share', $shareId) |
|
202 | - ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]); |
|
203 | - |
|
204 | - $declineAction = $notification->createAction(); |
|
205 | - $declineAction->setLabel('decline') |
|
206 | - ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE'); |
|
207 | - $notification->addAction($declineAction); |
|
208 | - |
|
209 | - $acceptAction = $notification->createAction(); |
|
210 | - $acceptAction->setLabel('accept') |
|
211 | - ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST'); |
|
212 | - $notification->addAction($acceptAction); |
|
213 | - |
|
214 | - $notificationManager->notify($notification); |
|
215 | - |
|
216 | - return new Http\DataResponse(); |
|
217 | - } catch (\Exception $e) { |
|
218 | - $this->logger->logException($e, [ |
|
219 | - 'message' => 'Server can not add remote share.', |
|
220 | - 'level' => ILogger::ERROR, |
|
221 | - 'app' => 'files_sharing' |
|
222 | - ]); |
|
223 | - throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); |
|
224 | - } |
|
225 | - } |
|
226 | - |
|
227 | - throw new OCSException('server can not add remote share, missing parameter', 400); |
|
228 | - } |
|
229 | - |
|
230 | - /** |
|
231 | - * @NoCSRFRequired |
|
232 | - * @PublicPage |
|
233 | - * |
|
234 | - * create re-share on behalf of another user |
|
235 | - * |
|
236 | - * @param int $id |
|
237 | - * @return Http\DataResponse |
|
238 | - * @throws OCSBadRequestException |
|
239 | - * @throws OCSForbiddenException |
|
240 | - * @throws OCSNotFoundException |
|
241 | - */ |
|
242 | - public function reShare($id) { |
|
243 | - |
|
244 | - $token = $this->request->getParam('token', null); |
|
245 | - $shareWith = $this->request->getParam('shareWith', null); |
|
246 | - $permission = (int)$this->request->getParam('permission', null); |
|
247 | - $remoteId = (int)$this->request->getParam('remoteId', null); |
|
248 | - |
|
249 | - if ($id === null || |
|
250 | - $token === null || |
|
251 | - $shareWith === null || |
|
252 | - $permission === null || |
|
253 | - $remoteId === null |
|
254 | - ) { |
|
255 | - throw new OCSBadRequestException(); |
|
256 | - } |
|
257 | - |
|
258 | - try { |
|
259 | - $share = $this->federatedShareProvider->getShareById($id); |
|
260 | - } catch (Share\Exceptions\ShareNotFound $e) { |
|
261 | - throw new OCSNotFoundException(); |
|
262 | - } |
|
263 | - |
|
264 | - // don't allow to share a file back to the owner |
|
265 | - list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); |
|
266 | - $owner = $share->getShareOwner(); |
|
267 | - $currentServer = $this->addressHandler->generateRemoteURL(); |
|
268 | - if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) { |
|
269 | - throw new OCSForbiddenException(); |
|
270 | - } |
|
271 | - |
|
272 | - if ($this->verifyShare($share, $token)) { |
|
273 | - |
|
274 | - // check if re-sharing is allowed |
|
275 | - if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) { |
|
276 | - $share->setPermissions($share->getPermissions() & $permission); |
|
277 | - // the recipient of the initial share is now the initiator for the re-share |
|
278 | - $share->setSharedBy($share->getSharedWith()); |
|
279 | - $share->setSharedWith($shareWith); |
|
280 | - try { |
|
281 | - $result = $this->federatedShareProvider->create($share); |
|
282 | - $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId); |
|
283 | - return new Http\DataResponse([ |
|
284 | - 'token' => $result->getToken(), |
|
285 | - 'remoteId' => $result->getId() |
|
286 | - ]); |
|
287 | - } catch (\Exception $e) { |
|
288 | - throw new OCSBadRequestException(); |
|
289 | - } |
|
290 | - } else { |
|
291 | - throw new OCSForbiddenException(); |
|
292 | - } |
|
293 | - } |
|
294 | - throw new OCSBadRequestException(); |
|
295 | - } |
|
296 | - |
|
297 | - /** |
|
298 | - * @NoCSRFRequired |
|
299 | - * @PublicPage |
|
300 | - * |
|
301 | - * accept server-to-server share |
|
302 | - * |
|
303 | - * @param int $id |
|
304 | - * @return Http\DataResponse |
|
305 | - * @throws OCSException |
|
306 | - */ |
|
307 | - public function acceptShare($id) { |
|
308 | - |
|
309 | - if (!$this->isS2SEnabled()) { |
|
310 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
311 | - } |
|
312 | - |
|
313 | - $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
314 | - |
|
315 | - try { |
|
316 | - $share = $this->federatedShareProvider->getShareById($id); |
|
317 | - } catch (Share\Exceptions\ShareNotFound $e) { |
|
318 | - return new Http\DataResponse(); |
|
319 | - } |
|
320 | - |
|
321 | - if ($this->verifyShare($share, $token)) { |
|
322 | - $this->executeAcceptShare($share); |
|
323 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
324 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
325 | - $remoteId = $this->federatedShareProvider->getRemoteId($share); |
|
326 | - $this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken()); |
|
327 | - } |
|
328 | - } |
|
329 | - |
|
330 | - return new Http\DataResponse(); |
|
331 | - } |
|
332 | - |
|
333 | - protected function executeAcceptShare(Share\IShare $share) { |
|
334 | - $fileId = (int) $share->getNode()->getId(); |
|
335 | - list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); |
|
336 | - |
|
337 | - $event = \OC::$server->getActivityManager()->generateEvent(); |
|
338 | - $event->setApp('files_sharing') |
|
339 | - ->setType('remote_share') |
|
340 | - ->setAffectedUser($this->getCorrectUid($share)) |
|
341 | - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]]) |
|
342 | - ->setObject('files', $fileId, $file) |
|
343 | - ->setLink($link); |
|
344 | - \OC::$server->getActivityManager()->publish($event); |
|
345 | - } |
|
346 | - |
|
347 | - /** |
|
348 | - * @NoCSRFRequired |
|
349 | - * @PublicPage |
|
350 | - * |
|
351 | - * decline server-to-server share |
|
352 | - * |
|
353 | - * @param int $id |
|
354 | - * @return Http\DataResponse |
|
355 | - * @throws OCSException |
|
356 | - */ |
|
357 | - public function declineShare($id) { |
|
358 | - |
|
359 | - if (!$this->isS2SEnabled()) { |
|
360 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
361 | - } |
|
362 | - |
|
363 | - $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
364 | - |
|
365 | - try { |
|
366 | - $share = $this->federatedShareProvider->getShareById($id); |
|
367 | - } catch (Share\Exceptions\ShareNotFound $e) { |
|
368 | - return new Http\DataResponse(); |
|
369 | - } |
|
370 | - |
|
371 | - if ($this->verifyShare($share, $token)) { |
|
372 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
373 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
374 | - $remoteId = $this->federatedShareProvider->getRemoteId($share); |
|
375 | - $this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken()); |
|
376 | - } |
|
377 | - $this->executeDeclineShare($share); |
|
378 | - } |
|
379 | - |
|
380 | - return new Http\DataResponse(); |
|
381 | - } |
|
382 | - |
|
383 | - /** |
|
384 | - * delete declined share and create a activity |
|
385 | - * |
|
386 | - * @param Share\IShare $share |
|
387 | - */ |
|
388 | - protected function executeDeclineShare(Share\IShare $share) { |
|
389 | - $this->federatedShareProvider->removeShareFromTable($share); |
|
390 | - $fileId = (int) $share->getNode()->getId(); |
|
391 | - list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); |
|
392 | - |
|
393 | - $event = \OC::$server->getActivityManager()->generateEvent(); |
|
394 | - $event->setApp('files_sharing') |
|
395 | - ->setType('remote_share') |
|
396 | - ->setAffectedUser($this->getCorrectUid($share)) |
|
397 | - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]]) |
|
398 | - ->setObject('files', $fileId, $file) |
|
399 | - ->setLink($link); |
|
400 | - \OC::$server->getActivityManager()->publish($event); |
|
401 | - |
|
402 | - } |
|
403 | - |
|
404 | - /** |
|
405 | - * check if we are the initiator or the owner of a re-share and return the correct UID |
|
406 | - * |
|
407 | - * @param Share\IShare $share |
|
408 | - * @return string |
|
409 | - */ |
|
410 | - protected function getCorrectUid(Share\IShare $share) { |
|
411 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
412 | - return $share->getShareOwner(); |
|
413 | - } |
|
414 | - |
|
415 | - return $share->getSharedBy(); |
|
416 | - } |
|
417 | - |
|
418 | - /** |
|
419 | - * @NoCSRFRequired |
|
420 | - * @PublicPage |
|
421 | - * |
|
422 | - * remove server-to-server share if it was unshared by the owner |
|
423 | - * |
|
424 | - * @param int $id |
|
425 | - * @return Http\DataResponse |
|
426 | - * @throws OCSException |
|
427 | - */ |
|
428 | - public function unshare($id) { |
|
429 | - |
|
430 | - if (!$this->isS2SEnabled()) { |
|
431 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
432 | - } |
|
433 | - |
|
434 | - $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
435 | - |
|
436 | - $qb = $this->connection->getQueryBuilder(); |
|
437 | - $qb->select('*') |
|
438 | - ->from('share_external') |
|
439 | - ->where( |
|
440 | - $qb->expr()->andX( |
|
441 | - $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), |
|
442 | - $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) |
|
443 | - ) |
|
444 | - ); |
|
445 | - |
|
446 | - $result = $qb->execute(); |
|
447 | - $share = $result->fetch(); |
|
448 | - $result->closeCursor(); |
|
449 | - |
|
450 | - if ($token && $id && !empty($share)) { |
|
451 | - |
|
452 | - $remote = $this->cleanupRemote($share['remote']); |
|
453 | - |
|
454 | - $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote); |
|
455 | - $mountpoint = $share['mountpoint']; |
|
456 | - $user = $share['user']; |
|
457 | - |
|
458 | - $qb = $this->connection->getQueryBuilder(); |
|
459 | - $qb->delete('share_external') |
|
460 | - ->where( |
|
461 | - $qb->expr()->andX( |
|
462 | - $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), |
|
463 | - $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) |
|
464 | - ) |
|
465 | - ); |
|
466 | - |
|
467 | - $result = $qb->execute(); |
|
468 | - $result->closeCursor(); |
|
469 | - |
|
470 | - if ($share['accepted']) { |
|
471 | - $path = trim($mountpoint, '/'); |
|
472 | - } else { |
|
473 | - $path = trim($share['name'], '/'); |
|
474 | - } |
|
475 | - |
|
476 | - $notificationManager = \OC::$server->getNotificationManager(); |
|
477 | - $notification = $notificationManager->createNotification(); |
|
478 | - $notification->setApp('files_sharing') |
|
479 | - ->setUser($share['user']) |
|
480 | - ->setObject('remote_share', (int)$share['id']); |
|
481 | - $notificationManager->markProcessed($notification); |
|
482 | - |
|
483 | - $event = \OC::$server->getActivityManager()->generateEvent(); |
|
484 | - $event->setApp('files_sharing') |
|
485 | - ->setType('remote_share') |
|
486 | - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path]) |
|
487 | - ->setAffectedUser($user) |
|
488 | - ->setObject('remote_share', (int)$share['id'], $path); |
|
489 | - \OC::$server->getActivityManager()->publish($event); |
|
490 | - } |
|
491 | - |
|
492 | - return new Http\DataResponse(); |
|
493 | - } |
|
494 | - |
|
495 | - private function cleanupRemote($remote) { |
|
496 | - $remote = substr($remote, strpos($remote, '://') + 3); |
|
497 | - |
|
498 | - return rtrim($remote, '/'); |
|
499 | - } |
|
500 | - |
|
501 | - |
|
502 | - /** |
|
503 | - * @NoCSRFRequired |
|
504 | - * @PublicPage |
|
505 | - * |
|
506 | - * federated share was revoked, either by the owner or the re-sharer |
|
507 | - * |
|
508 | - * @param int $id |
|
509 | - * @return Http\DataResponse |
|
510 | - * @throws OCSBadRequestException |
|
511 | - */ |
|
512 | - public function revoke($id) { |
|
513 | - $token = $this->request->getParam('token'); |
|
514 | - |
|
515 | - $share = $this->federatedShareProvider->getShareById($id); |
|
516 | - |
|
517 | - if ($this->verifyShare($share, $token)) { |
|
518 | - $this->federatedShareProvider->removeShareFromTable($share); |
|
519 | - return new Http\DataResponse(); |
|
520 | - } |
|
521 | - |
|
522 | - throw new OCSBadRequestException(); |
|
523 | - } |
|
524 | - |
|
525 | - /** |
|
526 | - * get share |
|
527 | - * |
|
528 | - * @param int $id |
|
529 | - * @param string $token |
|
530 | - * @return array|bool |
|
531 | - */ |
|
532 | - protected function getShare($id, $token) { |
|
533 | - $query = $this->connection->getQueryBuilder(); |
|
534 | - $query->select('*')->from($this->shareTable) |
|
535 | - ->where($query->expr()->eq('token', $query->createNamedParameter($token))) |
|
536 | - ->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE))) |
|
537 | - ->andWhere($query->expr()->eq('id', $query->createNamedParameter($id))); |
|
538 | - |
|
539 | - $result = $query->execute()->fetchAll(); |
|
540 | - |
|
541 | - if (!empty($result) && isset($result[0])) { |
|
542 | - return $result[0]; |
|
543 | - } |
|
544 | - |
|
545 | - return false; |
|
546 | - } |
|
547 | - |
|
548 | - /** |
|
549 | - * get file |
|
550 | - * |
|
551 | - * @param string $user |
|
552 | - * @param int $fileSource |
|
553 | - * @return array with internal path of the file and a absolute link to it |
|
554 | - */ |
|
555 | - private function getFile($user, $fileSource) { |
|
556 | - \OC_Util::setupFS($user); |
|
557 | - |
|
558 | - try { |
|
559 | - $file = \OC\Files\Filesystem::getPath($fileSource); |
|
560 | - } catch (NotFoundException $e) { |
|
561 | - $file = null; |
|
562 | - } |
|
563 | - $args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file); |
|
564 | - $link = \OCP\Util::linkToAbsolute('files', 'index.php', $args); |
|
565 | - |
|
566 | - return array($file, $link); |
|
567 | - |
|
568 | - } |
|
569 | - |
|
570 | - /** |
|
571 | - * check if server-to-server sharing is enabled |
|
572 | - * |
|
573 | - * @param bool $incoming |
|
574 | - * @return bool |
|
575 | - */ |
|
576 | - private function isS2SEnabled($incoming = false) { |
|
577 | - |
|
578 | - $result = \OCP\App::isEnabled('files_sharing'); |
|
579 | - |
|
580 | - if ($incoming) { |
|
581 | - $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled(); |
|
582 | - } else { |
|
583 | - $result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled(); |
|
584 | - } |
|
585 | - |
|
586 | - return $result; |
|
587 | - } |
|
588 | - |
|
589 | - /** |
|
590 | - * check if we got the right share |
|
591 | - * |
|
592 | - * @param Share\IShare $share |
|
593 | - * @param string $token |
|
594 | - * @return bool |
|
595 | - */ |
|
596 | - protected function verifyShare(Share\IShare $share, $token) { |
|
597 | - if ( |
|
598 | - $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE && |
|
599 | - $share->getToken() === $token |
|
600 | - ) { |
|
601 | - return true; |
|
602 | - } |
|
603 | - |
|
604 | - return false; |
|
605 | - } |
|
606 | - |
|
607 | - /** |
|
608 | - * @NoCSRFRequired |
|
609 | - * @PublicPage |
|
610 | - * |
|
611 | - * update share information to keep federated re-shares in sync |
|
612 | - * |
|
613 | - * @param int $id |
|
614 | - * @return Http\DataResponse |
|
615 | - * @throws OCSBadRequestException |
|
616 | - */ |
|
617 | - public function updatePermissions($id) { |
|
618 | - $token = $this->request->getParam('token', null); |
|
619 | - $permissions = $this->request->getParam('permissions', null); |
|
620 | - |
|
621 | - try { |
|
622 | - $share = $this->federatedShareProvider->getShareById($id); |
|
623 | - } catch (Share\Exceptions\ShareNotFound $e) { |
|
624 | - throw new OCSBadRequestException(); |
|
625 | - } |
|
626 | - |
|
627 | - $validPermission = ctype_digit($permissions); |
|
628 | - $validToken = $this->verifyShare($share, $token); |
|
629 | - if ($validPermission && $validToken) { |
|
630 | - $this->updatePermissionsInDatabase($share, (int)$permissions); |
|
631 | - } else { |
|
632 | - throw new OCSBadRequestException(); |
|
633 | - } |
|
634 | - |
|
635 | - return new Http\DataResponse(); |
|
636 | - } |
|
637 | - |
|
638 | - /** |
|
639 | - * update permissions in database |
|
640 | - * |
|
641 | - * @param IShare $share |
|
642 | - * @param int $permissions |
|
643 | - */ |
|
644 | - protected function updatePermissionsInDatabase(IShare $share, $permissions) { |
|
645 | - $query = $this->connection->getQueryBuilder(); |
|
646 | - $query->update('share') |
|
647 | - ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId()))) |
|
648 | - ->set('permissions', $query->createNamedParameter($permissions)) |
|
649 | - ->execute(); |
|
650 | - } |
|
651 | - |
|
652 | - /** |
|
653 | - * @NoCSRFRequired |
|
654 | - * @PublicPage |
|
655 | - * |
|
656 | - * change the owner of a server-to-server share |
|
657 | - * |
|
658 | - * @param int $id |
|
659 | - * @return Http\DataResponse |
|
660 | - * @throws \InvalidArgumentException |
|
661 | - * @throws OCSException |
|
662 | - */ |
|
663 | - public function move($id) { |
|
664 | - |
|
665 | - if (!$this->isS2SEnabled()) { |
|
666 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
667 | - } |
|
668 | - |
|
669 | - $token = $this->request->getParam('token'); |
|
670 | - $remote = $this->request->getParam('remote'); |
|
671 | - $newRemoteId = $this->request->getParam('remote_id', $id); |
|
672 | - $cloudId = $this->cloudIdManager->resolveCloudId($remote); |
|
673 | - |
|
674 | - $qb = $this->connection->getQueryBuilder(); |
|
675 | - $query = $qb->update('share_external') |
|
676 | - ->set('remote', $qb->createNamedParameter($cloudId->getRemote())) |
|
677 | - ->set('owner', $qb->createNamedParameter($cloudId->getUser())) |
|
678 | - ->set('remote_id', $qb->createNamedParameter($newRemoteId)) |
|
679 | - ->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id))) |
|
680 | - ->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token))); |
|
681 | - $affected = $query->execute(); |
|
682 | - |
|
683 | - if ($affected > 0) { |
|
684 | - return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]); |
|
685 | - } else { |
|
686 | - throw new OCSBadRequestException('Share not found or token invalid'); |
|
687 | - } |
|
688 | - } |
|
54 | + /** @var FederatedShareProvider */ |
|
55 | + private $federatedShareProvider; |
|
56 | + |
|
57 | + /** @var IDBConnection */ |
|
58 | + private $connection; |
|
59 | + |
|
60 | + /** @var Share\IManager */ |
|
61 | + private $shareManager; |
|
62 | + |
|
63 | + /** @var Notifications */ |
|
64 | + private $notifications; |
|
65 | + |
|
66 | + /** @var AddressHandler */ |
|
67 | + private $addressHandler; |
|
68 | + |
|
69 | + /** @var IUserManager */ |
|
70 | + private $userManager; |
|
71 | + |
|
72 | + /** @var string */ |
|
73 | + private $shareTable = 'share'; |
|
74 | + |
|
75 | + /** @var ICloudIdManager */ |
|
76 | + private $cloudIdManager; |
|
77 | + |
|
78 | + /** @var ILogger */ |
|
79 | + private $logger; |
|
80 | + |
|
81 | + /** |
|
82 | + * Server2Server constructor. |
|
83 | + * |
|
84 | + * @param string $appName |
|
85 | + * @param IRequest $request |
|
86 | + * @param FederatedShareProvider $federatedShareProvider |
|
87 | + * @param IDBConnection $connection |
|
88 | + * @param Share\IManager $shareManager |
|
89 | + * @param Notifications $notifications |
|
90 | + * @param AddressHandler $addressHandler |
|
91 | + * @param IUserManager $userManager |
|
92 | + * @param ICloudIdManager $cloudIdManager |
|
93 | + */ |
|
94 | + public function __construct($appName, |
|
95 | + IRequest $request, |
|
96 | + FederatedShareProvider $federatedShareProvider, |
|
97 | + IDBConnection $connection, |
|
98 | + Share\IManager $shareManager, |
|
99 | + Notifications $notifications, |
|
100 | + AddressHandler $addressHandler, |
|
101 | + IUserManager $userManager, |
|
102 | + ICloudIdManager $cloudIdManager, |
|
103 | + ILogger $logger |
|
104 | + ) { |
|
105 | + parent::__construct($appName, $request); |
|
106 | + |
|
107 | + $this->federatedShareProvider = $federatedShareProvider; |
|
108 | + $this->connection = $connection; |
|
109 | + $this->shareManager = $shareManager; |
|
110 | + $this->notifications = $notifications; |
|
111 | + $this->addressHandler = $addressHandler; |
|
112 | + $this->userManager = $userManager; |
|
113 | + $this->cloudIdManager = $cloudIdManager; |
|
114 | + $this->logger = $logger; |
|
115 | + } |
|
116 | + |
|
117 | + /** |
|
118 | + * @NoCSRFRequired |
|
119 | + * @PublicPage |
|
120 | + * |
|
121 | + * create a new share |
|
122 | + * |
|
123 | + * @return Http\DataResponse |
|
124 | + * @throws OCSException |
|
125 | + */ |
|
126 | + public function createShare() { |
|
127 | + |
|
128 | + if (!$this->isS2SEnabled(true)) { |
|
129 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
130 | + } |
|
131 | + |
|
132 | + $remote = isset($_POST['remote']) ? $_POST['remote'] : null; |
|
133 | + $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
134 | + $name = isset($_POST['name']) ? $_POST['name'] : null; |
|
135 | + $owner = isset($_POST['owner']) ? $_POST['owner'] : null; |
|
136 | + $sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null; |
|
137 | + $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null; |
|
138 | + $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null; |
|
139 | + $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null; |
|
140 | + $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null; |
|
141 | + |
|
142 | + if ($remote && $token && $name && $owner && $remoteId && $shareWith) { |
|
143 | + |
|
144 | + if (!\OCP\Util::isValidFileName($name)) { |
|
145 | + throw new OCSException('The mountpoint name contains invalid characters.', 400); |
|
146 | + } |
|
147 | + |
|
148 | + // FIXME this should be a method in the user management instead |
|
149 | + $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']); |
|
150 | + \OCP\Util::emitHook( |
|
151 | + '\OCA\Files_Sharing\API\Server2Server', |
|
152 | + 'preLoginNameUsedAsUserName', |
|
153 | + array('uid' => &$shareWith) |
|
154 | + ); |
|
155 | + $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']); |
|
156 | + |
|
157 | + if (!\OC::$server->getUserManager()->userExists($shareWith)) { |
|
158 | + throw new OCSException('User does not exists', 400); |
|
159 | + } |
|
160 | + |
|
161 | + \OC_Util::setupFS($shareWith); |
|
162 | + |
|
163 | + $externalManager = new \OCA\Files_Sharing\External\Manager( |
|
164 | + \OC::$server->getDatabaseConnection(), |
|
165 | + \OC\Files\Filesystem::getMountManager(), |
|
166 | + \OC\Files\Filesystem::getLoader(), |
|
167 | + \OC::$server->getHTTPClientService(), |
|
168 | + \OC::$server->getNotificationManager(), |
|
169 | + \OC::$server->query(\OCP\OCS\IDiscoveryService::class), |
|
170 | + $shareWith |
|
171 | + ); |
|
172 | + |
|
173 | + try { |
|
174 | + $externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId); |
|
175 | + $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external'); |
|
176 | + |
|
177 | + if ($ownerFederatedId === null) { |
|
178 | + $ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId(); |
|
179 | + } |
|
180 | + // if the owner of the share and the initiator are the same user |
|
181 | + // we also complete the federated share ID for the initiator |
|
182 | + if ($sharedByFederatedId === null && $owner === $sharedBy) { |
|
183 | + $sharedByFederatedId = $ownerFederatedId; |
|
184 | + } |
|
185 | + |
|
186 | + $event = \OC::$server->getActivityManager()->generateEvent(); |
|
187 | + $event->setApp('files_sharing') |
|
188 | + ->setType('remote_share') |
|
189 | + ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')]) |
|
190 | + ->setAffectedUser($shareWith) |
|
191 | + ->setObject('remote_share', (int)$shareId, $name); |
|
192 | + \OC::$server->getActivityManager()->publish($event); |
|
193 | + |
|
194 | + $urlGenerator = \OC::$server->getURLGenerator(); |
|
195 | + |
|
196 | + $notificationManager = \OC::$server->getNotificationManager(); |
|
197 | + $notification = $notificationManager->createNotification(); |
|
198 | + $notification->setApp('files_sharing') |
|
199 | + ->setUser($shareWith) |
|
200 | + ->setDateTime(new \DateTime()) |
|
201 | + ->setObject('remote_share', $shareId) |
|
202 | + ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]); |
|
203 | + |
|
204 | + $declineAction = $notification->createAction(); |
|
205 | + $declineAction->setLabel('decline') |
|
206 | + ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE'); |
|
207 | + $notification->addAction($declineAction); |
|
208 | + |
|
209 | + $acceptAction = $notification->createAction(); |
|
210 | + $acceptAction->setLabel('accept') |
|
211 | + ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST'); |
|
212 | + $notification->addAction($acceptAction); |
|
213 | + |
|
214 | + $notificationManager->notify($notification); |
|
215 | + |
|
216 | + return new Http\DataResponse(); |
|
217 | + } catch (\Exception $e) { |
|
218 | + $this->logger->logException($e, [ |
|
219 | + 'message' => 'Server can not add remote share.', |
|
220 | + 'level' => ILogger::ERROR, |
|
221 | + 'app' => 'files_sharing' |
|
222 | + ]); |
|
223 | + throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); |
|
224 | + } |
|
225 | + } |
|
226 | + |
|
227 | + throw new OCSException('server can not add remote share, missing parameter', 400); |
|
228 | + } |
|
229 | + |
|
230 | + /** |
|
231 | + * @NoCSRFRequired |
|
232 | + * @PublicPage |
|
233 | + * |
|
234 | + * create re-share on behalf of another user |
|
235 | + * |
|
236 | + * @param int $id |
|
237 | + * @return Http\DataResponse |
|
238 | + * @throws OCSBadRequestException |
|
239 | + * @throws OCSForbiddenException |
|
240 | + * @throws OCSNotFoundException |
|
241 | + */ |
|
242 | + public function reShare($id) { |
|
243 | + |
|
244 | + $token = $this->request->getParam('token', null); |
|
245 | + $shareWith = $this->request->getParam('shareWith', null); |
|
246 | + $permission = (int)$this->request->getParam('permission', null); |
|
247 | + $remoteId = (int)$this->request->getParam('remoteId', null); |
|
248 | + |
|
249 | + if ($id === null || |
|
250 | + $token === null || |
|
251 | + $shareWith === null || |
|
252 | + $permission === null || |
|
253 | + $remoteId === null |
|
254 | + ) { |
|
255 | + throw new OCSBadRequestException(); |
|
256 | + } |
|
257 | + |
|
258 | + try { |
|
259 | + $share = $this->federatedShareProvider->getShareById($id); |
|
260 | + } catch (Share\Exceptions\ShareNotFound $e) { |
|
261 | + throw new OCSNotFoundException(); |
|
262 | + } |
|
263 | + |
|
264 | + // don't allow to share a file back to the owner |
|
265 | + list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); |
|
266 | + $owner = $share->getShareOwner(); |
|
267 | + $currentServer = $this->addressHandler->generateRemoteURL(); |
|
268 | + if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) { |
|
269 | + throw new OCSForbiddenException(); |
|
270 | + } |
|
271 | + |
|
272 | + if ($this->verifyShare($share, $token)) { |
|
273 | + |
|
274 | + // check if re-sharing is allowed |
|
275 | + if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) { |
|
276 | + $share->setPermissions($share->getPermissions() & $permission); |
|
277 | + // the recipient of the initial share is now the initiator for the re-share |
|
278 | + $share->setSharedBy($share->getSharedWith()); |
|
279 | + $share->setSharedWith($shareWith); |
|
280 | + try { |
|
281 | + $result = $this->federatedShareProvider->create($share); |
|
282 | + $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId); |
|
283 | + return new Http\DataResponse([ |
|
284 | + 'token' => $result->getToken(), |
|
285 | + 'remoteId' => $result->getId() |
|
286 | + ]); |
|
287 | + } catch (\Exception $e) { |
|
288 | + throw new OCSBadRequestException(); |
|
289 | + } |
|
290 | + } else { |
|
291 | + throw new OCSForbiddenException(); |
|
292 | + } |
|
293 | + } |
|
294 | + throw new OCSBadRequestException(); |
|
295 | + } |
|
296 | + |
|
297 | + /** |
|
298 | + * @NoCSRFRequired |
|
299 | + * @PublicPage |
|
300 | + * |
|
301 | + * accept server-to-server share |
|
302 | + * |
|
303 | + * @param int $id |
|
304 | + * @return Http\DataResponse |
|
305 | + * @throws OCSException |
|
306 | + */ |
|
307 | + public function acceptShare($id) { |
|
308 | + |
|
309 | + if (!$this->isS2SEnabled()) { |
|
310 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
311 | + } |
|
312 | + |
|
313 | + $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
314 | + |
|
315 | + try { |
|
316 | + $share = $this->federatedShareProvider->getShareById($id); |
|
317 | + } catch (Share\Exceptions\ShareNotFound $e) { |
|
318 | + return new Http\DataResponse(); |
|
319 | + } |
|
320 | + |
|
321 | + if ($this->verifyShare($share, $token)) { |
|
322 | + $this->executeAcceptShare($share); |
|
323 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
324 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
325 | + $remoteId = $this->federatedShareProvider->getRemoteId($share); |
|
326 | + $this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken()); |
|
327 | + } |
|
328 | + } |
|
329 | + |
|
330 | + return new Http\DataResponse(); |
|
331 | + } |
|
332 | + |
|
333 | + protected function executeAcceptShare(Share\IShare $share) { |
|
334 | + $fileId = (int) $share->getNode()->getId(); |
|
335 | + list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); |
|
336 | + |
|
337 | + $event = \OC::$server->getActivityManager()->generateEvent(); |
|
338 | + $event->setApp('files_sharing') |
|
339 | + ->setType('remote_share') |
|
340 | + ->setAffectedUser($this->getCorrectUid($share)) |
|
341 | + ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]]) |
|
342 | + ->setObject('files', $fileId, $file) |
|
343 | + ->setLink($link); |
|
344 | + \OC::$server->getActivityManager()->publish($event); |
|
345 | + } |
|
346 | + |
|
347 | + /** |
|
348 | + * @NoCSRFRequired |
|
349 | + * @PublicPage |
|
350 | + * |
|
351 | + * decline server-to-server share |
|
352 | + * |
|
353 | + * @param int $id |
|
354 | + * @return Http\DataResponse |
|
355 | + * @throws OCSException |
|
356 | + */ |
|
357 | + public function declineShare($id) { |
|
358 | + |
|
359 | + if (!$this->isS2SEnabled()) { |
|
360 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
361 | + } |
|
362 | + |
|
363 | + $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
364 | + |
|
365 | + try { |
|
366 | + $share = $this->federatedShareProvider->getShareById($id); |
|
367 | + } catch (Share\Exceptions\ShareNotFound $e) { |
|
368 | + return new Http\DataResponse(); |
|
369 | + } |
|
370 | + |
|
371 | + if ($this->verifyShare($share, $token)) { |
|
372 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
373 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
374 | + $remoteId = $this->federatedShareProvider->getRemoteId($share); |
|
375 | + $this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken()); |
|
376 | + } |
|
377 | + $this->executeDeclineShare($share); |
|
378 | + } |
|
379 | + |
|
380 | + return new Http\DataResponse(); |
|
381 | + } |
|
382 | + |
|
383 | + /** |
|
384 | + * delete declined share and create a activity |
|
385 | + * |
|
386 | + * @param Share\IShare $share |
|
387 | + */ |
|
388 | + protected function executeDeclineShare(Share\IShare $share) { |
|
389 | + $this->federatedShareProvider->removeShareFromTable($share); |
|
390 | + $fileId = (int) $share->getNode()->getId(); |
|
391 | + list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); |
|
392 | + |
|
393 | + $event = \OC::$server->getActivityManager()->generateEvent(); |
|
394 | + $event->setApp('files_sharing') |
|
395 | + ->setType('remote_share') |
|
396 | + ->setAffectedUser($this->getCorrectUid($share)) |
|
397 | + ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]]) |
|
398 | + ->setObject('files', $fileId, $file) |
|
399 | + ->setLink($link); |
|
400 | + \OC::$server->getActivityManager()->publish($event); |
|
401 | + |
|
402 | + } |
|
403 | + |
|
404 | + /** |
|
405 | + * check if we are the initiator or the owner of a re-share and return the correct UID |
|
406 | + * |
|
407 | + * @param Share\IShare $share |
|
408 | + * @return string |
|
409 | + */ |
|
410 | + protected function getCorrectUid(Share\IShare $share) { |
|
411 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
412 | + return $share->getShareOwner(); |
|
413 | + } |
|
414 | + |
|
415 | + return $share->getSharedBy(); |
|
416 | + } |
|
417 | + |
|
418 | + /** |
|
419 | + * @NoCSRFRequired |
|
420 | + * @PublicPage |
|
421 | + * |
|
422 | + * remove server-to-server share if it was unshared by the owner |
|
423 | + * |
|
424 | + * @param int $id |
|
425 | + * @return Http\DataResponse |
|
426 | + * @throws OCSException |
|
427 | + */ |
|
428 | + public function unshare($id) { |
|
429 | + |
|
430 | + if (!$this->isS2SEnabled()) { |
|
431 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
432 | + } |
|
433 | + |
|
434 | + $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
435 | + |
|
436 | + $qb = $this->connection->getQueryBuilder(); |
|
437 | + $qb->select('*') |
|
438 | + ->from('share_external') |
|
439 | + ->where( |
|
440 | + $qb->expr()->andX( |
|
441 | + $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), |
|
442 | + $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) |
|
443 | + ) |
|
444 | + ); |
|
445 | + |
|
446 | + $result = $qb->execute(); |
|
447 | + $share = $result->fetch(); |
|
448 | + $result->closeCursor(); |
|
449 | + |
|
450 | + if ($token && $id && !empty($share)) { |
|
451 | + |
|
452 | + $remote = $this->cleanupRemote($share['remote']); |
|
453 | + |
|
454 | + $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote); |
|
455 | + $mountpoint = $share['mountpoint']; |
|
456 | + $user = $share['user']; |
|
457 | + |
|
458 | + $qb = $this->connection->getQueryBuilder(); |
|
459 | + $qb->delete('share_external') |
|
460 | + ->where( |
|
461 | + $qb->expr()->andX( |
|
462 | + $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), |
|
463 | + $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) |
|
464 | + ) |
|
465 | + ); |
|
466 | + |
|
467 | + $result = $qb->execute(); |
|
468 | + $result->closeCursor(); |
|
469 | + |
|
470 | + if ($share['accepted']) { |
|
471 | + $path = trim($mountpoint, '/'); |
|
472 | + } else { |
|
473 | + $path = trim($share['name'], '/'); |
|
474 | + } |
|
475 | + |
|
476 | + $notificationManager = \OC::$server->getNotificationManager(); |
|
477 | + $notification = $notificationManager->createNotification(); |
|
478 | + $notification->setApp('files_sharing') |
|
479 | + ->setUser($share['user']) |
|
480 | + ->setObject('remote_share', (int)$share['id']); |
|
481 | + $notificationManager->markProcessed($notification); |
|
482 | + |
|
483 | + $event = \OC::$server->getActivityManager()->generateEvent(); |
|
484 | + $event->setApp('files_sharing') |
|
485 | + ->setType('remote_share') |
|
486 | + ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path]) |
|
487 | + ->setAffectedUser($user) |
|
488 | + ->setObject('remote_share', (int)$share['id'], $path); |
|
489 | + \OC::$server->getActivityManager()->publish($event); |
|
490 | + } |
|
491 | + |
|
492 | + return new Http\DataResponse(); |
|
493 | + } |
|
494 | + |
|
495 | + private function cleanupRemote($remote) { |
|
496 | + $remote = substr($remote, strpos($remote, '://') + 3); |
|
497 | + |
|
498 | + return rtrim($remote, '/'); |
|
499 | + } |
|
500 | + |
|
501 | + |
|
502 | + /** |
|
503 | + * @NoCSRFRequired |
|
504 | + * @PublicPage |
|
505 | + * |
|
506 | + * federated share was revoked, either by the owner or the re-sharer |
|
507 | + * |
|
508 | + * @param int $id |
|
509 | + * @return Http\DataResponse |
|
510 | + * @throws OCSBadRequestException |
|
511 | + */ |
|
512 | + public function revoke($id) { |
|
513 | + $token = $this->request->getParam('token'); |
|
514 | + |
|
515 | + $share = $this->federatedShareProvider->getShareById($id); |
|
516 | + |
|
517 | + if ($this->verifyShare($share, $token)) { |
|
518 | + $this->federatedShareProvider->removeShareFromTable($share); |
|
519 | + return new Http\DataResponse(); |
|
520 | + } |
|
521 | + |
|
522 | + throw new OCSBadRequestException(); |
|
523 | + } |
|
524 | + |
|
525 | + /** |
|
526 | + * get share |
|
527 | + * |
|
528 | + * @param int $id |
|
529 | + * @param string $token |
|
530 | + * @return array|bool |
|
531 | + */ |
|
532 | + protected function getShare($id, $token) { |
|
533 | + $query = $this->connection->getQueryBuilder(); |
|
534 | + $query->select('*')->from($this->shareTable) |
|
535 | + ->where($query->expr()->eq('token', $query->createNamedParameter($token))) |
|
536 | + ->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE))) |
|
537 | + ->andWhere($query->expr()->eq('id', $query->createNamedParameter($id))); |
|
538 | + |
|
539 | + $result = $query->execute()->fetchAll(); |
|
540 | + |
|
541 | + if (!empty($result) && isset($result[0])) { |
|
542 | + return $result[0]; |
|
543 | + } |
|
544 | + |
|
545 | + return false; |
|
546 | + } |
|
547 | + |
|
548 | + /** |
|
549 | + * get file |
|
550 | + * |
|
551 | + * @param string $user |
|
552 | + * @param int $fileSource |
|
553 | + * @return array with internal path of the file and a absolute link to it |
|
554 | + */ |
|
555 | + private function getFile($user, $fileSource) { |
|
556 | + \OC_Util::setupFS($user); |
|
557 | + |
|
558 | + try { |
|
559 | + $file = \OC\Files\Filesystem::getPath($fileSource); |
|
560 | + } catch (NotFoundException $e) { |
|
561 | + $file = null; |
|
562 | + } |
|
563 | + $args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file); |
|
564 | + $link = \OCP\Util::linkToAbsolute('files', 'index.php', $args); |
|
565 | + |
|
566 | + return array($file, $link); |
|
567 | + |
|
568 | + } |
|
569 | + |
|
570 | + /** |
|
571 | + * check if server-to-server sharing is enabled |
|
572 | + * |
|
573 | + * @param bool $incoming |
|
574 | + * @return bool |
|
575 | + */ |
|
576 | + private function isS2SEnabled($incoming = false) { |
|
577 | + |
|
578 | + $result = \OCP\App::isEnabled('files_sharing'); |
|
579 | + |
|
580 | + if ($incoming) { |
|
581 | + $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled(); |
|
582 | + } else { |
|
583 | + $result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled(); |
|
584 | + } |
|
585 | + |
|
586 | + return $result; |
|
587 | + } |
|
588 | + |
|
589 | + /** |
|
590 | + * check if we got the right share |
|
591 | + * |
|
592 | + * @param Share\IShare $share |
|
593 | + * @param string $token |
|
594 | + * @return bool |
|
595 | + */ |
|
596 | + protected function verifyShare(Share\IShare $share, $token) { |
|
597 | + if ( |
|
598 | + $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE && |
|
599 | + $share->getToken() === $token |
|
600 | + ) { |
|
601 | + return true; |
|
602 | + } |
|
603 | + |
|
604 | + return false; |
|
605 | + } |
|
606 | + |
|
607 | + /** |
|
608 | + * @NoCSRFRequired |
|
609 | + * @PublicPage |
|
610 | + * |
|
611 | + * update share information to keep federated re-shares in sync |
|
612 | + * |
|
613 | + * @param int $id |
|
614 | + * @return Http\DataResponse |
|
615 | + * @throws OCSBadRequestException |
|
616 | + */ |
|
617 | + public function updatePermissions($id) { |
|
618 | + $token = $this->request->getParam('token', null); |
|
619 | + $permissions = $this->request->getParam('permissions', null); |
|
620 | + |
|
621 | + try { |
|
622 | + $share = $this->federatedShareProvider->getShareById($id); |
|
623 | + } catch (Share\Exceptions\ShareNotFound $e) { |
|
624 | + throw new OCSBadRequestException(); |
|
625 | + } |
|
626 | + |
|
627 | + $validPermission = ctype_digit($permissions); |
|
628 | + $validToken = $this->verifyShare($share, $token); |
|
629 | + if ($validPermission && $validToken) { |
|
630 | + $this->updatePermissionsInDatabase($share, (int)$permissions); |
|
631 | + } else { |
|
632 | + throw new OCSBadRequestException(); |
|
633 | + } |
|
634 | + |
|
635 | + return new Http\DataResponse(); |
|
636 | + } |
|
637 | + |
|
638 | + /** |
|
639 | + * update permissions in database |
|
640 | + * |
|
641 | + * @param IShare $share |
|
642 | + * @param int $permissions |
|
643 | + */ |
|
644 | + protected function updatePermissionsInDatabase(IShare $share, $permissions) { |
|
645 | + $query = $this->connection->getQueryBuilder(); |
|
646 | + $query->update('share') |
|
647 | + ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId()))) |
|
648 | + ->set('permissions', $query->createNamedParameter($permissions)) |
|
649 | + ->execute(); |
|
650 | + } |
|
651 | + |
|
652 | + /** |
|
653 | + * @NoCSRFRequired |
|
654 | + * @PublicPage |
|
655 | + * |
|
656 | + * change the owner of a server-to-server share |
|
657 | + * |
|
658 | + * @param int $id |
|
659 | + * @return Http\DataResponse |
|
660 | + * @throws \InvalidArgumentException |
|
661 | + * @throws OCSException |
|
662 | + */ |
|
663 | + public function move($id) { |
|
664 | + |
|
665 | + if (!$this->isS2SEnabled()) { |
|
666 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
667 | + } |
|
668 | + |
|
669 | + $token = $this->request->getParam('token'); |
|
670 | + $remote = $this->request->getParam('remote'); |
|
671 | + $newRemoteId = $this->request->getParam('remote_id', $id); |
|
672 | + $cloudId = $this->cloudIdManager->resolveCloudId($remote); |
|
673 | + |
|
674 | + $qb = $this->connection->getQueryBuilder(); |
|
675 | + $query = $qb->update('share_external') |
|
676 | + ->set('remote', $qb->createNamedParameter($cloudId->getRemote())) |
|
677 | + ->set('owner', $qb->createNamedParameter($cloudId->getUser())) |
|
678 | + ->set('remote_id', $qb->createNamedParameter($newRemoteId)) |
|
679 | + ->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id))) |
|
680 | + ->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token))); |
|
681 | + $affected = $query->execute(); |
|
682 | + |
|
683 | + if ($affected > 0) { |
|
684 | + return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]); |
|
685 | + } else { |
|
686 | + throw new OCSBadRequestException('Share not found or token invalid'); |
|
687 | + } |
|
688 | + } |
|
689 | 689 | } |
@@ -58,177 +58,177 @@ |
||
58 | 58 | */ |
59 | 59 | class MountPublicLinkController extends Controller { |
60 | 60 | |
61 | - /** @var FederatedShareProvider */ |
|
62 | - private $federatedShareProvider; |
|
63 | - |
|
64 | - /** @var AddressHandler */ |
|
65 | - private $addressHandler; |
|
66 | - |
|
67 | - /** @var IManager */ |
|
68 | - private $shareManager; |
|
69 | - |
|
70 | - /** @var ISession */ |
|
71 | - private $session; |
|
72 | - |
|
73 | - /** @var IL10N */ |
|
74 | - private $l; |
|
75 | - |
|
76 | - /** @var IUserSession */ |
|
77 | - private $userSession; |
|
78 | - |
|
79 | - /** @var IClientService */ |
|
80 | - private $clientService; |
|
81 | - |
|
82 | - /** @var ICloudIdManager */ |
|
83 | - private $cloudIdManager; |
|
84 | - |
|
85 | - /** |
|
86 | - * MountPublicLinkController constructor. |
|
87 | - * |
|
88 | - * @param string $appName |
|
89 | - * @param IRequest $request |
|
90 | - * @param FederatedShareProvider $federatedShareProvider |
|
91 | - * @param IManager $shareManager |
|
92 | - * @param AddressHandler $addressHandler |
|
93 | - * @param ISession $session |
|
94 | - * @param IL10N $l |
|
95 | - * @param IUserSession $userSession |
|
96 | - * @param IClientService $clientService |
|
97 | - * @param ICloudIdManager $cloudIdManager |
|
98 | - */ |
|
99 | - public function __construct($appName, |
|
100 | - IRequest $request, |
|
101 | - FederatedShareProvider $federatedShareProvider, |
|
102 | - IManager $shareManager, |
|
103 | - AddressHandler $addressHandler, |
|
104 | - ISession $session, |
|
105 | - IL10N $l, |
|
106 | - IUserSession $userSession, |
|
107 | - IClientService $clientService, |
|
108 | - ICloudIdManager $cloudIdManager |
|
109 | - ) { |
|
110 | - parent::__construct($appName, $request); |
|
111 | - |
|
112 | - $this->federatedShareProvider = $federatedShareProvider; |
|
113 | - $this->shareManager = $shareManager; |
|
114 | - $this->addressHandler = $addressHandler; |
|
115 | - $this->session = $session; |
|
116 | - $this->l = $l; |
|
117 | - $this->userSession = $userSession; |
|
118 | - $this->clientService = $clientService; |
|
119 | - $this->cloudIdManager = $cloudIdManager; |
|
120 | - } |
|
121 | - |
|
122 | - /** |
|
123 | - * send federated share to a user of a public link |
|
124 | - * |
|
125 | - * @NoCSRFRequired |
|
126 | - * @PublicPage |
|
127 | - * @BruteForceProtection(action=publicLink2FederatedShare) |
|
128 | - * |
|
129 | - * @param string $shareWith |
|
130 | - * @param string $token |
|
131 | - * @param string $password |
|
132 | - * @return JSONResponse |
|
133 | - */ |
|
134 | - public function createFederatedShare($shareWith, $token, $password = '') { |
|
135 | - |
|
136 | - if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) { |
|
137 | - return new JSONResponse( |
|
138 | - ['message' => 'This server doesn\'t support outgoing federated shares'], |
|
139 | - Http::STATUS_BAD_REQUEST |
|
140 | - ); |
|
141 | - } |
|
142 | - |
|
143 | - try { |
|
144 | - list(, $server) = $this->addressHandler->splitUserRemote($shareWith); |
|
145 | - $share = $this->shareManager->getShareByToken($token); |
|
146 | - } catch (HintException $e) { |
|
147 | - return new JSONResponse(['message' => $e->getHint()], Http::STATUS_BAD_REQUEST); |
|
148 | - } |
|
149 | - |
|
150 | - // make sure that user is authenticated in case of a password protected link |
|
151 | - $storedPassword = $share->getPassword(); |
|
152 | - $authenticated = $this->session->get('public_link_authenticated') === $share->getId() || |
|
153 | - $this->shareManager->checkPassword($share, $password); |
|
154 | - if (!empty($storedPassword) && !$authenticated ) { |
|
155 | - $response = new JSONResponse( |
|
156 | - ['message' => 'No permission to access the share'], |
|
157 | - Http::STATUS_BAD_REQUEST |
|
158 | - ); |
|
159 | - $response->throttle(); |
|
160 | - return $response; |
|
161 | - } |
|
162 | - |
|
163 | - $share->setSharedWith($shareWith); |
|
164 | - |
|
165 | - try { |
|
166 | - $this->federatedShareProvider->create($share); |
|
167 | - } catch (\Exception $e) { |
|
168 | - \OC::$server->getLogger()->logException($e, [ |
|
169 | - 'level' => ILogger::WARN, |
|
170 | - 'app' => 'federatedfilesharing', |
|
171 | - ]); |
|
172 | - return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST); |
|
173 | - } |
|
174 | - |
|
175 | - return new JSONResponse(['remoteUrl' => $server]); |
|
176 | - } |
|
177 | - |
|
178 | - /** |
|
179 | - * ask other server to get a federated share |
|
180 | - * |
|
181 | - * @NoAdminRequired |
|
182 | - * |
|
183 | - * @param string $token |
|
184 | - * @param string $remote |
|
185 | - * @param string $password |
|
186 | - * @param string $owner (only for legacy reasons, can be removed with legacyMountPublicLink()) |
|
187 | - * @param string $ownerDisplayName (only for legacy reasons, can be removed with legacyMountPublicLink()) |
|
188 | - * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink()) |
|
189 | - * @return JSONResponse |
|
190 | - */ |
|
191 | - public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') { |
|
192 | - // check if server admin allows to mount public links from other servers |
|
193 | - if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) { |
|
194 | - return new JSONResponse(['message' => $this->l->t('Server to server sharing is not enabled on this server')], Http::STATUS_BAD_REQUEST); |
|
195 | - } |
|
196 | - |
|
197 | - $cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL()); |
|
198 | - |
|
199 | - $httpClient = $this->clientService->newClient(); |
|
200 | - |
|
201 | - try { |
|
202 | - $response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare', |
|
203 | - [ |
|
204 | - 'body' => |
|
205 | - [ |
|
206 | - 'token' => $token, |
|
207 | - 'shareWith' => rtrim($cloudId->getId(), '/'), |
|
208 | - 'password' => $password |
|
209 | - ], |
|
210 | - 'connect_timeout' => 10, |
|
211 | - ] |
|
212 | - ); |
|
213 | - } catch (\Exception $e) { |
|
214 | - if (empty($password)) { |
|
215 | - $message = $this->l->t("Couldn't establish a federated share."); |
|
216 | - } else { |
|
217 | - $message = $this->l->t("Couldn't establish a federated share, maybe the password was wrong."); |
|
218 | - } |
|
219 | - return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST); |
|
220 | - } |
|
221 | - |
|
222 | - $body = $response->getBody(); |
|
223 | - $result = json_decode($body, true); |
|
224 | - |
|
225 | - if (is_array($result) && isset($result['remoteUrl'])) { |
|
226 | - return new JSONResponse(['message' => $this->l->t('Federated Share request sent, you will receive an invitation. Check your notifications.')]); |
|
227 | - } |
|
228 | - |
|
229 | - // if we doesn't get the expected response we assume that we try to add |
|
230 | - // a federated share from a Nextcloud <= 9 server |
|
231 | - $message = $this->l->t("Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9)."); |
|
232 | - return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST); |
|
233 | - } |
|
61 | + /** @var FederatedShareProvider */ |
|
62 | + private $federatedShareProvider; |
|
63 | + |
|
64 | + /** @var AddressHandler */ |
|
65 | + private $addressHandler; |
|
66 | + |
|
67 | + /** @var IManager */ |
|
68 | + private $shareManager; |
|
69 | + |
|
70 | + /** @var ISession */ |
|
71 | + private $session; |
|
72 | + |
|
73 | + /** @var IL10N */ |
|
74 | + private $l; |
|
75 | + |
|
76 | + /** @var IUserSession */ |
|
77 | + private $userSession; |
|
78 | + |
|
79 | + /** @var IClientService */ |
|
80 | + private $clientService; |
|
81 | + |
|
82 | + /** @var ICloudIdManager */ |
|
83 | + private $cloudIdManager; |
|
84 | + |
|
85 | + /** |
|
86 | + * MountPublicLinkController constructor. |
|
87 | + * |
|
88 | + * @param string $appName |
|
89 | + * @param IRequest $request |
|
90 | + * @param FederatedShareProvider $federatedShareProvider |
|
91 | + * @param IManager $shareManager |
|
92 | + * @param AddressHandler $addressHandler |
|
93 | + * @param ISession $session |
|
94 | + * @param IL10N $l |
|
95 | + * @param IUserSession $userSession |
|
96 | + * @param IClientService $clientService |
|
97 | + * @param ICloudIdManager $cloudIdManager |
|
98 | + */ |
|
99 | + public function __construct($appName, |
|
100 | + IRequest $request, |
|
101 | + FederatedShareProvider $federatedShareProvider, |
|
102 | + IManager $shareManager, |
|
103 | + AddressHandler $addressHandler, |
|
104 | + ISession $session, |
|
105 | + IL10N $l, |
|
106 | + IUserSession $userSession, |
|
107 | + IClientService $clientService, |
|
108 | + ICloudIdManager $cloudIdManager |
|
109 | + ) { |
|
110 | + parent::__construct($appName, $request); |
|
111 | + |
|
112 | + $this->federatedShareProvider = $federatedShareProvider; |
|
113 | + $this->shareManager = $shareManager; |
|
114 | + $this->addressHandler = $addressHandler; |
|
115 | + $this->session = $session; |
|
116 | + $this->l = $l; |
|
117 | + $this->userSession = $userSession; |
|
118 | + $this->clientService = $clientService; |
|
119 | + $this->cloudIdManager = $cloudIdManager; |
|
120 | + } |
|
121 | + |
|
122 | + /** |
|
123 | + * send federated share to a user of a public link |
|
124 | + * |
|
125 | + * @NoCSRFRequired |
|
126 | + * @PublicPage |
|
127 | + * @BruteForceProtection(action=publicLink2FederatedShare) |
|
128 | + * |
|
129 | + * @param string $shareWith |
|
130 | + * @param string $token |
|
131 | + * @param string $password |
|
132 | + * @return JSONResponse |
|
133 | + */ |
|
134 | + public function createFederatedShare($shareWith, $token, $password = '') { |
|
135 | + |
|
136 | + if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) { |
|
137 | + return new JSONResponse( |
|
138 | + ['message' => 'This server doesn\'t support outgoing federated shares'], |
|
139 | + Http::STATUS_BAD_REQUEST |
|
140 | + ); |
|
141 | + } |
|
142 | + |
|
143 | + try { |
|
144 | + list(, $server) = $this->addressHandler->splitUserRemote($shareWith); |
|
145 | + $share = $this->shareManager->getShareByToken($token); |
|
146 | + } catch (HintException $e) { |
|
147 | + return new JSONResponse(['message' => $e->getHint()], Http::STATUS_BAD_REQUEST); |
|
148 | + } |
|
149 | + |
|
150 | + // make sure that user is authenticated in case of a password protected link |
|
151 | + $storedPassword = $share->getPassword(); |
|
152 | + $authenticated = $this->session->get('public_link_authenticated') === $share->getId() || |
|
153 | + $this->shareManager->checkPassword($share, $password); |
|
154 | + if (!empty($storedPassword) && !$authenticated ) { |
|
155 | + $response = new JSONResponse( |
|
156 | + ['message' => 'No permission to access the share'], |
|
157 | + Http::STATUS_BAD_REQUEST |
|
158 | + ); |
|
159 | + $response->throttle(); |
|
160 | + return $response; |
|
161 | + } |
|
162 | + |
|
163 | + $share->setSharedWith($shareWith); |
|
164 | + |
|
165 | + try { |
|
166 | + $this->federatedShareProvider->create($share); |
|
167 | + } catch (\Exception $e) { |
|
168 | + \OC::$server->getLogger()->logException($e, [ |
|
169 | + 'level' => ILogger::WARN, |
|
170 | + 'app' => 'federatedfilesharing', |
|
171 | + ]); |
|
172 | + return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST); |
|
173 | + } |
|
174 | + |
|
175 | + return new JSONResponse(['remoteUrl' => $server]); |
|
176 | + } |
|
177 | + |
|
178 | + /** |
|
179 | + * ask other server to get a federated share |
|
180 | + * |
|
181 | + * @NoAdminRequired |
|
182 | + * |
|
183 | + * @param string $token |
|
184 | + * @param string $remote |
|
185 | + * @param string $password |
|
186 | + * @param string $owner (only for legacy reasons, can be removed with legacyMountPublicLink()) |
|
187 | + * @param string $ownerDisplayName (only for legacy reasons, can be removed with legacyMountPublicLink()) |
|
188 | + * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink()) |
|
189 | + * @return JSONResponse |
|
190 | + */ |
|
191 | + public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') { |
|
192 | + // check if server admin allows to mount public links from other servers |
|
193 | + if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) { |
|
194 | + return new JSONResponse(['message' => $this->l->t('Server to server sharing is not enabled on this server')], Http::STATUS_BAD_REQUEST); |
|
195 | + } |
|
196 | + |
|
197 | + $cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL()); |
|
198 | + |
|
199 | + $httpClient = $this->clientService->newClient(); |
|
200 | + |
|
201 | + try { |
|
202 | + $response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare', |
|
203 | + [ |
|
204 | + 'body' => |
|
205 | + [ |
|
206 | + 'token' => $token, |
|
207 | + 'shareWith' => rtrim($cloudId->getId(), '/'), |
|
208 | + 'password' => $password |
|
209 | + ], |
|
210 | + 'connect_timeout' => 10, |
|
211 | + ] |
|
212 | + ); |
|
213 | + } catch (\Exception $e) { |
|
214 | + if (empty($password)) { |
|
215 | + $message = $this->l->t("Couldn't establish a federated share."); |
|
216 | + } else { |
|
217 | + $message = $this->l->t("Couldn't establish a federated share, maybe the password was wrong."); |
|
218 | + } |
|
219 | + return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST); |
|
220 | + } |
|
221 | + |
|
222 | + $body = $response->getBody(); |
|
223 | + $result = json_decode($body, true); |
|
224 | + |
|
225 | + if (is_array($result) && isset($result['remoteUrl'])) { |
|
226 | + return new JSONResponse(['message' => $this->l->t('Federated Share request sent, you will receive an invitation. Check your notifications.')]); |
|
227 | + } |
|
228 | + |
|
229 | + // if we doesn't get the expected response we assume that we try to add |
|
230 | + // a federated share from a Nextcloud <= 9 server |
|
231 | + $message = $this->l->t("Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9)."); |
|
232 | + return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST); |
|
233 | + } |
|
234 | 234 | } |
@@ -53,1036 +53,1036 @@ |
||
53 | 53 | */ |
54 | 54 | class ShareByMailProvider implements IShareProvider { |
55 | 55 | |
56 | - /** @var IDBConnection */ |
|
57 | - private $dbConnection; |
|
58 | - |
|
59 | - /** @var ILogger */ |
|
60 | - private $logger; |
|
61 | - |
|
62 | - /** @var ISecureRandom */ |
|
63 | - private $secureRandom; |
|
64 | - |
|
65 | - /** @var IUserManager */ |
|
66 | - private $userManager; |
|
67 | - |
|
68 | - /** @var IRootFolder */ |
|
69 | - private $rootFolder; |
|
70 | - |
|
71 | - /** @var IL10N */ |
|
72 | - private $l; |
|
73 | - |
|
74 | - /** @var IMailer */ |
|
75 | - private $mailer; |
|
76 | - |
|
77 | - /** @var IURLGenerator */ |
|
78 | - private $urlGenerator; |
|
79 | - |
|
80 | - /** @var IManager */ |
|
81 | - private $activityManager; |
|
82 | - |
|
83 | - /** @var SettingsManager */ |
|
84 | - private $settingsManager; |
|
85 | - |
|
86 | - /** @var Defaults */ |
|
87 | - private $defaults; |
|
88 | - |
|
89 | - /** @var IHasher */ |
|
90 | - private $hasher; |
|
91 | - |
|
92 | - /** @var CapabilitiesManager */ |
|
93 | - private $capabilitiesManager; |
|
94 | - |
|
95 | - /** |
|
96 | - * Return the identifier of this provider. |
|
97 | - * |
|
98 | - * @return string Containing only [a-zA-Z0-9] |
|
99 | - */ |
|
100 | - public function identifier() { |
|
101 | - return 'ocMailShare'; |
|
102 | - } |
|
103 | - |
|
104 | - /** |
|
105 | - * DefaultShareProvider constructor. |
|
106 | - * |
|
107 | - * @param IDBConnection $connection |
|
108 | - * @param ISecureRandom $secureRandom |
|
109 | - * @param IUserManager $userManager |
|
110 | - * @param IRootFolder $rootFolder |
|
111 | - * @param IL10N $l |
|
112 | - * @param ILogger $logger |
|
113 | - * @param IMailer $mailer |
|
114 | - * @param IURLGenerator $urlGenerator |
|
115 | - * @param IManager $activityManager |
|
116 | - * @param SettingsManager $settingsManager |
|
117 | - * @param Defaults $defaults |
|
118 | - * @param IHasher $hasher |
|
119 | - * @param CapabilitiesManager $capabilitiesManager |
|
120 | - */ |
|
121 | - public function __construct( |
|
122 | - IDBConnection $connection, |
|
123 | - ISecureRandom $secureRandom, |
|
124 | - IUserManager $userManager, |
|
125 | - IRootFolder $rootFolder, |
|
126 | - IL10N $l, |
|
127 | - ILogger $logger, |
|
128 | - IMailer $mailer, |
|
129 | - IURLGenerator $urlGenerator, |
|
130 | - IManager $activityManager, |
|
131 | - SettingsManager $settingsManager, |
|
132 | - Defaults $defaults, |
|
133 | - IHasher $hasher, |
|
134 | - CapabilitiesManager $capabilitiesManager |
|
135 | - ) { |
|
136 | - $this->dbConnection = $connection; |
|
137 | - $this->secureRandom = $secureRandom; |
|
138 | - $this->userManager = $userManager; |
|
139 | - $this->rootFolder = $rootFolder; |
|
140 | - $this->l = $l; |
|
141 | - $this->logger = $logger; |
|
142 | - $this->mailer = $mailer; |
|
143 | - $this->urlGenerator = $urlGenerator; |
|
144 | - $this->activityManager = $activityManager; |
|
145 | - $this->settingsManager = $settingsManager; |
|
146 | - $this->defaults = $defaults; |
|
147 | - $this->hasher = $hasher; |
|
148 | - $this->capabilitiesManager = $capabilitiesManager; |
|
149 | - } |
|
150 | - |
|
151 | - /** |
|
152 | - * Share a path |
|
153 | - * |
|
154 | - * @param IShare $share |
|
155 | - * @return IShare The share object |
|
156 | - * @throws ShareNotFound |
|
157 | - * @throws \Exception |
|
158 | - */ |
|
159 | - public function create(IShare $share) { |
|
160 | - |
|
161 | - $shareWith = $share->getSharedWith(); |
|
162 | - /* |
|
56 | + /** @var IDBConnection */ |
|
57 | + private $dbConnection; |
|
58 | + |
|
59 | + /** @var ILogger */ |
|
60 | + private $logger; |
|
61 | + |
|
62 | + /** @var ISecureRandom */ |
|
63 | + private $secureRandom; |
|
64 | + |
|
65 | + /** @var IUserManager */ |
|
66 | + private $userManager; |
|
67 | + |
|
68 | + /** @var IRootFolder */ |
|
69 | + private $rootFolder; |
|
70 | + |
|
71 | + /** @var IL10N */ |
|
72 | + private $l; |
|
73 | + |
|
74 | + /** @var IMailer */ |
|
75 | + private $mailer; |
|
76 | + |
|
77 | + /** @var IURLGenerator */ |
|
78 | + private $urlGenerator; |
|
79 | + |
|
80 | + /** @var IManager */ |
|
81 | + private $activityManager; |
|
82 | + |
|
83 | + /** @var SettingsManager */ |
|
84 | + private $settingsManager; |
|
85 | + |
|
86 | + /** @var Defaults */ |
|
87 | + private $defaults; |
|
88 | + |
|
89 | + /** @var IHasher */ |
|
90 | + private $hasher; |
|
91 | + |
|
92 | + /** @var CapabilitiesManager */ |
|
93 | + private $capabilitiesManager; |
|
94 | + |
|
95 | + /** |
|
96 | + * Return the identifier of this provider. |
|
97 | + * |
|
98 | + * @return string Containing only [a-zA-Z0-9] |
|
99 | + */ |
|
100 | + public function identifier() { |
|
101 | + return 'ocMailShare'; |
|
102 | + } |
|
103 | + |
|
104 | + /** |
|
105 | + * DefaultShareProvider constructor. |
|
106 | + * |
|
107 | + * @param IDBConnection $connection |
|
108 | + * @param ISecureRandom $secureRandom |
|
109 | + * @param IUserManager $userManager |
|
110 | + * @param IRootFolder $rootFolder |
|
111 | + * @param IL10N $l |
|
112 | + * @param ILogger $logger |
|
113 | + * @param IMailer $mailer |
|
114 | + * @param IURLGenerator $urlGenerator |
|
115 | + * @param IManager $activityManager |
|
116 | + * @param SettingsManager $settingsManager |
|
117 | + * @param Defaults $defaults |
|
118 | + * @param IHasher $hasher |
|
119 | + * @param CapabilitiesManager $capabilitiesManager |
|
120 | + */ |
|
121 | + public function __construct( |
|
122 | + IDBConnection $connection, |
|
123 | + ISecureRandom $secureRandom, |
|
124 | + IUserManager $userManager, |
|
125 | + IRootFolder $rootFolder, |
|
126 | + IL10N $l, |
|
127 | + ILogger $logger, |
|
128 | + IMailer $mailer, |
|
129 | + IURLGenerator $urlGenerator, |
|
130 | + IManager $activityManager, |
|
131 | + SettingsManager $settingsManager, |
|
132 | + Defaults $defaults, |
|
133 | + IHasher $hasher, |
|
134 | + CapabilitiesManager $capabilitiesManager |
|
135 | + ) { |
|
136 | + $this->dbConnection = $connection; |
|
137 | + $this->secureRandom = $secureRandom; |
|
138 | + $this->userManager = $userManager; |
|
139 | + $this->rootFolder = $rootFolder; |
|
140 | + $this->l = $l; |
|
141 | + $this->logger = $logger; |
|
142 | + $this->mailer = $mailer; |
|
143 | + $this->urlGenerator = $urlGenerator; |
|
144 | + $this->activityManager = $activityManager; |
|
145 | + $this->settingsManager = $settingsManager; |
|
146 | + $this->defaults = $defaults; |
|
147 | + $this->hasher = $hasher; |
|
148 | + $this->capabilitiesManager = $capabilitiesManager; |
|
149 | + } |
|
150 | + |
|
151 | + /** |
|
152 | + * Share a path |
|
153 | + * |
|
154 | + * @param IShare $share |
|
155 | + * @return IShare The share object |
|
156 | + * @throws ShareNotFound |
|
157 | + * @throws \Exception |
|
158 | + */ |
|
159 | + public function create(IShare $share) { |
|
160 | + |
|
161 | + $shareWith = $share->getSharedWith(); |
|
162 | + /* |
|
163 | 163 | * Check if file is not already shared with the remote user |
164 | 164 | */ |
165 | - $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); |
|
166 | - if (!empty($alreadyShared)) { |
|
167 | - $message = 'Sharing %s failed, this item is already shared with %s'; |
|
168 | - $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
169 | - $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
170 | - throw new \Exception($message_t); |
|
171 | - } |
|
172 | - |
|
173 | - // if the admin enforces a password for all mail shares we create a |
|
174 | - // random password and send it to the recipient |
|
175 | - $password = ''; |
|
176 | - $passwordEnforced = $this->settingsManager->enforcePasswordProtection(); |
|
177 | - if ($passwordEnforced) { |
|
178 | - $password = $this->autoGeneratePassword($share); |
|
179 | - } |
|
180 | - |
|
181 | - $shareId = $this->createMailShare($share); |
|
182 | - $send = $this->sendPassword($share, $password); |
|
183 | - if ($passwordEnforced && $send === false) { |
|
184 | - $this->sendPasswordToOwner($share, $password); |
|
185 | - } |
|
186 | - |
|
187 | - $this->createShareActivity($share); |
|
188 | - $data = $this->getRawShare($shareId); |
|
189 | - |
|
190 | - return $this->createShareObject($data); |
|
191 | - |
|
192 | - } |
|
193 | - |
|
194 | - /** |
|
195 | - * auto generate password in case of password enforcement on mail shares |
|
196 | - * |
|
197 | - * @param IShare $share |
|
198 | - * @return string |
|
199 | - * @throws \Exception |
|
200 | - */ |
|
201 | - protected function autoGeneratePassword($share) { |
|
202 | - $initiatorUser = $this->userManager->get($share->getSharedBy()); |
|
203 | - $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
204 | - $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); |
|
205 | - |
|
206 | - if ($initiatorEMailAddress === null && !$allowPasswordByMail) { |
|
207 | - throw new \Exception( |
|
208 | - $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
209 | - ); |
|
210 | - } |
|
211 | - |
|
212 | - $passwordPolicy = $this->getPasswordPolicy(); |
|
213 | - $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS; |
|
214 | - $passwordLength = 8; |
|
215 | - if (!empty($passwordPolicy)) { |
|
216 | - $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength; |
|
217 | - $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : ''; |
|
218 | - } |
|
219 | - |
|
220 | - $password = $this->secureRandom->generate($passwordLength, $passwordCharset); |
|
221 | - |
|
222 | - $share->setPassword($this->hasher->hash($password)); |
|
223 | - |
|
224 | - return $password; |
|
225 | - } |
|
226 | - |
|
227 | - /** |
|
228 | - * get password policy |
|
229 | - * |
|
230 | - * @return array |
|
231 | - */ |
|
232 | - protected function getPasswordPolicy() { |
|
233 | - $capabilities = $this->capabilitiesManager->getCapabilities(); |
|
234 | - if (isset($capabilities['password_policy'])) { |
|
235 | - return $capabilities['password_policy']; |
|
236 | - } |
|
237 | - |
|
238 | - return []; |
|
239 | - } |
|
240 | - |
|
241 | - /** |
|
242 | - * create activity if a file/folder was shared by mail |
|
243 | - * |
|
244 | - * @param IShare $share |
|
245 | - */ |
|
246 | - protected function createShareActivity(IShare $share) { |
|
247 | - |
|
248 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
249 | - |
|
250 | - $this->publishActivity( |
|
251 | - Activity::SUBJECT_SHARED_EMAIL_SELF, |
|
252 | - [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
|
253 | - $share->getSharedBy(), |
|
254 | - $share->getNode()->getId(), |
|
255 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
256 | - ); |
|
257 | - |
|
258 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
259 | - $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
260 | - $fileId = $share->getNode()->getId(); |
|
261 | - $nodes = $ownerFolder->getById($fileId); |
|
262 | - $ownerPath = $nodes[0]->getPath(); |
|
263 | - $this->publishActivity( |
|
264 | - Activity::SUBJECT_SHARED_EMAIL_BY, |
|
265 | - [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
|
266 | - $share->getShareOwner(), |
|
267 | - $fileId, |
|
268 | - $ownerFolder->getRelativePath($ownerPath) |
|
269 | - ); |
|
270 | - } |
|
271 | - |
|
272 | - } |
|
273 | - |
|
274 | - /** |
|
275 | - * create activity if a file/folder was shared by mail |
|
276 | - * |
|
277 | - * @param IShare $share |
|
278 | - * @param string $sharedWith |
|
279 | - * @param bool $sendToSelf |
|
280 | - */ |
|
281 | - protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) { |
|
282 | - |
|
283 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
284 | - |
|
285 | - if ($sendToSelf) { |
|
286 | - $this->publishActivity( |
|
287 | - Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, |
|
288 | - [$userFolder->getRelativePath($share->getNode()->getPath())], |
|
289 | - $share->getSharedBy(), |
|
290 | - $share->getNode()->getId(), |
|
291 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
292 | - ); |
|
293 | - } else { |
|
294 | - $this->publishActivity( |
|
295 | - Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, |
|
296 | - [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
|
297 | - $share->getSharedBy(), |
|
298 | - $share->getNode()->getId(), |
|
299 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
300 | - ); |
|
301 | - } |
|
302 | - } |
|
303 | - |
|
304 | - |
|
305 | - /** |
|
306 | - * publish activity if a file/folder was shared by mail |
|
307 | - * |
|
308 | - * @param $subject |
|
309 | - * @param $parameters |
|
310 | - * @param $affectedUser |
|
311 | - * @param $fileId |
|
312 | - * @param $filePath |
|
313 | - */ |
|
314 | - protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) { |
|
315 | - $event = $this->activityManager->generateEvent(); |
|
316 | - $event->setApp('sharebymail') |
|
317 | - ->setType('shared') |
|
318 | - ->setSubject($subject, $parameters) |
|
319 | - ->setAffectedUser($affectedUser) |
|
320 | - ->setObject('files', $fileId, $filePath); |
|
321 | - $this->activityManager->publish($event); |
|
322 | - |
|
323 | - } |
|
324 | - |
|
325 | - /** |
|
326 | - * @param IShare $share |
|
327 | - * @return int |
|
328 | - * @throws \Exception |
|
329 | - */ |
|
330 | - protected function createMailShare(IShare $share) { |
|
331 | - $share->setToken($this->generateToken()); |
|
332 | - $shareId = $this->addShareToDB( |
|
333 | - $share->getNodeId(), |
|
334 | - $share->getNodeType(), |
|
335 | - $share->getSharedWith(), |
|
336 | - $share->getSharedBy(), |
|
337 | - $share->getShareOwner(), |
|
338 | - $share->getPermissions(), |
|
339 | - $share->getToken(), |
|
340 | - $share->getPassword() |
|
341 | - ); |
|
342 | - |
|
343 | - try { |
|
344 | - $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', |
|
345 | - ['token' => $share->getToken()]); |
|
346 | - $this->sendMailNotification( |
|
347 | - $share->getNode()->getName(), |
|
348 | - $link, |
|
349 | - $share->getSharedBy(), |
|
350 | - $share->getSharedWith(), |
|
351 | - $share->getExpirationDate() |
|
352 | - ); |
|
353 | - } catch (HintException $hintException) { |
|
354 | - $this->logger->logException($hintException, [ |
|
355 | - 'message' => 'Failed to send share by mail.', |
|
356 | - 'level' => ILogger::ERROR, |
|
357 | - 'app' => 'sharebymail', |
|
358 | - ]); |
|
359 | - $this->removeShareFromTable($shareId); |
|
360 | - throw $hintException; |
|
361 | - } catch (\Exception $e) { |
|
362 | - $this->logger->logException($e, [ |
|
363 | - 'message' => 'Failed to send share by mail.', |
|
364 | - 'level' => ILogger::ERROR, |
|
365 | - 'app' => 'sharebymail', |
|
366 | - ]); |
|
367 | - $this->removeShareFromTable($shareId); |
|
368 | - throw new HintException('Failed to send share by mail', |
|
369 | - $this->l->t('Failed to send share by email')); |
|
370 | - } |
|
371 | - |
|
372 | - return $shareId; |
|
373 | - |
|
374 | - } |
|
375 | - |
|
376 | - /** |
|
377 | - * @param string $filename |
|
378 | - * @param string $link |
|
379 | - * @param string $initiator |
|
380 | - * @param string $shareWith |
|
381 | - * @param \DateTime|null $expiration |
|
382 | - * @throws \Exception If mail couldn't be sent |
|
383 | - */ |
|
384 | - protected function sendMailNotification($filename, |
|
385 | - $link, |
|
386 | - $initiator, |
|
387 | - $shareWith, |
|
388 | - \DateTime $expiration = null) { |
|
389 | - $initiatorUser = $this->userManager->get($initiator); |
|
390 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
391 | - $message = $this->mailer->createMessage(); |
|
392 | - |
|
393 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [ |
|
394 | - 'filename' => $filename, |
|
395 | - 'link' => $link, |
|
396 | - 'initiator' => $initiatorDisplayName, |
|
397 | - 'expiration' => $expiration, |
|
398 | - 'shareWith' => $shareWith, |
|
399 | - ]); |
|
400 | - |
|
401 | - $emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename))); |
|
402 | - $emailTemplate->addHeader(); |
|
403 | - $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false); |
|
404 | - $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]); |
|
405 | - |
|
406 | - $emailTemplate->addBodyText( |
|
407 | - htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')), |
|
408 | - $text |
|
409 | - ); |
|
410 | - $emailTemplate->addBodyButton( |
|
411 | - $this->l->t('Open »%s«', [$filename]), |
|
412 | - $link |
|
413 | - ); |
|
414 | - |
|
415 | - $message->setTo([$shareWith]); |
|
416 | - |
|
417 | - // The "From" contains the sharers name |
|
418 | - $instanceName = $this->defaults->getName(); |
|
419 | - $senderName = $this->l->t( |
|
420 | - '%s via %s', |
|
421 | - [ |
|
422 | - $initiatorDisplayName, |
|
423 | - $instanceName |
|
424 | - ] |
|
425 | - ); |
|
426 | - $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
427 | - |
|
428 | - // The "Reply-To" is set to the sharer if an mail address is configured |
|
429 | - // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
430 | - $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
431 | - if($initiatorEmail !== null) { |
|
432 | - $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
433 | - $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
434 | - } else { |
|
435 | - $emailTemplate->addFooter(); |
|
436 | - } |
|
437 | - |
|
438 | - $message->useTemplate($emailTemplate); |
|
439 | - $this->mailer->send($message); |
|
440 | - } |
|
441 | - |
|
442 | - /** |
|
443 | - * send password to recipient of a mail share |
|
444 | - * |
|
445 | - * @param IShare $share |
|
446 | - * @param string $password |
|
447 | - * @return bool |
|
448 | - */ |
|
449 | - protected function sendPassword(IShare $share, $password) { |
|
450 | - |
|
451 | - $filename = $share->getNode()->getName(); |
|
452 | - $initiator = $share->getSharedBy(); |
|
453 | - $shareWith = $share->getSharedWith(); |
|
454 | - |
|
455 | - if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) { |
|
456 | - return false; |
|
457 | - } |
|
458 | - |
|
459 | - $initiatorUser = $this->userManager->get($initiator); |
|
460 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
461 | - $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
462 | - |
|
463 | - $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]); |
|
464 | - $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
465 | - |
|
466 | - $message = $this->mailer->createMessage(); |
|
467 | - |
|
468 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [ |
|
469 | - 'filename' => $filename, |
|
470 | - 'password' => $password, |
|
471 | - 'initiator' => $initiatorDisplayName, |
|
472 | - 'initiatorEmail' => $initiatorEmailAddress, |
|
473 | - 'shareWith' => $shareWith, |
|
474 | - ]); |
|
475 | - |
|
476 | - $emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName])); |
|
477 | - $emailTemplate->addHeader(); |
|
478 | - $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
479 | - $emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart); |
|
480 | - $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password])); |
|
481 | - |
|
482 | - // The "From" contains the sharers name |
|
483 | - $instanceName = $this->defaults->getName(); |
|
484 | - $senderName = $this->l->t( |
|
485 | - '%s via %s', |
|
486 | - [ |
|
487 | - $initiatorDisplayName, |
|
488 | - $instanceName |
|
489 | - ] |
|
490 | - ); |
|
491 | - $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
492 | - if ($initiatorEmailAddress !== null) { |
|
493 | - $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
|
494 | - $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
495 | - } else { |
|
496 | - $emailTemplate->addFooter(); |
|
497 | - } |
|
498 | - |
|
499 | - $message->setTo([$shareWith]); |
|
500 | - $message->useTemplate($emailTemplate); |
|
501 | - $this->mailer->send($message); |
|
502 | - |
|
503 | - $this->createPasswordSendActivity($share, $shareWith, false); |
|
504 | - |
|
505 | - return true; |
|
506 | - } |
|
507 | - |
|
508 | - /** |
|
509 | - * send auto generated password to the owner. This happens if the admin enforces |
|
510 | - * a password for mail shares and forbid to send the password by mail to the recipient |
|
511 | - * |
|
512 | - * @param IShare $share |
|
513 | - * @param string $password |
|
514 | - * @return bool |
|
515 | - * @throws \Exception |
|
516 | - */ |
|
517 | - protected function sendPasswordToOwner(IShare $share, $password) { |
|
518 | - |
|
519 | - $filename = $share->getNode()->getName(); |
|
520 | - $initiator = $this->userManager->get($share->getSharedBy()); |
|
521 | - $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; |
|
522 | - $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); |
|
523 | - $shareWith = $share->getSharedWith(); |
|
524 | - |
|
525 | - if ($initiatorEMailAddress === null) { |
|
526 | - throw new \Exception( |
|
527 | - $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
528 | - ); |
|
529 | - } |
|
530 | - |
|
531 | - $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); |
|
532 | - |
|
533 | - $message = $this->mailer->createMessage(); |
|
534 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [ |
|
535 | - 'filename' => $filename, |
|
536 | - 'password' => $password, |
|
537 | - 'initiator' => $initiatorDisplayName, |
|
538 | - 'initiatorEmail' => $initiatorEMailAddress, |
|
539 | - 'shareWith' => $shareWith, |
|
540 | - ]); |
|
541 | - |
|
542 | - $emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith])); |
|
543 | - $emailTemplate->addHeader(); |
|
544 | - $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
545 | - $emailTemplate->addBodyText($bodyPart); |
|
546 | - $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password])); |
|
547 | - $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); |
|
548 | - $emailTemplate->addFooter(); |
|
549 | - |
|
550 | - if ($initiatorEMailAddress) { |
|
551 | - $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]); |
|
552 | - } |
|
553 | - $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); |
|
554 | - $message->useTemplate($emailTemplate); |
|
555 | - $this->mailer->send($message); |
|
556 | - |
|
557 | - $this->createPasswordSendActivity($share, $shareWith, true); |
|
558 | - |
|
559 | - return true; |
|
560 | - } |
|
561 | - |
|
562 | - /** |
|
563 | - * generate share token |
|
564 | - * |
|
565 | - * @return string |
|
566 | - */ |
|
567 | - protected function generateToken($size = 15) { |
|
568 | - $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE); |
|
569 | - return $token; |
|
570 | - } |
|
571 | - |
|
572 | - /** |
|
573 | - * Get all children of this share |
|
574 | - * |
|
575 | - * @param IShare $parent |
|
576 | - * @return IShare[] |
|
577 | - */ |
|
578 | - public function getChildren(IShare $parent) { |
|
579 | - $children = []; |
|
580 | - |
|
581 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
582 | - $qb->select('*') |
|
583 | - ->from('share') |
|
584 | - ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
585 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
586 | - ->orderBy('id'); |
|
587 | - |
|
588 | - $cursor = $qb->execute(); |
|
589 | - while($data = $cursor->fetch()) { |
|
590 | - $children[] = $this->createShareObject($data); |
|
591 | - } |
|
592 | - $cursor->closeCursor(); |
|
593 | - |
|
594 | - return $children; |
|
595 | - } |
|
596 | - |
|
597 | - /** |
|
598 | - * add share to the database and return the ID |
|
599 | - * |
|
600 | - * @param int $itemSource |
|
601 | - * @param string $itemType |
|
602 | - * @param string $shareWith |
|
603 | - * @param string $sharedBy |
|
604 | - * @param string $uidOwner |
|
605 | - * @param int $permissions |
|
606 | - * @param string $token |
|
607 | - * @return int |
|
608 | - */ |
|
609 | - protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) { |
|
610 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
611 | - $qb->insert('share') |
|
612 | - ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
613 | - ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
614 | - ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
615 | - ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
616 | - ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
617 | - ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
618 | - ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
619 | - ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
620 | - ->setValue('token', $qb->createNamedParameter($token)) |
|
621 | - ->setValue('password', $qb->createNamedParameter($password)) |
|
622 | - ->setValue('stime', $qb->createNamedParameter(time())); |
|
623 | - |
|
624 | - /* |
|
165 | + $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); |
|
166 | + if (!empty($alreadyShared)) { |
|
167 | + $message = 'Sharing %s failed, this item is already shared with %s'; |
|
168 | + $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
169 | + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
170 | + throw new \Exception($message_t); |
|
171 | + } |
|
172 | + |
|
173 | + // if the admin enforces a password for all mail shares we create a |
|
174 | + // random password and send it to the recipient |
|
175 | + $password = ''; |
|
176 | + $passwordEnforced = $this->settingsManager->enforcePasswordProtection(); |
|
177 | + if ($passwordEnforced) { |
|
178 | + $password = $this->autoGeneratePassword($share); |
|
179 | + } |
|
180 | + |
|
181 | + $shareId = $this->createMailShare($share); |
|
182 | + $send = $this->sendPassword($share, $password); |
|
183 | + if ($passwordEnforced && $send === false) { |
|
184 | + $this->sendPasswordToOwner($share, $password); |
|
185 | + } |
|
186 | + |
|
187 | + $this->createShareActivity($share); |
|
188 | + $data = $this->getRawShare($shareId); |
|
189 | + |
|
190 | + return $this->createShareObject($data); |
|
191 | + |
|
192 | + } |
|
193 | + |
|
194 | + /** |
|
195 | + * auto generate password in case of password enforcement on mail shares |
|
196 | + * |
|
197 | + * @param IShare $share |
|
198 | + * @return string |
|
199 | + * @throws \Exception |
|
200 | + */ |
|
201 | + protected function autoGeneratePassword($share) { |
|
202 | + $initiatorUser = $this->userManager->get($share->getSharedBy()); |
|
203 | + $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
204 | + $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); |
|
205 | + |
|
206 | + if ($initiatorEMailAddress === null && !$allowPasswordByMail) { |
|
207 | + throw new \Exception( |
|
208 | + $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
209 | + ); |
|
210 | + } |
|
211 | + |
|
212 | + $passwordPolicy = $this->getPasswordPolicy(); |
|
213 | + $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS; |
|
214 | + $passwordLength = 8; |
|
215 | + if (!empty($passwordPolicy)) { |
|
216 | + $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength; |
|
217 | + $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : ''; |
|
218 | + } |
|
219 | + |
|
220 | + $password = $this->secureRandom->generate($passwordLength, $passwordCharset); |
|
221 | + |
|
222 | + $share->setPassword($this->hasher->hash($password)); |
|
223 | + |
|
224 | + return $password; |
|
225 | + } |
|
226 | + |
|
227 | + /** |
|
228 | + * get password policy |
|
229 | + * |
|
230 | + * @return array |
|
231 | + */ |
|
232 | + protected function getPasswordPolicy() { |
|
233 | + $capabilities = $this->capabilitiesManager->getCapabilities(); |
|
234 | + if (isset($capabilities['password_policy'])) { |
|
235 | + return $capabilities['password_policy']; |
|
236 | + } |
|
237 | + |
|
238 | + return []; |
|
239 | + } |
|
240 | + |
|
241 | + /** |
|
242 | + * create activity if a file/folder was shared by mail |
|
243 | + * |
|
244 | + * @param IShare $share |
|
245 | + */ |
|
246 | + protected function createShareActivity(IShare $share) { |
|
247 | + |
|
248 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
249 | + |
|
250 | + $this->publishActivity( |
|
251 | + Activity::SUBJECT_SHARED_EMAIL_SELF, |
|
252 | + [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
|
253 | + $share->getSharedBy(), |
|
254 | + $share->getNode()->getId(), |
|
255 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
256 | + ); |
|
257 | + |
|
258 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
259 | + $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
260 | + $fileId = $share->getNode()->getId(); |
|
261 | + $nodes = $ownerFolder->getById($fileId); |
|
262 | + $ownerPath = $nodes[0]->getPath(); |
|
263 | + $this->publishActivity( |
|
264 | + Activity::SUBJECT_SHARED_EMAIL_BY, |
|
265 | + [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
|
266 | + $share->getShareOwner(), |
|
267 | + $fileId, |
|
268 | + $ownerFolder->getRelativePath($ownerPath) |
|
269 | + ); |
|
270 | + } |
|
271 | + |
|
272 | + } |
|
273 | + |
|
274 | + /** |
|
275 | + * create activity if a file/folder was shared by mail |
|
276 | + * |
|
277 | + * @param IShare $share |
|
278 | + * @param string $sharedWith |
|
279 | + * @param bool $sendToSelf |
|
280 | + */ |
|
281 | + protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) { |
|
282 | + |
|
283 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
284 | + |
|
285 | + if ($sendToSelf) { |
|
286 | + $this->publishActivity( |
|
287 | + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, |
|
288 | + [$userFolder->getRelativePath($share->getNode()->getPath())], |
|
289 | + $share->getSharedBy(), |
|
290 | + $share->getNode()->getId(), |
|
291 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
292 | + ); |
|
293 | + } else { |
|
294 | + $this->publishActivity( |
|
295 | + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, |
|
296 | + [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
|
297 | + $share->getSharedBy(), |
|
298 | + $share->getNode()->getId(), |
|
299 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
300 | + ); |
|
301 | + } |
|
302 | + } |
|
303 | + |
|
304 | + |
|
305 | + /** |
|
306 | + * publish activity if a file/folder was shared by mail |
|
307 | + * |
|
308 | + * @param $subject |
|
309 | + * @param $parameters |
|
310 | + * @param $affectedUser |
|
311 | + * @param $fileId |
|
312 | + * @param $filePath |
|
313 | + */ |
|
314 | + protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) { |
|
315 | + $event = $this->activityManager->generateEvent(); |
|
316 | + $event->setApp('sharebymail') |
|
317 | + ->setType('shared') |
|
318 | + ->setSubject($subject, $parameters) |
|
319 | + ->setAffectedUser($affectedUser) |
|
320 | + ->setObject('files', $fileId, $filePath); |
|
321 | + $this->activityManager->publish($event); |
|
322 | + |
|
323 | + } |
|
324 | + |
|
325 | + /** |
|
326 | + * @param IShare $share |
|
327 | + * @return int |
|
328 | + * @throws \Exception |
|
329 | + */ |
|
330 | + protected function createMailShare(IShare $share) { |
|
331 | + $share->setToken($this->generateToken()); |
|
332 | + $shareId = $this->addShareToDB( |
|
333 | + $share->getNodeId(), |
|
334 | + $share->getNodeType(), |
|
335 | + $share->getSharedWith(), |
|
336 | + $share->getSharedBy(), |
|
337 | + $share->getShareOwner(), |
|
338 | + $share->getPermissions(), |
|
339 | + $share->getToken(), |
|
340 | + $share->getPassword() |
|
341 | + ); |
|
342 | + |
|
343 | + try { |
|
344 | + $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', |
|
345 | + ['token' => $share->getToken()]); |
|
346 | + $this->sendMailNotification( |
|
347 | + $share->getNode()->getName(), |
|
348 | + $link, |
|
349 | + $share->getSharedBy(), |
|
350 | + $share->getSharedWith(), |
|
351 | + $share->getExpirationDate() |
|
352 | + ); |
|
353 | + } catch (HintException $hintException) { |
|
354 | + $this->logger->logException($hintException, [ |
|
355 | + 'message' => 'Failed to send share by mail.', |
|
356 | + 'level' => ILogger::ERROR, |
|
357 | + 'app' => 'sharebymail', |
|
358 | + ]); |
|
359 | + $this->removeShareFromTable($shareId); |
|
360 | + throw $hintException; |
|
361 | + } catch (\Exception $e) { |
|
362 | + $this->logger->logException($e, [ |
|
363 | + 'message' => 'Failed to send share by mail.', |
|
364 | + 'level' => ILogger::ERROR, |
|
365 | + 'app' => 'sharebymail', |
|
366 | + ]); |
|
367 | + $this->removeShareFromTable($shareId); |
|
368 | + throw new HintException('Failed to send share by mail', |
|
369 | + $this->l->t('Failed to send share by email')); |
|
370 | + } |
|
371 | + |
|
372 | + return $shareId; |
|
373 | + |
|
374 | + } |
|
375 | + |
|
376 | + /** |
|
377 | + * @param string $filename |
|
378 | + * @param string $link |
|
379 | + * @param string $initiator |
|
380 | + * @param string $shareWith |
|
381 | + * @param \DateTime|null $expiration |
|
382 | + * @throws \Exception If mail couldn't be sent |
|
383 | + */ |
|
384 | + protected function sendMailNotification($filename, |
|
385 | + $link, |
|
386 | + $initiator, |
|
387 | + $shareWith, |
|
388 | + \DateTime $expiration = null) { |
|
389 | + $initiatorUser = $this->userManager->get($initiator); |
|
390 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
391 | + $message = $this->mailer->createMessage(); |
|
392 | + |
|
393 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [ |
|
394 | + 'filename' => $filename, |
|
395 | + 'link' => $link, |
|
396 | + 'initiator' => $initiatorDisplayName, |
|
397 | + 'expiration' => $expiration, |
|
398 | + 'shareWith' => $shareWith, |
|
399 | + ]); |
|
400 | + |
|
401 | + $emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename))); |
|
402 | + $emailTemplate->addHeader(); |
|
403 | + $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false); |
|
404 | + $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]); |
|
405 | + |
|
406 | + $emailTemplate->addBodyText( |
|
407 | + htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')), |
|
408 | + $text |
|
409 | + ); |
|
410 | + $emailTemplate->addBodyButton( |
|
411 | + $this->l->t('Open »%s«', [$filename]), |
|
412 | + $link |
|
413 | + ); |
|
414 | + |
|
415 | + $message->setTo([$shareWith]); |
|
416 | + |
|
417 | + // The "From" contains the sharers name |
|
418 | + $instanceName = $this->defaults->getName(); |
|
419 | + $senderName = $this->l->t( |
|
420 | + '%s via %s', |
|
421 | + [ |
|
422 | + $initiatorDisplayName, |
|
423 | + $instanceName |
|
424 | + ] |
|
425 | + ); |
|
426 | + $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
427 | + |
|
428 | + // The "Reply-To" is set to the sharer if an mail address is configured |
|
429 | + // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
430 | + $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
431 | + if($initiatorEmail !== null) { |
|
432 | + $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
433 | + $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
434 | + } else { |
|
435 | + $emailTemplate->addFooter(); |
|
436 | + } |
|
437 | + |
|
438 | + $message->useTemplate($emailTemplate); |
|
439 | + $this->mailer->send($message); |
|
440 | + } |
|
441 | + |
|
442 | + /** |
|
443 | + * send password to recipient of a mail share |
|
444 | + * |
|
445 | + * @param IShare $share |
|
446 | + * @param string $password |
|
447 | + * @return bool |
|
448 | + */ |
|
449 | + protected function sendPassword(IShare $share, $password) { |
|
450 | + |
|
451 | + $filename = $share->getNode()->getName(); |
|
452 | + $initiator = $share->getSharedBy(); |
|
453 | + $shareWith = $share->getSharedWith(); |
|
454 | + |
|
455 | + if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) { |
|
456 | + return false; |
|
457 | + } |
|
458 | + |
|
459 | + $initiatorUser = $this->userManager->get($initiator); |
|
460 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
461 | + $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
462 | + |
|
463 | + $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]); |
|
464 | + $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
465 | + |
|
466 | + $message = $this->mailer->createMessage(); |
|
467 | + |
|
468 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [ |
|
469 | + 'filename' => $filename, |
|
470 | + 'password' => $password, |
|
471 | + 'initiator' => $initiatorDisplayName, |
|
472 | + 'initiatorEmail' => $initiatorEmailAddress, |
|
473 | + 'shareWith' => $shareWith, |
|
474 | + ]); |
|
475 | + |
|
476 | + $emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName])); |
|
477 | + $emailTemplate->addHeader(); |
|
478 | + $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
479 | + $emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart); |
|
480 | + $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password])); |
|
481 | + |
|
482 | + // The "From" contains the sharers name |
|
483 | + $instanceName = $this->defaults->getName(); |
|
484 | + $senderName = $this->l->t( |
|
485 | + '%s via %s', |
|
486 | + [ |
|
487 | + $initiatorDisplayName, |
|
488 | + $instanceName |
|
489 | + ] |
|
490 | + ); |
|
491 | + $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
492 | + if ($initiatorEmailAddress !== null) { |
|
493 | + $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
|
494 | + $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
495 | + } else { |
|
496 | + $emailTemplate->addFooter(); |
|
497 | + } |
|
498 | + |
|
499 | + $message->setTo([$shareWith]); |
|
500 | + $message->useTemplate($emailTemplate); |
|
501 | + $this->mailer->send($message); |
|
502 | + |
|
503 | + $this->createPasswordSendActivity($share, $shareWith, false); |
|
504 | + |
|
505 | + return true; |
|
506 | + } |
|
507 | + |
|
508 | + /** |
|
509 | + * send auto generated password to the owner. This happens if the admin enforces |
|
510 | + * a password for mail shares and forbid to send the password by mail to the recipient |
|
511 | + * |
|
512 | + * @param IShare $share |
|
513 | + * @param string $password |
|
514 | + * @return bool |
|
515 | + * @throws \Exception |
|
516 | + */ |
|
517 | + protected function sendPasswordToOwner(IShare $share, $password) { |
|
518 | + |
|
519 | + $filename = $share->getNode()->getName(); |
|
520 | + $initiator = $this->userManager->get($share->getSharedBy()); |
|
521 | + $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; |
|
522 | + $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); |
|
523 | + $shareWith = $share->getSharedWith(); |
|
524 | + |
|
525 | + if ($initiatorEMailAddress === null) { |
|
526 | + throw new \Exception( |
|
527 | + $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
528 | + ); |
|
529 | + } |
|
530 | + |
|
531 | + $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); |
|
532 | + |
|
533 | + $message = $this->mailer->createMessage(); |
|
534 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [ |
|
535 | + 'filename' => $filename, |
|
536 | + 'password' => $password, |
|
537 | + 'initiator' => $initiatorDisplayName, |
|
538 | + 'initiatorEmail' => $initiatorEMailAddress, |
|
539 | + 'shareWith' => $shareWith, |
|
540 | + ]); |
|
541 | + |
|
542 | + $emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith])); |
|
543 | + $emailTemplate->addHeader(); |
|
544 | + $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
545 | + $emailTemplate->addBodyText($bodyPart); |
|
546 | + $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password])); |
|
547 | + $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); |
|
548 | + $emailTemplate->addFooter(); |
|
549 | + |
|
550 | + if ($initiatorEMailAddress) { |
|
551 | + $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]); |
|
552 | + } |
|
553 | + $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); |
|
554 | + $message->useTemplate($emailTemplate); |
|
555 | + $this->mailer->send($message); |
|
556 | + |
|
557 | + $this->createPasswordSendActivity($share, $shareWith, true); |
|
558 | + |
|
559 | + return true; |
|
560 | + } |
|
561 | + |
|
562 | + /** |
|
563 | + * generate share token |
|
564 | + * |
|
565 | + * @return string |
|
566 | + */ |
|
567 | + protected function generateToken($size = 15) { |
|
568 | + $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE); |
|
569 | + return $token; |
|
570 | + } |
|
571 | + |
|
572 | + /** |
|
573 | + * Get all children of this share |
|
574 | + * |
|
575 | + * @param IShare $parent |
|
576 | + * @return IShare[] |
|
577 | + */ |
|
578 | + public function getChildren(IShare $parent) { |
|
579 | + $children = []; |
|
580 | + |
|
581 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
582 | + $qb->select('*') |
|
583 | + ->from('share') |
|
584 | + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
585 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
586 | + ->orderBy('id'); |
|
587 | + |
|
588 | + $cursor = $qb->execute(); |
|
589 | + while($data = $cursor->fetch()) { |
|
590 | + $children[] = $this->createShareObject($data); |
|
591 | + } |
|
592 | + $cursor->closeCursor(); |
|
593 | + |
|
594 | + return $children; |
|
595 | + } |
|
596 | + |
|
597 | + /** |
|
598 | + * add share to the database and return the ID |
|
599 | + * |
|
600 | + * @param int $itemSource |
|
601 | + * @param string $itemType |
|
602 | + * @param string $shareWith |
|
603 | + * @param string $sharedBy |
|
604 | + * @param string $uidOwner |
|
605 | + * @param int $permissions |
|
606 | + * @param string $token |
|
607 | + * @return int |
|
608 | + */ |
|
609 | + protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) { |
|
610 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
611 | + $qb->insert('share') |
|
612 | + ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
613 | + ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
614 | + ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
615 | + ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
616 | + ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
617 | + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
618 | + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
619 | + ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
620 | + ->setValue('token', $qb->createNamedParameter($token)) |
|
621 | + ->setValue('password', $qb->createNamedParameter($password)) |
|
622 | + ->setValue('stime', $qb->createNamedParameter(time())); |
|
623 | + |
|
624 | + /* |
|
625 | 625 | * Added to fix https://github.com/owncloud/core/issues/22215 |
626 | 626 | * Can be removed once we get rid of ajax/share.php |
627 | 627 | */ |
628 | - $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
628 | + $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
629 | 629 | |
630 | - $qb->execute(); |
|
631 | - $id = $qb->getLastInsertId(); |
|
630 | + $qb->execute(); |
|
631 | + $id = $qb->getLastInsertId(); |
|
632 | 632 | |
633 | - return (int)$id; |
|
634 | - } |
|
633 | + return (int)$id; |
|
634 | + } |
|
635 | 635 | |
636 | - /** |
|
637 | - * Update a share |
|
638 | - * |
|
639 | - * @param IShare $share |
|
640 | - * @param string|null $plainTextPassword |
|
641 | - * @return IShare The share object |
|
642 | - */ |
|
643 | - public function update(IShare $share, $plainTextPassword = null) { |
|
636 | + /** |
|
637 | + * Update a share |
|
638 | + * |
|
639 | + * @param IShare $share |
|
640 | + * @param string|null $plainTextPassword |
|
641 | + * @return IShare The share object |
|
642 | + */ |
|
643 | + public function update(IShare $share, $plainTextPassword = null) { |
|
644 | 644 | |
645 | - $originalShare = $this->getShareById($share->getId()); |
|
645 | + $originalShare = $this->getShareById($share->getId()); |
|
646 | 646 | |
647 | - // a real password was given |
|
648 | - $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; |
|
647 | + // a real password was given |
|
648 | + $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; |
|
649 | 649 | |
650 | - if($validPassword && $originalShare->getPassword() !== $share->getPassword()) { |
|
651 | - $this->sendPassword($share, $plainTextPassword); |
|
652 | - } |
|
653 | - /* |
|
650 | + if($validPassword && $originalShare->getPassword() !== $share->getPassword()) { |
|
651 | + $this->sendPassword($share, $plainTextPassword); |
|
652 | + } |
|
653 | + /* |
|
654 | 654 | * We allow updating the permissions and password of mail shares |
655 | 655 | */ |
656 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
657 | - $qb->update('share') |
|
658 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
659 | - ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
660 | - ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
661 | - ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
662 | - ->set('password', $qb->createNamedParameter($share->getPassword())) |
|
663 | - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) |
|
664 | - ->execute(); |
|
665 | - |
|
666 | - return $share; |
|
667 | - } |
|
668 | - |
|
669 | - /** |
|
670 | - * @inheritdoc |
|
671 | - */ |
|
672 | - public function move(IShare $share, $recipient) { |
|
673 | - /** |
|
674 | - * nothing to do here, mail shares are only outgoing shares |
|
675 | - */ |
|
676 | - return $share; |
|
677 | - } |
|
678 | - |
|
679 | - /** |
|
680 | - * Delete a share (owner unShares the file) |
|
681 | - * |
|
682 | - * @param IShare $share |
|
683 | - */ |
|
684 | - public function delete(IShare $share) { |
|
685 | - $this->removeShareFromTable($share->getId()); |
|
686 | - } |
|
687 | - |
|
688 | - /** |
|
689 | - * @inheritdoc |
|
690 | - */ |
|
691 | - public function deleteFromSelf(IShare $share, $recipient) { |
|
692 | - // nothing to do here, mail shares are only outgoing shares |
|
693 | - } |
|
694 | - |
|
695 | - /** |
|
696 | - * @inheritdoc |
|
697 | - */ |
|
698 | - public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
699 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
700 | - $qb->select('*') |
|
701 | - ->from('share'); |
|
702 | - |
|
703 | - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
704 | - |
|
705 | - /** |
|
706 | - * Reshares for this user are shares where they are the owner. |
|
707 | - */ |
|
708 | - if ($reshares === false) { |
|
709 | - //Special case for old shares created via the web UI |
|
710 | - $or1 = $qb->expr()->andX( |
|
711 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
712 | - $qb->expr()->isNull('uid_initiator') |
|
713 | - ); |
|
714 | - |
|
715 | - $qb->andWhere( |
|
716 | - $qb->expr()->orX( |
|
717 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
718 | - $or1 |
|
719 | - ) |
|
720 | - ); |
|
721 | - } else { |
|
722 | - $qb->andWhere( |
|
723 | - $qb->expr()->orX( |
|
724 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
725 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
726 | - ) |
|
727 | - ); |
|
728 | - } |
|
729 | - |
|
730 | - if ($node !== null) { |
|
731 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
732 | - } |
|
733 | - |
|
734 | - if ($limit !== -1) { |
|
735 | - $qb->setMaxResults($limit); |
|
736 | - } |
|
737 | - |
|
738 | - $qb->setFirstResult($offset); |
|
739 | - $qb->orderBy('id'); |
|
740 | - |
|
741 | - $cursor = $qb->execute(); |
|
742 | - $shares = []; |
|
743 | - while($data = $cursor->fetch()) { |
|
744 | - $shares[] = $this->createShareObject($data); |
|
745 | - } |
|
746 | - $cursor->closeCursor(); |
|
747 | - |
|
748 | - return $shares; |
|
749 | - } |
|
750 | - |
|
751 | - /** |
|
752 | - * @inheritdoc |
|
753 | - */ |
|
754 | - public function getShareById($id, $recipientId = null) { |
|
755 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
756 | - |
|
757 | - $qb->select('*') |
|
758 | - ->from('share') |
|
759 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
760 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
761 | - |
|
762 | - $cursor = $qb->execute(); |
|
763 | - $data = $cursor->fetch(); |
|
764 | - $cursor->closeCursor(); |
|
765 | - |
|
766 | - if ($data === false) { |
|
767 | - throw new ShareNotFound(); |
|
768 | - } |
|
769 | - |
|
770 | - try { |
|
771 | - $share = $this->createShareObject($data); |
|
772 | - } catch (InvalidShare $e) { |
|
773 | - throw new ShareNotFound(); |
|
774 | - } |
|
775 | - |
|
776 | - return $share; |
|
777 | - } |
|
778 | - |
|
779 | - /** |
|
780 | - * Get shares for a given path |
|
781 | - * |
|
782 | - * @param \OCP\Files\Node $path |
|
783 | - * @return IShare[] |
|
784 | - */ |
|
785 | - public function getSharesByPath(Node $path) { |
|
786 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
787 | - |
|
788 | - $cursor = $qb->select('*') |
|
789 | - ->from('share') |
|
790 | - ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
791 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
792 | - ->execute(); |
|
793 | - |
|
794 | - $shares = []; |
|
795 | - while($data = $cursor->fetch()) { |
|
796 | - $shares[] = $this->createShareObject($data); |
|
797 | - } |
|
798 | - $cursor->closeCursor(); |
|
799 | - |
|
800 | - return $shares; |
|
801 | - } |
|
802 | - |
|
803 | - /** |
|
804 | - * @inheritdoc |
|
805 | - */ |
|
806 | - public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
807 | - /** @var IShare[] $shares */ |
|
808 | - $shares = []; |
|
809 | - |
|
810 | - //Get shares directly with this user |
|
811 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
812 | - $qb->select('*') |
|
813 | - ->from('share'); |
|
814 | - |
|
815 | - // Order by id |
|
816 | - $qb->orderBy('id'); |
|
817 | - |
|
818 | - // Set limit and offset |
|
819 | - if ($limit !== -1) { |
|
820 | - $qb->setMaxResults($limit); |
|
821 | - } |
|
822 | - $qb->setFirstResult($offset); |
|
823 | - |
|
824 | - $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
825 | - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
826 | - |
|
827 | - // Filter by node if provided |
|
828 | - if ($node !== null) { |
|
829 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
830 | - } |
|
831 | - |
|
832 | - $cursor = $qb->execute(); |
|
833 | - |
|
834 | - while($data = $cursor->fetch()) { |
|
835 | - $shares[] = $this->createShareObject($data); |
|
836 | - } |
|
837 | - $cursor->closeCursor(); |
|
838 | - |
|
839 | - |
|
840 | - return $shares; |
|
841 | - } |
|
842 | - |
|
843 | - /** |
|
844 | - * Get a share by token |
|
845 | - * |
|
846 | - * @param string $token |
|
847 | - * @return IShare |
|
848 | - * @throws ShareNotFound |
|
849 | - */ |
|
850 | - public function getShareByToken($token) { |
|
851 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
852 | - |
|
853 | - $cursor = $qb->select('*') |
|
854 | - ->from('share') |
|
855 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
856 | - ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
857 | - ->execute(); |
|
858 | - |
|
859 | - $data = $cursor->fetch(); |
|
860 | - |
|
861 | - if ($data === false) { |
|
862 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
863 | - } |
|
864 | - |
|
865 | - try { |
|
866 | - $share = $this->createShareObject($data); |
|
867 | - } catch (InvalidShare $e) { |
|
868 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
869 | - } |
|
870 | - |
|
871 | - return $share; |
|
872 | - } |
|
873 | - |
|
874 | - /** |
|
875 | - * remove share from table |
|
876 | - * |
|
877 | - * @param string $shareId |
|
878 | - */ |
|
879 | - protected function removeShareFromTable($shareId) { |
|
880 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
881 | - $qb->delete('share') |
|
882 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
883 | - $qb->execute(); |
|
884 | - } |
|
885 | - |
|
886 | - /** |
|
887 | - * Create a share object from an database row |
|
888 | - * |
|
889 | - * @param array $data |
|
890 | - * @return IShare |
|
891 | - * @throws InvalidShare |
|
892 | - * @throws ShareNotFound |
|
893 | - */ |
|
894 | - protected function createShareObject($data) { |
|
895 | - |
|
896 | - $share = new Share($this->rootFolder, $this->userManager); |
|
897 | - $share->setId((int)$data['id']) |
|
898 | - ->setShareType((int)$data['share_type']) |
|
899 | - ->setPermissions((int)$data['permissions']) |
|
900 | - ->setTarget($data['file_target']) |
|
901 | - ->setMailSend((bool)$data['mail_send']) |
|
902 | - ->setToken($data['token']); |
|
903 | - |
|
904 | - $shareTime = new \DateTime(); |
|
905 | - $shareTime->setTimestamp((int)$data['stime']); |
|
906 | - $share->setShareTime($shareTime); |
|
907 | - $share->setSharedWith($data['share_with']); |
|
908 | - $share->setPassword($data['password']); |
|
909 | - |
|
910 | - if ($data['uid_initiator'] !== null) { |
|
911 | - $share->setShareOwner($data['uid_owner']); |
|
912 | - $share->setSharedBy($data['uid_initiator']); |
|
913 | - } else { |
|
914 | - //OLD SHARE |
|
915 | - $share->setSharedBy($data['uid_owner']); |
|
916 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
917 | - |
|
918 | - $owner = $path->getOwner(); |
|
919 | - $share->setShareOwner($owner->getUID()); |
|
920 | - } |
|
921 | - |
|
922 | - if ($data['expiration'] !== null) { |
|
923 | - $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
924 | - if ($expiration !== false) { |
|
925 | - $share->setExpirationDate($expiration); |
|
926 | - } |
|
927 | - } |
|
928 | - |
|
929 | - $share->setNodeId((int)$data['file_source']); |
|
930 | - $share->setNodeType($data['item_type']); |
|
931 | - |
|
932 | - $share->setProviderId($this->identifier()); |
|
933 | - |
|
934 | - return $share; |
|
935 | - } |
|
936 | - |
|
937 | - /** |
|
938 | - * Get the node with file $id for $user |
|
939 | - * |
|
940 | - * @param string $userId |
|
941 | - * @param int $id |
|
942 | - * @return \OCP\Files\File|\OCP\Files\Folder |
|
943 | - * @throws InvalidShare |
|
944 | - */ |
|
945 | - private function getNode($userId, $id) { |
|
946 | - try { |
|
947 | - $userFolder = $this->rootFolder->getUserFolder($userId); |
|
948 | - } catch (NoUserException $e) { |
|
949 | - throw new InvalidShare(); |
|
950 | - } |
|
951 | - |
|
952 | - $nodes = $userFolder->getById($id); |
|
953 | - |
|
954 | - if (empty($nodes)) { |
|
955 | - throw new InvalidShare(); |
|
956 | - } |
|
957 | - |
|
958 | - return $nodes[0]; |
|
959 | - } |
|
960 | - |
|
961 | - /** |
|
962 | - * A user is deleted from the system |
|
963 | - * So clean up the relevant shares. |
|
964 | - * |
|
965 | - * @param string $uid |
|
966 | - * @param int $shareType |
|
967 | - */ |
|
968 | - public function userDeleted($uid, $shareType) { |
|
969 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
970 | - |
|
971 | - $qb->delete('share') |
|
972 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
973 | - ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
974 | - ->execute(); |
|
975 | - } |
|
976 | - |
|
977 | - /** |
|
978 | - * This provider does not support group shares |
|
979 | - * |
|
980 | - * @param string $gid |
|
981 | - */ |
|
982 | - public function groupDeleted($gid) { |
|
983 | - } |
|
984 | - |
|
985 | - /** |
|
986 | - * This provider does not support group shares |
|
987 | - * |
|
988 | - * @param string $uid |
|
989 | - * @param string $gid |
|
990 | - */ |
|
991 | - public function userDeletedFromGroup($uid, $gid) { |
|
992 | - } |
|
993 | - |
|
994 | - /** |
|
995 | - * get database row of a give share |
|
996 | - * |
|
997 | - * @param $id |
|
998 | - * @return array |
|
999 | - * @throws ShareNotFound |
|
1000 | - */ |
|
1001 | - protected function getRawShare($id) { |
|
1002 | - |
|
1003 | - // Now fetch the inserted share and create a complete share object |
|
1004 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
1005 | - $qb->select('*') |
|
1006 | - ->from('share') |
|
1007 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
1008 | - |
|
1009 | - $cursor = $qb->execute(); |
|
1010 | - $data = $cursor->fetch(); |
|
1011 | - $cursor->closeCursor(); |
|
1012 | - |
|
1013 | - if ($data === false) { |
|
1014 | - throw new ShareNotFound; |
|
1015 | - } |
|
1016 | - |
|
1017 | - return $data; |
|
1018 | - } |
|
1019 | - |
|
1020 | - public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
1021 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
1022 | - $qb->select('*') |
|
1023 | - ->from('share', 's') |
|
1024 | - ->andWhere($qb->expr()->orX( |
|
1025 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1026 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1027 | - )) |
|
1028 | - ->andWhere( |
|
1029 | - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
1030 | - ); |
|
1031 | - |
|
1032 | - /** |
|
1033 | - * Reshares for this user are shares where they are the owner. |
|
1034 | - */ |
|
1035 | - if ($reshares === false) { |
|
1036 | - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
1037 | - } else { |
|
1038 | - $qb->andWhere( |
|
1039 | - $qb->expr()->orX( |
|
1040 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
1041 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
1042 | - ) |
|
1043 | - ); |
|
1044 | - } |
|
1045 | - |
|
1046 | - $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
1047 | - $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
1048 | - |
|
1049 | - $qb->orderBy('id'); |
|
1050 | - |
|
1051 | - $cursor = $qb->execute(); |
|
1052 | - $shares = []; |
|
1053 | - while ($data = $cursor->fetch()) { |
|
1054 | - $shares[$data['fileid']][] = $this->createShareObject($data); |
|
1055 | - } |
|
1056 | - $cursor->closeCursor(); |
|
1057 | - |
|
1058 | - return $shares; |
|
1059 | - } |
|
1060 | - |
|
1061 | - /** |
|
1062 | - * @inheritdoc |
|
1063 | - */ |
|
1064 | - public function getAccessList($nodes, $currentAccess) { |
|
1065 | - $ids = []; |
|
1066 | - foreach ($nodes as $node) { |
|
1067 | - $ids[] = $node->getId(); |
|
1068 | - } |
|
1069 | - |
|
1070 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
1071 | - $qb->select('share_with') |
|
1072 | - ->from('share') |
|
1073 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
1074 | - ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
1075 | - ->andWhere($qb->expr()->orX( |
|
1076 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1077 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1078 | - )) |
|
1079 | - ->setMaxResults(1); |
|
1080 | - $cursor = $qb->execute(); |
|
1081 | - |
|
1082 | - $mail = $cursor->fetch() !== false; |
|
1083 | - $cursor->closeCursor(); |
|
1084 | - |
|
1085 | - return ['public' => $mail]; |
|
1086 | - } |
|
656 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
657 | + $qb->update('share') |
|
658 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
659 | + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
660 | + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
661 | + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
662 | + ->set('password', $qb->createNamedParameter($share->getPassword())) |
|
663 | + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) |
|
664 | + ->execute(); |
|
665 | + |
|
666 | + return $share; |
|
667 | + } |
|
668 | + |
|
669 | + /** |
|
670 | + * @inheritdoc |
|
671 | + */ |
|
672 | + public function move(IShare $share, $recipient) { |
|
673 | + /** |
|
674 | + * nothing to do here, mail shares are only outgoing shares |
|
675 | + */ |
|
676 | + return $share; |
|
677 | + } |
|
678 | + |
|
679 | + /** |
|
680 | + * Delete a share (owner unShares the file) |
|
681 | + * |
|
682 | + * @param IShare $share |
|
683 | + */ |
|
684 | + public function delete(IShare $share) { |
|
685 | + $this->removeShareFromTable($share->getId()); |
|
686 | + } |
|
687 | + |
|
688 | + /** |
|
689 | + * @inheritdoc |
|
690 | + */ |
|
691 | + public function deleteFromSelf(IShare $share, $recipient) { |
|
692 | + // nothing to do here, mail shares are only outgoing shares |
|
693 | + } |
|
694 | + |
|
695 | + /** |
|
696 | + * @inheritdoc |
|
697 | + */ |
|
698 | + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
699 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
700 | + $qb->select('*') |
|
701 | + ->from('share'); |
|
702 | + |
|
703 | + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
704 | + |
|
705 | + /** |
|
706 | + * Reshares for this user are shares where they are the owner. |
|
707 | + */ |
|
708 | + if ($reshares === false) { |
|
709 | + //Special case for old shares created via the web UI |
|
710 | + $or1 = $qb->expr()->andX( |
|
711 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
712 | + $qb->expr()->isNull('uid_initiator') |
|
713 | + ); |
|
714 | + |
|
715 | + $qb->andWhere( |
|
716 | + $qb->expr()->orX( |
|
717 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
718 | + $or1 |
|
719 | + ) |
|
720 | + ); |
|
721 | + } else { |
|
722 | + $qb->andWhere( |
|
723 | + $qb->expr()->orX( |
|
724 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
725 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
726 | + ) |
|
727 | + ); |
|
728 | + } |
|
729 | + |
|
730 | + if ($node !== null) { |
|
731 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
732 | + } |
|
733 | + |
|
734 | + if ($limit !== -1) { |
|
735 | + $qb->setMaxResults($limit); |
|
736 | + } |
|
737 | + |
|
738 | + $qb->setFirstResult($offset); |
|
739 | + $qb->orderBy('id'); |
|
740 | + |
|
741 | + $cursor = $qb->execute(); |
|
742 | + $shares = []; |
|
743 | + while($data = $cursor->fetch()) { |
|
744 | + $shares[] = $this->createShareObject($data); |
|
745 | + } |
|
746 | + $cursor->closeCursor(); |
|
747 | + |
|
748 | + return $shares; |
|
749 | + } |
|
750 | + |
|
751 | + /** |
|
752 | + * @inheritdoc |
|
753 | + */ |
|
754 | + public function getShareById($id, $recipientId = null) { |
|
755 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
756 | + |
|
757 | + $qb->select('*') |
|
758 | + ->from('share') |
|
759 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
760 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
761 | + |
|
762 | + $cursor = $qb->execute(); |
|
763 | + $data = $cursor->fetch(); |
|
764 | + $cursor->closeCursor(); |
|
765 | + |
|
766 | + if ($data === false) { |
|
767 | + throw new ShareNotFound(); |
|
768 | + } |
|
769 | + |
|
770 | + try { |
|
771 | + $share = $this->createShareObject($data); |
|
772 | + } catch (InvalidShare $e) { |
|
773 | + throw new ShareNotFound(); |
|
774 | + } |
|
775 | + |
|
776 | + return $share; |
|
777 | + } |
|
778 | + |
|
779 | + /** |
|
780 | + * Get shares for a given path |
|
781 | + * |
|
782 | + * @param \OCP\Files\Node $path |
|
783 | + * @return IShare[] |
|
784 | + */ |
|
785 | + public function getSharesByPath(Node $path) { |
|
786 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
787 | + |
|
788 | + $cursor = $qb->select('*') |
|
789 | + ->from('share') |
|
790 | + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
791 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
792 | + ->execute(); |
|
793 | + |
|
794 | + $shares = []; |
|
795 | + while($data = $cursor->fetch()) { |
|
796 | + $shares[] = $this->createShareObject($data); |
|
797 | + } |
|
798 | + $cursor->closeCursor(); |
|
799 | + |
|
800 | + return $shares; |
|
801 | + } |
|
802 | + |
|
803 | + /** |
|
804 | + * @inheritdoc |
|
805 | + */ |
|
806 | + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
807 | + /** @var IShare[] $shares */ |
|
808 | + $shares = []; |
|
809 | + |
|
810 | + //Get shares directly with this user |
|
811 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
812 | + $qb->select('*') |
|
813 | + ->from('share'); |
|
814 | + |
|
815 | + // Order by id |
|
816 | + $qb->orderBy('id'); |
|
817 | + |
|
818 | + // Set limit and offset |
|
819 | + if ($limit !== -1) { |
|
820 | + $qb->setMaxResults($limit); |
|
821 | + } |
|
822 | + $qb->setFirstResult($offset); |
|
823 | + |
|
824 | + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
825 | + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
826 | + |
|
827 | + // Filter by node if provided |
|
828 | + if ($node !== null) { |
|
829 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
830 | + } |
|
831 | + |
|
832 | + $cursor = $qb->execute(); |
|
833 | + |
|
834 | + while($data = $cursor->fetch()) { |
|
835 | + $shares[] = $this->createShareObject($data); |
|
836 | + } |
|
837 | + $cursor->closeCursor(); |
|
838 | + |
|
839 | + |
|
840 | + return $shares; |
|
841 | + } |
|
842 | + |
|
843 | + /** |
|
844 | + * Get a share by token |
|
845 | + * |
|
846 | + * @param string $token |
|
847 | + * @return IShare |
|
848 | + * @throws ShareNotFound |
|
849 | + */ |
|
850 | + public function getShareByToken($token) { |
|
851 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
852 | + |
|
853 | + $cursor = $qb->select('*') |
|
854 | + ->from('share') |
|
855 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
856 | + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
857 | + ->execute(); |
|
858 | + |
|
859 | + $data = $cursor->fetch(); |
|
860 | + |
|
861 | + if ($data === false) { |
|
862 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
863 | + } |
|
864 | + |
|
865 | + try { |
|
866 | + $share = $this->createShareObject($data); |
|
867 | + } catch (InvalidShare $e) { |
|
868 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
869 | + } |
|
870 | + |
|
871 | + return $share; |
|
872 | + } |
|
873 | + |
|
874 | + /** |
|
875 | + * remove share from table |
|
876 | + * |
|
877 | + * @param string $shareId |
|
878 | + */ |
|
879 | + protected function removeShareFromTable($shareId) { |
|
880 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
881 | + $qb->delete('share') |
|
882 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
883 | + $qb->execute(); |
|
884 | + } |
|
885 | + |
|
886 | + /** |
|
887 | + * Create a share object from an database row |
|
888 | + * |
|
889 | + * @param array $data |
|
890 | + * @return IShare |
|
891 | + * @throws InvalidShare |
|
892 | + * @throws ShareNotFound |
|
893 | + */ |
|
894 | + protected function createShareObject($data) { |
|
895 | + |
|
896 | + $share = new Share($this->rootFolder, $this->userManager); |
|
897 | + $share->setId((int)$data['id']) |
|
898 | + ->setShareType((int)$data['share_type']) |
|
899 | + ->setPermissions((int)$data['permissions']) |
|
900 | + ->setTarget($data['file_target']) |
|
901 | + ->setMailSend((bool)$data['mail_send']) |
|
902 | + ->setToken($data['token']); |
|
903 | + |
|
904 | + $shareTime = new \DateTime(); |
|
905 | + $shareTime->setTimestamp((int)$data['stime']); |
|
906 | + $share->setShareTime($shareTime); |
|
907 | + $share->setSharedWith($data['share_with']); |
|
908 | + $share->setPassword($data['password']); |
|
909 | + |
|
910 | + if ($data['uid_initiator'] !== null) { |
|
911 | + $share->setShareOwner($data['uid_owner']); |
|
912 | + $share->setSharedBy($data['uid_initiator']); |
|
913 | + } else { |
|
914 | + //OLD SHARE |
|
915 | + $share->setSharedBy($data['uid_owner']); |
|
916 | + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
917 | + |
|
918 | + $owner = $path->getOwner(); |
|
919 | + $share->setShareOwner($owner->getUID()); |
|
920 | + } |
|
921 | + |
|
922 | + if ($data['expiration'] !== null) { |
|
923 | + $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
924 | + if ($expiration !== false) { |
|
925 | + $share->setExpirationDate($expiration); |
|
926 | + } |
|
927 | + } |
|
928 | + |
|
929 | + $share->setNodeId((int)$data['file_source']); |
|
930 | + $share->setNodeType($data['item_type']); |
|
931 | + |
|
932 | + $share->setProviderId($this->identifier()); |
|
933 | + |
|
934 | + return $share; |
|
935 | + } |
|
936 | + |
|
937 | + /** |
|
938 | + * Get the node with file $id for $user |
|
939 | + * |
|
940 | + * @param string $userId |
|
941 | + * @param int $id |
|
942 | + * @return \OCP\Files\File|\OCP\Files\Folder |
|
943 | + * @throws InvalidShare |
|
944 | + */ |
|
945 | + private function getNode($userId, $id) { |
|
946 | + try { |
|
947 | + $userFolder = $this->rootFolder->getUserFolder($userId); |
|
948 | + } catch (NoUserException $e) { |
|
949 | + throw new InvalidShare(); |
|
950 | + } |
|
951 | + |
|
952 | + $nodes = $userFolder->getById($id); |
|
953 | + |
|
954 | + if (empty($nodes)) { |
|
955 | + throw new InvalidShare(); |
|
956 | + } |
|
957 | + |
|
958 | + return $nodes[0]; |
|
959 | + } |
|
960 | + |
|
961 | + /** |
|
962 | + * A user is deleted from the system |
|
963 | + * So clean up the relevant shares. |
|
964 | + * |
|
965 | + * @param string $uid |
|
966 | + * @param int $shareType |
|
967 | + */ |
|
968 | + public function userDeleted($uid, $shareType) { |
|
969 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
970 | + |
|
971 | + $qb->delete('share') |
|
972 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
973 | + ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
974 | + ->execute(); |
|
975 | + } |
|
976 | + |
|
977 | + /** |
|
978 | + * This provider does not support group shares |
|
979 | + * |
|
980 | + * @param string $gid |
|
981 | + */ |
|
982 | + public function groupDeleted($gid) { |
|
983 | + } |
|
984 | + |
|
985 | + /** |
|
986 | + * This provider does not support group shares |
|
987 | + * |
|
988 | + * @param string $uid |
|
989 | + * @param string $gid |
|
990 | + */ |
|
991 | + public function userDeletedFromGroup($uid, $gid) { |
|
992 | + } |
|
993 | + |
|
994 | + /** |
|
995 | + * get database row of a give share |
|
996 | + * |
|
997 | + * @param $id |
|
998 | + * @return array |
|
999 | + * @throws ShareNotFound |
|
1000 | + */ |
|
1001 | + protected function getRawShare($id) { |
|
1002 | + |
|
1003 | + // Now fetch the inserted share and create a complete share object |
|
1004 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
1005 | + $qb->select('*') |
|
1006 | + ->from('share') |
|
1007 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
1008 | + |
|
1009 | + $cursor = $qb->execute(); |
|
1010 | + $data = $cursor->fetch(); |
|
1011 | + $cursor->closeCursor(); |
|
1012 | + |
|
1013 | + if ($data === false) { |
|
1014 | + throw new ShareNotFound; |
|
1015 | + } |
|
1016 | + |
|
1017 | + return $data; |
|
1018 | + } |
|
1019 | + |
|
1020 | + public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
1021 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
1022 | + $qb->select('*') |
|
1023 | + ->from('share', 's') |
|
1024 | + ->andWhere($qb->expr()->orX( |
|
1025 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1026 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1027 | + )) |
|
1028 | + ->andWhere( |
|
1029 | + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
1030 | + ); |
|
1031 | + |
|
1032 | + /** |
|
1033 | + * Reshares for this user are shares where they are the owner. |
|
1034 | + */ |
|
1035 | + if ($reshares === false) { |
|
1036 | + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
1037 | + } else { |
|
1038 | + $qb->andWhere( |
|
1039 | + $qb->expr()->orX( |
|
1040 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
1041 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
1042 | + ) |
|
1043 | + ); |
|
1044 | + } |
|
1045 | + |
|
1046 | + $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
1047 | + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
1048 | + |
|
1049 | + $qb->orderBy('id'); |
|
1050 | + |
|
1051 | + $cursor = $qb->execute(); |
|
1052 | + $shares = []; |
|
1053 | + while ($data = $cursor->fetch()) { |
|
1054 | + $shares[$data['fileid']][] = $this->createShareObject($data); |
|
1055 | + } |
|
1056 | + $cursor->closeCursor(); |
|
1057 | + |
|
1058 | + return $shares; |
|
1059 | + } |
|
1060 | + |
|
1061 | + /** |
|
1062 | + * @inheritdoc |
|
1063 | + */ |
|
1064 | + public function getAccessList($nodes, $currentAccess) { |
|
1065 | + $ids = []; |
|
1066 | + foreach ($nodes as $node) { |
|
1067 | + $ids[] = $node->getId(); |
|
1068 | + } |
|
1069 | + |
|
1070 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
1071 | + $qb->select('share_with') |
|
1072 | + ->from('share') |
|
1073 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
1074 | + ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
1075 | + ->andWhere($qb->expr()->orX( |
|
1076 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1077 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1078 | + )) |
|
1079 | + ->setMaxResults(1); |
|
1080 | + $cursor = $qb->execute(); |
|
1081 | + |
|
1082 | + $mail = $cursor->fetch() !== false; |
|
1083 | + $cursor->closeCursor(); |
|
1084 | + |
|
1085 | + return ['public' => $mail]; |
|
1086 | + } |
|
1087 | 1087 | |
1088 | 1088 | } |
@@ -28,32 +28,32 @@ |
||
28 | 28 | |
29 | 29 | class SyncJob extends TimedJob { |
30 | 30 | |
31 | - /** @var SyncFederationAddressBooks */ |
|
32 | - protected $syncService; |
|
31 | + /** @var SyncFederationAddressBooks */ |
|
32 | + protected $syncService; |
|
33 | 33 | |
34 | - /** @var ILogger */ |
|
35 | - protected $logger; |
|
34 | + /** @var ILogger */ |
|
35 | + protected $logger; |
|
36 | 36 | |
37 | - /** |
|
38 | - * @param SyncFederationAddressBooks $syncService |
|
39 | - * @param ILogger $logger |
|
40 | - */ |
|
41 | - public function __construct(SyncFederationAddressBooks $syncService, ILogger $logger) { |
|
42 | - // Run once a day |
|
43 | - $this->setInterval(24 * 60 * 60); |
|
44 | - $this->syncService = $syncService; |
|
45 | - $this->logger = $logger; |
|
46 | - } |
|
37 | + /** |
|
38 | + * @param SyncFederationAddressBooks $syncService |
|
39 | + * @param ILogger $logger |
|
40 | + */ |
|
41 | + public function __construct(SyncFederationAddressBooks $syncService, ILogger $logger) { |
|
42 | + // Run once a day |
|
43 | + $this->setInterval(24 * 60 * 60); |
|
44 | + $this->syncService = $syncService; |
|
45 | + $this->logger = $logger; |
|
46 | + } |
|
47 | 47 | |
48 | - protected function run($argument) { |
|
49 | - $this->syncService->syncThemAll(function($url, $ex) { |
|
50 | - if ($ex instanceof \Exception) { |
|
51 | - $this->logger->logException($ex, [ |
|
52 | - 'message' => "Error while syncing $url.", |
|
53 | - 'level' => ILogger::ERROR, |
|
54 | - 'app' => 'fed-sync', |
|
55 | - ]); |
|
56 | - } |
|
57 | - }); |
|
58 | - } |
|
48 | + protected function run($argument) { |
|
49 | + $this->syncService->syncThemAll(function($url, $ex) { |
|
50 | + if ($ex instanceof \Exception) { |
|
51 | + $this->logger->logException($ex, [ |
|
52 | + 'message' => "Error while syncing $url.", |
|
53 | + 'level' => ILogger::ERROR, |
|
54 | + 'app' => 'fed-sync', |
|
55 | + ]); |
|
56 | + } |
|
57 | + }); |
|
58 | + } |
|
59 | 59 | } |
@@ -38,54 +38,54 @@ |
||
38 | 38 | |
39 | 39 | class AddServerMiddleware extends Middleware { |
40 | 40 | |
41 | - /** @var string */ |
|
42 | - protected $appName; |
|
41 | + /** @var string */ |
|
42 | + protected $appName; |
|
43 | 43 | |
44 | - /** @var IL10N */ |
|
45 | - protected $l; |
|
44 | + /** @var IL10N */ |
|
45 | + protected $l; |
|
46 | 46 | |
47 | - /** @var ILogger */ |
|
48 | - protected $logger; |
|
47 | + /** @var ILogger */ |
|
48 | + protected $logger; |
|
49 | 49 | |
50 | - /** |
|
51 | - * @param string $appName |
|
52 | - * @param IL10N $l |
|
53 | - * @param ILogger $logger |
|
54 | - */ |
|
55 | - public function __construct($appName, IL10N $l, ILogger $logger) { |
|
56 | - $this->appName = $appName; |
|
57 | - $this->l = $l; |
|
58 | - $this->logger = $logger; |
|
59 | - } |
|
50 | + /** |
|
51 | + * @param string $appName |
|
52 | + * @param IL10N $l |
|
53 | + * @param ILogger $logger |
|
54 | + */ |
|
55 | + public function __construct($appName, IL10N $l, ILogger $logger) { |
|
56 | + $this->appName = $appName; |
|
57 | + $this->l = $l; |
|
58 | + $this->logger = $logger; |
|
59 | + } |
|
60 | 60 | |
61 | - /** |
|
62 | - * Log error message and return a response which can be displayed to the user |
|
63 | - * |
|
64 | - * @param Controller $controller |
|
65 | - * @param string $methodName |
|
66 | - * @param \Exception $exception |
|
67 | - * @return JSONResponse |
|
68 | - * @throws \Exception |
|
69 | - */ |
|
70 | - public function afterException($controller, $methodName, \Exception $exception) { |
|
71 | - if (($controller instanceof SettingsController) === false) { |
|
72 | - throw $exception; |
|
73 | - } |
|
74 | - $this->logger->logException($exception, [ |
|
75 | - 'level' => ILogger::ERROR, |
|
76 | - 'app' => $this->appName, |
|
77 | - ]); |
|
78 | - if ($exception instanceof HintException) { |
|
79 | - $message = $exception->getHint(); |
|
80 | - } else { |
|
81 | - $message = $exception->getMessage(); |
|
82 | - } |
|
61 | + /** |
|
62 | + * Log error message and return a response which can be displayed to the user |
|
63 | + * |
|
64 | + * @param Controller $controller |
|
65 | + * @param string $methodName |
|
66 | + * @param \Exception $exception |
|
67 | + * @return JSONResponse |
|
68 | + * @throws \Exception |
|
69 | + */ |
|
70 | + public function afterException($controller, $methodName, \Exception $exception) { |
|
71 | + if (($controller instanceof SettingsController) === false) { |
|
72 | + throw $exception; |
|
73 | + } |
|
74 | + $this->logger->logException($exception, [ |
|
75 | + 'level' => ILogger::ERROR, |
|
76 | + 'app' => $this->appName, |
|
77 | + ]); |
|
78 | + if ($exception instanceof HintException) { |
|
79 | + $message = $exception->getHint(); |
|
80 | + } else { |
|
81 | + $message = $exception->getMessage(); |
|
82 | + } |
|
83 | 83 | |
84 | - return new JSONResponse( |
|
85 | - ['message' => $message], |
|
86 | - Http::STATUS_BAD_REQUEST |
|
87 | - ); |
|
84 | + return new JSONResponse( |
|
85 | + ['message' => $message], |
|
86 | + Http::STATUS_BAD_REQUEST |
|
87 | + ); |
|
88 | 88 | |
89 | - } |
|
89 | + } |
|
90 | 90 | |
91 | 91 | } |
@@ -41,252 +41,252 @@ |
||
41 | 41 | |
42 | 42 | class TrustedServers { |
43 | 43 | |
44 | - /** after a user list was exchanged at least once successfully */ |
|
45 | - const STATUS_OK = 1; |
|
46 | - /** waiting for shared secret or initial user list exchange */ |
|
47 | - const STATUS_PENDING = 2; |
|
48 | - /** something went wrong, misconfigured server, software bug,... user interaction needed */ |
|
49 | - const STATUS_FAILURE = 3; |
|
50 | - /** remote server revoked access */ |
|
51 | - const STATUS_ACCESS_REVOKED = 4; |
|
52 | - |
|
53 | - /** @var dbHandler */ |
|
54 | - private $dbHandler; |
|
55 | - |
|
56 | - /** @var IClientService */ |
|
57 | - private $httpClientService; |
|
58 | - |
|
59 | - /** @var ILogger */ |
|
60 | - private $logger; |
|
61 | - |
|
62 | - /** @var IJobList */ |
|
63 | - private $jobList; |
|
64 | - |
|
65 | - /** @var ISecureRandom */ |
|
66 | - private $secureRandom; |
|
67 | - |
|
68 | - /** @var IConfig */ |
|
69 | - private $config; |
|
70 | - |
|
71 | - /** @var EventDispatcherInterface */ |
|
72 | - private $dispatcher; |
|
73 | - |
|
74 | - /** @var ITimeFactory */ |
|
75 | - private $timeFactory; |
|
76 | - |
|
77 | - /** |
|
78 | - * @param DbHandler $dbHandler |
|
79 | - * @param IClientService $httpClientService |
|
80 | - * @param ILogger $logger |
|
81 | - * @param IJobList $jobList |
|
82 | - * @param ISecureRandom $secureRandom |
|
83 | - * @param IConfig $config |
|
84 | - * @param EventDispatcherInterface $dispatcher |
|
85 | - * @param ITimeFactory $timeFactory |
|
86 | - */ |
|
87 | - public function __construct( |
|
88 | - DbHandler $dbHandler, |
|
89 | - IClientService $httpClientService, |
|
90 | - ILogger $logger, |
|
91 | - IJobList $jobList, |
|
92 | - ISecureRandom $secureRandom, |
|
93 | - IConfig $config, |
|
94 | - EventDispatcherInterface $dispatcher, |
|
95 | - ITimeFactory $timeFactory |
|
96 | - ) { |
|
97 | - $this->dbHandler = $dbHandler; |
|
98 | - $this->httpClientService = $httpClientService; |
|
99 | - $this->logger = $logger; |
|
100 | - $this->jobList = $jobList; |
|
101 | - $this->secureRandom = $secureRandom; |
|
102 | - $this->config = $config; |
|
103 | - $this->dispatcher = $dispatcher; |
|
104 | - $this->timeFactory = $timeFactory; |
|
105 | - } |
|
106 | - |
|
107 | - /** |
|
108 | - * add server to the list of trusted servers |
|
109 | - * |
|
110 | - * @param $url |
|
111 | - * @return int server id |
|
112 | - */ |
|
113 | - public function addServer($url) { |
|
114 | - $url = $this->updateProtocol($url); |
|
115 | - $result = $this->dbHandler->addServer($url); |
|
116 | - if ($result) { |
|
117 | - $token = $this->secureRandom->generate(16); |
|
118 | - $this->dbHandler->addToken($url, $token); |
|
119 | - $this->jobList->add( |
|
120 | - RequestSharedSecret::class, |
|
121 | - [ |
|
122 | - 'url' => $url, |
|
123 | - 'token' => $token, |
|
124 | - 'created' => $this->timeFactory->getTime() |
|
125 | - ] |
|
126 | - ); |
|
127 | - } |
|
128 | - |
|
129 | - return $result; |
|
130 | - } |
|
131 | - |
|
132 | - /** |
|
133 | - * enable/disable to automatically add servers to the list of trusted servers |
|
134 | - * once a federated share was created and accepted successfully |
|
135 | - * |
|
136 | - * @param bool $status |
|
137 | - */ |
|
138 | - public function setAutoAddServers($status) { |
|
139 | - $value = $status ? '1' : '0'; |
|
140 | - $this->config->setAppValue('federation', 'autoAddServers', $value); |
|
141 | - } |
|
142 | - |
|
143 | - /** |
|
144 | - * return if we automatically add servers to the list of trusted servers |
|
145 | - * once a federated share was created and accepted successfully |
|
146 | - * |
|
147 | - * @return bool |
|
148 | - */ |
|
149 | - public function getAutoAddServers() { |
|
150 | - $value = $this->config->getAppValue('federation', 'autoAddServers', '0'); |
|
151 | - return $value === '1'; |
|
152 | - } |
|
153 | - |
|
154 | - /** |
|
155 | - * get shared secret for the given server |
|
156 | - * |
|
157 | - * @param string $url |
|
158 | - * @return string |
|
159 | - */ |
|
160 | - public function getSharedSecret($url) { |
|
161 | - return $this->dbHandler->getSharedSecret($url); |
|
162 | - } |
|
163 | - |
|
164 | - /** |
|
165 | - * add shared secret for the given server |
|
166 | - * |
|
167 | - * @param string $url |
|
168 | - * @param $sharedSecret |
|
169 | - */ |
|
170 | - public function addSharedSecret($url, $sharedSecret) { |
|
171 | - $this->dbHandler->addSharedSecret($url, $sharedSecret); |
|
172 | - } |
|
173 | - |
|
174 | - /** |
|
175 | - * remove server from the list of trusted servers |
|
176 | - * |
|
177 | - * @param int $id |
|
178 | - */ |
|
179 | - public function removeServer($id) { |
|
180 | - $server = $this->dbHandler->getServerById($id); |
|
181 | - $this->dbHandler->removeServer($id); |
|
182 | - $event = new GenericEvent($server['url_hash']); |
|
183 | - $this->dispatcher->dispatch('OCP\Federation\TrustedServerEvent::remove', $event); |
|
184 | - } |
|
185 | - |
|
186 | - /** |
|
187 | - * get all trusted servers |
|
188 | - * |
|
189 | - * @return array |
|
190 | - */ |
|
191 | - public function getServers() { |
|
192 | - return $this->dbHandler->getAllServer(); |
|
193 | - } |
|
194 | - |
|
195 | - /** |
|
196 | - * check if given server is a trusted Nextcloud server |
|
197 | - * |
|
198 | - * @param string $url |
|
199 | - * @return bool |
|
200 | - */ |
|
201 | - public function isTrustedServer($url) { |
|
202 | - return $this->dbHandler->serverExists($url); |
|
203 | - } |
|
204 | - |
|
205 | - /** |
|
206 | - * set server status |
|
207 | - * |
|
208 | - * @param string $url |
|
209 | - * @param int $status |
|
210 | - */ |
|
211 | - public function setServerStatus($url, $status) { |
|
212 | - $this->dbHandler->setServerStatus($url, $status); |
|
213 | - } |
|
214 | - |
|
215 | - /** |
|
216 | - * @param string $url |
|
217 | - * @return int |
|
218 | - */ |
|
219 | - public function getServerStatus($url) { |
|
220 | - return $this->dbHandler->getServerStatus($url); |
|
221 | - } |
|
222 | - |
|
223 | - /** |
|
224 | - * check if URL point to a ownCloud/Nextcloud server |
|
225 | - * |
|
226 | - * @param string $url |
|
227 | - * @return bool |
|
228 | - */ |
|
229 | - public function isOwnCloudServer($url) { |
|
230 | - $isValidOwnCloud = false; |
|
231 | - $client = $this->httpClientService->newClient(); |
|
232 | - try { |
|
233 | - $result = $client->get( |
|
234 | - $url . '/status.php', |
|
235 | - [ |
|
236 | - 'timeout' => 3, |
|
237 | - 'connect_timeout' => 3, |
|
238 | - ] |
|
239 | - ); |
|
240 | - if ($result->getStatusCode() === Http::STATUS_OK) { |
|
241 | - $isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody()); |
|
242 | - |
|
243 | - } |
|
244 | - } catch (\Exception $e) { |
|
245 | - \OC::$server->getLogger()->logException($e, [ |
|
246 | - 'message' => 'No Nextcloud server.', |
|
247 | - 'level' => ILogger::DEBUG, |
|
248 | - 'app' => 'federation', |
|
249 | - ]); |
|
250 | - return false; |
|
251 | - } |
|
252 | - |
|
253 | - return $isValidOwnCloud; |
|
254 | - } |
|
255 | - |
|
256 | - /** |
|
257 | - * check if ownCloud version is >= 9.0 |
|
258 | - * |
|
259 | - * @param $status |
|
260 | - * @return bool |
|
261 | - * @throws HintException |
|
262 | - */ |
|
263 | - protected function checkOwnCloudVersion($status) { |
|
264 | - $decoded = json_decode($status, true); |
|
265 | - if (!empty($decoded) && isset($decoded['version'])) { |
|
266 | - if (!version_compare($decoded['version'], '9.0.0', '>=')) { |
|
267 | - throw new HintException('Remote server version is too low. 9.0 is required.'); |
|
268 | - } |
|
269 | - return true; |
|
270 | - } |
|
271 | - return false; |
|
272 | - } |
|
273 | - |
|
274 | - /** |
|
275 | - * check if the URL contain a protocol, if not add https |
|
276 | - * |
|
277 | - * @param string $url |
|
278 | - * @return string |
|
279 | - */ |
|
280 | - protected function updateProtocol($url) { |
|
281 | - if ( |
|
282 | - strpos($url, 'https://') === 0 |
|
283 | - || strpos($url, 'http://') === 0 |
|
284 | - ) { |
|
285 | - |
|
286 | - return $url; |
|
287 | - |
|
288 | - } |
|
289 | - |
|
290 | - return 'https://' . $url; |
|
291 | - } |
|
44 | + /** after a user list was exchanged at least once successfully */ |
|
45 | + const STATUS_OK = 1; |
|
46 | + /** waiting for shared secret or initial user list exchange */ |
|
47 | + const STATUS_PENDING = 2; |
|
48 | + /** something went wrong, misconfigured server, software bug,... user interaction needed */ |
|
49 | + const STATUS_FAILURE = 3; |
|
50 | + /** remote server revoked access */ |
|
51 | + const STATUS_ACCESS_REVOKED = 4; |
|
52 | + |
|
53 | + /** @var dbHandler */ |
|
54 | + private $dbHandler; |
|
55 | + |
|
56 | + /** @var IClientService */ |
|
57 | + private $httpClientService; |
|
58 | + |
|
59 | + /** @var ILogger */ |
|
60 | + private $logger; |
|
61 | + |
|
62 | + /** @var IJobList */ |
|
63 | + private $jobList; |
|
64 | + |
|
65 | + /** @var ISecureRandom */ |
|
66 | + private $secureRandom; |
|
67 | + |
|
68 | + /** @var IConfig */ |
|
69 | + private $config; |
|
70 | + |
|
71 | + /** @var EventDispatcherInterface */ |
|
72 | + private $dispatcher; |
|
73 | + |
|
74 | + /** @var ITimeFactory */ |
|
75 | + private $timeFactory; |
|
76 | + |
|
77 | + /** |
|
78 | + * @param DbHandler $dbHandler |
|
79 | + * @param IClientService $httpClientService |
|
80 | + * @param ILogger $logger |
|
81 | + * @param IJobList $jobList |
|
82 | + * @param ISecureRandom $secureRandom |
|
83 | + * @param IConfig $config |
|
84 | + * @param EventDispatcherInterface $dispatcher |
|
85 | + * @param ITimeFactory $timeFactory |
|
86 | + */ |
|
87 | + public function __construct( |
|
88 | + DbHandler $dbHandler, |
|
89 | + IClientService $httpClientService, |
|
90 | + ILogger $logger, |
|
91 | + IJobList $jobList, |
|
92 | + ISecureRandom $secureRandom, |
|
93 | + IConfig $config, |
|
94 | + EventDispatcherInterface $dispatcher, |
|
95 | + ITimeFactory $timeFactory |
|
96 | + ) { |
|
97 | + $this->dbHandler = $dbHandler; |
|
98 | + $this->httpClientService = $httpClientService; |
|
99 | + $this->logger = $logger; |
|
100 | + $this->jobList = $jobList; |
|
101 | + $this->secureRandom = $secureRandom; |
|
102 | + $this->config = $config; |
|
103 | + $this->dispatcher = $dispatcher; |
|
104 | + $this->timeFactory = $timeFactory; |
|
105 | + } |
|
106 | + |
|
107 | + /** |
|
108 | + * add server to the list of trusted servers |
|
109 | + * |
|
110 | + * @param $url |
|
111 | + * @return int server id |
|
112 | + */ |
|
113 | + public function addServer($url) { |
|
114 | + $url = $this->updateProtocol($url); |
|
115 | + $result = $this->dbHandler->addServer($url); |
|
116 | + if ($result) { |
|
117 | + $token = $this->secureRandom->generate(16); |
|
118 | + $this->dbHandler->addToken($url, $token); |
|
119 | + $this->jobList->add( |
|
120 | + RequestSharedSecret::class, |
|
121 | + [ |
|
122 | + 'url' => $url, |
|
123 | + 'token' => $token, |
|
124 | + 'created' => $this->timeFactory->getTime() |
|
125 | + ] |
|
126 | + ); |
|
127 | + } |
|
128 | + |
|
129 | + return $result; |
|
130 | + } |
|
131 | + |
|
132 | + /** |
|
133 | + * enable/disable to automatically add servers to the list of trusted servers |
|
134 | + * once a federated share was created and accepted successfully |
|
135 | + * |
|
136 | + * @param bool $status |
|
137 | + */ |
|
138 | + public function setAutoAddServers($status) { |
|
139 | + $value = $status ? '1' : '0'; |
|
140 | + $this->config->setAppValue('federation', 'autoAddServers', $value); |
|
141 | + } |
|
142 | + |
|
143 | + /** |
|
144 | + * return if we automatically add servers to the list of trusted servers |
|
145 | + * once a federated share was created and accepted successfully |
|
146 | + * |
|
147 | + * @return bool |
|
148 | + */ |
|
149 | + public function getAutoAddServers() { |
|
150 | + $value = $this->config->getAppValue('federation', 'autoAddServers', '0'); |
|
151 | + return $value === '1'; |
|
152 | + } |
|
153 | + |
|
154 | + /** |
|
155 | + * get shared secret for the given server |
|
156 | + * |
|
157 | + * @param string $url |
|
158 | + * @return string |
|
159 | + */ |
|
160 | + public function getSharedSecret($url) { |
|
161 | + return $this->dbHandler->getSharedSecret($url); |
|
162 | + } |
|
163 | + |
|
164 | + /** |
|
165 | + * add shared secret for the given server |
|
166 | + * |
|
167 | + * @param string $url |
|
168 | + * @param $sharedSecret |
|
169 | + */ |
|
170 | + public function addSharedSecret($url, $sharedSecret) { |
|
171 | + $this->dbHandler->addSharedSecret($url, $sharedSecret); |
|
172 | + } |
|
173 | + |
|
174 | + /** |
|
175 | + * remove server from the list of trusted servers |
|
176 | + * |
|
177 | + * @param int $id |
|
178 | + */ |
|
179 | + public function removeServer($id) { |
|
180 | + $server = $this->dbHandler->getServerById($id); |
|
181 | + $this->dbHandler->removeServer($id); |
|
182 | + $event = new GenericEvent($server['url_hash']); |
|
183 | + $this->dispatcher->dispatch('OCP\Federation\TrustedServerEvent::remove', $event); |
|
184 | + } |
|
185 | + |
|
186 | + /** |
|
187 | + * get all trusted servers |
|
188 | + * |
|
189 | + * @return array |
|
190 | + */ |
|
191 | + public function getServers() { |
|
192 | + return $this->dbHandler->getAllServer(); |
|
193 | + } |
|
194 | + |
|
195 | + /** |
|
196 | + * check if given server is a trusted Nextcloud server |
|
197 | + * |
|
198 | + * @param string $url |
|
199 | + * @return bool |
|
200 | + */ |
|
201 | + public function isTrustedServer($url) { |
|
202 | + return $this->dbHandler->serverExists($url); |
|
203 | + } |
|
204 | + |
|
205 | + /** |
|
206 | + * set server status |
|
207 | + * |
|
208 | + * @param string $url |
|
209 | + * @param int $status |
|
210 | + */ |
|
211 | + public function setServerStatus($url, $status) { |
|
212 | + $this->dbHandler->setServerStatus($url, $status); |
|
213 | + } |
|
214 | + |
|
215 | + /** |
|
216 | + * @param string $url |
|
217 | + * @return int |
|
218 | + */ |
|
219 | + public function getServerStatus($url) { |
|
220 | + return $this->dbHandler->getServerStatus($url); |
|
221 | + } |
|
222 | + |
|
223 | + /** |
|
224 | + * check if URL point to a ownCloud/Nextcloud server |
|
225 | + * |
|
226 | + * @param string $url |
|
227 | + * @return bool |
|
228 | + */ |
|
229 | + public function isOwnCloudServer($url) { |
|
230 | + $isValidOwnCloud = false; |
|
231 | + $client = $this->httpClientService->newClient(); |
|
232 | + try { |
|
233 | + $result = $client->get( |
|
234 | + $url . '/status.php', |
|
235 | + [ |
|
236 | + 'timeout' => 3, |
|
237 | + 'connect_timeout' => 3, |
|
238 | + ] |
|
239 | + ); |
|
240 | + if ($result->getStatusCode() === Http::STATUS_OK) { |
|
241 | + $isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody()); |
|
242 | + |
|
243 | + } |
|
244 | + } catch (\Exception $e) { |
|
245 | + \OC::$server->getLogger()->logException($e, [ |
|
246 | + 'message' => 'No Nextcloud server.', |
|
247 | + 'level' => ILogger::DEBUG, |
|
248 | + 'app' => 'federation', |
|
249 | + ]); |
|
250 | + return false; |
|
251 | + } |
|
252 | + |
|
253 | + return $isValidOwnCloud; |
|
254 | + } |
|
255 | + |
|
256 | + /** |
|
257 | + * check if ownCloud version is >= 9.0 |
|
258 | + * |
|
259 | + * @param $status |
|
260 | + * @return bool |
|
261 | + * @throws HintException |
|
262 | + */ |
|
263 | + protected function checkOwnCloudVersion($status) { |
|
264 | + $decoded = json_decode($status, true); |
|
265 | + if (!empty($decoded) && isset($decoded['version'])) { |
|
266 | + if (!version_compare($decoded['version'], '9.0.0', '>=')) { |
|
267 | + throw new HintException('Remote server version is too low. 9.0 is required.'); |
|
268 | + } |
|
269 | + return true; |
|
270 | + } |
|
271 | + return false; |
|
272 | + } |
|
273 | + |
|
274 | + /** |
|
275 | + * check if the URL contain a protocol, if not add https |
|
276 | + * |
|
277 | + * @param string $url |
|
278 | + * @return string |
|
279 | + */ |
|
280 | + protected function updateProtocol($url) { |
|
281 | + if ( |
|
282 | + strpos($url, 'https://') === 0 |
|
283 | + || strpos($url, 'http://') === 0 |
|
284 | + ) { |
|
285 | + |
|
286 | + return $url; |
|
287 | + |
|
288 | + } |
|
289 | + |
|
290 | + return 'https://' . $url; |
|
291 | + } |
|
292 | 292 | } |