diff options
Diffstat (limited to 'apps/oauth2/lib/Controller')
-rw-r--r-- | apps/oauth2/lib/Controller/LoginRedirectorController.php | 109 | ||||
-rw-r--r-- | apps/oauth2/lib/Controller/OauthApiController.php | 43 | ||||
-rw-r--r-- | apps/oauth2/lib/Controller/SettingsController.php | 34 |
3 files changed, 78 insertions, 108 deletions
diff --git a/apps/oauth2/lib/Controller/LoginRedirectorController.php b/apps/oauth2/lib/Controller/LoginRedirectorController.php index 4c5b905a1ee..7241b35cdcf 100644 --- a/apps/oauth2/lib/Controller/LoginRedirectorController.php +++ b/apps/oauth2/lib/Controller/LoginRedirectorController.php @@ -3,52 +3,32 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Kate Döen <kate.doeen@nextcloud.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: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\OAuth2\Controller; +use OC\Core\Controller\ClientFlowLoginController; use OCA\OAuth2\Db\ClientMapper; use OCA\OAuth2\Exceptions\ClientNotFoundException; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\Attribute\PublicPage; +use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\IAppConfig; +use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; +use OCP\Security\ISecureRandom; +#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)] class LoginRedirectorController extends Controller { - /** @var IURLGenerator */ - private $urlGenerator; - /** @var ClientMapper */ - private $clientMapper; - /** @var ISession */ - private $session; - /** @var IL10N */ - private $l; - /** * @param string $appName * @param IRequest $request @@ -57,37 +37,39 @@ class LoginRedirectorController extends Controller { * @param ISession $session * @param IL10N $l */ - public function __construct(string $appName, + public function __construct( + string $appName, IRequest $request, - IURLGenerator $urlGenerator, - ClientMapper $clientMapper, - ISession $session, - IL10N $l) { + private IURLGenerator $urlGenerator, + private ClientMapper $clientMapper, + private ISession $session, + private IL10N $l, + private ISecureRandom $random, + private IAppConfig $appConfig, + private IConfig $config, + ) { parent::__construct($appName, $request); - $this->urlGenerator = $urlGenerator; - $this->clientMapper = $clientMapper; - $this->session = $session; - $this->l = $l; } /** - * @PublicPage - * @NoCSRFRequired - * @UseSession - * * Authorize the user * * @param string $client_id Client ID * @param string $state State of the flow * @param string $response_type Response type for the flow + * @param string $redirect_uri URI to redirect to after the flow (is only used for legacy ownCloud clients) * @return TemplateResponse<Http::STATUS_OK, array{}>|RedirectResponse<Http::STATUS_SEE_OTHER, array{}> * * 200: Client not found * 303: Redirect to login URL */ + #[PublicPage] + #[NoCSRFRequired] + #[UseSession] public function authorize($client_id, $state, - $response_type): TemplateResponse|RedirectResponse { + $response_type, + string $redirect_uri = ''): TemplateResponse|RedirectResponse { try { $client = $this->clientMapper->getByIdentifier($client_id); } catch (ClientNotFoundException $e) { @@ -103,14 +85,39 @@ class LoginRedirectorController extends Controller { return new RedirectResponse($url); } + $enableOcClients = $this->config->getSystemValueBool('oauth2.enable_oc_clients', false); + + $providedRedirectUri = ''; + if ($enableOcClients && $client->getRedirectUri() === 'http://localhost:*') { + $providedRedirectUri = $redirect_uri; + } + $this->session->set('oauth.state', $state); - $targetUrl = $this->urlGenerator->linkToRouteAbsolute( - 'core.ClientFlowLogin.showAuthPickerPage', - [ - 'clientIdentifier' => $client->getClientIdentifier(), - ] - ); + if (in_array($client->getName(), $this->appConfig->getValueArray('oauth2', 'skipAuthPickerApplications', []))) { + /** @see ClientFlowLoginController::showAuthPickerPage **/ + $stateToken = $this->random->generate( + 64, + ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS + ); + $this->session->set(ClientFlowLoginController::STATE_NAME, $stateToken); + $targetUrl = $this->urlGenerator->linkToRouteAbsolute( + 'core.ClientFlowLogin.grantPage', + [ + 'stateToken' => $stateToken, + 'clientIdentifier' => $client->getClientIdentifier(), + 'providedRedirectUri' => $providedRedirectUri, + ] + ); + } else { + $targetUrl = $this->urlGenerator->linkToRouteAbsolute( + 'core.ClientFlowLogin.showAuthPickerPage', + [ + 'clientIdentifier' => $client->getClientIdentifier(), + 'providedRedirectUri' => $providedRedirectUri, + ] + ); + } return new RedirectResponse($targetUrl); } } diff --git a/apps/oauth2/lib/Controller/OauthApiController.php b/apps/oauth2/lib/Controller/OauthApiController.php index 46b68b1d585..11f17fda4bf 100644 --- a/apps/oauth2/lib/Controller/OauthApiController.php +++ b/apps/oauth2/lib/Controller/OauthApiController.php @@ -3,28 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Kate Döen <kate.doeen@nextcloud.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: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\OAuth2\Controller; @@ -35,6 +15,10 @@ use OCA\OAuth2\Exceptions\AccessTokenNotFoundException; use OCA\OAuth2\Exceptions\ClientNotFoundException; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Authentication\Exceptions\ExpiredTokenException; @@ -46,6 +30,7 @@ use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; use Psr\Log\LoggerInterface; +#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)] class OauthApiController extends Controller { // the authorization code expires after 10 minutes public const AUTHORIZATION_CODE_EXPIRES_AFTER = 10 * 60; @@ -67,10 +52,6 @@ class OauthApiController extends Controller { } /** - * @PublicPage - * @NoCSRFRequired - * @BruteForceProtection(action=oauth2GetToken) - * * Get a token * * @param string $grant_type Token type that should be granted @@ -84,9 +65,12 @@ class OauthApiController extends Controller { * 200: Token returned * 400: Getting token is not possible */ + #[PublicPage] + #[NoCSRFRequired] + #[BruteForceProtection(action: 'oauth2GetToken')] public function getToken( string $grant_type, ?string $code, ?string $refresh_token, - ?string $client_id, ?string $client_secret + ?string $client_id, ?string $client_secret, ): JSONResponse { // We only handle two types @@ -156,7 +140,8 @@ class OauthApiController extends Controller { } try { - $storedClientSecret = $this->crypto->decrypt($client->getSecret()); + $storedClientSecretHash = $client->getSecret(); + $clientSecretHash = bin2hex($this->crypto->calculateHMAC($client_secret)); } catch (\Exception $e) { $this->logger->error('OAuth client secret decryption error', ['exception' => $e]); // we don't throttle here because it might not be a bruteforce attack @@ -165,7 +150,7 @@ class OauthApiController extends Controller { ], Http::STATUS_BAD_REQUEST); } // The client id and secret must match. Else we don't provide an access token! - if ($client->getClientIdentifier() !== $client_id || $storedClientSecret !== $client_secret) { + if ($client->getClientIdentifier() !== $client_id || $storedClientSecretHash !== $clientSecretHash) { $response = new JSONResponse([ 'error' => 'invalid_client', ], Http::STATUS_BAD_REQUEST); diff --git a/apps/oauth2/lib/Controller/SettingsController.php b/apps/oauth2/lib/Controller/SettingsController.php index d49dd6da038..9bd02c8a2cd 100644 --- a/apps/oauth2/lib/Controller/SettingsController.php +++ b/apps/oauth2/lib/Controller/SettingsController.php @@ -3,30 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Patrik Kernstock <info@pkern.at> - * @author rakekniven <mark.ziegler@rakekniven.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: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\OAuth2\Controller; @@ -57,7 +35,7 @@ class SettingsController extends Controller { private IL10N $l, private IAuthTokenProvider $tokenProvider, private IUserManager $userManager, - private ICrypto $crypto + private ICrypto $crypto, ) { parent::__construct($appName, $request); } @@ -72,8 +50,8 @@ class SettingsController extends Controller { $client->setName($name); $client->setRedirectUri($redirectUri); $secret = $this->secureRandom->generate(64, self::validChars); - $encryptedSecret = $this->crypto->encrypt($secret); - $client->setSecret($encryptedSecret); + $hashedSecret = bin2hex($this->crypto->calculateHMAC($secret)); + $client->setSecret($hashedSecret); $client->setClientIdentifier($this->secureRandom->generate(64, self::validChars)); $client = $this->clientMapper->insert($client); @@ -91,7 +69,7 @@ class SettingsController extends Controller { public function deleteClient(int $id): JSONResponse { $client = $this->clientMapper->getByUid($id); - $this->userManager->callForAllUsers(function (IUser $user) use ($client) { + $this->userManager->callForSeenUsers(function (IUser $user) use ($client): void { $this->tokenProvider->invalidateTokensOfUser($user->getUID(), $client->getName()); }); |