From 5625c65c9ea3b053f1d943046a1f75e401526af2 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 24 Apr 2023 12:42:04 +0200 Subject: [PATCH 1/4] fix(controllers): Migrate to UseSession attribute Signed-off-by: Joas Schilling --- lib/Controller/FilesIntegrationController.php | 5 +++-- lib/Controller/PageController.php | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/Controller/FilesIntegrationController.php b/lib/Controller/FilesIntegrationController.php index 4606eaf5aab..be50a1165a7 100644 --- a/lib/Controller/FilesIntegrationController.php +++ b/lib/Controller/FilesIntegrationController.php @@ -31,6 +31,7 @@ use OCA\Talk\Service\RoomService; use OCA\Talk\TalkSession; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSException; use OCP\AppFramework\OCS\OCSNotFoundException; @@ -148,7 +149,6 @@ public function getRoomByFileId(string $fileId): DataResponse { /** * @PublicPage - * @UseSession * @BruteForceProtection(action=shareinfo) * * Returns the token of the room associated to the file id of the given @@ -173,12 +173,13 @@ public function getRoomByFileId(string $fileId): DataResponse { * Besides the token of the room this also returns the current user ID and * display name, if any; this is needed by the Talk sidebar to know the * actual current user, as the public share page uses the incognito mode and - * thus logged in users as seen as guests. + * thus logged-in users as seen as guests. * * @param string $shareToken * @return DataResponse the status code is "200 OK" if a room is returned, * or "404 Not found" if the given share token was invalid. */ + #[UseSession] public function getRoomByShareToken(string $shareToken): DataResponse { if ($this->config->getAppValue('spreed', 'conversations_files', '1') !== '1' || $this->config->getAppValue('spreed', 'conversations_files_public_shares', '1') !== '1') { diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 88ddddf61ca..c4a3f0d5026 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -40,6 +40,7 @@ use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Http\RedirectResponse; @@ -126,13 +127,13 @@ public function __construct( /** * @PublicPage * @NoCSRFRequired - * @UseSession * @BruteForceProtection(action=talkRoomToken) * * @param string $token * @return Response * @throws HintException */ + #[UseSession] public function showCall(string $token): Response { // This is the entry point from the `/call/{token}` URL which is hardcoded in the server. return $this->index($token); @@ -141,7 +142,6 @@ public function showCall(string $token): Response { /** * @PublicPage * @NoCSRFRequired - * @UseSession * @BruteForceProtection(action=talkRoomPassword) * * @param string $token @@ -149,6 +149,7 @@ public function showCall(string $token): Response { * @return Response * @throws HintException */ + #[UseSession] public function authenticatePassword(string $token, string $password = ''): Response { // This is the entry point from the `/call/{token}` URL which is hardcoded in the server. return $this->index($token, '', $password); @@ -177,7 +178,6 @@ public function duplicateSession(): Response { /** * @PublicPage * @NoCSRFRequired - * @UseSession * @BruteForceProtection(action=talkRoomToken) * * @param string $token @@ -186,6 +186,7 @@ public function duplicateSession(): Response { * @return TemplateResponse|RedirectResponse * @throws HintException */ + #[UseSession] public function index(string $token = '', string $callUser = '', string $password = ''): Response { $bruteForceToken = $token; $user = $this->userSession->getUser(); From a51ebee5a24619c15981a0b6abd08f790ad659df Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 24 Apr 2023 12:54:48 +0200 Subject: [PATCH 2/4] fix(controllers): Migrate to BruteForceProtection attribute Signed-off-by: Joas Schilling --- lib/Controller/FilesIntegrationController.php | 3 ++- lib/Controller/PageController.php | 9 +++++---- lib/Controller/RecordingController.php | 7 +++---- lib/Controller/RoomController.php | 11 ++++++----- lib/Controller/SignalingController.php | 7 ++++--- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/Controller/FilesIntegrationController.php b/lib/Controller/FilesIntegrationController.php index be50a1165a7..ba011c635b6 100644 --- a/lib/Controller/FilesIntegrationController.php +++ b/lib/Controller/FilesIntegrationController.php @@ -31,6 +31,7 @@ use OCA\Talk\Service\RoomService; use OCA\Talk\TalkSession; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSException; @@ -149,7 +150,6 @@ public function getRoomByFileId(string $fileId): DataResponse { /** * @PublicPage - * @BruteForceProtection(action=shareinfo) * * Returns the token of the room associated to the file id of the given * share token. @@ -180,6 +180,7 @@ public function getRoomByFileId(string $fileId): DataResponse { * or "404 Not found" if the given share token was invalid. */ #[UseSession] + #[BruteForceProtection(action: 'shareinfo')] public function getRoomByShareToken(string $shareToken): DataResponse { if ($this->config->getAppValue('spreed', 'conversations_files', '1') !== '1' || $this->config->getAppValue('spreed', 'conversations_files_public_shares', '1') !== '1') { diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index c4a3f0d5026..1efe8c198ec 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -40,6 +40,7 @@ use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\NotFoundResponse; @@ -127,13 +128,13 @@ public function __construct( /** * @PublicPage * @NoCSRFRequired - * @BruteForceProtection(action=talkRoomToken) * * @param string $token * @return Response * @throws HintException */ #[UseSession] + #[BruteForceProtection(action: 'talkRoomToken')] public function showCall(string $token): Response { // This is the entry point from the `/call/{token}` URL which is hardcoded in the server. return $this->index($token); @@ -142,7 +143,6 @@ public function showCall(string $token): Response { /** * @PublicPage * @NoCSRFRequired - * @BruteForceProtection(action=talkRoomPassword) * * @param string $token * @param string $password @@ -150,6 +150,7 @@ public function showCall(string $token): Response { * @throws HintException */ #[UseSession] + #[BruteForceProtection(action: 'talkRoomPassword')] public function authenticatePassword(string $token, string $password = ''): Response { // This is the entry point from the `/call/{token}` URL which is hardcoded in the server. return $this->index($token, '', $password); @@ -178,7 +179,6 @@ public function duplicateSession(): Response { /** * @PublicPage * @NoCSRFRequired - * @BruteForceProtection(action=talkRoomToken) * * @param string $token * @param string $callUser @@ -186,6 +186,7 @@ public function duplicateSession(): Response { * @return TemplateResponse|RedirectResponse * @throws HintException */ + #[BruteForceProtection(action: 'talkRoomToken')] #[UseSession] public function index(string $token = '', string $callUser = '', string $password = ''): Response { $bruteForceToken = $token; @@ -309,11 +310,11 @@ public function index(string $token = '', string $callUser = '', string $passwor /** * @PublicPage * @NoCSRFRequired - * @BruteForceProtection(action=talkRoomToken) * * @param string $token * @return TemplateResponse|NotFoundResponse */ + #[BruteForceProtection(action: 'talkRoomToken')] public function recording(string $token): Response { try { $room = $this->manager->getRoomByToken($token); diff --git a/lib/Controller/RecordingController.php b/lib/Controller/RecordingController.php index 702f9bbc3de..b016e00c464 100644 --- a/lib/Controller/RecordingController.php +++ b/lib/Controller/RecordingController.php @@ -36,6 +36,7 @@ use OCA\Talk\Service\RecordingService; use OCA\Talk\Service\RoomService; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\DataResponse; use OCP\Http\Client\IClientService; use OCP\IRequest; @@ -132,10 +133,10 @@ protected function getInputStream(): string { * Backend API to update recording status by backends. * * @PublicPage - * @BruteForceProtection(action=talkRecordingSecret) * * @return DataResponse */ + #[BruteForceProtection(action: 'talkRecordingSecret')] public function backend(): DataResponse { $json = $this->getInputStream(); if (!$this->validateBackendRequest($json)) { @@ -292,10 +293,8 @@ public function stop(): DataResponse { /** * @PublicPage * @RequireRoom - * @BruteForceProtection(action=talkRecordingSecret) - * - * @return DataResponse */ + #[BruteForceProtection(action: 'talkRecordingSecret')] public function store(string $owner): DataResponse { $data = $this->room->getToken(); if (!$this->validateBackendRequest($data)) { diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index 0656d038cc3..c8121437e0e 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -53,6 +53,7 @@ use OCA\Talk\Webinary; use OCP\App\IAppManager; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\EventDispatcher\IEventDispatcher; @@ -284,10 +285,10 @@ public function getListedRooms(string $searchTerm = ''): DataResponse { * * @NoAdminRequired * @RequireLoggedInParticipant - * @BruteForceProtection(action=talkRoomToken) * * @return DataResponse */ + #[BruteForceProtection(action: 'talkRoomToken')] public function getBreakoutRooms(): DataResponse { try { $rooms = $this->breakoutRoomService->getBreakoutRooms($this->room, $this->participant); @@ -312,11 +313,11 @@ public function getBreakoutRooms(): DataResponse { /** * @PublicPage - * @BruteForceProtection(action=talkRoomToken) * * @param string $token * @return DataResponse */ + #[BruteForceProtection(action: 'talkRoomToken')] public function getSingleRoom(string $token): DataResponse { try { $isSIPBridgeRequest = $this->validateSIPBridgeRequest($token); @@ -1222,13 +1223,13 @@ public function setPassword(string $password): DataResponse { /** * @PublicPage - * @BruteForceProtection(action=talkRoomPassword) * * @param string $token * @param string $password * @param bool $force * @return DataResponse */ + #[BruteForceProtection(action: 'talkRoomPassword')] public function joinRoom(string $token, string $password = '', bool $force = true): DataResponse { $sessionId = $this->session->getSessionForRoom($token); try { @@ -1305,11 +1306,11 @@ public function joinRoom(string $token, string $password = '', bool $force = tru /** * @PublicPage * @RequireRoom - * @BruteForceProtection(action=talkSipBridgeSecret) * * @param string $pin * @return DataResponse */ + #[BruteForceProtection(action: 'talkSipBridgeSecret')] public function getParticipantByDialInPin(string $pin): DataResponse { try { if (!$this->validateSIPBridgeRequest($this->room->getToken())) { @@ -1335,10 +1336,10 @@ public function getParticipantByDialInPin(string $pin): DataResponse { /** * @PublicPage * @RequireRoom - * @BruteForceProtection(action=talkSipBridgeSecret) * * @return DataResponse */ + #[BruteForceProtection(action: 'talkSipBridgeSecret')] public function createGuestByDialIn(): DataResponse { try { if (!$this->validateSIPBridgeRequest($this->room->getToken())) { diff --git a/lib/Controller/SignalingController.php b/lib/Controller/SignalingController.php index 40a8acd8a31..1652672fa13 100644 --- a/lib/Controller/SignalingController.php +++ b/lib/Controller/SignalingController.php @@ -40,6 +40,7 @@ use OCA\Talk\Signaling\Messages; use OCA\Talk\TalkSession; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\AppFramework\Utility\ITimeFactory; @@ -145,11 +146,11 @@ private function validateRecordingBackendRequest(string $data): bool { /** * @PublicPage - * @BruteForceProtection(action=talkRoomToken) * * @param string $token * @return DataResponse */ + #[BruteForceProtection(action: 'talkRoomToken')] public function getSettings(string $token = ''): DataResponse { $isRecordingRequest = false; @@ -176,7 +177,7 @@ public function getSettings(string $token = ''): DataResponse { } } catch (RoomNotFoundException $e) { $response = new DataResponse([], Http::STATUS_NOT_FOUND); - $response->throttle(['token' => $token]); + $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); return $response; } @@ -526,10 +527,10 @@ protected function getInputStream(): string { * https://nextcloud-spreed-signaling.readthedocs.io/en/latest/standalone-signaling-api-v1/#backend-requests * * @PublicPage - * @BruteForceProtection(action=talkSignalingSecret) * * @return DataResponse */ + #[BruteForceProtection(action: 'talkSignalingSecret')] public function backend(): DataResponse { $json = $this->getInputStream(); if (!$this->validateBackendRequest($json)) { From e5693a79038d26426a66416b73340c1646737ce7 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 24 Apr 2023 13:00:44 +0200 Subject: [PATCH 3/4] fix(controllers): Always specify the bruteforced action Signed-off-by: Joas Schilling --- lib/Controller/FilesIntegrationController.php | 2 +- lib/Controller/PageController.php | 10 +++++----- lib/Controller/RecordingController.php | 4 ++-- lib/Controller/RoomController.php | 15 ++++++++------- lib/Controller/SignalingController.php | 2 +- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/Controller/FilesIntegrationController.php b/lib/Controller/FilesIntegrationController.php index ba011c635b6..0b47254e07a 100644 --- a/lib/Controller/FilesIntegrationController.php +++ b/lib/Controller/FilesIntegrationController.php @@ -197,7 +197,7 @@ public function getRoomByShareToken(string $shareToken): DataResponse { } } catch (ShareNotFound $e) { $response = new DataResponse([], Http::STATUS_NOT_FOUND); - $response->throttle(['token' => $shareToken]); + $response->throttle(['token' => $shareToken, 'action' => 'shareinfo']); return $response; } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 1efe8c198ec..3c5781587e4 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -258,7 +258,7 @@ public function index(string $token = '', string $callUser = '', string $passwor $response = new RedirectResponse($passwordVerification['url']); } - $response->throttle(['token' => $token]); + $response->throttle(['token' => $token, 'action' => 'talkRoomPassword']); return $response; } } @@ -302,7 +302,7 @@ public function index(string $token = '', string $callUser = '', string $passwor $response->setContentSecurityPolicy($csp); if ($throttle) { // Logged-in user tried to access a chat they can not access - $response->throttle(['token' => $bruteForceToken]); + $response->throttle(['token' => $bruteForceToken, 'action' => 'talkRoomToken']); } return $response; } @@ -320,7 +320,7 @@ public function recording(string $token): Response { $room = $this->manager->getRoomByToken($token); } catch (RoomNotFoundException $e) { $response = new NotFoundResponse(); - $response->throttle(['token' => $token]); + $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); return $response; } @@ -377,7 +377,7 @@ protected function guestEnterRoom(string $token, string $password): Response { $response = new RedirectResponse($this->url->linkToRoute('core.login.showLoginForm', [ 'redirect_url' => $redirectUrl, ])); - $response->throttle(['token' => $token]); + $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); return $response; } @@ -401,7 +401,7 @@ protected function guestEnterRoom(string $token, string $password): Response { } else { $response = new RedirectResponse($passwordVerification['url']); } - $response->throttle(['token' => $token]); + $response->throttle(['token' => $token, 'action' => 'talkRoomPassword']); return $response; } } diff --git a/lib/Controller/RecordingController.php b/lib/Controller/RecordingController.php index b016e00c464..f4ab1a6b303 100644 --- a/lib/Controller/RecordingController.php +++ b/lib/Controller/RecordingController.php @@ -147,7 +147,7 @@ public function backend(): DataResponse { 'message' => 'The request could not be authenticated.', ], ], Http::STATUS_FORBIDDEN); - $response->throttle(); + $response->throttle(['action' => 'talkRecordingSecret']); return $response; } @@ -305,7 +305,7 @@ public function store(string $owner): DataResponse { 'message' => 'The request could not be authenticated.', ], ], Http::STATUS_UNAUTHORIZED); - $response->throttle(); + $response->throttle(['action' => 'talkRecordingSecret']); return $response; } diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index c8121437e0e..23e7953ff83 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -366,7 +366,7 @@ public function getSingleRoom(string $token): DataResponse { return new DataResponse($this->formatRoom($room, $participant, $statuses, $isSIPBridgeRequest), Http::STATUS_OK, $this->getTalkHashHeader()); } catch (RoomNotFoundException $e) { $response = new DataResponse([], Http::STATUS_NOT_FOUND); - $response->throttle(['token' => $token]); + $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); return $response; } } @@ -1230,6 +1230,7 @@ public function setPassword(string $password): DataResponse { * @return DataResponse */ #[BruteForceProtection(action: 'talkRoomPassword')] + #[BruteForceProtection(action: 'talkRoomToken')] public function joinRoom(string $token, string $password = '', bool $force = true): DataResponse { $sessionId = $this->session->getSessionForRoom($token); try { @@ -1285,11 +1286,11 @@ public function joinRoom(string $token, string $password = '', bool $force = tru $this->throttler->resetDelay($this->request->getRemoteAddress(), 'talkRoomToken', ['token' => $token]); } catch (InvalidPasswordException $e) { $response = new DataResponse([], Http::STATUS_FORBIDDEN); - $response->throttle(['token' => $token]); + $response->throttle(['token' => $token, 'action' => 'talkRoomPassword']); return $response; } catch (UnauthorizedException $e) { $response = new DataResponse([], Http::STATUS_NOT_FOUND); - $response->throttle(['token' => $token]); + $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); return $response; } @@ -1315,12 +1316,12 @@ public function getParticipantByDialInPin(string $pin): DataResponse { try { if (!$this->validateSIPBridgeRequest($this->room->getToken())) { $response = new DataResponse([], Http::STATUS_UNAUTHORIZED); - $response->throttle(); + $response->throttle(['action' => 'talkSipBridgeSecret']); return $response; } } catch (UnauthorizedException $e) { $response = new DataResponse([], Http::STATUS_UNAUTHORIZED); - $response->throttle(); + $response->throttle(['action' => 'talkSipBridgeSecret']); return $response; } @@ -1344,12 +1345,12 @@ public function createGuestByDialIn(): DataResponse { try { if (!$this->validateSIPBridgeRequest($this->room->getToken())) { $response = new DataResponse([], Http::STATUS_UNAUTHORIZED); - $response->throttle(); + $response->throttle(['action' => 'talkSipBridgeSecret']); return $response; } } catch (UnauthorizedException $e) { $response = new DataResponse([], Http::STATUS_UNAUTHORIZED); - $response->throttle(); + $response->throttle(['action' => 'talkSipBridgeSecret']); return $response; } diff --git a/lib/Controller/SignalingController.php b/lib/Controller/SignalingController.php index 1652672fa13..bff25ff8e7c 100644 --- a/lib/Controller/SignalingController.php +++ b/lib/Controller/SignalingController.php @@ -541,7 +541,7 @@ public function backend(): DataResponse { 'message' => 'The request could not be authenticated.', ], ]); - $response->throttle(); + $response->throttle(['action' => 'talkSignalingSecret']); return $response; } From 2bef32969f4f00cec4bfed1592b15e4372248458 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 24 Apr 2023 13:10:55 +0200 Subject: [PATCH 4/4] fix(controllers): Migrate to proper "multiple bruteforce protections" support Signed-off-by: Joas Schilling --- lib/Controller/RoomController.php | 9 ++++----- lib/Controller/SignalingController.php | 13 ++++--------- lib/Middleware/InjectionMiddleware.php | 26 ++++++++++++++++++-------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index 23e7953ff83..8d4f9d2a4ce 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -318,15 +318,14 @@ public function getBreakoutRooms(): DataResponse { * @return DataResponse */ #[BruteForceProtection(action: 'talkRoomToken')] + #[BruteForceProtection(action: 'talkSipBridgeSecret')] public function getSingleRoom(string $token): DataResponse { try { $isSIPBridgeRequest = $this->validateSIPBridgeRequest($token); } catch (UnauthorizedException $e) { - $ip = $this->request->getRemoteAddress(); - $action = 'talkSipBridgeSecret'; - $this->throttler->sleepDelay($ip, $action); - $this->throttler->registerAttempt($action, $ip); - return new DataResponse([], Http::STATUS_UNAUTHORIZED); + $response = new DataResponse([], Http::STATUS_UNAUTHORIZED); + $response->throttle(['action' => 'talkSipBridgeSecret']); + return $response; } // The SIP bridge only needs room details (public, sip enabled, lobby state, etc) diff --git a/lib/Controller/SignalingController.php b/lib/Controller/SignalingController.php index bff25ff8e7c..4fb627b85a9 100644 --- a/lib/Controller/SignalingController.php +++ b/lib/Controller/SignalingController.php @@ -61,7 +61,6 @@ class SignalingController extends OCSController { public const EVENT_BACKEND_SIGNALING_ROOMS = self::class . '::signalingBackendRoom'; - private IConfig $serverConfig; private Config $talkConfig; private \OCA\Talk\Signaling\Manager $signalingManager; private TalkSession $session; @@ -74,7 +73,6 @@ class SignalingController extends OCSController { private IEventDispatcher $dispatcher; private ITimeFactory $timeFactory; private IClientService $clientService; - private IThrottler $throttler; private LoggerInterface $logger; private ?string $userId; @@ -99,7 +97,6 @@ public function __construct( ?string $UserId, ) { parent::__construct($appName, $request); - $this->serverConfig = $serverConfig; $this->talkConfig = $talkConfig; $this->signalingManager = $signalingManager; $this->session = $session; @@ -112,7 +109,6 @@ public function __construct( $this->dispatcher = $dispatcher; $this->timeFactory = $timeFactory; $this->clientService = $clientService; - $this->throttler = $throttler; $this->logger = $logger; $this->userId = $UserId; } @@ -151,16 +147,15 @@ private function validateRecordingBackendRequest(string $data): bool { * @return DataResponse */ #[BruteForceProtection(action: 'talkRoomToken')] + #[BruteForceProtection(action: 'talkRecordingSecret')] public function getSettings(string $token = ''): DataResponse { $isRecordingRequest = false; if (!empty($this->request->getHeader('Talk-Recording-Random')) || !empty($this->request->getHeader('Talk-Recording-Checksum'))) { if (!$this->validateRecordingBackendRequest('')) { - $ip = $this->request->getRemoteAddress(); - $action = 'talkRecordingSecret'; - $this->throttler->sleepDelay($ip, $action); - $this->throttler->registerAttempt($action, $ip); - return new DataResponse([], Http::STATUS_UNAUTHORIZED); + $response = new DataResponse([], Http::STATUS_UNAUTHORIZED); + $response->throttle(['action' => 'talkRecordingSecret']); + return $response; } $isRecordingRequest = true; diff --git a/lib/Middleware/InjectionMiddleware.php b/lib/Middleware/InjectionMiddleware.php index 67e10d56634..e697cc20159 100644 --- a/lib/Middleware/InjectionMiddleware.php +++ b/lib/Middleware/InjectionMiddleware.php @@ -39,6 +39,7 @@ use OCA\Talk\Webinary; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\RedirectToDefaultAppResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; @@ -263,15 +264,24 @@ public function afterException($controller, $methodName, \Exception $exception): if ($exception instanceof RoomNotFoundException || $exception instanceof ParticipantNotFoundException) { if ($controller instanceof OCSController) { - $isBruteForceProtected = $this->reflector->hasAnnotation('BruteForceProtection'); - if ($isBruteForceProtected) { - $ip = $this->request->getRemoteAddress(); - $action = 'talkRoomToken'; - $this->throttler->sleepDelay($ip, $action); - $this->throttler->registerAttempt($action, $ip, [ - 'token' => $this->request->getParam('token') ?? '', - ]); + $reflectionMethod = new \ReflectionMethod($controller, $methodName); + $attributes = $reflectionMethod->getAttributes(BruteForceProtection::class); + + if (!empty($attributes)) { + foreach ($attributes as $attribute) { + /** @var BruteForceProtection $protection */ + $protection = $attribute->newInstance(); + $action = $protection->getAction(); + + if ('talkRoomToken' === $action) { + $this->throttler->sleepDelay($this->request->getRemoteAddress(), $action); + $this->throttler->registerAttempt($action, $this->request->getRemoteAddress(), [ + 'token' => $this->request->getParam('token') ?? '', + ]); + } + } } + throw new OCSException('', Http::STATUS_NOT_FOUND); }