diff options
Diffstat (limited to 'lib/private/Authentication')
68 files changed, 853 insertions, 2002 deletions
diff --git a/lib/private/Authentication/Events/ARemoteWipeEvent.php b/lib/private/Authentication/Events/ARemoteWipeEvent.php index e36b7d1087b..ba1e93d26ae 100644 --- a/lib/private/Authentication/Events/ARemoteWipeEvent.php +++ b/lib/private/Authentication/Events/ARemoteWipeEvent.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; diff --git a/lib/private/Authentication/Events/AppPasswordCreatedEvent.php b/lib/private/Authentication/Events/AppPasswordCreatedEvent.php index a90abd25026..bf502ade0cc 100644 --- a/lib/private/Authentication/Events/AppPasswordCreatedEvent.php +++ b/lib/private/Authentication/Events/AppPasswordCreatedEvent.php @@ -3,38 +3,19 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; -use OC\Authentication\Token\IToken; +use OCP\Authentication\Token\IToken; use OCP\EventDispatcher\Event; class AppPasswordCreatedEvent extends Event { - /** @var IToken */ - private $token; - - public function __construct(IToken $token) { + public function __construct( + private IToken $token, + ) { parent::__construct(); - $this->token = $token; } public function getToken(): IToken { diff --git a/lib/private/Authentication/Events/LoginFailed.php b/lib/private/Authentication/Events/LoginFailed.php index ef702e40a59..23eeaef87ad 100644 --- a/lib/private/Authentication/Events/LoginFailed.php +++ b/lib/private/Authentication/Events/LoginFailed.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, 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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; diff --git a/lib/private/Authentication/Events/RemoteWipeFinished.php b/lib/private/Authentication/Events/RemoteWipeFinished.php index 10e72de096c..9704ebff3f5 100644 --- a/lib/private/Authentication/Events/RemoteWipeFinished.php +++ b/lib/private/Authentication/Events/RemoteWipeFinished.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; diff --git a/lib/private/Authentication/Events/RemoteWipeStarted.php b/lib/private/Authentication/Events/RemoteWipeStarted.php index 273a319b1fb..ad0f72f0e09 100644 --- a/lib/private/Authentication/Events/RemoteWipeStarted.php +++ b/lib/private/Authentication/Events/RemoteWipeStarted.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; diff --git a/lib/private/Authentication/Exceptions/ExpiredTokenException.php b/lib/private/Authentication/Exceptions/ExpiredTokenException.php index 0dc92b45920..eed2358d29d 100644 --- a/lib/private/Authentication/Exceptions/ExpiredTokenException.php +++ b/lib/private/Authentication/Exceptions/ExpiredTokenException.php @@ -3,41 +3,26 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Exceptions; use OC\Authentication\Token\IToken; -class ExpiredTokenException extends InvalidTokenException { - /** @var IToken */ - private $token; - - public function __construct(IToken $token) { - parent::__construct(); - - $this->token = $token; +/** + * @deprecated 28.0.0 use {@see \OCP\Authentication\Exceptions\ExpiredTokenException} instead + */ +class ExpiredTokenException extends \OCP\Authentication\Exceptions\ExpiredTokenException { + public function __construct( + IToken $token, + ) { + parent::__construct($token); } public function getToken(): IToken { - return $this->token; + $token = parent::getToken(); + /** @var IToken $token We know that we passed OC interface from constructor */ + return $token; } } diff --git a/lib/private/Authentication/Exceptions/InvalidProviderException.php b/lib/private/Authentication/Exceptions/InvalidProviderException.php index 86b44e4d0e2..9dbf3a7782a 100644 --- a/lib/private/Authentication/Exceptions/InvalidProviderException.php +++ b/lib/private/Authentication/Exceptions/InvalidProviderException.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Exceptions; @@ -29,7 +12,7 @@ use Exception; use Throwable; class InvalidProviderException extends Exception { - public function __construct(string $providerId, Throwable $previous = null) { + public function __construct(string $providerId, ?Throwable $previous = null) { parent::__construct("The provider '$providerId' does not exist'", 0, $previous); } } diff --git a/lib/private/Authentication/Exceptions/InvalidTokenException.php b/lib/private/Authentication/Exceptions/InvalidTokenException.php index acaabff6b88..d23f767e69b 100644 --- a/lib/private/Authentication/Exceptions/InvalidTokenException.php +++ b/lib/private/Authentication/Exceptions/InvalidTokenException.php @@ -1,30 +1,15 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; -use Exception; - -class InvalidTokenException extends Exception { +/** + * @deprecated 28.0.0 use OCP version instead + */ +class InvalidTokenException extends \OCP\Authentication\Exceptions\InvalidTokenException { } diff --git a/lib/private/Authentication/Exceptions/LoginRequiredException.php b/lib/private/Authentication/Exceptions/LoginRequiredException.php index cc3e8636214..d18ec4f1fbf 100644 --- a/lib/private/Authentication/Exceptions/LoginRequiredException.php +++ b/lib/private/Authentication/Exceptions/LoginRequiredException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php b/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php index 28829390f70..ec833a5a3d0 100644 --- a/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php +++ b/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/PasswordlessTokenException.php b/lib/private/Authentication/Exceptions/PasswordlessTokenException.php index c974cc3d6fd..a11e9a5f8d5 100644 --- a/lib/private/Authentication/Exceptions/PasswordlessTokenException.php +++ b/lib/private/Authentication/Exceptions/PasswordlessTokenException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php b/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php index 7710673ce7c..aa331f31fd0 100644 --- a/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php +++ b/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php @@ -3,25 +3,8 @@ 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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php b/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php index 9733c4547b4..8873b2c9f85 100644 --- a/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php +++ b/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php b/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php index 520372f3087..257f59772fc 100644 --- a/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php +++ b/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/WipeTokenException.php b/lib/private/Authentication/Exceptions/WipeTokenException.php index 1c60ab9da78..6bf0565434a 100644 --- a/lib/private/Authentication/Exceptions/WipeTokenException.php +++ b/lib/private/Authentication/Exceptions/WipeTokenException.php @@ -3,41 +3,26 @@ 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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Exceptions; use OC\Authentication\Token\IToken; -class WipeTokenException extends InvalidTokenException { - /** @var IToken */ - private $token; - - public function __construct(IToken $token) { - parent::__construct(); - - $this->token = $token; +/** + * @deprecated 28.0.0 use {@see \OCP\Authentication\Exceptions\WipeTokenException} instead + */ +class WipeTokenException extends \OCP\Authentication\Exceptions\WipeTokenException { + public function __construct( + IToken $token, + ) { + parent::__construct($token); } public function getToken(): IToken { - return $this->token; + $token = parent::getToken(); + /** @var IToken $token We know that we passed OC interface from constructor */ + return $token; } } diff --git a/lib/private/Authentication/Listeners/LoginFailedListener.php b/lib/private/Authentication/Listeners/LoginFailedListener.php index d2971e07b55..0358887bb86 100644 --- a/lib/private/Authentication/Listeners/LoginFailedListener.php +++ b/lib/private/Authentication/Listeners/LoginFailedListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; diff --git a/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php b/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php index edebb2a2641..457630eff27 100644 --- a/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php +++ b/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -46,7 +28,7 @@ class RemoteWipeActivityListener implements IEventListener { private $logger; public function __construct(IActvityManager $activityManager, - LoggerInterface $logger) { + LoggerInterface $logger) { $this->activityManager = $activityManager; $this->logger = $logger; } diff --git a/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php b/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php index cba2b183589..96878c44123 100644 --- a/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php +++ b/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -57,9 +39,9 @@ class RemoteWipeEmailListener implements IEventListener { private $logger; public function __construct(IMailer $mailer, - IUserManager $userManager, - IL10nFactory $l10nFactory, - LoggerInterface $logger) { + IUserManager $userManager, + IL10nFactory $l10nFactory, + LoggerInterface $logger) { $this->mailer = $mailer; $this->userManager = $userManager; $this->l10n = $l10nFactory->get('core'); diff --git a/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php b/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php index 81feab32746..5781c1edf16 100644 --- a/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php +++ b/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -45,7 +27,7 @@ class RemoteWipeNotificationsListener implements IEventListener { private $timeFactory; public function __construct(INotificationManager $notificationManager, - ITimeFactory $timeFactory) { + ITimeFactory $timeFactory) { $this->notificationManager = $notificationManager; $this->timeFactory = $timeFactory; } @@ -63,7 +45,7 @@ class RemoteWipeNotificationsListener implements IEventListener { $notification->setApp('auth') ->setUser($token->getUID()) ->setDateTime($this->timeFactory->getDateTime()) - ->setObject('token', (string) $token->getId()) + ->setObject('token', (string)$token->getId()) ->setSubject($event, [ 'name' => $token->getName(), ]); diff --git a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php index 5e657be0763..a619021d192 100644 --- a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -33,29 +16,34 @@ use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Storage\IStorage; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\UserDeletedEvent; +use Psr\Log\LoggerInterface; +/** @template-implements IEventListener<BeforeUserDeletedEvent|UserDeletedEvent> */ class UserDeletedFilesCleanupListener implements IEventListener { /** @var array<string,IStorage> */ private $homeStorageCache = []; - /** @var IMountProviderCollection */ - private $mountProviderCollection; - - public function __construct(IMountProviderCollection $mountProviderCollection) { - $this->mountProviderCollection = $mountProviderCollection; + public function __construct( + private IMountProviderCollection $mountProviderCollection, + private LoggerInterface $logger, + ) { } public function handle(Event $event): void { + $user = $event->getUser(); + // since we can't reliably get the user home storage after the user is deleted // but the user deletion might get canceled during the before event // we only cache the user home storage during the before event and then do the // action deletion during the after event if ($event instanceof BeforeUserDeletedEvent) { - $userHome = $this->mountProviderCollection->getHomeMountForUser($event->getUser()); + $this->logger->debug('Prepare deleting storage for user {userId}', ['userId' => $user->getUID()]); + + $userHome = $this->mountProviderCollection->getHomeMountForUser($user); $storage = $userHome->getStorage(); if (!$storage) { - throw new \Exception("User has no home storage"); + throw new \Exception('Account has no home storage'); } // remove all wrappers, so we do the delete directly on the home storage bypassing any wrapper @@ -67,16 +55,18 @@ class UserDeletedFilesCleanupListener implements IEventListener { $this->homeStorageCache[$event->getUser()->getUID()] = $storage; } if ($event instanceof UserDeletedEvent) { - if (!isset($this->homeStorageCache[$event->getUser()->getUID()])) { - throw new \Exception("UserDeletedEvent fired without matching BeforeUserDeletedEvent"); + if (!isset($this->homeStorageCache[$user->getUID()])) { + throw new \Exception('UserDeletedEvent fired without matching BeforeUserDeletedEvent'); } - $storage = $this->homeStorageCache[$event->getUser()->getUID()]; + $storage = $this->homeStorageCache[$user->getUID()]; $cache = $storage->getCache(); $storage->rmdir(''); + $this->logger->debug('Deleted storage for user {userId}', ['userId' => $user->getUID()]); + if ($cache instanceof Cache) { $cache->clear(); } else { - throw new \Exception("Home storage has invalid cache"); + throw new \Exception('Home storage has invalid cache'); } } } diff --git a/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php index e478c21239f..5f21c640780 100644 --- a/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; diff --git a/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php index a09a08568d5..3631c04432c 100644 --- a/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -44,7 +26,7 @@ class UserDeletedTokenCleanupListener implements IEventListener { private $logger; public function __construct(Manager $manager, - LoggerInterface $logger) { + LoggerInterface $logger) { $this->manager = $manager; $this->logger = $logger; } diff --git a/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php index 4927c3ac7f9..67f8ff7cfcd 100644 --- a/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -31,6 +14,7 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\User\Events\UserDeletedEvent; +/** @template-implements IEventListener<UserDeletedEvent> */ class UserDeletedWebAuthnCleanupListener implements IEventListener { /** @var PublicKeyCredentialMapper */ private $credentialMapper; diff --git a/lib/private/Authentication/Listeners/UserLoggedInListener.php b/lib/private/Authentication/Listeners/UserLoggedInListener.php index df030c44749..a8d4baeafa1 100644 --- a/lib/private/Authentication/Listeners/UserLoggedInListener.php +++ b/lib/private/Authentication/Listeners/UserLoggedInListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; diff --git a/lib/private/Authentication/Login/ALoginCommand.php b/lib/private/Authentication/Login/ALoginCommand.php index 7944eac1b62..a9f51f0da9e 100644 --- a/lib/private/Authentication/Login/ALoginCommand.php +++ b/lib/private/Authentication/Login/ALoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/Chain.php b/lib/private/Authentication/Login/Chain.php index 3c3179472c4..fc90d9225a7 100644 --- a/lib/private/Authentication/Login/Chain.php +++ b/lib/private/Authentication/Login/Chain.php @@ -3,90 +3,26 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; class Chain { - /** @var PreLoginHookCommand */ - private $preLoginHookCommand; - - /** @var UserDisabledCheckCommand */ - private $userDisabledCheckCommand; - - /** @var UidLoginCommand */ - private $uidLoginCommand; - - /** @var EmailLoginCommand */ - private $emailLoginCommand; - - /** @var LoggedInCheckCommand */ - private $loggedInCheckCommand; - - /** @var CompleteLoginCommand */ - private $completeLoginCommand; - - /** @var CreateSessionTokenCommand */ - private $createSessionTokenCommand; - - /** @var ClearLostPasswordTokensCommand */ - private $clearLostPasswordTokensCommand; - - /** @var UpdateLastPasswordConfirmCommand */ - private $updateLastPasswordConfirmCommand; - - /** @var SetUserTimezoneCommand */ - private $setUserTimezoneCommand; - - /** @var TwoFactorCommand */ - private $twoFactorCommand; - - /** @var FinishRememberedLoginCommand */ - private $finishRememberedLoginCommand; - - public function __construct(PreLoginHookCommand $preLoginHookCommand, - UserDisabledCheckCommand $userDisabledCheckCommand, - UidLoginCommand $uidLoginCommand, - EmailLoginCommand $emailLoginCommand, - LoggedInCheckCommand $loggedInCheckCommand, - CompleteLoginCommand $completeLoginCommand, - CreateSessionTokenCommand $createSessionTokenCommand, - ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, - UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, - SetUserTimezoneCommand $setUserTimezoneCommand, - TwoFactorCommand $twoFactorCommand, - FinishRememberedLoginCommand $finishRememberedLoginCommand + public function __construct( + private PreLoginHookCommand $preLoginHookCommand, + private UserDisabledCheckCommand $userDisabledCheckCommand, + private UidLoginCommand $uidLoginCommand, + private LoggedInCheckCommand $loggedInCheckCommand, + private CompleteLoginCommand $completeLoginCommand, + private CreateSessionTokenCommand $createSessionTokenCommand, + private ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, + private UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, + private SetUserTimezoneCommand $setUserTimezoneCommand, + private TwoFactorCommand $twoFactorCommand, + private FinishRememberedLoginCommand $finishRememberedLoginCommand, + private FlowV2EphemeralSessionsCommand $flowV2EphemeralSessionsCommand, ) { - $this->preLoginHookCommand = $preLoginHookCommand; - $this->userDisabledCheckCommand = $userDisabledCheckCommand; - $this->uidLoginCommand = $uidLoginCommand; - $this->emailLoginCommand = $emailLoginCommand; - $this->loggedInCheckCommand = $loggedInCheckCommand; - $this->completeLoginCommand = $completeLoginCommand; - $this->createSessionTokenCommand = $createSessionTokenCommand; - $this->clearLostPasswordTokensCommand = $clearLostPasswordTokensCommand; - $this->updateLastPasswordConfirmCommand = $updateLastPasswordConfirmCommand; - $this->setUserTimezoneCommand = $setUserTimezoneCommand; - $this->twoFactorCommand = $twoFactorCommand; - $this->finishRememberedLoginCommand = $finishRememberedLoginCommand; } public function process(LoginData $loginData): LoginResult { @@ -94,9 +30,9 @@ class Chain { $chain ->setNext($this->userDisabledCheckCommand) ->setNext($this->uidLoginCommand) - ->setNext($this->emailLoginCommand) ->setNext($this->loggedInCheckCommand) ->setNext($this->completeLoginCommand) + ->setNext($this->flowV2EphemeralSessionsCommand) ->setNext($this->createSessionTokenCommand) ->setNext($this->clearLostPasswordTokensCommand) ->setNext($this->updateLastPasswordConfirmCommand) diff --git a/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php b/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php index 1643b5af1d5..40369c383ac 100644 --- a/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php +++ b/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/CompleteLoginCommand.php b/lib/private/Authentication/Login/CompleteLoginCommand.php index 46f7f1a3fa7..ec6fdf75f40 100644 --- a/lib/private/Authentication/Login/CompleteLoginCommand.php +++ b/lib/private/Authentication/Login/CompleteLoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/CreateSessionTokenCommand.php b/lib/private/Authentication/Login/CreateSessionTokenCommand.php index ba237dfbf20..7619ad90d93 100644 --- a/lib/private/Authentication/Login/CreateSessionTokenCommand.php +++ b/lib/private/Authentication/Login/CreateSessionTokenCommand.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -39,7 +20,7 @@ class CreateSessionTokenCommand extends ALoginCommand { private $userSession; public function __construct(IConfig $config, - Session $userSession) { + Session $userSession) { $this->config = $config; $this->userSession = $userSession; } diff --git a/lib/private/Authentication/Login/EmailLoginCommand.php b/lib/private/Authentication/Login/EmailLoginCommand.php deleted file mode 100644 index 315fe450697..00000000000 --- a/lib/private/Authentication/Login/EmailLoginCommand.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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\Login; - -use OCP\IUserManager; - -class EmailLoginCommand extends ALoginCommand { - /** @var IUserManager */ - private $userManager; - - public function __construct(IUserManager $userManager) { - $this->userManager = $userManager; - } - - public function process(LoginData $loginData): LoginResult { - if ($loginData->getUser() === false) { - if (!filter_var($loginData->getUsername(), FILTER_VALIDATE_EMAIL)) { - return $this->processNextOrFinishSuccessfully($loginData); - } - - $users = $this->userManager->getByEmail($loginData->getUsername()); - // we only allow login by email if unique - if (count($users) === 1) { - // FIXME: This is a workaround to still stick to configured LDAP login filters - // this can be removed once the email login is properly implemented in the local user backend - // as described in https://github.com/nextcloud/server/issues/5221 - if ($users[0]->getBackendClassName() === 'LDAP') { - return $this->processNextOrFinishSuccessfully($loginData); - } - - $username = $users[0]->getUID(); - if ($username !== $loginData->getUsername()) { - $user = $this->userManager->checkPassword( - $username, - $loginData->getPassword() - ); - if ($user !== false) { - $loginData->setUser($user); - $loginData->setUsername($username); - } - } - } - } - - return $this->processNextOrFinishSuccessfully($loginData); - } -} diff --git a/lib/private/Authentication/Login/FinishRememberedLoginCommand.php b/lib/private/Authentication/Login/FinishRememberedLoginCommand.php index 04ed7ed846e..3eb1f8f1a65 100644 --- a/lib/private/Authentication/Login/FinishRememberedLoginCommand.php +++ b/lib/private/Authentication/Login/FinishRememberedLoginCommand.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -41,7 +23,7 @@ class FinishRememberedLoginCommand extends ALoginCommand { } public function process(LoginData $loginData): LoginResult { - if ($loginData->isRememberLogin() && $this->config->getSystemValue('auto_logout', false) === false) { + if ($loginData->isRememberLogin() && !$this->config->getSystemValueBool('auto_logout', false)) { $this->userSession->createRememberMeToken($loginData->getUser()); } diff --git a/lib/private/Authentication/Login/FlowV2EphemeralSessionsCommand.php b/lib/private/Authentication/Login/FlowV2EphemeralSessionsCommand.php new file mode 100644 index 00000000000..82dd829334d --- /dev/null +++ b/lib/private/Authentication/Login/FlowV2EphemeralSessionsCommand.php @@ -0,0 +1,30 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Authentication\Login; + +use OC\Core\Controller\ClientFlowLoginV2Controller; +use OCP\ISession; +use OCP\IURLGenerator; + +class FlowV2EphemeralSessionsCommand extends ALoginCommand { + public function __construct( + private ISession $session, + private IURLGenerator $urlGenerator, + ) { + } + + public function process(LoginData $loginData): LoginResult { + $loginV2GrantRoute = $this->urlGenerator->linkToRoute('core.ClientFlowLoginV2.grantPage'); + if (str_starts_with($loginData->getRedirectUrl() ?? '', $loginV2GrantRoute)) { + $this->session->set(ClientFlowLoginV2Controller::EPHEMERAL_NAME, true); + } + + return $this->processNextOrFinishSuccessfully($loginData); + } +} diff --git a/lib/private/Authentication/Login/LoggedInCheckCommand.php b/lib/private/Authentication/Login/LoggedInCheckCommand.php index dc1a4d2d883..b6b59ced6ce 100644 --- a/lib/private/Authentication/Login/LoggedInCheckCommand.php +++ b/lib/private/Authentication/Login/LoggedInCheckCommand.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -39,7 +20,7 @@ class LoggedInCheckCommand extends ALoginCommand { private $dispatcher; public function __construct(LoggerInterface $logger, - IEventDispatcher $dispatcher) { + IEventDispatcher $dispatcher) { $this->logger = $logger; $this->dispatcher = $dispatcher; } diff --git a/lib/private/Authentication/Login/LoginData.php b/lib/private/Authentication/Login/LoginData.php index 240a1dc6476..1ad97a9d559 100644 --- a/lib/private/Authentication/Login/LoginData.php +++ b/lib/private/Authentication/Login/LoginData.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -55,11 +37,11 @@ class LoginData { private $rememberLogin = true; public function __construct(IRequest $request, - string $username, - ?string $password, - string $redirectUrl = null, - string $timeZone = '', - string $timeZoneOffset = '') { + string $username, + ?string $password, + ?string $redirectUrl = null, + string $timeZone = '', + string $timeZoneOffset = '') { $this->request = $request; $this->username = $username; $this->password = $password; diff --git a/lib/private/Authentication/Login/LoginResult.php b/lib/private/Authentication/Login/LoginResult.php index dec012c2fc9..95e87b520e3 100644 --- a/lib/private/Authentication/Login/LoginResult.php +++ b/lib/private/Authentication/Login/LoginResult.php @@ -3,28 +3,13 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; +use OC\Core\Controller\LoginController; + class LoginResult { /** @var bool */ private $success; @@ -59,7 +44,10 @@ class LoginResult { return $result; } - public static function failure(LoginData $data, string $msg = null): LoginResult { + /** + * @param LoginController::LOGIN_MSG_*|null $msg + */ + public static function failure(LoginData $data, ?string $msg = null): LoginResult { $result = new static(false, $data); if ($msg !== null) { $result->setErrorMessage($msg); diff --git a/lib/private/Authentication/Login/PreLoginHookCommand.php b/lib/private/Authentication/Login/PreLoginHookCommand.php index 21c97433f5a..d5aa174094d 100644 --- a/lib/private/Authentication/Login/PreLoginHookCommand.php +++ b/lib/private/Authentication/Login/PreLoginHookCommand.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/SetUserTimezoneCommand.php b/lib/private/Authentication/Login/SetUserTimezoneCommand.php index f68fce1771e..90bc444ae7d 100644 --- a/lib/private/Authentication/Login/SetUserTimezoneCommand.php +++ b/lib/private/Authentication/Login/SetUserTimezoneCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -36,7 +19,7 @@ class SetUserTimezoneCommand extends ALoginCommand { private $session; public function __construct(IConfig $config, - ISession $session) { + ISession $session) { $this->config = $config; $this->session = $session; } diff --git a/lib/private/Authentication/Login/TwoFactorCommand.php b/lib/private/Authentication/Login/TwoFactorCommand.php index 256d88ffa81..fc5285221a2 100644 --- a/lib/private/Authentication/Login/TwoFactorCommand.php +++ b/lib/private/Authentication/Login/TwoFactorCommand.php @@ -3,35 +3,17 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; -use function array_pop; -use function count; use OC\Authentication\TwoFactorAuth\Manager; use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\IURLGenerator; +use function array_pop; +use function count; class TwoFactorCommand extends ALoginCommand { /** @var Manager */ @@ -44,8 +26,8 @@ class TwoFactorCommand extends ALoginCommand { private $urlGenerator; public function __construct(Manager $twoFactorManager, - MandatoryTwoFactor $mandatoryTwoFactor, - IURLGenerator $urlGenerator) { + MandatoryTwoFactor $mandatoryTwoFactor, + IURLGenerator $urlGenerator) { $this->twoFactorManager = $twoFactorManager; $this->mandatoryTwoFactor = $mandatoryTwoFactor; $this->urlGenerator = $urlGenerator; diff --git a/lib/private/Authentication/Login/UidLoginCommand.php b/lib/private/Authentication/Login/UidLoginCommand.php index d3216b6aad8..511b5f61e0e 100644 --- a/lib/private/Authentication/Login/UidLoginCommand.php +++ b/lib/private/Authentication/Login/UidLoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php b/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php index 571ea931d8a..0582239e9de 100644 --- a/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php +++ b/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/UserDisabledCheckCommand.php b/lib/private/Authentication/Login/UserDisabledCheckCommand.php index 7cf4c7235ec..8777aa6dcea 100644 --- a/lib/private/Authentication/Login/UserDisabledCheckCommand.php +++ b/lib/private/Authentication/Login/UserDisabledCheckCommand.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -38,7 +20,7 @@ class UserDisabledCheckCommand extends ALoginCommand { private $logger; public function __construct(IUserManager $userManager, - LoggerInterface $logger) { + LoggerInterface $logger) { $this->userManager = $userManager; $this->logger = $logger; } diff --git a/lib/private/Authentication/Login/WebAuthnChain.php b/lib/private/Authentication/Login/WebAuthnChain.php index f3ebc313a44..ae523c43da6 100644 --- a/lib/private/Authentication/Login/WebAuthnChain.php +++ b/lib/private/Authentication/Login/WebAuthnChain.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, 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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -57,15 +40,15 @@ class WebAuthnChain { private $webAuthnLoginCommand; public function __construct(UserDisabledCheckCommand $userDisabledCheckCommand, - WebAuthnLoginCommand $webAuthnLoginCommand, - LoggedInCheckCommand $loggedInCheckCommand, - CompleteLoginCommand $completeLoginCommand, - CreateSessionTokenCommand $createSessionTokenCommand, - ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, - UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, - SetUserTimezoneCommand $setUserTimezoneCommand, - TwoFactorCommand $twoFactorCommand, - FinishRememberedLoginCommand $finishRememberedLoginCommand + WebAuthnLoginCommand $webAuthnLoginCommand, + LoggedInCheckCommand $loggedInCheckCommand, + CompleteLoginCommand $completeLoginCommand, + CreateSessionTokenCommand $createSessionTokenCommand, + ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, + UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, + SetUserTimezoneCommand $setUserTimezoneCommand, + TwoFactorCommand $twoFactorCommand, + FinishRememberedLoginCommand $finishRememberedLoginCommand, ) { $this->userDisabledCheckCommand = $userDisabledCheckCommand; $this->webAuthnLoginCommand = $webAuthnLoginCommand; diff --git a/lib/private/Authentication/Login/WebAuthnLoginCommand.php b/lib/private/Authentication/Login/WebAuthnLoginCommand.php index 478a579853c..8f14e5b3f6d 100644 --- a/lib/private/Authentication/Login/WebAuthnLoginCommand.php +++ b/lib/private/Authentication/Login/WebAuthnLoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, 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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/LoginCredentials/Credentials.php b/lib/private/Authentication/LoginCredentials/Credentials.php index 69bfc2ba198..3414034b33c 100644 --- a/lib/private/Authentication/LoginCredentials/Credentials.php +++ b/lib/private/Authentication/LoginCredentials/Credentials.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\LoginCredentials; diff --git a/lib/private/Authentication/LoginCredentials/Store.php b/lib/private/Authentication/LoginCredentials/Store.php index 3a09e983ee8..67c5712715c 100644 --- a/lib/private/Authentication/LoginCredentials/Store.php +++ b/lib/private/Authentication/LoginCredentials/Store.php @@ -3,36 +3,20 @@ declare(strict_types=1); /** - * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\LoginCredentials; -use OC\Authentication\Exceptions\InvalidTokenException; +use Exception; use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Token\IProvider; use OCP\Authentication\Exceptions\CredentialsUnavailableException; +use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\Authentication\LoginCredentials\ICredentials; use OCP\Authentication\LoginCredentials\IStore; use OCP\ISession; +use OCP\Security\ICrypto; use OCP\Session\Exceptions\SessionNotAvailableException; use OCP\Util; use Psr\Log\LoggerInterface; @@ -47,9 +31,12 @@ class Store implements IStore { /** @var IProvider|null */ private $tokenProvider; - public function __construct(ISession $session, - LoggerInterface $logger, - IProvider $tokenProvider = null) { + public function __construct( + ISession $session, + LoggerInterface $logger, + private readonly ICrypto $crypto, + ?IProvider $tokenProvider = null, + ) { $this->session = $session; $this->logger = $logger; $this->tokenProvider = $tokenProvider; @@ -63,6 +50,9 @@ class Store implements IStore { * @param array $params */ public function authenticate(array $params) { + if ($params['password'] !== null) { + $params['password'] = $this->crypto->encrypt((string)$params['password']); + } $this->session->set('login_credentials', json_encode($params)); } @@ -109,6 +99,13 @@ class Store implements IStore { if ($trySession && $this->session->exists('login_credentials')) { /** @var array $creds */ $creds = json_decode($this->session->get('login_credentials'), true); + if ($creds['password'] !== null) { + try { + $creds['password'] = $this->crypto->decrypt($creds['password']); + } catch (Exception $e) { + //decryption failed, continue with old password as it is + } + } return new Credentials( $creds['uid'], $creds['loginName'] ?? $this->session->get('loginname') ?? $creds['uid'], // Pre 20 didn't have a loginName property, hence fall back to the session value and then to the UID diff --git a/lib/private/Authentication/Notifications/Notifier.php b/lib/private/Authentication/Notifications/Notifier.php index 8cf5d653771..a81e385d8b1 100644 --- a/lib/private/Authentication/Notifications/Notifier.php +++ b/lib/private/Authentication/Notifications/Notifier.php @@ -3,33 +3,15 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Notifications; -use InvalidArgumentException; use OCP\L10N\IFactory as IL10nFactory; use OCP\Notification\INotification; use OCP\Notification\INotifier; +use OCP\Notification\UnknownNotificationException; class Notifier implements INotifier { /** @var IL10nFactory */ @@ -45,7 +27,7 @@ class Notifier implements INotifier { public function prepare(INotification $notification, string $languageCode): INotification { if ($notification->getApp() !== 'auth') { // Not my app => throw - throw new InvalidArgumentException(); + throw new UnknownNotificationException(); } // Read the language from the notification @@ -70,7 +52,7 @@ class Notifier implements INotifier { return $notification; default: // Unknown subject => Unknown notification => throw - throw new InvalidArgumentException(); + throw new UnknownNotificationException(); } } diff --git a/lib/private/Authentication/Token/INamedToken.php b/lib/private/Authentication/Token/INamedToken.php index 96a7719fb41..9a90cfc7d76 100644 --- a/lib/private/Authentication/Token/INamedToken.php +++ b/lib/private/Authentication/Token/INamedToken.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Daniel Kesselberg (mail@danielkesselberg.de) - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php index b5af3f3a5ee..d47427e79bf 100644 --- a/lib/private/Authentication/Token/IProvider.php +++ b/lib/private/Authentication/Token/IProvider.php @@ -3,36 +3,17 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Marcel Waldvogel <marcel.waldvogel@uni-konstanz.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Token; -use OC\Authentication\Exceptions\ExpiredTokenException; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; -use OC\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Exceptions\ExpiredTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Token\IToken as OCPIToken; interface IProvider { /** @@ -45,16 +26,18 @@ interface IProvider { * @param string $name Name will be trimmed to 120 chars when longer * @param int $type token type * @param int $remember whether the session token should be used for remember-me - * @return IToken + * @return OCPIToken * @throws \RuntimeException when OpenSSL reports a problem */ public function generateToken(string $token, - string $uid, - string $loginName, - ?string $password, - string $name, - int $type = IToken::TEMPORARY_TOKEN, - int $remember = IToken::DO_NOT_REMEMBER): IToken; + string $uid, + string $loginName, + ?string $password, + string $name, + int $type = OCPIToken::TEMPORARY_TOKEN, + int $remember = OCPIToken::DO_NOT_REMEMBER, + ?array $scope = null, + ): OCPIToken; /** * Get a token by token id @@ -63,9 +46,9 @@ interface IProvider { * @throws InvalidTokenException * @throws ExpiredTokenException * @throws WipeTokenException - * @return IToken + * @return OCPIToken */ - public function getToken(string $tokenId): IToken; + public function getToken(string $tokenId): OCPIToken; /** * Get a token by token id @@ -74,9 +57,9 @@ interface IProvider { * @throws InvalidTokenException * @throws ExpiredTokenException * @throws WipeTokenException - * @return IToken + * @return OCPIToken */ - public function getTokenById(int $tokenId): IToken; + public function getTokenById(int $tokenId): OCPIToken; /** * Duplicate an existing session token @@ -85,9 +68,9 @@ interface IProvider { * @param string $sessionId * @throws InvalidTokenException * @throws \RuntimeException when OpenSSL reports a problem - * @return IToken The new token + * @return OCPIToken The new token */ - public function renewSessionToken(string $oldSessionId, string $sessionId): IToken; + public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken; /** * Invalidate (delete) the given session token @@ -110,18 +93,23 @@ interface IProvider { public function invalidateOldTokens(); /** + * Invalidate (delete) tokens last used before a given date + */ + public function invalidateLastUsedBefore(string $uid, int $before): void; + + /** * Save the updated token * - * @param IToken $token + * @param OCPIToken $token */ - public function updateToken(IToken $token); + public function updateToken(OCPIToken $token); /** * Update token activity timestamp * - * @param IToken $token + * @param OCPIToken $token */ - public function updateTokenActivity(IToken $token); + public function updateTokenActivity(OCPIToken $token); /** * Get all tokens of a user @@ -130,49 +118,49 @@ interface IProvider { * where a high number of (session) tokens is generated * * @param string $uid - * @return IToken[] + * @return OCPIToken[] */ public function getTokenByUser(string $uid): array; /** * Get the (unencrypted) password of the given token * - * @param IToken $savedToken + * @param OCPIToken $savedToken * @param string $tokenId * @throws InvalidTokenException * @throws PasswordlessTokenException * @return string */ - public function getPassword(IToken $savedToken, string $tokenId): string; + public function getPassword(OCPIToken $savedToken, string $tokenId): string; /** * Encrypt and set the password of the given token * - * @param IToken $token + * @param OCPIToken $token * @param string $tokenId * @param string $password * @throws InvalidTokenException */ - public function setPassword(IToken $token, string $tokenId, string $password); + public function setPassword(OCPIToken $token, string $tokenId, string $password); /** * Rotate the token. Useful for for example oauth tokens * - * @param IToken $token + * @param OCPIToken $token * @param string $oldTokenId * @param string $newTokenId - * @return IToken + * @return OCPIToken * @throws \RuntimeException when OpenSSL reports a problem */ - public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken; + public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken; /** * Marks a token as having an invalid password. * - * @param IToken $token + * @param OCPIToken $token * @param string $tokenId */ - public function markPasswordInvalid(IToken $token, string $tokenId); + public function markPasswordInvalid(OCPIToken $token, string $tokenId); /** * Update all the passwords of $uid if required diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php index 5ca4eaea843..2028a0b328c 100644 --- a/lib/private/Authentication/Token/IToken.php +++ b/lib/private/Authentication/Token/IToken.php @@ -1,134 +1,17 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Token; -use JsonSerializable; - -interface IToken extends JsonSerializable { - public const TEMPORARY_TOKEN = 0; - public const PERMANENT_TOKEN = 1; - public const WIPE_TOKEN = 2; - public const DO_NOT_REMEMBER = 0; - public const REMEMBER = 1; - - /** - * Get the token ID - * - * @return int - */ - public function getId(): int; - - /** - * Get the user UID - * - * @return string - */ - public function getUID(): string; - - /** - * Get the login name used when generating the token - * - * @return string - */ - public function getLoginName(): string; - - /** - * Get the (encrypted) login password - * - * @return string|null - */ - public function getPassword(); - - /** - * Get the timestamp of the last password check - * - * @return int - */ - public function getLastCheck(): int; - - /** - * Set the timestamp of the last password check - * - * @param int $time - */ - public function setLastCheck(int $time); - - /** - * Get the authentication scope for this token - * - * @return string - */ - public function getScope(): string; +use OCP\Authentication\Token\IToken as OCPIToken; - /** - * Get the authentication scope for this token - * - * @return array - */ - public function getScopeAsArray(): array; - - /** - * Set the authentication scope for this token - * - * @param array $scope - */ - public function setScope($scope); - - /** - * Get the name of the token - * @return string - */ - public function getName(): string; - - /** - * Get the remember state of the token - * - * @return int - */ - public function getRemember(): int; - - /** - * Set the token - * - * @param string $token - */ - public function setToken(string $token); - - /** - * Set the password - * - * @param string $password - */ - public function setPassword(string $password); - - /** - * Set the expiration time of the token - * - * @param int|null $expires - */ - public function setExpires($expires); +/** + * @deprecated 28.0.0 use {@see \OCP\Authentication\Token\IToken} instead + */ +interface IToken extends OCPIToken { } diff --git a/lib/private/Authentication/Token/IWipeableToken.php b/lib/private/Authentication/Token/IWipeableToken.php index 5ba994e0d5e..fc1476785cd 100644 --- a/lib/private/Authentication/Token/IWipeableToken.php +++ b/lib/private/Authentication/Token/IWipeableToken.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; diff --git a/lib/private/Authentication/Token/Manager.php b/lib/private/Authentication/Token/Manager.php index 59c7ca714c6..6953f47b004 100644 --- a/lib/private/Authentication/Token/Manager.php +++ b/lib/private/Authentication/Token/Manager.php @@ -1,39 +1,22 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use OC\Authentication\Exceptions\ExpiredTokenException; -use OC\Authentication\Exceptions\InvalidTokenException; +use OC\Authentication\Exceptions\InvalidTokenException as OcInvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; -use OC\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Exceptions\ExpiredTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Token\IProvider as OCPIProvider; +use OCP\Authentication\Token\IToken as OCPIToken; -class Manager implements IProvider { +class Manager implements IProvider, OCPIProvider { /** @var PublicKeyTokenProvider */ private $publicKeyTokenProvider; @@ -51,15 +34,17 @@ class Manager implements IProvider { * @param string $name Name will be trimmed to 120 chars when longer * @param int $type token type * @param int $remember whether the session token should be used for remember-me - * @return IToken + * @return OCPIToken */ public function generateToken(string $token, - string $uid, - string $loginName, - $password, - string $name, - int $type = IToken::TEMPORARY_TOKEN, - int $remember = IToken::DO_NOT_REMEMBER): IToken { + string $uid, + string $loginName, + $password, + string $name, + int $type = OCPIToken::TEMPORARY_TOKEN, + int $remember = OCPIToken::DO_NOT_REMEMBER, + ?array $scope = null, + ): OCPIToken { if (mb_strlen($name) > 128) { $name = mb_substr($name, 0, 120) . '…'; } @@ -72,7 +57,8 @@ class Manager implements IProvider { $password, $name, $type, - $remember + $remember, + $scope, ); } catch (UniqueConstraintViolationException $e) { // It's rare, but if two requests of the same session (e.g. env-based SAML) @@ -92,10 +78,10 @@ class Manager implements IProvider { /** * Save the updated token * - * @param IToken $token + * @param OCPIToken $token * @throws InvalidTokenException */ - public function updateToken(IToken $token) { + public function updateToken(OCPIToken $token) { $provider = $this->getProvider($token); $provider->updateToken($token); } @@ -104,16 +90,16 @@ class Manager implements IProvider { * Update token activity timestamp * * @throws InvalidTokenException - * @param IToken $token + * @param OCPIToken $token */ - public function updateTokenActivity(IToken $token) { + public function updateTokenActivity(OCPIToken $token) { $provider = $this->getProvider($token); $provider->updateTokenActivity($token); } /** * @param string $uid - * @return IToken[] + * @return OCPIToken[] */ public function getTokenByUser(string $uid): array { return $this->publicKeyTokenProvider->getTokenByUser($uid); @@ -125,9 +111,9 @@ class Manager implements IProvider { * @param string $tokenId * @throws InvalidTokenException * @throws \RuntimeException when OpenSSL reports a problem - * @return IToken + * @return OCPIToken */ - public function getToken(string $tokenId): IToken { + public function getToken(string $tokenId): OCPIToken { try { return $this->publicKeyTokenProvider->getToken($tokenId); } catch (WipeTokenException $e) { @@ -144,9 +130,9 @@ class Manager implements IProvider { * * @param int $tokenId * @throws InvalidTokenException - * @return IToken + * @return OCPIToken */ - public function getTokenById(int $tokenId): IToken { + public function getTokenById(int $tokenId): OCPIToken { try { return $this->publicKeyTokenProvider->getTokenById($tokenId); } catch (ExpiredTokenException $e) { @@ -162,9 +148,9 @@ class Manager implements IProvider { * @param string $oldSessionId * @param string $sessionId * @throws InvalidTokenException - * @return IToken + * @return OCPIToken */ - public function renewSessionToken(string $oldSessionId, string $sessionId): IToken { + public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken { try { return $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId); } catch (ExpiredTokenException $e) { @@ -175,18 +161,18 @@ class Manager implements IProvider { } /** - * @param IToken $savedToken + * @param OCPIToken $savedToken * @param string $tokenId session token * @throws InvalidTokenException * @throws PasswordlessTokenException * @return string */ - public function getPassword(IToken $savedToken, string $tokenId): string { + public function getPassword(OCPIToken $savedToken, string $tokenId): string { $provider = $this->getProvider($savedToken); return $provider->getPassword($savedToken, $tokenId); } - public function setPassword(IToken $token, string $tokenId, string $password) { + public function setPassword(OCPIToken $token, string $tokenId, string $password) { $provider = $this->getProvider($token); $provider->setPassword($token, $tokenId, $password); } @@ -203,40 +189,55 @@ class Manager implements IProvider { $this->publicKeyTokenProvider->invalidateOldTokens(); } + public function invalidateLastUsedBefore(string $uid, int $before): void { + $this->publicKeyTokenProvider->invalidateLastUsedBefore($uid, $before); + } + /** - * @param IToken $token + * @param OCPIToken $token * @param string $oldTokenId * @param string $newTokenId - * @return IToken + * @return OCPIToken * @throws InvalidTokenException * @throws \RuntimeException when OpenSSL reports a problem */ - public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken { + public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken { if ($token instanceof PublicKeyToken) { return $this->publicKeyTokenProvider->rotate($token, $oldTokenId, $newTokenId); } - throw new InvalidTokenException(); + /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */ + throw new OcInvalidTokenException(); } /** - * @param IToken $token + * @param OCPIToken $token * @return IProvider * @throws InvalidTokenException */ - private function getProvider(IToken $token): IProvider { + private function getProvider(OCPIToken $token): IProvider { if ($token instanceof PublicKeyToken) { return $this->publicKeyTokenProvider; } - throw new InvalidTokenException(); + /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */ + throw new OcInvalidTokenException(); } - public function markPasswordInvalid(IToken $token, string $tokenId) { + public function markPasswordInvalid(OCPIToken $token, string $tokenId) { $this->getProvider($token)->markPasswordInvalid($token, $tokenId); } public function updatePasswords(string $uid, string $password) { $this->publicKeyTokenProvider->updatePasswords($uid, $password); } + + public function invalidateTokensOfUser(string $uid, ?string $clientName) { + $tokens = $this->getTokenByUser($uid); + foreach ($tokens as $token) { + if ($clientName === null || ($token->getName() === $clientName)) { + $this->invalidateTokenById($uid, $token->getId()); + } + } + } } diff --git a/lib/private/Authentication/Token/PublicKeyToken.php b/lib/private/Authentication/Token/PublicKeyToken.php index 45335e17c31..be427ab4839 100644 --- a/lib/private/Authentication/Token/PublicKeyToken.php +++ b/lib/private/Authentication/Token/PublicKeyToken.php @@ -3,31 +3,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; use OCP\AppFramework\Db\Entity; +use OCP\Authentication\Token\IToken; +use OCP\DB\Types; /** * @method void setId(int $id) @@ -106,16 +89,16 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { $this->addType('passwordHash', 'string'); $this->addType('name', 'string'); $this->addType('token', 'string'); - $this->addType('type', 'int'); - $this->addType('remember', 'int'); - $this->addType('lastActivity', 'int'); - $this->addType('lastCheck', 'int'); + $this->addType('type', Types::INTEGER); + $this->addType('remember', Types::INTEGER); + $this->addType('lastActivity', Types::INTEGER); + $this->addType('lastCheck', Types::INTEGER); $this->addType('scope', 'string'); - $this->addType('expires', 'int'); + $this->addType('expires', Types::INTEGER); $this->addType('publicKey', 'string'); $this->addType('privateKey', 'string'); - $this->addType('version', 'int'); - $this->addType('passwordInvalid', 'bool'); + $this->addType('version', Types::INTEGER); + $this->addType('passwordInvalid', Types::BOOLEAN); } public function getId(): int { @@ -137,10 +120,8 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { /** * Get the (encrypted) login password - * - * @return string|null */ - public function getPassword() { + public function getPassword(): ?string { return parent::getPassword(); } @@ -165,10 +146,8 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { /** * Get the timestamp of the last password check - * - * @param int $time */ - public function setLastCheck(int $time) { + public function setLastCheck(int $time): void { parent::setLastCheck($time); } @@ -185,13 +164,13 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { $scope = json_decode($this->getScope(), true); if (!$scope) { return [ - 'filesystem' => true + IToken::SCOPE_FILESYSTEM => true ]; } return $scope; } - public function setScope($scope) { + public function setScope(array|string|null $scope): void { if (is_array($scope)) { parent::setScope(json_encode($scope)); } else { @@ -211,15 +190,15 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { return parent::getRemember(); } - public function setToken(string $token) { + public function setToken(string $token): void { parent::setToken($token); } - public function setPassword(string $password = null) { + public function setPassword(?string $password = null): void { parent::setPassword($password); } - public function setExpires($expires) { + public function setExpires($expires): void { parent::setExpires($expires); } diff --git a/lib/private/Authentication/Token/PublicKeyTokenMapper.php b/lib/private/Authentication/Token/PublicKeyTokenMapper.php index 7b11ef8adf3..9aabd69e57a 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenMapper.php +++ b/lib/private/Authentication/Token/PublicKeyTokenMapper.php @@ -3,32 +3,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; +use OCP\Authentication\Token\IToken; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -42,8 +24,6 @@ class PublicKeyTokenMapper extends QBMapper { /** * Invalidate (delete) a given token - * - * @param string $token */ public function invalidate(string $token) { /* @var $qb IQueryBuilder */ @@ -51,22 +31,34 @@ class PublicKeyTokenMapper extends QBMapper { $qb->delete($this->tableName) ->where($qb->expr()->eq('token', $qb->createNamedParameter($token))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))) - ->execute(); + ->executeStatement(); } /** * @param int $olderThan - * @param int $remember + * @param int $type + * @param int|null $remember */ - public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER) { + public function invalidateOld(int $olderThan, int $type = IToken::TEMPORARY_TOKEN, ?int $remember = null) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->delete($this->tableName) + $delete = $qb->delete($this->tableName) ->where($qb->expr()->lt('last_activity', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT))) - ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT))) - ->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT))) - ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))) - ->execute(); + ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT))) + ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); + if ($remember !== null) { + $delete->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT))); + } + $delete->executeStatement(); + } + + public function invalidateLastUsedBefore(string $uid, int $before): int { + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->tableName) + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) + ->andWhere($qb->expr()->lt('last_activity', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT))) + ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); + return $qb->executeStatement(); } /** @@ -81,7 +73,7 @@ class PublicKeyTokenMapper extends QBMapper { ->from($this->tableName) ->where($qb->expr()->eq('token', $qb->createNamedParameter($token))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))) - ->execute(); + ->executeQuery(); $data = $result->fetch(); $result->closeCursor(); @@ -103,7 +95,7 @@ class PublicKeyTokenMapper extends QBMapper { ->from($this->tableName) ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))) - ->execute(); + ->executeQuery(); $data = $result->fetch(); $result->closeCursor(); @@ -130,7 +122,7 @@ class PublicKeyTokenMapper extends QBMapper { ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))) ->setMaxResults(1000); - $result = $qb->execute(); + $result = $qb->executeQuery(); $data = $result->fetchAll(); $result->closeCursor(); @@ -141,14 +133,15 @@ class PublicKeyTokenMapper extends QBMapper { return $entities; } - public function deleteById(string $uid, int $id) { + public function getTokenByUserAndId(string $uid, int $id): ?string { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->delete($this->tableName) + $qb->select('token') + ->from($this->tableName) ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); - $qb->execute(); + return $qb->executeQuery()->fetchOne() ?: null; } /** @@ -161,7 +154,7 @@ class PublicKeyTokenMapper extends QBMapper { $qb->delete($this->tableName) ->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR)) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); - $qb->execute(); + $qb->executeStatement(); } public function deleteTempToken(PublicKeyToken $except) { @@ -173,7 +166,7 @@ class PublicKeyTokenMapper extends QBMapper { ->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($except->getId()))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); - $qb->execute(); + $qb->executeStatement(); } public function hasExpiredTokens(string $uid): bool { @@ -184,7 +177,7 @@ class PublicKeyTokenMapper extends QBMapper { ->andWhere($qb->expr()->eq('password_invalid', $qb->createNamedParameter(true), IQueryBuilder::PARAM_BOOL)) ->setMaxResults(1); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $data = $cursor->fetchAll(); $cursor->closeCursor(); @@ -229,4 +222,31 @@ class PublicKeyTokenMapper extends QBMapper { ); $update->executeStatement(); } + + public function updateHashesForUser(string $userId, string $passwordHash): void { + $qb = $this->db->getQueryBuilder(); + $update = $qb->update($this->getTableName()) + ->set('password_hash', $qb->createNamedParameter($passwordHash)) + ->where( + $qb->expr()->eq('uid', $qb->createNamedParameter($userId)) + ); + $update->executeStatement(); + } + + public function getFirstTokenForUser(string $userId): ?PublicKeyToken { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($userId))) + ->setMaxResults(1) + ->orderBy('id'); + $result = $qb->executeQuery(); + + $data = $result->fetch(); + $result->closeCursor(); + if ($data === false) { + return null; + } + return PublicKeyToken::fromRow($data); + } } diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php index c8adec24b31..12c3a1d535b 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php +++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php @@ -1,43 +1,23 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ 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\TokenPasswordExpiredException; use OC\Authentication\Exceptions\WipeTokenException; -use OCP\AppFramework\Db\TTransactional; -use OCP\Cache\CappedMemoryCache; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\TTransactional; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Token\IToken as OCPIToken; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\IUserManager; @@ -46,6 +26,10 @@ use OCP\Security\IHasher; use Psr\Log\LoggerInterface; class PublicKeyTokenProvider implements IProvider { + public const TOKEN_MIN_LENGTH = 22; + /** Token cache TTL in seconds */ + private const TOKEN_CACHE_TTL = 10; + use TTransactional; /** @var PublicKeyTokenMapper */ @@ -65,18 +49,20 @@ class PublicKeyTokenProvider implements IProvider { /** @var ITimeFactory */ private $time; - /** @var CappedMemoryCache */ + /** @var ICache */ private $cache; - private IHasher $hasher; + /** @var IHasher */ + private $hasher; public function __construct(PublicKeyTokenMapper $mapper, - ICrypto $crypto, - IConfig $config, - IDBConnection $db, - LoggerInterface $logger, - ITimeFactory $time, - IHasher $hasher) { + ICrypto $crypto, + IConfig $config, + IDBConnection $db, + LoggerInterface $logger, + ITimeFactory $time, + IHasher $hasher, + ICacheFactory $cacheFactory) { $this->mapper = $mapper; $this->crypto = $crypto; $this->config = $config; @@ -84,7 +70,9 @@ class PublicKeyTokenProvider implements IProvider { $this->logger = $logger; $this->time = $time; - $this->cache = new CappedMemoryCache(); + $this->cache = $cacheFactory->isLocalCacheAvailable() + ? $cacheFactory->createLocal('authtoken_') + : $cacheFactory->createInMemory(); $this->hasher = $hasher; } @@ -92,78 +80,144 @@ class PublicKeyTokenProvider implements IProvider { * {@inheritDoc} */ public function generateToken(string $token, - string $uid, - string $loginName, - ?string $password, - string $name, - int $type = IToken::TEMPORARY_TOKEN, - int $remember = IToken::DO_NOT_REMEMBER): IToken { + string $uid, + string $loginName, + ?string $password, + string $name, + int $type = OCPIToken::TEMPORARY_TOKEN, + int $remember = OCPIToken::DO_NOT_REMEMBER, + ?array $scope = null, + ): OCPIToken { + if (strlen($token) < self::TOKEN_MIN_LENGTH) { + $exception = new InvalidTokenException('Token is too short, minimum of ' . self::TOKEN_MIN_LENGTH . ' characters is required, ' . strlen($token) . ' characters given'); + $this->logger->error('Invalid token provided when generating new token', ['exception' => $exception]); + throw $exception; + } + if (mb_strlen($name) > 128) { $name = mb_substr($name, 0, 120) . '…'; } + // We need to check against one old token to see if there is a password + // hash that we can reuse for detecting outdated passwords + $randomOldToken = $this->mapper->getFirstTokenForUser($uid); + $oldTokenMatches = $randomOldToken && $randomOldToken->getPasswordHash() && $password !== null && $this->hasher->verify(sha1($password) . $password, $randomOldToken->getPasswordHash()); + $dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember); + + if ($oldTokenMatches) { + $dbToken->setPasswordHash($randomOldToken->getPasswordHash()); + } + + if ($scope !== null) { + $dbToken->setScope($scope); + } + $this->mapper->insert($dbToken); + if (!$oldTokenMatches && $password !== null) { + $this->updatePasswords($uid, $password); + } + // Add the token to the cache - $this->cache[$dbToken->getToken()] = $dbToken; + $this->cacheToken($dbToken); return $dbToken; } - public function getToken(string $tokenId): IToken { + public function getToken(string $tokenId): OCPIToken { + /** + * Token length: 72 + * @see \OC\Core\Controller\ClientFlowLoginController::generateAppPassword + * @see \OC\Core\Controller\AppPasswordController::getAppPassword + * @see \OC\Core\Command\User\AddAppPassword::execute + * @see \OC\Core\Service\LoginFlowV2Service::flowDone + * @see \OCA\Talk\MatterbridgeManager::generatePassword + * @see \OCA\Preferred_Providers\Controller\PasswordController::generateAppPassword + * @see \OCA\GlobalSiteSelector\TokenHandler::generateAppPassword + * + * Token length: 22-256 - https://www.php.net/manual/en/session.configuration.php#ini.session.sid-length + * @see \OC\User\Session::createSessionToken + * + * Token length: 29 + * @see \OCA\Settings\Controller\AuthSettingsController::generateRandomDeviceToken + * @see \OCA\Registration\Service\RegistrationService::generateAppPassword + */ + if (strlen($tokenId) < self::TOKEN_MIN_LENGTH) { + throw new InvalidTokenException('Token is too short for a generated token, should be the password during basic auth'); + } + $tokenHash = $this->hashToken($tokenId); + if ($token = $this->getTokenFromCache($tokenHash)) { + $this->checkToken($token); + return $token; + } - if (isset($this->cache[$tokenHash])) { - if ($this->cache[$tokenHash] instanceof DoesNotExistException) { - $ex = $this->cache[$tokenHash]; - throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex); - } - $token = $this->cache[$tokenHash]; - } else { + try { + $token = $this->mapper->getToken($tokenHash); + $this->cacheToken($token); + } catch (DoesNotExistException $ex) { try { - $token = $this->mapper->getToken($this->hashToken($tokenId)); - $this->cache[$token->getToken()] = $token; - } catch (DoesNotExistException $ex) { - try { - $token = $this->mapper->getToken($this->hashTokenWithEmptySecret($tokenId)); - $this->cache[$token->getToken()] = $token; - $this->rotate($token, $tokenId, $tokenId); - } catch (DoesNotExistException $ex2) { - $this->cache[$tokenHash] = $ex2; - throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex); - } + $token = $this->mapper->getToken($this->hashTokenWithEmptySecret($tokenId)); + $this->rotate($token, $tokenId, $tokenId); + } catch (DoesNotExistException) { + $this->cacheInvalidHash($tokenHash); + throw new InvalidTokenException('Token does not exist: ' . $ex->getMessage(), 0, $ex); } } - if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) { - throw new ExpiredTokenException($token); - } + $this->checkToken($token); - if ($token->getType() === IToken::WIPE_TOKEN) { - throw new WipeTokenException($token); + return $token; + } + + /** + * @throws InvalidTokenException when token doesn't exist + */ + private function getTokenFromCache(string $tokenHash): ?PublicKeyToken { + $serializedToken = $this->cache->get($tokenHash); + if ($serializedToken === false) { + return null; } - if ($token->getPasswordInvalid() === true) { - //The password is invalid we should throw an TokenPasswordExpiredException - throw new TokenPasswordExpiredException($token); + if ($serializedToken === null) { + return null; } - return $token; + $token = unserialize($serializedToken, [ + 'allowed_classes' => [PublicKeyToken::class], + ]); + + return $token instanceof PublicKeyToken ? $token : null; + } + + private function cacheToken(PublicKeyToken $token): void { + $this->cache->set($token->getToken(), serialize($token), self::TOKEN_CACHE_TTL); } - public function getTokenById(int $tokenId): IToken { + private function cacheInvalidHash(string $tokenHash): void { + // Invalid entries can be kept longer in cache since it’s unlikely to reuse them + $this->cache->set($tokenHash, false, self::TOKEN_CACHE_TTL * 2); + } + + public function getTokenById(int $tokenId): OCPIToken { try { $token = $this->mapper->getTokenById($tokenId); } catch (DoesNotExistException $ex) { throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex); } + $this->checkToken($token); + + return $token; + } + + private function checkToken($token): void { if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) { throw new ExpiredTokenException($token); } - if ($token->getType() === IToken::WIPE_TOKEN) { + if ($token->getType() === OCPIToken::WIPE_TOKEN) { throw new WipeTokenException($token); } @@ -171,18 +225,14 @@ class PublicKeyTokenProvider implements IProvider { //The password is invalid we should throw an TokenPasswordExpiredException throw new TokenPasswordExpiredException($token); } - - return $token; } - public function renewSessionToken(string $oldSessionId, string $sessionId): IToken { - $this->cache->clear(); - + public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken { return $this->atomic(function () use ($oldSessionId, $sessionId) { $token = $this->getToken($oldSessionId); if (!($token instanceof PublicKeyToken)) { - throw new InvalidTokenException("Invalid token type"); + throw new InvalidTokenException('Invalid token type'); } $password = null; @@ -190,16 +240,21 @@ class PublicKeyTokenProvider implements IProvider { $privateKey = $this->decrypt($token->getPrivateKey(), $oldSessionId); $password = $this->decryptPassword($token->getPassword(), $privateKey); } + + $scope = $token->getScope() === '' ? null : $token->getScopeAsArray(); $newToken = $this->generateToken( $sessionId, $token->getUID(), $token->getLoginName(), $password, $token->getName(), - IToken::TEMPORARY_TOKEN, - $token->getRemember() + OCPIToken::TEMPORARY_TOKEN, + $token->getRemember(), + $scope, ); + $this->cacheToken($newToken); + $this->cacheInvalidHash($token->getToken()); $this->mapper->delete($token); return $newToken; @@ -207,43 +262,55 @@ class PublicKeyTokenProvider implements IProvider { } public function invalidateToken(string $token) { - $this->cache->clear(); - + $tokenHash = $this->hashToken($token); $this->mapper->invalidate($this->hashToken($token)); $this->mapper->invalidate($this->hashTokenWithEmptySecret($token)); + $this->cacheInvalidHash($tokenHash); } public function invalidateTokenById(string $uid, int $id) { - $this->cache->clear(); + $token = $this->mapper->getTokenById($id); + if ($token->getUID() !== $uid) { + return; + } + $this->mapper->invalidate($token->getToken()); + $this->cacheInvalidHash($token->getToken()); - $this->mapper->deleteById($uid, $id); } public function invalidateOldTokens() { - $this->cache->clear(); - - $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24); + $olderThan = $this->time->getTime() - $this->config->getSystemValueInt('session_lifetime', 60 * 60 * 24); $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']); - $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER); - $rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); + $this->mapper->invalidateOld($olderThan, OCPIToken::TEMPORARY_TOKEN, OCPIToken::DO_NOT_REMEMBER); + + $rememberThreshold = $this->time->getTime() - $this->config->getSystemValueInt('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); $this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']); - $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER); + $this->mapper->invalidateOld($rememberThreshold, OCPIToken::TEMPORARY_TOKEN, OCPIToken::REMEMBER); + + $wipeThreshold = $this->time->getTime() - $this->config->getSystemValueInt('token_auth_wipe_token_retention', 60 * 60 * 24 * 60); + $this->logger->debug('Invalidating auth tokens marked for remote wipe older than ' . date('c', $wipeThreshold), ['app' => 'cron']); + $this->mapper->invalidateOld($wipeThreshold, OCPIToken::WIPE_TOKEN); + + $authTokenThreshold = $this->time->getTime() - $this->config->getSystemValueInt('token_auth_token_retention', 60 * 60 * 24 * 365); + $this->logger->debug('Invalidating auth tokens older than ' . date('c', $authTokenThreshold), ['app' => 'cron']); + $this->mapper->invalidateOld($authTokenThreshold, OCPIToken::PERMANENT_TOKEN); } - public function updateToken(IToken $token) { - $this->cache->clear(); + public function invalidateLastUsedBefore(string $uid, int $before): void { + $this->mapper->invalidateLastUsedBefore($uid, $before); + } + public function updateToken(OCPIToken $token) { if (!($token instanceof PublicKeyToken)) { - throw new InvalidTokenException("Invalid token type"); + throw new InvalidTokenException('Invalid token type'); } $this->mapper->update($token); + $this->cacheToken($token); } - public function updateTokenActivity(IToken $token) { - $this->cache->clear(); - + public function updateTokenActivity(OCPIToken $token) { if (!($token instanceof PublicKeyToken)) { - throw new InvalidTokenException("Invalid token type"); + throw new InvalidTokenException('Invalid token type'); } $activityInterval = $this->config->getSystemValueInt('token_auth_activity_update', 60); @@ -254,6 +321,7 @@ class PublicKeyTokenProvider implements IProvider { if ($token->getLastActivity() < ($now - $activityInterval)) { $token->setLastActivity($now); $this->mapper->updateActivity($token, $now); + $this->cacheToken($token); } } @@ -261,9 +329,9 @@ class PublicKeyTokenProvider implements IProvider { return $this->mapper->getTokenByUser($uid); } - public function getPassword(IToken $savedToken, string $tokenId): string { + public function getPassword(OCPIToken $savedToken, string $tokenId): string { if (!($savedToken instanceof PublicKeyToken)) { - throw new InvalidTokenException("Invalid token type"); + throw new InvalidTokenException('Invalid token type'); } if ($savedToken->getPassword() === null) { @@ -277,35 +345,34 @@ class PublicKeyTokenProvider implements IProvider { return $this->decryptPassword($savedToken->getPassword(), $privateKey); } - public function setPassword(IToken $token, string $tokenId, string $password) { - $this->cache->clear(); - + public function setPassword(OCPIToken $token, string $tokenId, string $password) { if (!($token instanceof PublicKeyToken)) { - throw new InvalidTokenException("Invalid token type"); + throw new InvalidTokenException('Invalid token type'); } - // When changing passwords all temp tokens are deleted - $this->mapper->deleteTempToken($token); + $this->atomic(function () use ($password, $token) { + // When changing passwords all temp tokens are deleted + $this->mapper->deleteTempToken($token); - // Update the password for all tokens - $tokens = $this->mapper->getTokenByUser($token->getUID()); - foreach ($tokens as $t) { - $publicKey = $t->getPublicKey(); - $t->setPassword($this->encryptPassword($password, $publicKey)); - $t->setPasswordHash($this->hashPassword($password)); - $this->updateToken($t); - } + // Update the password for all tokens + $tokens = $this->mapper->getTokenByUser($token->getUID()); + $hashedPassword = $this->hashPassword($password); + foreach ($tokens as $t) { + $publicKey = $t->getPublicKey(); + $t->setPassword($this->encryptPassword($password, $publicKey)); + $t->setPasswordHash($hashedPassword); + $this->updateToken($t); + } + }, $this->db); } private function hashPassword(string $password): string { return $this->hasher->hash(sha1($password) . $password); } - public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken { - $this->cache->clear(); - + public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken { if (!($token instanceof PublicKeyToken)) { - throw new InvalidTokenException("Invalid token type"); + throw new InvalidTokenException('Invalid token type'); } // Decrypt private key with oldTokenId @@ -320,7 +387,7 @@ class PublicKeyTokenProvider implements IProvider { } private function encrypt(string $plaintext, string $token): string { - $secret = $this->config->getSystemValue('secret'); + $secret = $this->config->getSystemValueString('secret'); return $this->crypto->encrypt($plaintext, $token . $secret); } @@ -328,7 +395,7 @@ class PublicKeyTokenProvider implements IProvider { * @throws InvalidTokenException */ private function decrypt(string $cipherText, string $token): string { - $secret = $this->config->getSystemValue('secret'); + $secret = $this->config->getSystemValueString('secret'); try { return $this->crypto->decrypt($cipherText, $token . $secret); } catch (\Exception $ex) { @@ -338,7 +405,7 @@ class PublicKeyTokenProvider implements IProvider { } catch (\Exception $ex2) { // Delete the invalid token $this->invalidateToken($token); - throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex2); + throw new InvalidTokenException('Could not decrypt token password: ' . $ex->getMessage(), 0, $ex2); } } } @@ -358,12 +425,12 @@ class PublicKeyTokenProvider implements IProvider { } private function hashToken(string $token): string { - $secret = $this->config->getSystemValue('secret'); + $secret = $this->config->getSystemValueString('secret'); return hash('sha512', $token . $secret); } /** - * @deprecated Fallback for instances where the secret might not have been set by accident + * @deprecated 26.0.0 Fallback for instances where the secret might not have been set by accident */ private function hashTokenWithEmptySecret(string $token): string { return hash('sha512', $token); @@ -373,12 +440,12 @@ class PublicKeyTokenProvider implements IProvider { * @throws \RuntimeException when OpenSSL reports a problem */ private function newToken(string $token, - string $uid, - string $loginName, - $password, - string $name, - int $type, - int $remember): PublicKeyToken { + string $uid, + string $loginName, + $password, + string $name, + int $type, + int $remember): PublicKeyToken { $dbToken = new PublicKeyToken(); $dbToken->setUid($uid); $dbToken->setLoginName($loginName); @@ -426,61 +493,67 @@ class PublicKeyTokenProvider implements IProvider { return $dbToken; } - public function markPasswordInvalid(IToken $token, string $tokenId) { - $this->cache->clear(); - + public function markPasswordInvalid(OCPIToken $token, string $tokenId) { if (!($token instanceof PublicKeyToken)) { - throw new InvalidTokenException("Invalid token type"); + throw new InvalidTokenException('Invalid token type'); } $token->setPasswordInvalid(true); $this->mapper->update($token); + $this->cacheToken($token); } public function updatePasswords(string $uid, string $password) { - $this->cache->clear(); - // prevent setting an empty pw as result of pw-less-login if ($password === '' || !$this->config->getSystemValueBool('auth.storeCryptedPassword', true)) { return; } - // Update the password for all tokens - $tokens = $this->mapper->getTokenByUser($uid); - $newPasswordHash = null; - - /** - * - true: The password hash could not be verified anymore - * and the token needs to be updated with the newly encrypted password - * - false: The hash could still be verified - * - missing: The hash needs to be verified - */ - $hashNeedsUpdate = []; - - foreach ($tokens as $t) { - if (!isset($hashNeedsUpdate[$t->getPasswordHash()])) { - if ($t->getPasswordHash() === null) { - $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true; - } elseif (!$this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) { - $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true; - } else { - $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = false; + $this->atomic(function () use ($password, $uid) { + // Update the password for all tokens + $tokens = $this->mapper->getTokenByUser($uid); + $newPasswordHash = null; + + /** + * - true: The password hash could not be verified anymore + * and the token needs to be updated with the newly encrypted password + * - false: The hash could still be verified + * - missing: The hash needs to be verified + */ + $hashNeedsUpdate = []; + + foreach ($tokens as $t) { + if (!isset($hashNeedsUpdate[$t->getPasswordHash()])) { + if ($t->getPasswordHash() === null) { + $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true; + } elseif (!$this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) { + $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true; + } else { + $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = false; + } } - } - $needsUpdating = $hashNeedsUpdate[$t->getPasswordHash() ?: ''] ?? true; - - if ($needsUpdating) { - if ($newPasswordHash === null) { - $newPasswordHash = $this->hashPassword($password); + $needsUpdating = $hashNeedsUpdate[$t->getPasswordHash() ?: ''] ?? true; + + if ($needsUpdating) { + if ($newPasswordHash === null) { + $newPasswordHash = $this->hashPassword($password); + } + + $publicKey = $t->getPublicKey(); + $t->setPassword($this->encryptPassword($password, $publicKey)); + $t->setPasswordHash($newPasswordHash); + $t->setPasswordInvalid(false); + $this->updateToken($t); } + } - $publicKey = $t->getPublicKey(); - $t->setPassword($this->encryptPassword($password, $publicKey)); - $t->setPasswordHash($newPasswordHash); - $t->setPasswordInvalid(false); - $this->updateToken($t); + // If password hashes are different we update them all to be equal so + // that the next execution only needs to verify once + if (count($hashNeedsUpdate) > 1) { + $newPasswordHash = $this->hashPassword($password); + $this->mapper->updateHashesForUser($uid, $newPasswordHash); } - } + }, $this->db); } private function logOpensslError() { diff --git a/lib/private/Authentication/Token/RemoteWipe.php b/lib/private/Authentication/Token/RemoteWipe.php index 5fd01cfbe87..80ba330b66d 100644 --- a/lib/private/Authentication/Token/RemoteWipe.php +++ b/lib/private/Authentication/Token/RemoteWipe.php @@ -3,38 +3,19 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; -use Psr\Log\LoggerInterface; -use function array_filter; use OC\Authentication\Events\RemoteWipeFinished; use OC\Authentication\Events\RemoteWipeStarted; -use OC\Authentication\Exceptions\InvalidTokenException; -use OC\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; use OCP\EventDispatcher\IEventDispatcher; use OCP\IUser; +use Psr\Log\LoggerInterface; +use function array_filter; class RemoteWipe { /** @var IProvider */ @@ -47,8 +28,8 @@ class RemoteWipe { private $logger; public function __construct(IProvider $tokenProvider, - IEventDispatcher $eventDispatcher, - LoggerInterface $logger) { + IEventDispatcher $eventDispatcher, + LoggerInterface $logger) { $this->tokenProvider = $tokenProvider; $this->eventDispatcher = $eventDispatcher; $this->logger = $logger; @@ -117,7 +98,7 @@ class RemoteWipe { $dbToken = $e->getToken(); - $this->logger->info("user " . $dbToken->getUID() . " started a remote wipe"); + $this->logger->info('user ' . $dbToken->getUID() . ' started a remote wipe'); $this->eventDispatcher->dispatch(RemoteWipeStarted::class, new RemoteWipeStarted($dbToken)); @@ -145,7 +126,7 @@ class RemoteWipe { $this->tokenProvider->invalidateToken($token); - $this->logger->info("user " . $dbToken->getUID() . " finished a remote wipe"); + $this->logger->info('user ' . $dbToken->getUID() . ' finished a remote wipe'); $this->eventDispatcher->dispatch(RemoteWipeFinished::class, new RemoteWipeFinished($dbToken)); return true; diff --git a/lib/private/Authentication/Token/TokenCleanupJob.php b/lib/private/Authentication/Token/TokenCleanupJob.php index 292f8f310e8..e6d1e69e9b4 100644 --- a/lib/private/Authentication/Token/TokenCleanupJob.php +++ b/lib/private/Authentication/Token/TokenCleanupJob.php @@ -1,23 +1,8 @@ <?php + /** - * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> - * - * @author Thomas Citharel <nextcloud@tcit.fr> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Token; diff --git a/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php b/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php index 19d80218562..cc468dbeba0 100644 --- a/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php +++ b/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php @@ -3,30 +3,11 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth\Db; -use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use function array_map; @@ -48,7 +29,7 @@ class ProviderUserAssignmentDao { * Get all assigned provider IDs for the given user ID * * @return array<string, bool> where the array key is the provider ID (string) and the - * value is the enabled state (bool) + * value is the enabled state (bool) */ public function getState(string $uid): array { $qb = $this->conn->getQueryBuilder(); @@ -56,10 +37,10 @@ class ProviderUserAssignmentDao { $query = $qb->select('provider_id', 'enabled') ->from(self::TABLE_NAME) ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))); - $result = $query->execute(); + $result = $query->executeQuery(); $providers = []; foreach ($result->fetchAll() as $row) { - $providers[(string)$row['provider_id']] = 1 === (int)$row['enabled']; + $providers[(string)$row['provider_id']] = (int)$row['enabled'] === 1; } $result->closeCursor(); @@ -69,41 +50,38 @@ class ProviderUserAssignmentDao { /** * Persist a new/updated (provider_id, uid, enabled) tuple */ - public function persist(string $providerId, string $uid, int $enabled) { - $qb = $this->conn->getQueryBuilder(); - - try { - // Insert a new entry - $insertQuery = $qb->insert(self::TABLE_NAME)->values([ - 'provider_id' => $qb->createNamedParameter($providerId), - 'uid' => $qb->createNamedParameter($uid), - 'enabled' => $qb->createNamedParameter($enabled, IQueryBuilder::PARAM_INT), - ]); - - $insertQuery->execute(); - } catch (UniqueConstraintViolationException $ex) { - // There is already an entry -> update it - $updateQuery = $qb->update(self::TABLE_NAME) - ->set('enabled', $qb->createNamedParameter($enabled)) - ->where($qb->expr()->eq('provider_id', $qb->createNamedParameter($providerId))) - ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))); - $updateQuery->execute(); + public function persist(string $providerId, string $uid, int $enabled): void { + $conn = $this->conn; + + // Insert a new entry + if ($conn->insertIgnoreConflict(self::TABLE_NAME, [ + 'provider_id' => $providerId, + 'uid' => $uid, + 'enabled' => $enabled, + ])) { + return; } + + // There is already an entry -> update it + $qb = $conn->getQueryBuilder(); + $updateQuery = $qb->update(self::TABLE_NAME) + ->set('enabled', $qb->createNamedParameter($enabled)) + ->where($qb->expr()->eq('provider_id', $qb->createNamedParameter($providerId))) + ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))); + $updateQuery->executeStatement(); } /** * Delete all provider states of a user and return the provider IDs * - * @param string $uid - * - * @return int[] + * @return list<array{provider_id: string, uid: string, enabled: bool}> */ public function deleteByUser(string $uid): array { $qb1 = $this->conn->getQueryBuilder(); $selectQuery = $qb1->select('*') ->from(self::TABLE_NAME) ->where($qb1->expr()->eq('uid', $qb1->createNamedParameter($uid))); - $selectResult = $selectQuery->execute(); + $selectResult = $selectQuery->executeQuery(); $rows = $selectResult->fetchAll(); $selectResult->closeCursor(); @@ -111,23 +89,23 @@ class ProviderUserAssignmentDao { $deleteQuery = $qb2 ->delete(self::TABLE_NAME) ->where($qb2->expr()->eq('uid', $qb2->createNamedParameter($uid))); - $deleteQuery->execute(); + $deleteQuery->executeStatement(); - return array_map(function (array $row) { + return array_values(array_map(function (array $row) { return [ - 'provider_id' => $row['provider_id'], - 'uid' => $row['uid'], - 'enabled' => 1 === (int) $row['enabled'], + 'provider_id' => (string)$row['provider_id'], + 'uid' => (string)$row['uid'], + 'enabled' => ((int)$row['enabled']) === 1, ]; - }, $rows); + }, $rows)); } - public function deleteAll(string $providerId) { + public function deleteAll(string $providerId): void { $qb = $this->conn->getQueryBuilder(); $deleteQuery = $qb->delete(self::TABLE_NAME) ->where($qb->expr()->eq('provider_id', $qb->createNamedParameter($providerId))); - $deleteQuery->execute(); + $deleteQuery->executeStatement(); } } diff --git a/lib/private/Authentication/TwoFactorAuth/EnforcementState.php b/lib/private/Authentication/TwoFactorAuth/EnforcementState.php index b95128c1e0f..e02064bc8f7 100644 --- a/lib/private/Authentication/TwoFactorAuth/EnforcementState.php +++ b/lib/private/Authentication/TwoFactorAuth/EnforcementState.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; @@ -45,8 +28,8 @@ class EnforcementState implements JsonSerializable { * @param string[] $excludedGroups */ public function __construct(bool $enforced, - array $enforcedGroups = [], - array $excludedGroups = []) { + array $enforcedGroups = [], + array $excludedGroups = []) { $this->enforced = $enforced; $this->enforcedGroups = $enforcedGroups; $this->excludedGroups = $excludedGroups; diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php index d62556465df..07aa98610ed 100644 --- a/lib/private/Authentication/TwoFactorAuth/Manager.php +++ b/lib/private/Authentication/TwoFactorAuth/Manager.php @@ -1,41 +1,25 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\TwoFactorAuth; use BadMethodCallException; use Exception; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Token\IProvider as TokenProvider; use OCP\Activity\IManager; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\Authentication\TwoFactorAuth\IActivatableAtLogin; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderChallengeFailed; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderChallengePassed; use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserDisabled; use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserEnabled; use OCP\EventDispatcher\IEventDispatcher; @@ -44,8 +28,6 @@ use OCP\ISession; use OCP\IUser; use OCP\Session\Exceptions\SessionNotAvailableException; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\GenericEvent; use function array_diff; use function array_filter; @@ -85,23 +67,19 @@ class Manager { /** @var IEventDispatcher */ private $dispatcher; - /** @var EventDispatcherInterface */ - private $legacyDispatcher; - /** @psalm-var array<string, bool> */ private $userIsTwoFactorAuthenticated = []; public function __construct(ProviderLoader $providerLoader, - IRegistry $providerRegistry, - MandatoryTwoFactor $mandatoryTwoFactor, - ISession $session, - IConfig $config, - IManager $activityManager, - LoggerInterface $logger, - TokenProvider $tokenProvider, - ITimeFactory $timeFactory, - IEventDispatcher $eventDispatcher, - EventDispatcherInterface $legacyDispatcher) { + IRegistry $providerRegistry, + MandatoryTwoFactor $mandatoryTwoFactor, + ISession $session, + IConfig $config, + IManager $activityManager, + LoggerInterface $logger, + TokenProvider $tokenProvider, + ITimeFactory $timeFactory, + IEventDispatcher $eventDispatcher) { $this->providerLoader = $providerLoader; $this->providerRegistry = $providerRegistry; $this->mandatoryTwoFactor = $mandatoryTwoFactor; @@ -112,14 +90,10 @@ class Manager { $this->tokenProvider = $tokenProvider; $this->timeFactory = $timeFactory; $this->dispatcher = $eventDispatcher; - $this->legacyDispatcher = $legacyDispatcher; } /** * Determine whether the user must provide a second factor challenge - * - * @param IUser $user - * @return boolean */ public function isTwoFactorAuthenticated(IUser $user): bool { if (isset($this->userIsTwoFactorAuthenticated[$user->getUID()])) { @@ -143,18 +117,13 @@ class Manager { /** * Get a 2FA provider by its ID - * - * @param IUser $user - * @param string $challengeProviderId - * @return IProvider|null */ - public function getProvider(IUser $user, string $challengeProviderId) { + public function getProvider(IUser $user, string $challengeProviderId): ?IProvider { $providers = $this->getProviderSet($user)->getProviders(); return $providers[$challengeProviderId] ?? null; } /** - * @param IUser $user * @return IActivatableAtLogin[] * @throws Exception */ @@ -224,7 +193,7 @@ class Manager { if (!empty($missing)) { // There was at least one provider missing - $this->logger->alert(count($missing) . " two-factor auth providers failed to load", ['app' => 'core']); + $this->logger->alert(count($missing) . ' two-factor auth providers failed to load', ['app' => 'core']); return true; } @@ -280,21 +249,17 @@ class Manager { $sessionId = $this->session->getId(); $token = $this->tokenProvider->getToken($sessionId); $tokenId = $token->getId(); - $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $tokenId); - - $dispatchEvent = new GenericEvent($user, ['provider' => $provider->getDisplayName()]); - $this->legacyDispatcher->dispatch(IProvider::EVENT_SUCCESS, $dispatchEvent); + $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', (string)$tokenId); $this->dispatcher->dispatchTyped(new TwoFactorProviderForUserEnabled($user, $provider)); + $this->dispatcher->dispatchTyped(new TwoFactorProviderChallengePassed($user, $provider)); $this->publishEvent($user, 'twofactor_success', [ 'provider' => $provider->getDisplayName(), ]); } else { - $dispatchEvent = new GenericEvent($user, ['provider' => $provider->getDisplayName()]); - $this->legacyDispatcher->dispatch(IProvider::EVENT_FAILED, $dispatchEvent); - $this->dispatcher->dispatchTyped(new TwoFactorProviderForUserDisabled($user, $provider)); + $this->dispatcher->dispatchTyped(new TwoFactorProviderChallengeFailed($user, $provider)); $this->publishEvent($user, 'twofactor_failed', [ 'provider' => $provider->getDisplayName(), @@ -330,21 +295,21 @@ class Manager { * @param IUser $user the currently logged in user * @return boolean */ - public function needsSecondFactor(IUser $user = null): bool { + public function needsSecondFactor(?IUser $user = null): bool { if ($user === null) { return false; } - // If we are authenticated using an app password skip all this - if ($this->session->exists('app_password')) { + // If we are authenticated using an app password or AppAPI Auth, skip all this + if ($this->session->exists('app_password') || $this->session->get('app_api') === true) { return false; } // First check if the session tells us we should do 2FA (99% case) if (!$this->session->exists(self::SESSION_UID_KEY)) { // Check if the session tells us it is 2FA authenticated already - if ($this->session->exists(self::SESSION_UID_DONE) && - $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) { + if ($this->session->exists(self::SESSION_UID_DONE) + && $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) { return false; } @@ -358,7 +323,7 @@ class Manager { $tokenId = $token->getId(); $tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa'); - if (!\in_array((string) $tokenId, $tokensNeeding2FA, true)) { + if (!\in_array((string)$tokenId, $tokensNeeding2FA, true)) { $this->session->set(self::SESSION_UID_DONE, $user->getUID()); return false; } @@ -395,14 +360,19 @@ class Manager { $id = $this->session->getId(); $token = $this->tokenProvider->getToken($id); - $this->config->setUserValue($user->getUID(), 'login_token_2fa', (string) $token->getId(), $this->timeFactory->getTime()); + $this->config->setUserValue($user->getUID(), 'login_token_2fa', (string)$token->getId(), (string)$this->timeFactory->getTime()); } public function clearTwoFactorPending(string $userId) { $tokensNeeding2FA = $this->config->getUserKeys($userId, 'login_token_2fa'); foreach ($tokensNeeding2FA as $tokenId) { - $this->tokenProvider->invalidateTokenById($userId, (int)$tokenId); + $this->config->deleteUserValue($userId, 'login_token_2fa', $tokenId); + + try { + $this->tokenProvider->invalidateTokenById($userId, (int)$tokenId); + } catch (DoesNotExistException $e) { + } } } } diff --git a/lib/private/Authentication/TwoFactorAuth/MandatoryTwoFactor.php b/lib/private/Authentication/TwoFactorAuth/MandatoryTwoFactor.php index 3bfbd77941b..37c9d3fc550 100644 --- a/lib/private/Authentication/TwoFactorAuth/MandatoryTwoFactor.php +++ b/lib/private/Authentication/TwoFactorAuth/MandatoryTwoFactor.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; diff --git a/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php b/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php index 25cdc387f61..7e674a01dd8 100644 --- a/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php +++ b/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php @@ -3,32 +3,13 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; use Exception; -use OC; -use OC_App; +use OC\AppFramework\Bootstrap\Coordinator; use OCP\App\IAppManager; use OCP\AppFramework\QueryException; use OCP\Authentication\TwoFactorAuth\IProvider; @@ -37,15 +18,10 @@ use OCP\IUser; class ProviderLoader { public const BACKUP_CODES_APP_ID = 'twofactor_backupcodes'; - /** @var IAppManager */ - private $appManager; - - /** @var OC\AppFramework\Bootstrap\Coordinator */ - private $coordinator; - - public function __construct(IAppManager $appManager, OC\AppFramework\Bootstrap\Coordinator $coordinator) { - $this->appManager = $appManager; - $this->coordinator = $coordinator; + public function __construct( + private IAppManager $appManager, + private Coordinator $coordinator, + ) { } /** @@ -66,7 +42,7 @@ class ProviderLoader { foreach ($providerClasses as $class) { try { $this->loadTwoFactorApp($appId); - $provider = OC::$server->query($class); + $provider = \OCP\Server::get($class); $providers[$provider->getId()] = $provider; } catch (QueryException $exc) { // Provider class can not be resolved @@ -76,12 +52,12 @@ class ProviderLoader { } } - $registeredProviders = $this->coordinator->getRegistrationContext()->getTwoFactorProviders(); + $registeredProviders = $this->coordinator->getRegistrationContext()?->getTwoFactorProviders() ?? []; foreach ($registeredProviders as $provider) { try { $this->loadTwoFactorApp($provider->getAppId()); - $provider = OC::$server->query($provider->getService()); - $providers[$provider->getId()] = $provider; + $providerInstance = \OCP\Server::get($provider->getService()); + $providers[$providerInstance->getId()] = $providerInstance; } catch (QueryException $exc) { // Provider class can not be resolved throw new Exception('Could not load two-factor auth provider ' . $provider->getService()); @@ -93,12 +69,10 @@ class ProviderLoader { /** * Load an app by ID if it has not been loaded yet - * - * @param string $appId */ - protected function loadTwoFactorApp(string $appId) { - if (!OC_App::isAppLoaded($appId)) { - OC_App::loadApp($appId); + protected function loadTwoFactorApp(string $appId): void { + if (!$this->appManager->isAppLoaded($appId)) { + $this->appManager->loadApp($appId); } } } diff --git a/lib/private/Authentication/TwoFactorAuth/ProviderManager.php b/lib/private/Authentication/TwoFactorAuth/ProviderManager.php index c7c075bdab3..5ce4c598154 100644 --- a/lib/private/Authentication/TwoFactorAuth/ProviderManager.php +++ b/lib/private/Authentication/TwoFactorAuth/ProviderManager.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; diff --git a/lib/private/Authentication/TwoFactorAuth/ProviderSet.php b/lib/private/Authentication/TwoFactorAuth/ProviderSet.php index af270fb83c8..15b82be6dec 100644 --- a/lib/private/Authentication/TwoFactorAuth/ProviderSet.php +++ b/lib/private/Authentication/TwoFactorAuth/ProviderSet.php @@ -3,31 +3,14 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; -use function array_filter; use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; use OCP\Authentication\TwoFactorAuth\IProvider; +use function array_filter; /** * Contains all two-factor provider information for the two-factor login challenge diff --git a/lib/private/Authentication/TwoFactorAuth/Registry.php b/lib/private/Authentication/TwoFactorAuth/Registry.php index 6c82572578c..544f60c4f97 100644 --- a/lib/private/Authentication/TwoFactorAuth/Registry.php +++ b/lib/private/Authentication/TwoFactorAuth/Registry.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; @@ -31,6 +13,9 @@ use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IRegistry; use OCP\Authentication\TwoFactorAuth\RegistryEvent; use OCP\Authentication\TwoFactorAuth\TwoFactorProviderDisabled; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserRegistered; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserUnregistered; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderUserDeleted; use OCP\EventDispatcher\IEventDispatcher; use OCP\IUser; @@ -42,7 +27,7 @@ class Registry implements IRegistry { private $dispatcher; public function __construct(ProviderUserAssignmentDao $assignmentDao, - IEventDispatcher $dispatcher) { + IEventDispatcher $dispatcher) { $this->assignmentDao = $assignmentDao; $this->dispatcher = $dispatcher; } @@ -56,6 +41,7 @@ class Registry implements IRegistry { $event = new RegistryEvent($provider, $user); $this->dispatcher->dispatch(self::EVENT_PROVIDER_ENABLED, $event); + $this->dispatcher->dispatchTyped(new TwoFactorProviderForUserRegistered($user, $provider)); } public function disableProviderFor(IProvider $provider, IUser $user) { @@ -63,12 +49,14 @@ class Registry implements IRegistry { $event = new RegistryEvent($provider, $user); $this->dispatcher->dispatch(self::EVENT_PROVIDER_DISABLED, $event); + $this->dispatcher->dispatchTyped(new TwoFactorProviderForUserUnregistered($user, $provider)); } public function deleteUserData(IUser $user): void { foreach ($this->assignmentDao->deleteByUser($user->getUID()) as $provider) { $event = new TwoFactorProviderDisabled($provider['provider_id']); $this->dispatcher->dispatchTyped($event); + $this->dispatcher->dispatchTyped(new TwoFactorProviderUserDeleted($user, $provider['provider_id'])); } } diff --git a/lib/private/Authentication/WebAuthn/CredentialRepository.php b/lib/private/Authentication/WebAuthn/CredentialRepository.php index e5c3fcf1618..203f2ef9020 100644 --- a/lib/private/Authentication/WebAuthn/CredentialRepository.php +++ b/lib/private/Authentication/WebAuthn/CredentialRepository.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, 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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\WebAuthn; @@ -61,7 +44,7 @@ class CredentialRepository implements PublicKeyCredentialSourceRepository { }, $entities); } - public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, string $name = null): PublicKeyCredentialEntity { + public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, ?string $name = null, bool $userVerification = false): PublicKeyCredentialEntity { $oldEntity = null; try { @@ -75,19 +58,24 @@ class CredentialRepository implements PublicKeyCredentialSourceRepository { $name = 'default'; } - $entity = PublicKeyCredentialEntity::fromPublicKeyCrendentialSource($name, $publicKeyCredentialSource); + $entity = PublicKeyCredentialEntity::fromPublicKeyCrendentialSource($name, $publicKeyCredentialSource, $userVerification); if ($oldEntity) { $entity->setId($oldEntity->getId()); if ($defaultName) { $entity->setName($oldEntity->getName()); } + + // Don't downgrade UV just because it was skipped during a login due to another key + if ($oldEntity->getUserVerification()) { + $entity->setUserVerification(true); + } } return $this->credentialMapper->insertOrUpdate($entity); } - public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, string $name = null): void { + public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, ?string $name = null): void { $this->saveAndReturnCredentialSource($publicKeyCredentialSource, $name); } } diff --git a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php index 6f97ded483d..6c4bc3ca81b 100644 --- a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php +++ b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\WebAuthn\Db; @@ -41,6 +23,10 @@ use Webauthn\PublicKeyCredentialSource; * @method void setPublicKeyCredentialId(string $id); * @method string getData(); * @method void setData(string $data); + * + * @since 30.0.0 Add userVerification attribute + * @method bool|null getUserVerification(); + * @method void setUserVerification(bool $userVerification); */ class PublicKeyCredentialEntity extends Entity implements JsonSerializable { /** @var string */ @@ -55,20 +41,25 @@ class PublicKeyCredentialEntity extends Entity implements JsonSerializable { /** @var string */ protected $data; + /** @var bool|null */ + protected $userVerification; + public function __construct() { $this->addType('name', 'string'); $this->addType('uid', 'string'); $this->addType('publicKeyCredentialId', 'string'); $this->addType('data', 'string'); + $this->addType('userVerification', 'boolean'); } - public static function fromPublicKeyCrendentialSource(string $name, PublicKeyCredentialSource $publicKeyCredentialSource): PublicKeyCredentialEntity { + public static function fromPublicKeyCrendentialSource(string $name, PublicKeyCredentialSource $publicKeyCredentialSource, bool $userVerification): PublicKeyCredentialEntity { $publicKeyCredentialEntity = new self(); $publicKeyCredentialEntity->setName($name); $publicKeyCredentialEntity->setUid($publicKeyCredentialSource->getUserHandle()); $publicKeyCredentialEntity->setPublicKeyCredentialId(base64_encode($publicKeyCredentialSource->getPublicKeyCredentialId())); $publicKeyCredentialEntity->setData(json_encode($publicKeyCredentialSource)); + $publicKeyCredentialEntity->setUserVerification($userVerification); return $publicKeyCredentialEntity; } diff --git a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php index 72c557eb53a..fa7304157c8 100644 --- a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php +++ b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\WebAuthn\Db; diff --git a/lib/private/Authentication/WebAuthn/Manager.php b/lib/private/Authentication/WebAuthn/Manager.php index 744a3fa354a..96dc0719b54 100644 --- a/lib/private/Authentication/WebAuthn/Manager.php +++ b/lib/private/Authentication/WebAuthn/Manager.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\WebAuthn; @@ -72,7 +53,7 @@ class Manager { CredentialRepository $repository, PublicKeyCredentialMapper $credentialMapper, LoggerInterface $logger, - IConfig $config + IConfig $config, ) { $this->repository = $repository; $this->credentialMapper = $credentialMapper; @@ -83,15 +64,15 @@ class Manager { public function startRegistration(IUser $user, string $serverHost): PublicKeyCredentialCreationOptions { $rpEntity = new PublicKeyCredentialRpEntity( 'Nextcloud', //Name - $this->stripPort($serverHost), //ID + $this->stripPort($serverHost), //ID null //Icon ); $userEntity = new PublicKeyCredentialUserEntity( - $user->getUID(), //Name - $user->getUID(), //ID - $user->getDisplayName() //Display name -// 'https://foo.example.co/avatar/123e4567-e89b-12d3-a456-426655440000' //Icon + $user->getUID(), // Name + $user->getUID(), // ID + $user->getDisplayName() // Display name + // 'https://foo.example.co/avatar/123e4567-e89b-12d3-a456-426655440000' //Icon ); $challenge = random_bytes(32); @@ -107,9 +88,10 @@ class Manager { ]; $authenticatorSelectionCriteria = new AuthenticatorSelectionCriteria( + AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_NO_PREFERENCE, + AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED, null, false, - AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED ); return new PublicKeyCredentialCreationOptions( @@ -117,11 +99,10 @@ class Manager { $userEntity, $challenge, $publicKeyCredentialParametersList, - $timeout, - $excludedPublicKeyDescriptors, $authenticatorSelectionCriteria, PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE, - null + $excludedPublicKeyDescriptors, + $timeout, ); } @@ -149,7 +130,7 @@ class Manager { try { // Load the data $publicKeyCredential = $publicKeyCredentialLoader->load($data); - $response = $publicKeyCredential->getResponse(); + $response = $publicKeyCredential->response; // Check if the response is an Authenticator Attestation Response if (!$response instanceof AuthenticatorAttestationResponse) { @@ -162,13 +143,16 @@ class Manager { $publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check( $response, $publicKeyCredentialCreationOptions, - $request); + $request, + ['localhost'], + ); } catch (\Throwable $exception) { throw $exception; } // Persist the data - return $this->repository->saveAndReturnCredentialSource($publicKeyCredentialSource, $name); + $userVerification = $response->attestationObject->authData->isUserVerified(); + return $this->repository->saveAndReturnCredentialSource($publicKeyCredentialSource, $name, $userVerification); } private function stripPort(string $serverHost): string { @@ -177,21 +161,25 @@ class Manager { public function startAuthentication(string $uid, string $serverHost): PublicKeyCredentialRequestOptions { // List of registered PublicKeyCredentialDescriptor classes associated to the user - $registeredPublicKeyCredentialDescriptors = array_map(function (PublicKeyCredentialEntity $entity) { + $userVerificationRequirement = AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED; + $registeredPublicKeyCredentialDescriptors = array_map(function (PublicKeyCredentialEntity $entity) use (&$userVerificationRequirement) { + if ($entity->getUserVerification() !== true) { + $userVerificationRequirement = AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED; + } $credential = $entity->toPublicKeyCredentialSource(); return new PublicKeyCredentialDescriptor( - $credential->getType(), - $credential->getPublicKeyCredentialId() + $credential->type, + $credential->publicKeyCredentialId, ); }, $this->credentialMapper->findAllForUid($uid)); // Public Key Credential Request Options return new PublicKeyCredentialRequestOptions( - random_bytes(32), // Challenge - 60000, // Timeout - $this->stripPort($serverHost), // Relying Party ID - $registeredPublicKeyCredentialDescriptors, // Registered PublicKeyCredentialDescriptor classes - AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED + random_bytes(32), // Challenge + $this->stripPort($serverHost), // Relying Party ID + $registeredPublicKeyCredentialDescriptors, // Registered PublicKeyCredentialDescriptor classes + $userVerificationRequirement, + 60000, // Timeout ); } @@ -213,16 +201,15 @@ class Manager { $tokenBindingHandler, $extensionOutputCheckerHandler, $algorithmManager, - null, - $this->logger, ); + $authenticatorAssertionResponseValidator->setLogger($this->logger); try { $this->logger->debug('Loading publickey credentials from: ' . $data); // Load the data $publicKeyCredential = $publicKeyCredentialLoader->load($data); - $response = $publicKeyCredential->getResponse(); + $response = $publicKeyCredential->response; // Check if the response is an Authenticator Attestation Response if (!$response instanceof AuthenticatorAssertionResponse) { @@ -233,18 +220,17 @@ class Manager { $request = ServerRequest::fromGlobals(); $publicKeyCredentialSource = $authenticatorAssertionResponseValidator->check( - $publicKeyCredential->getRawId(), + $publicKeyCredential->rawId, $response, $publicKeyCredentialRequestOptions, $request, - $uid + $uid, + ['localhost'], ); } catch (\Throwable $e) { throw $e; } - - return true; } @@ -260,14 +246,6 @@ class Manager { } public function isWebAuthnAvailable(): bool { - if (!extension_loaded('bcmath')) { - return false; - } - - if (!extension_loaded('gmp')) { - return false; - } - if (!$this->config->getSystemValueBool('auth.webauthn.enabled', true)) { return false; } |