From 8f91df8f5f3e3d39b796f9ac5bfea2f14841a915 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Tue, 20 Feb 2024 09:32:33 +0100 Subject: [PATCH] feat(core): Add OCS endpoint for confirming the user password Signed-off-by: provokateurin --- core/Controller/AppPasswordController.php | 36 ++++++++ core/openapi.json | 107 ++++++++++++++++++++++ core/routes.php | 1 + 3 files changed, 144 insertions(+) diff --git a/core/Controller/AppPasswordController.php b/core/Controller/AppPasswordController.php index a4b7791997ab8..2575729fe850c 100644 --- a/core/Controller/AppPasswordController.php +++ b/core/Controller/AppPasswordController.php @@ -31,7 +31,9 @@ use OC\Authentication\Events\AppPasswordCreatedEvent; use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; +use OC\User\Session; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSForbiddenException; use OCP\Authentication\Exceptions\CredentialsUnavailableException; @@ -41,6 +43,8 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\IRequest; use OCP\ISession; +use OCP\IUserManager; +use OCP\Security\Bruteforce\IThrottler; use OCP\Security\ISecureRandom; class AppPasswordController extends \OCP\AppFramework\OCSController { @@ -52,6 +56,9 @@ public function __construct( private IProvider $tokenProvider, private IStore $credentialStore, private IEventDispatcher $eventDispatcher, + private Session $userSession, + private IUserManager $userManager, + private IThrottler $throttler, ) { parent::__construct($appName, $request); } @@ -165,4 +172,33 @@ public function rotateAppPassword(): DataResponse { 'apppassword' => $newToken, ]); } + + /** + * Confirm the user password + * + * @NoAdminRequired + * @BruteForceProtection(action=sudo) + * + * @param string $password The password of the user + * + * @return DataResponse|DataResponse, array{}> + * + * 200: Password confirmation succeeded + * 403: Password confirmation failed + */ + #[UseSession] + public function confirmUserPassword(string $password): DataResponse { + $loginName = $this->userSession->getLoginName(); + $loginResult = $this->userManager->checkPassword($loginName, $password); + if ($loginResult === false) { + $response = new DataResponse([], Http::STATUS_FORBIDDEN); + $response->throttle(['loginName' => $loginName]); + return $response; + } + + $confirmTimestamp = time(); + $this->session->set('last-password-confirm', $confirmTimestamp); + $this->throttler->resetDelay($this->request->getRemoteAddress(), 'sudo', ['loginName' => $loginName]); + return new DataResponse(['lastLogin' => $confirmTimestamp], Http::STATUS_OK); + } } diff --git a/core/openapi.json b/core/openapi.json index 9bfee0d40b937..5a33f547a923e 100644 --- a/core/openapi.json +++ b/core/openapi.json @@ -2475,6 +2475,113 @@ } } }, + "/ocs/v2.php/core/apppassword/confirm": { + "put": { + "operationId": "app_password-confirm-user-password", + "summary": "Confirm the user password", + "tags": [ + "app_password" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "password", + "in": "query", + "description": "The password of the user", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Password confirmation succeeded", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "lastLogin" + ], + "properties": { + "lastLogin": { + "type": "integer", + "format": "int64" + } + } + } + } + } + } + } + } + } + }, + "403": { + "description": "Password confirmation failed", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/hovercard/v1/{userId}": { "get": { "operationId": "hover_card-get-user", diff --git a/core/routes.php b/core/routes.php index fe1fe6fcd7500..9f8bc5c2c1e1e 100644 --- a/core/routes.php +++ b/core/routes.php @@ -123,6 +123,7 @@ ['root' => '/core', 'name' => 'AppPassword#getAppPassword', 'url' => '/getapppassword', 'verb' => 'GET'], ['root' => '/core', 'name' => 'AppPassword#rotateAppPassword', 'url' => '/apppassword/rotate', 'verb' => 'POST'], ['root' => '/core', 'name' => 'AppPassword#deleteAppPassword', 'url' => '/apppassword', 'verb' => 'DELETE'], + ['root' => '/core', 'name' => 'AppPassword#confirmUserPassword', 'url' => '/apppassword/confirm', 'verb' => 'PUT'], ['root' => '/hovercard', 'name' => 'HoverCard#getUser', 'url' => '/v1/{userId}', 'verb' => 'GET'],