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;
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 {
private IProvider $tokenProvider,
private IStore $credentialStore,
private IEventDispatcher $eventDispatcher,
+ private Session $userSession,
+ private IUserManager $userManager,
+ private IThrottler $throttler,
) {
parent::__construct($appName, $request);
}
'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);
+ }
}
}
}
},
+ "/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",
['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'],
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;
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;
/** @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;
$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',
$this->random,
$this->tokenProvider,
$this->credentialStore,
- $this->eventDispatcher
+ $this->eventDispatcher,
+ $this->userSession,
+ $this->userManager,
+ $this->throttler
);
}