summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/AppFramework/Http/Request.php5
-rw-r--r--lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php29
-rw-r--r--lib/private/Authentication/Token/Manager.php33
-rw-r--r--lib/private/Authentication/Token/PublicKeyTokenProvider.php11
-rw-r--r--lib/private/Collaboration/Collaborators/UserPlugin.php5
-rw-r--r--lib/private/DB/Connection.php3
-rw-r--r--lib/private/DirectEditing/Manager.php236
-rw-r--r--lib/private/DirectEditing/Token.php76
-rw-r--r--lib/private/Files/Node/File.php15
-rw-r--r--lib/private/Files/Node/HookConnector.php2
-rw-r--r--lib/private/Files/Node/Node.php23
-rw-r--r--lib/private/Files/Storage/DAV.php28
-rw-r--r--lib/private/Files/Storage/Local.php26
-rw-r--r--lib/private/Files/View.php24
-rw-r--r--lib/private/Group/Manager.php2
-rw-r--r--lib/private/Log/ExceptionSerializer.php4
-rw-r--r--lib/private/Server.php5
-rw-r--r--lib/private/Settings/Manager.php2
-rw-r--r--lib/private/Setup.php4
-rw-r--r--lib/private/Share20/DefaultShareProvider.php10
-rw-r--r--lib/private/Share20/Manager.php133
-rw-r--r--lib/private/Template/JSConfigHelper.php12
-rw-r--r--lib/private/URLGenerator.php5
-rw-r--r--lib/private/User/Database.php15
-rw-r--r--lib/private/User/Manager.php22
25 files changed, 653 insertions, 77 deletions
diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php
index 587178e8ede..9d210cd8419 100644
--- a/lib/private/AppFramework/Http/Request.php
+++ b/lib/private/AppFramework/Http/Request.php
@@ -853,6 +853,10 @@ class Request implements \ArrayAccess, \Countable, IRequest {
* @return string Server host
*/
public function getInsecureServerHost(): string {
+ if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
+ return $this->getOverwriteHost();
+ }
+
$host = 'localhost';
if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
@@ -868,6 +872,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
$host = $this->server['SERVER_NAME'];
}
}
+
return $host;
}
diff --git a/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php b/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php
new file mode 100644
index 00000000000..6719037b4a0
--- /dev/null
+++ b/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php
@@ -0,0 +1,29 @@
+<?php
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Authentication\Exceptions;
+
+class TokenPasswordExpiredException extends ExpiredTokenException {
+
+}
diff --git a/lib/private/Authentication/Token/Manager.php b/lib/private/Authentication/Token/Manager.php
index ea94efce54d..0642d4426c4 100644
--- a/lib/private/Authentication/Token/Manager.php
+++ b/lib/private/Authentication/Token/Manager.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace OC\Authentication\Token;
+use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Authentication\Exceptions\ExpiredTokenException;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
@@ -60,15 +61,29 @@ class Manager implements IProvider {
string $name,
int $type = IToken::TEMPORARY_TOKEN,
int $remember = IToken::DO_NOT_REMEMBER): IToken {
- return $this->publicKeyTokenProvider->generateToken(
- $token,
- $uid,
- $loginName,
- $password,
- $name,
- $type,
- $remember
- );
+ try {
+ return $this->publicKeyTokenProvider->generateToken(
+ $token,
+ $uid,
+ $loginName,
+ $password,
+ $name,
+ $type,
+ $remember
+ );
+ } catch (UniqueConstraintViolationException $e) {
+ // It's rare, but if two requests of the same session (e.g. env-based SAML)
+ // try to create the session token they might end up here at the same time
+ // because we use the session ID as token and the db token is created anew
+ // with every request.
+ //
+ // If the UIDs match, then this should be fine.
+ $existing = $this->getToken($token);
+ if ($existing->getUID() !== $uid) {
+ throw new \Exception('Token conflict handled, but UIDs do not match. This should not happen', 0, $e);
+ }
+ return $existing;
+ }
}
/**
diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php
index 624e2c0cadc..19987bec253 100644
--- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php
+++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php
@@ -25,6 +25,7 @@ namespace OC\Authentication\Token;
use OC\Authentication\Exceptions\ExpiredTokenException;
use OC\Authentication\Exceptions\InvalidTokenException;
+use OC\Authentication\Exceptions\TokenPasswordExpiredException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Exceptions\WipeTokenException;
use OC\Cache\CappedMemoryCache;
@@ -108,6 +109,11 @@ class PublicKeyTokenProvider implements IProvider {
throw new WipeTokenException($token);
}
+ if ($token->getPasswordInvalid() === true) {
+ //The password is invalid we should throw an TokenPasswordExpiredException
+ throw new TokenPasswordExpiredException($token);
+ }
+
return $token;
}
@@ -126,6 +132,11 @@ class PublicKeyTokenProvider implements IProvider {
throw new WipeTokenException($token);
}
+ if ($token->getPasswordInvalid() === true) {
+ //The password is invalid we should throw an TokenPasswordExpiredException
+ throw new TokenPasswordExpiredException($token);
+ }
+
return $token;
}
diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php
index 971b7025564..9de269224f5 100644
--- a/lib/private/Collaboration/Collaborators/UserPlugin.php
+++ b/lib/private/Collaboration/Collaborators/UserPlugin.php
@@ -71,7 +71,7 @@ class UserPlugin implements ISearchPlugin {
foreach ($userGroups as $userGroup) {
$usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $limit, $offset);
foreach ($usersTmp as $uid => $userDisplayName) {
- $users[$uid] = $userDisplayName;
+ $users[(string) $uid] = $userDisplayName;
}
}
} else {
@@ -80,7 +80,7 @@ class UserPlugin implements ISearchPlugin {
foreach ($usersTmp as $user) {
if ($user->isEnabled()) { // Don't keep deactivated users
- $users[$user->getUID()] = $user->getDisplayName();
+ $users[(string) $user->getUID()] = $user->getDisplayName();
}
}
}
@@ -94,6 +94,7 @@ class UserPlugin implements ISearchPlugin {
$foundUserById = false;
$lowerSearch = strtolower($search);
foreach ($users as $uid => $userDisplayName) {
+ $uid = (string) $uid;
if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) {
if (strtolower($uid) === $lowerSearch) {
$foundUserById = true;
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php
index f9c6f4155a3..65e1d84bfb1 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -38,6 +38,7 @@ use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception\ConstraintViolationException;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Schema\Schema;
+use Doctrine\DBAL\TransactionIsolationLevel;
use OC\DB\QueryBuilder\QueryBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
@@ -148,7 +149,7 @@ class Connection extends ReconnectWrapper implements IDBConnection {
$this->adapter = new $params['adapter']($this);
$this->tablePrefix = $params['tablePrefix'];
- parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED);
+ $this->setTransactionIsolation(TransactionIsolationLevel::READ_COMMITTED);
}
/**
diff --git a/lib/private/DirectEditing/Manager.php b/lib/private/DirectEditing/Manager.php
new file mode 100644
index 00000000000..26adad9e572
--- /dev/null
+++ b/lib/private/DirectEditing/Manager.php
@@ -0,0 +1,236 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\DirectEditing;
+
+use Doctrine\DBAL\FetchMode;
+use OCP\AppFramework\Http\NotFoundResponse;
+use OCP\AppFramework\Http\Response;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DirectEditing\ACreateFromTemplate;
+use OCP\DirectEditing\IEditor;
+use \OCP\DirectEditing\IManager;
+use OCP\DirectEditing\IToken;
+use OCP\DirectEditing\RegisterDirectEditorEvent;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Files\File;
+use OCP\Files\IRootFolder;
+use OCP\Files\NotFoundException;
+use OCP\IDBConnection;
+use OCP\IUserSession;
+use OCP\Security\ISecureRandom;
+use OCP\Share\IShare;
+
+class Manager implements IManager {
+
+ private const TOKEN_CLEANUP_TIME = 12 * 60 * 60 ;
+
+ public const TABLE_TOKENS = 'direct_edit';
+
+ /** @var IEditor[] */
+ private $editors = [];
+
+ /** @var IDBConnection */
+ private $connection;
+ /**
+ * @var ISecureRandom
+ */
+ private $random;
+ private $userId;
+ private $rootFolder;
+
+ public function __construct(
+ ISecureRandom $random,
+ IDBConnection $connection,
+ IUserSession $userSession,
+ IRootFolder $rootFolder
+ ) {
+ $this->random = $random;
+ $this->connection = $connection;
+ $this->userId = $userSession->getUser() ? $userSession->getUser()->getUID() : null;
+ $this->rootFolder = $rootFolder;
+ }
+
+ public function registerDirectEditor(IEditor $directEditor): void {
+ $this->editors[$directEditor->getId()] = $directEditor;
+ }
+
+ public function getEditors(): array {
+ return $this->editors;
+ }
+
+ public function getTemplates(string $editor, string $type): array {
+ if (!array_key_exists($editor, $this->editors)) {
+ throw new \RuntimeException('No matching editor found');
+ }
+ $templates = [];
+ foreach ($this->editors[$editor]->getCreators() as $creator) {
+ if ($creator instanceof ACreateFromTemplate && $creator->getId() === $type) {
+ $templates = $creator->getTemplates();
+ }
+ }
+ $return = [];
+ $return['templates'] = $templates;
+ return $return;
+ }
+
+ public function create(string $path, string $editorId, string $creatorId, $templateId = null): string {
+ $userFolder = $this->rootFolder->getUserFolder($this->userId);
+ $file = $userFolder->newFile($path);
+ $editor = $this->getEditor($editorId);
+ $creators = $editor->getCreators();
+ foreach ($creators as $creator) {
+ if ($creator->getId() === $creatorId) {
+ $creator->create($file, $creatorId, $templateId);
+ return $this->createToken($editorId, $file);
+ }
+ }
+ throw new \RuntimeException('No creator found');
+ }
+
+ public function open(int $fileId, string $editorId = null): string {
+ $file = $this->rootFolder->getUserFolder($this->userId)->getById($fileId);
+ if (count($file) === 0 || !($file[0] instanceof File) || $file === null) {
+ throw new NotFoundException();
+ }
+ /** @var File $file */
+ $file = $file[0];
+
+ if ($editorId === null) {
+ $editorId = $this->findEditorForFile($file);
+ }
+
+ return $this->createToken($editorId, $file);
+ }
+
+ private function findEditorForFile(File $file) {
+ foreach ($this->editors as $editor) {
+ if (in_array($file->getMimeType(), $editor->getMimetypes())) {
+ return $editor->getId();
+ }
+ }
+ throw new \RuntimeException('No default editor found for files mimetype');
+ }
+
+ public function edit(string $token): Response {
+ try {
+ /** @var IEditor $editor */
+ $tokenObject = $this->getToken($token);
+ if ($tokenObject->hasBeenAccessed()) {
+ throw new \RuntimeException('Token has already been used and can only be used for followup requests');
+ }
+ $editor = $this->getEditor($tokenObject->getEditor());
+ $this->accessToken($token);
+
+ } catch (\Throwable $throwable) {
+ $this->invalidateToken($token);
+ return new NotFoundResponse();
+ }
+ return $editor->open($tokenObject);
+ }
+
+ public function editSecure(File $file, string $editorId): TemplateResponse {
+ // TODO: Implementation in follow up
+ }
+
+ private function getEditor($editorId): IEditor {
+ if (!array_key_exists($editorId, $this->editors)) {
+ throw new \RuntimeException('No editor found');
+ }
+ return $this->editors[$editorId];
+ }
+
+ public function getToken(string $token): IToken {
+ $query = $this->connection->getQueryBuilder();
+ $query->select('*')->from(self::TABLE_TOKENS)
+ ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
+ $result = $query->execute();
+ if ($tokenRow = $result->fetch(FetchMode::ASSOCIATIVE)) {
+ return new Token($this, $tokenRow);
+ }
+ throw new \RuntimeException('Failed to validate the token');
+ }
+
+ public function cleanup(): int {
+ $query = $this->connection->getQueryBuilder();
+ $query->delete(self::TABLE_TOKENS)
+ ->where($query->expr()->lt('timestamp', $query->createNamedParameter(time() - self::TOKEN_CLEANUP_TIME)));
+ return $query->execute();
+ }
+
+ public function refreshToken(string $token): bool {
+ $query = $this->connection->getQueryBuilder();
+ $query->update(self::TABLE_TOKENS)
+ ->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
+ ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
+ $result = $query->execute();
+ return $result !== 0;
+ }
+
+
+ public function invalidateToken(string $token): bool {
+ $query = $this->connection->getQueryBuilder();
+ $query->delete(self::TABLE_TOKENS)
+ ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
+ $result = $query->execute();
+ return $result !== 0;
+ }
+
+ public function accessToken(string $token): bool {
+ $query = $this->connection->getQueryBuilder();
+ $query->update(self::TABLE_TOKENS)
+ ->set('accessed', $query->createNamedParameter(true, IQueryBuilder::PARAM_BOOL))
+ ->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
+ ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
+ $result = $query->execute();
+ return $result !== 0;
+ }
+
+ public function invokeTokenScope($userId): void {
+ \OC_User::setIncognitoMode(true);
+ \OC_User::setUserId($userId);
+ }
+
+ public function createToken($editorId, File $file, IShare $share = null): string {
+ $token = $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE);
+ $query = $this->connection->getQueryBuilder();
+ $query->insert(self::TABLE_TOKENS)
+ ->values([
+ 'token' => $query->createNamedParameter($token),
+ 'editor_id' => $query->createNamedParameter($editorId),
+ 'file_id' => $query->createNamedParameter($file->getId()),
+ 'user_id' => $query->createNamedParameter($this->userId),
+ 'share_id' => $query->createNamedParameter($share !== null ? $share->getId(): null),
+ 'timestamp' => $query->createNamedParameter(time())
+ ]);
+ $query->execute();
+ return $token;
+ }
+
+ public function getFileForToken($userId, $fileId) {
+ $userFolder = $this->rootFolder->getUserFolder($userId);
+ return $userFolder->getById($fileId)[0];
+ }
+
+}
diff --git a/lib/private/DirectEditing/Token.php b/lib/private/DirectEditing/Token.php
new file mode 100644
index 00000000000..148621a2cf3
--- /dev/null
+++ b/lib/private/DirectEditing/Token.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\DirectEditing;
+
+
+use OCP\DirectEditing\IToken;
+use OCP\Files\File;
+
+class Token implements IToken {
+
+ /** @var Manager */
+ private $manager;
+ private $data;
+
+ public function __construct(Manager $manager, $data) {
+ $this->manager = $manager;
+ $this->data = $data;
+ }
+
+ public function extend(): void {
+ $this->manager->refreshToken($this->data['token']);
+ }
+
+ public function invalidate(): void {
+ $this->manager->invalidateToken($this->data['token']);
+ }
+
+ public function getFile(): File {
+ if ($this->data['share_id'] !== null) {
+ return $this->manager->getShareForToken($this->data['share_id']);
+ }
+ return $this->manager->getFileForToken($this->data['user_id'], $this->data['file_id']);
+ }
+
+ public function getToken(): string {
+ return $this->data['token'];
+ }
+
+ public function useTokenScope(): void {
+ $this->manager->invokeTokenScope($this->data['user_id']);
+ }
+
+ public function hasBeenAccessed(): bool {
+ return (bool) $this->data['accessed'];
+ }
+
+ public function getEditor(): string {
+ return $this->data['editor_id'];
+ }
+
+ public function getUser(): string {
+ return $this->data['user_id'];
+ }
+
+}
diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php
index b504c7a29da..e4669f70709 100644
--- a/lib/private/Files/Node/File.php
+++ b/lib/private/Files/Node/File.php
@@ -28,6 +28,7 @@ namespace OC\Files\Node;
use OCP\Files\GenericFileException;
use OCP\Files\NotPermittedException;
+use OCP\Lock\LockedException;
class File extends Node implements \OCP\Files\File {
/**
@@ -42,7 +43,8 @@ class File extends Node implements \OCP\Files\File {
/**
* @return string
- * @throws \OCP\Files\NotPermittedException
+ * @throws NotPermittedException
+ * @throws LockedException
*/
public function getContent() {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) {
@@ -57,8 +59,9 @@ class File extends Node implements \OCP\Files\File {
/**
* @param string|resource $data
- * @throws \OCP\Files\NotPermittedException
+ * @throws NotPermittedException
* @throws \OCP\Files\GenericFileException
+ * @throws LockedException
*/
public function putContent($data) {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
@@ -76,7 +79,8 @@ class File extends Node implements \OCP\Files\File {
/**
* @param string $mode
* @return resource
- * @throws \OCP\Files\NotPermittedException
+ * @throws NotPermittedException
+ * @throws LockedException
*/
public function fopen($mode) {
$preHooks = array();
@@ -113,6 +117,11 @@ class File extends Node implements \OCP\Files\File {
}
}
+ /**
+ * @throws NotPermittedException
+ * @throws \OCP\Files\InvalidPathException
+ * @throws \OCP\Files\NotFoundException
+ */
public function delete() {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
$this->sendHooks(array('preDelete'));
diff --git a/lib/private/Files/Node/HookConnector.php b/lib/private/Files/Node/HookConnector.php
index b792b584077..417bb4980ee 100644
--- a/lib/private/Files/Node/HookConnector.php
+++ b/lib/private/Files/Node/HookConnector.php
@@ -24,10 +24,10 @@ namespace OC\Files\Node;
use OC\Files\Filesystem;
use OC\Files\View;
+use OCP\EventDispatcher\GenericEvent;
use OCP\Files\FileInfo;
use OCP\Util;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
class HookConnector {
/**
diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php
index 95d16cf5c99..7f50524f28d 100644
--- a/lib/private/Files/Node/Node.php
+++ b/lib/private/Files/Node/Node.php
@@ -33,6 +33,7 @@ use OCP\Files\FileInfo;
use OCP\Files\InvalidPathException;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
+use OCP\Lock\LockedException;
use Symfony\Component\EventDispatcher\GenericEvent;
// FIXME: this class really should be abstract
@@ -75,6 +76,7 @@ class Node implements \OCP\Files\Node {
*
* @param string $path path
* @return string non-existing node class
+ * @throws \Exception
*/
protected function createNonExistingNode($path) {
throw new \Exception('Must be implemented by subclasses');
@@ -117,6 +119,8 @@ class Node implements \OCP\Files\Node {
/**
* @param int $permissions
* @return bool
+ * @throws InvalidPathException
+ * @throws NotFoundException
*/
protected function checkPermissions($permissions) {
return ($this->getPermissions() & $permissions) === $permissions;
@@ -127,7 +131,9 @@ class Node implements \OCP\Files\Node {
/**
* @param int $mtime
- * @throws \OCP\Files\NotPermittedException
+ * @throws InvalidPathException
+ * @throws NotFoundException
+ * @throws NotPermittedException
*/
public function touch($mtime = null) {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
@@ -366,7 +372,7 @@ class Node implements \OCP\Files\Node {
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
- * @throws \OCP\Lock\LockedException
+ * @throws LockedException
*/
public function lock($type) {
$this->view->lockFile($this->path, $type);
@@ -374,7 +380,7 @@ class Node implements \OCP\Files\Node {
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
- * @throws \OCP\Lock\LockedException
+ * @throws LockedException
*/
public function changeLock($type) {
$this->view->changeLock($this->path, $type);
@@ -382,7 +388,7 @@ class Node implements \OCP\Files\Node {
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
- * @throws \OCP\Lock\LockedException
+ * @throws LockedException
*/
public function unlock($type) {
$this->view->unlockFile($this->path, $type);
@@ -390,8 +396,10 @@ class Node implements \OCP\Files\Node {
/**
* @param string $targetPath
- * @throws \OCP\Files\NotPermittedException if copy not allowed or failed
* @return \OC\Files\Node\Node
+ * @throws InvalidPathException
+ * @throws NotFoundException
+ * @throws NotPermittedException if copy not allowed or failed
*/
public function copy($targetPath) {
$targetPath = $this->normalizePath($targetPath);
@@ -414,8 +422,11 @@ class Node implements \OCP\Files\Node {
/**
* @param string $targetPath
- * @throws \OCP\Files\NotPermittedException if move not allowed or failed
* @return \OC\Files\Node\Node
+ * @throws InvalidPathException
+ * @throws NotFoundException
+ * @throws NotPermittedException if move not allowed or failed
+ * @throws LockedException
*/
public function move($targetPath) {
$targetPath = $this->normalizePath($targetPath);
diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php
index 69e6f309c31..ca96e6b7434 100644
--- a/lib/private/Files/Storage/DAV.php
+++ b/lib/private/Files/Storage/DAV.php
@@ -34,7 +34,6 @@
namespace OC\Files\Storage;
use Exception;
-use GuzzleHttp\Exception\RequestException;
use Icewind\Streams\CallbackWrapper;
use Icewind\Streams\IteratorDirectory;
use OC\Files\Filesystem;
@@ -45,6 +44,8 @@ use OCP\Files\FileInfo;
use OCP\Files\ForbiddenException;
use OCP\Files\StorageInvalidException;
use OCP\Files\StorageNotAvailableException;
+use OCP\Http\Client\IClientService;
+use OCP\ICertificateManager;
use OCP\ILogger;
use OCP\Util;
use Psr\Http\Message\ResponseInterface;
@@ -79,8 +80,10 @@ class DAV extends Common {
protected $client;
/** @var ArrayCache */
protected $statCache;
- /** @var \OCP\Http\Client\IClientService */
+ /** @var IClientService */
protected $httpClientService;
+ /** @var ICertificateManager */
+ protected $certManager;
/**
* @param array $params
@@ -111,13 +114,9 @@ class DAV extends Common {
}
if ($this->secure === true) {
// inject mock for testing
- $certManager = \OC::$server->getCertificateManager();
- if (is_null($certManager)) { //no user
- $certManager = \OC::$server->getCertificateManager(null);
- }
- $certPath = $certManager->getAbsoluteBundlePath();
- if (file_exists($certPath)) {
- $this->certPath = $certPath;
+ $this->certManager = \OC::$server->getCertificateManager();
+ if (is_null($this->certManager)) { //no user
+ $this->certManager = \OC::$server->getCertificateManager(null);
}
}
$this->root = $params['root'] ?? '/';
@@ -150,8 +149,15 @@ class DAV extends Common {
$this->client = new Client($settings);
$this->client->setThrowExceptions(true);
- if ($this->secure === true && $this->certPath) {
- $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
+
+ if($this->secure === true) {
+ $certPath = $this->certManager->getAbsoluteBundlePath();
+ if (file_exists($certPath)) {
+ $this->certPath = $certPath;
+ }
+ if ($this->certPath) {
+ $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
+ }
}
}
diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php
index e9a9e8e9885..aade0cdaf25 100644
--- a/lib/private/Files/Storage/Local.php
+++ b/lib/private/Files/Storage/Local.php
@@ -424,12 +424,26 @@ class Local extends \OC\Files\Storage\Common {
public function getETag($path) {
if ($this->is_file($path)) {
$stat = $this->stat($path);
- return md5(
- $stat['mtime'] .
- $stat['ino'] .
- $stat['dev'] .
- $stat['size']
- );
+
+ if ($stat === false) {
+ return md5('');
+ }
+
+ $toHash = '';
+ if (isset($stat['mtime'])) {
+ $toHash .= $stat['mtime'];
+ }
+ if (isset($stat['ino'])) {
+ $toHash .= $stat['ino'];
+ }
+ if (isset($stat['dev'])) {
+ $toHash .= $stat['dev'];
+ }
+ if (isset($stat['size'])) {
+ $toHash .= $stat['size'];
+ }
+
+ return md5($toHash);
} else {
return parent::getETag($path);
}
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index 4f4c4b90f2c..ed962bde1a4 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -589,6 +589,7 @@ class View {
/**
* @param string $path
* @return mixed
+ * @throws LockedException
*/
public function file_get_contents($path) {
return $this->basicOperation('file_get_contents', $path, array('read'));
@@ -640,7 +641,7 @@ class View {
* @param string $path
* @param string|resource $data
* @return bool|mixed
- * @throws \Exception
+ * @throws LockedException
*/
public function file_put_contents($path, $data) {
if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
@@ -739,6 +740,7 @@ class View {
* @param string $path2 target path
*
* @return bool|mixed
+ * @throws LockedException
*/
public function rename($path1, $path2) {
$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
@@ -962,6 +964,7 @@ class View {
* @param string $path
* @param string $mode 'r' or 'w'
* @return resource
+ * @throws LockedException
*/
public function fopen($path, $mode) {
$mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
@@ -1117,7 +1120,7 @@ class View {
* @param array $hooks (optional)
* @param mixed $extraParam (optional)
* @return mixed
- * @throws \Exception
+ * @throws LockedException
*
* This method takes requests for basic filesystem functions (e.g. reading & writing
* files), processes hooks and proxies, sanitises paths, and finally passes them on to
@@ -1919,7 +1922,7 @@ class View {
* @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
*
* @return bool False if the path is excluded from locking, true otherwise
- * @throws \OCP\Lock\LockedException if the path is already locked
+ * @throws LockedException if the path is already locked
*/
private function lockPath($path, $type, $lockMountPoint = false) {
$absolutePath = $this->getAbsolutePath($path);
@@ -1939,9 +1942,9 @@ class View {
$this->lockingProvider
);
}
- } catch (\OCP\Lock\LockedException $e) {
+ } catch (LockedException $e) {
// rethrow with the a human-readable path
- throw new \OCP\Lock\LockedException(
+ throw new LockedException(
$this->getPathRelativeToFiles($absolutePath),
$e
);
@@ -1959,7 +1962,7 @@ class View {
* @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
*
* @return bool False if the path is excluded from locking, true otherwise
- * @throws \OCP\Lock\LockedException if the path is already locked
+ * @throws LockedException if the path is already locked
*/
public function changeLock($path, $type, $lockMountPoint = false) {
$path = Filesystem::normalizePath($path);
@@ -1980,15 +1983,15 @@ class View {
$this->lockingProvider
);
}
- } catch (\OCP\Lock\LockedException $e) {
+ } catch (LockedException $e) {
try {
// rethrow with the a human-readable path
- throw new \OCP\Lock\LockedException(
+ throw new LockedException(
$this->getPathRelativeToFiles($absolutePath),
$e
);
} catch (\InvalidArgumentException $e) {
- throw new \OCP\Lock\LockedException(
+ throw new LockedException(
$absolutePath,
$e
);
@@ -2007,6 +2010,7 @@ class View {
* @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
*
* @return bool False if the path is excluded from locking, true otherwise
+ * @throws LockedException
*/
private function unlockPath($path, $type, $lockMountPoint = false) {
$absolutePath = $this->getAbsolutePath($path);
@@ -2038,6 +2042,7 @@ class View {
* @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
*
* @return bool False if the path is excluded from locking, true otherwise
+ * @throws LockedException
*/
public function lockFile($path, $type, $lockMountPoint = false) {
$absolutePath = $this->getAbsolutePath($path);
@@ -2064,6 +2069,7 @@ class View {
* @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
*
* @return bool False if the path is excluded from locking, true otherwise
+ * @throws LockedException
*/
public function unlockFile($path, $type, $lockMountPoint = false) {
$absolutePath = $this->getAbsolutePath($path);
diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php
index 0e51b4ec2cc..dc4a7fbd11c 100644
--- a/lib/private/Group/Manager.php
+++ b/lib/private/Group/Manager.php
@@ -392,7 +392,7 @@ class Manager extends PublicEmitter implements IGroupManager {
$matchingUsers = [];
foreach ($groupUsers as $groupUser) {
- $matchingUsers[$groupUser->getUID()] = $groupUser->getDisplayName();
+ $matchingUsers[(string) $groupUser->getUID()] = $groupUser->getDisplayName();
}
return $matchingUsers;
}
diff --git a/lib/private/Log/ExceptionSerializer.php b/lib/private/Log/ExceptionSerializer.php
index 8cfdb57b225..a3b855aea26 100644
--- a/lib/private/Log/ExceptionSerializer.php
+++ b/lib/private/Log/ExceptionSerializer.php
@@ -92,7 +92,9 @@ class ExceptionSerializer {
];
private function editTrace(array &$sensitiveValues, array $traceLine): array {
- $sensitiveValues = array_merge($sensitiveValues, $traceLine['args']);
+ if (isset($traceLine['args'])) {
+ $sensitiveValues = array_merge($sensitiveValues, $traceLine['args']);
+ }
$traceLine['args'] = ['*** sensitive parameters replaced ***'];
return $traceLine;
}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index b4af17ba288..fa2a521b6b1 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -199,6 +199,8 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(\OCP\Contacts\IManager::class, \OC\ContactsManager::class);
$this->registerAlias('ContactsManager', \OCP\Contacts\IManager::class);
+ $this->registerAlias(\OCP\DirectEditing\IManager::class, \OC\DirectEditing\Manager::class);
+
$this->registerAlias(IActionFactory::class, ActionFactory::class);
@@ -315,9 +317,6 @@ class Server extends ServerContainer implements IServerContainer {
});
$this->registerAlias('LazyRootFolder', \OCP\Files\IRootFolder::class);
- $this->registerService(\OC\User\Manager::class, function (Server $c) {
- return new \OC\User\Manager($c->getConfig(), $c->getEventDispatcher());
- });
$this->registerAlias('UserManager', \OC\User\Manager::class);
$this->registerAlias(\OCP\IUserManager::class, \OC\User\Manager::class);
diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php
index a5ffa9e0f1e..1205cb81525 100644
--- a/lib/private/Settings/Manager.php
+++ b/lib/private/Settings/Manager.php
@@ -348,7 +348,7 @@ class Manager implements IManager {
}
$sections = [
- 0 => [new Section('personal-info', $this->l->t('Personal info'), 0, $this->url->imagePath('core', 'actions/info.svg'))],
+ 0 => [new Section('personal-info', $this->l->t('Personal info'), 0, $this->url->imagePath('core', 'actions/user.svg'))],
5 => [new Section('security', $this->l->t('Security'), 0, $this->url->imagePath('settings', 'password.svg'))],
15 => [new Section('sync-clients', $this->l->t('Mobile & desktop'), 0, $this->url->imagePath('core', 'clients/phone.svg'))],
];
diff --git a/lib/private/Setup.php b/lib/private/Setup.php
index 266c70846c6..4ebd5618189 100644
--- a/lib/private/Setup.php
+++ b/lib/private/Setup.php
@@ -49,6 +49,7 @@ use OC\Authentication\Token\DefaultTokenCleanupJob;
use OC\Authentication\Token\DefaultTokenProvider;
use OC\Log\Rotate;
use OC\Preview\BackgroundCleanupJob;
+use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Defaults;
use OCP\IGroup;
use OCP\IL10N;
@@ -421,6 +422,9 @@ class Setup {
$userSession->login($username, $password);
$userSession->createSessionToken($request, $userSession->getUser()->getUID(), $username, $password);
+ $session = $userSession->getSession();
+ $session->set('last-password-confirm', \OC::$server->query(ITimeFactory::class)->getTime());
+
// Set email for admin
if (!empty($options['adminemail'])) {
$config->setUserValue($user->getUID(), 'settings', 'email', $options['adminemail']);
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index c25a7b03d5c..152e5d55394 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -144,9 +144,19 @@ class DefaultShareProvider implements IShareProvider {
//Set the UID of the user we share with
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
$qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
+
+ //If an expiration date is set store it
+ if ($share->getExpirationDate() !== null) {
+ $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
+ }
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
//Set the GID of the group we share with
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
+
+ //If an expiration date is set store it
+ if ($share->getExpirationDate() !== null) {
+ $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
+ }
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
//set label for public link
$qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index d9809fd128a..db9c704871d 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -55,6 +55,7 @@ use OCP\IUser;
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Mail\IMailer;
+use OCP\Security\Events\ValidatePasswordPolicyEvent;
use OCP\Security\IHasher;
use OCP\Security\ISecureRandom;
use OCP\Share;
@@ -191,8 +192,7 @@ class Manager implements IManager {
// Let others verify the password
try {
- $event = new GenericEvent($password);
- $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
+ $this->eventDispatcher->dispatch(new ValidatePasswordPolicyEvent($password));
} catch (HintException $e) {
throw new \Exception($e->getHint());
}
@@ -360,6 +360,77 @@ class Manager implements IManager {
* @throws \InvalidArgumentException
* @throws \Exception
*/
+ protected function validateExpirationDateInternal(\OCP\Share\IShare $share) {
+ $expirationDate = $share->getExpirationDate();
+
+ if ($expirationDate !== null) {
+ //Make sure the expiration date is a date
+ $expirationDate->setTime(0, 0, 0);
+
+ $date = new \DateTime();
+ $date->setTime(0, 0, 0);
+ if ($date >= $expirationDate) {
+ $message = $this->l->t('Expiration date is in the past');
+ throw new GenericShareException($message, $message, 404);
+ }
+ }
+
+ // If expiredate is empty set a default one if there is a default
+ $fullId = null;
+ try {
+ $fullId = $share->getFullId();
+ } catch (\UnexpectedValueException $e) {
+ // This is a new share
+ }
+
+ if ($fullId === null && $expirationDate === null && $this->shareApiInternalDefaultExpireDate()) {
+ $expirationDate = new \DateTime();
+ $expirationDate->setTime(0,0,0);
+ $expirationDate->add(new \DateInterval('P'.$this->shareApiInternalDefaultExpireDays().'D'));
+ }
+
+ // If we enforce the expiration date check that is does not exceed
+ if ($this->shareApiInternalDefaultExpireDateEnforced()) {
+ if ($expirationDate === null) {
+ throw new \InvalidArgumentException('Expiration date is enforced');
+ }
+
+ $date = new \DateTime();
+ $date->setTime(0, 0, 0);
+ $date->add(new \DateInterval('P' . $this->shareApiInternalDefaultExpireDays() . 'D'));
+ if ($date < $expirationDate) {
+ $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiInternalDefaultExpireDays()]);
+ throw new GenericShareException($message, $message, 404);
+ }
+ }
+
+ $accepted = true;
+ $message = '';
+ \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
+ 'expirationDate' => &$expirationDate,
+ 'accepted' => &$accepted,
+ 'message' => &$message,
+ 'passwordSet' => $share->getPassword() !== null,
+ ]);
+
+ if (!$accepted) {
+ throw new \Exception($message);
+ }
+
+ $share->setExpirationDate($expirationDate);
+
+ return $share;
+ }
+
+ /**
+ * Validate if the expiration date fits the system settings
+ *
+ * @param \OCP\Share\IShare $share The share to validate the expiration date of
+ * @return \OCP\Share\IShare The modified share object
+ * @throws GenericShareException
+ * @throws \InvalidArgumentException
+ * @throws \Exception
+ */
protected function validateExpirationDate(\OCP\Share\IShare $share) {
$expirationDate = $share->getExpirationDate();
@@ -636,8 +707,16 @@ class Manager implements IManager {
//Verify share type
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
$this->userCreateChecks($share);
+
+ //Verify the expiration date
+ $share = $this->validateExpirationDateInternal($share);
+
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
$this->groupCreateChecks($share);
+
+ //Verify the expiration date
+ $share = $this->validateExpirationDateInternal($share);
+
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
$this->linkCreateChecks($share);
$this->setLinkParent($share);
@@ -653,7 +732,7 @@ class Manager implements IManager {
);
//Verify the expiration date
- $this->validateExpirationDate($share);
+ $share = $this->validateExpirationDate($share);
//Verify the password
$this->verifyPassword($share->getPassword());
@@ -850,8 +929,20 @@ class Manager implements IManager {
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
$this->userCreateChecks($share);
+
+ if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
+ //Verify the expiration date
+ $this->validateExpirationDate($share);
+ $expirationDateUpdated = true;
+ }
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
$this->groupCreateChecks($share);
+
+ if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
+ //Verify the expiration date
+ $this->validateExpirationDate($share);
+ $expirationDateUpdated = true;
+ }
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
$this->linkCreateChecks($share);
@@ -1575,7 +1666,7 @@ class Manager implements IManager {
}
/**
- * Is default expire date enabled
+ * Is default link expire date enabled
*
* @return bool
*/
@@ -1584,7 +1675,7 @@ class Manager implements IManager {
}
/**
- * Is default expire date enforced
+ * Is default link expire date enforced
*`
* @return bool
*/
@@ -1593,9 +1684,9 @@ class Manager implements IManager {
$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
}
+
/**
- * Number of default expire days
- *shareApiLinkAllowPublicUpload
+ * Number of default link expire days
* @return int
*/
public function shareApiLinkDefaultExpireDays() {
@@ -1603,6 +1694,34 @@ class Manager implements IManager {
}
/**
+ * Is default internal expire date enabled
+ *
+ * @return bool
+ */
+ public function shareApiInternalDefaultExpireDate(): bool {
+ return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
+ }
+
+ /**
+ * Is default expire date enforced
+ *`
+ * @return bool
+ */
+ public function shareApiInternalDefaultExpireDateEnforced(): bool {
+ return $this->shareApiInternalDefaultExpireDate() &&
+ $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
+ }
+
+
+ /**
+ * Number of default expire days
+ * @return int
+ */
+ public function shareApiInternalDefaultExpireDays(): int {
+ return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
+ }
+
+ /**
* Allow public upload on link shares
*
* @return bool
diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php
index df5eda9121b..065bf2545e0 100644
--- a/lib/private/Template/JSConfigHelper.php
+++ b/lib/private/Template/JSConfigHelper.php
@@ -149,6 +149,13 @@ class JSConfigHelper {
}
$outgoingServer2serverShareEnabled = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
+ $defaultInternalExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
+ $defaultInternalExpireDate = $defaultInternalExpireDateEnforced = null;
+ if ($defaultInternalExpireDateEnabled) {
+ $defaultInternalExpireDate = (int) $this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
+ $defaultInternalExpireDateEnforced = $this->config->getAppValue('core', 'shareapi_internal_enforce_expire_date', 'no') === 'yes';
+ }
+
$countOfDataLocation = 0;
$dataLocation = str_replace(\OC::$SERVERROOT .'/', '', $this->config->getSystemValue('datadirectory', ''), $countOfDataLocation);
if($countOfDataLocation !== 1 || !$this->groupManager->isAdmin($uid)) {
@@ -255,7 +262,10 @@ class JSConfigHelper {
'resharingAllowed' => \OC\Share\Share::isResharingAllowed(),
'remoteShareAllowed' => $outgoingServer2serverShareEnabled,
'federatedCloudShareDoc' => $this->urlGenerator->linkToDocs('user-sharing-federated'),
- 'allowGroupSharing' => \OC::$server->getShareManager()->allowGroupSharing()
+ 'allowGroupSharing' => \OC::$server->getShareManager()->allowGroupSharing(),
+ 'defaultInternalExpireDateEnabled' => $defaultInternalExpireDateEnabled,
+ 'defaultInternalExpireDate' => $defaultInternalExpireDate,
+ 'defaultInternalExpireDateEnforced' => $defaultInternalExpireDateEnforced,
]
]),
"_theme" => json_encode([
diff --git a/lib/private/URLGenerator.php b/lib/private/URLGenerator.php
index 167690f3a6e..1fb69031f03 100644
--- a/lib/private/URLGenerator.php
+++ b/lib/private/URLGenerator.php
@@ -94,8 +94,9 @@ class URLGenerator implements IURLGenerator {
public function linkToOCSRouteAbsolute(string $routeName, array $arguments = []): string {
$route = \OC::$server->getRouter()->generate('ocs.'.$routeName, $arguments, false);
- if (strpos($route, '/index.php') === 0) {
- $route = substr($route, 10);
+ $indexPhpPos = strpos($route, '/index.php/');
+ if ($indexPhpPos !== false) {
+ $route = substr($route, $indexPhpPos + 10);
}
$route = substr($route, 7);
diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php
index 23dbe8c2334..a4c35deb2b8 100644
--- a/lib/private/User/Database.php
+++ b/lib/private/User/Database.php
@@ -58,7 +58,9 @@ declare(strict_types=1);
namespace OC\User;
use OC\Cache\CappedMemoryCache;
+use OCP\EventDispatcher\IEventDispatcher;
use OCP\IDBConnection;
+use OCP\Security\Events\ValidatePasswordPolicyEvent;
use OCP\User\Backend\ABackend;
use OCP\User\Backend\ICheckPasswordBackend;
use OCP\User\Backend\ICountUsersBackend;
@@ -68,7 +70,6 @@ use OCP\User\Backend\IGetHomeBackend;
use OCP\User\Backend\IGetRealUIDBackend;
use OCP\User\Backend\ISetDisplayNameBackend;
use OCP\User\Backend\ISetPasswordBackend;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
@@ -86,7 +87,7 @@ class Database extends ABackend
/** @var CappedMemoryCache */
private $cache;
- /** @var EventDispatcherInterface */
+ /** @var IEventDispatcher */
private $eventDispatcher;
/** @var IDBConnection */
@@ -98,13 +99,13 @@ class Database extends ABackend
/**
* \OC\User\Database constructor.
*
- * @param EventDispatcherInterface $eventDispatcher
+ * @param IEventDispatcher $eventDispatcher
* @param string $table
*/
public function __construct($eventDispatcher = null, $table = 'users') {
$this->cache = new CappedMemoryCache();
$this->table = $table;
- $this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OC::$server->getEventDispatcher();
+ $this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OC::$server->query(IEventDispatcher::class);
}
/**
@@ -130,8 +131,7 @@ class Database extends ABackend
$this->fixDI();
if (!$this->userExists($uid)) {
- $event = new GenericEvent($password);
- $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
+ $this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));
$qb = $this->dbConn->getQueryBuilder();
$qb->insert($this->table)
@@ -199,8 +199,7 @@ class Database extends ABackend
$this->fixDI();
if ($this->userExists($uid)) {
- $event = new GenericEvent($password);
- $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
+ $this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));
$hasher = \OC::$server->getHasher();
$hashedPassword = $hasher->hash($password);
diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php
index da2d354b7ef..29cae3da79b 100644
--- a/lib/private/User/Manager.php
+++ b/lib/private/User/Manager.php
@@ -33,12 +33,15 @@ namespace OC\User;
use OC\Hooks\PublicEmitter;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IUser;
use OCP\IUserBackend;
use OCP\IUserManager;
use OCP\User\Backend\IGetRealUIDBackend;
+use OCP\User\Events\CreateUserEvent;
+use OCP\User\Events\UserCreatedEvent;
use OCP\UserInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -72,17 +75,24 @@ class Manager extends PublicEmitter implements IUserManager {
/** @var IConfig */
private $config;
+
/** @var EventDispatcherInterface */
private $dispatcher;
- public function __construct(IConfig $config, EventDispatcherInterface $dispatcher) {
+ /** @var IEventDispatcher */
+ private $eventDispatcher;
+
+ public function __construct(IConfig $config,
+ EventDispatcherInterface $oldDispatcher,
+ IEventDispatcher $eventDispatcher) {
$this->config = $config;
- $this->dispatcher = $dispatcher;
+ $this->dispatcher = $oldDispatcher;
$cachedUsers = &$this->cachedUsers;
$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
/** @var \OC\User\User $user */
unset($cachedUsers[$user->getUID()]);
});
+ $this->eventDispatcher = $eventDispatcher;
}
/**
@@ -349,6 +359,7 @@ class Manager extends PublicEmitter implements IUserManager {
}
$this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
+ $this->eventDispatcher->dispatchTyped(new CreateUserEvent($uid, $password));
$state = $backend->createUser($uid, $password);
if($state === false) {
throw new \InvalidArgumentException($l->t('Could not create user'));
@@ -356,6 +367,7 @@ class Manager extends PublicEmitter implements IUserManager {
$user = $this->getUserObject($uid, $backend);
if ($user instanceof IUser) {
$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
+ $this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
}
return $user;
}
@@ -460,11 +472,11 @@ class Manager extends PublicEmitter implements IUserManager {
->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
-
+
$result = $queryBuilder->execute();
$count = $result->fetchColumn();
$result->closeCursor();
-
+
if ($count !== false) {
$count = (int)$count;
} else {
@@ -494,7 +506,7 @@ class Manager extends PublicEmitter implements IUserManager {
$result = $queryBuilder->execute();
$count = $result->fetchColumn();
$result->closeCursor();
-
+
if ($count !== false) {
$count = (int)$count;
} else {