Browse Source

feat(core): Add OCS endpoint for confirming the user password

Signed-off-by: provokateurin <kate@provokateurin.de>
tags/v29.0.0beta1
provokateurin 3 months ago
parent
commit
6243a9471d
Failed to extract signature

+ 36
- 0
core/Controller/AppPasswordController.php View File

@@ -31,7 +31,9 @@ namespace OC\Core\Controller;
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\Authentication\LoginCredentials\IStore;
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 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
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 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
'apppassword' => $newToken,
]);
}

/**
* Confirm the user password
*
* @NoAdminRequired
* @BruteForceProtection(action=sudo)
*
* @param string $password The password of the user
*
* @return DataResponse<Http::STATUS_OK, array{lastLogin: int}, array{}>|DataResponse<Http::STATUS_FORBIDDEN, array<empty>, 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);
}
}

+ 107
- 0
core/openapi.json View File

@@ -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",

+ 1
- 0
core/routes.php View File

@@ -123,6 +123,7 @@ $application->registerRoutes($this, [
['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'],


+ 19
- 1
tests/Core/Controller/AppPasswordControllerTest.php View File

@@ -29,6 +29,7 @@ use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Token\IProvider;
use OC\Authentication\Token\IToken;
use OC\Core\Controller\AppPasswordController;
use OC\User\Session;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\Authentication\Exceptions\CredentialsUnavailableException;
@@ -38,6 +39,8 @@ use OCP\Authentication\LoginCredentials\IStore;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserManager;
use OCP\Security\Bruteforce\IThrottler;
use OCP\Security\ISecureRandom;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
@@ -61,6 +64,15 @@ class AppPasswordControllerTest extends TestCase {
/** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
private $eventDispatcher;

/** @var Session|MockObject */
private $userSession;

/** @var IUserManager|MockObject */
private $userManager;

/** @var IThrottler|MockObject */
private $throttler;

/** @var AppPasswordController */
private $controller;

@@ -73,6 +85,9 @@ class AppPasswordControllerTest extends TestCase {
$this->credentialStore = $this->createMock(IStore::class);
$this->request = $this->createMock(IRequest::class);
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
$this->userSession = $this->createMock(Session::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->throttler = $this->createMock(IThrottler::class);

$this->controller = new AppPasswordController(
'core',
@@ -81,7 +96,10 @@ class AppPasswordControllerTest extends TestCase {
$this->random,
$this->tokenProvider,
$this->credentialStore,
$this->eventDispatcher
$this->eventDispatcher,
$this->userSession,
$this->userManager,
$this->throttler
);
}


Loading…
Cancel
Save