From 0076d6bbf0ac0c3cfff596625f48d008c83d359e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Wed, 20 Sep 2023 17:13:28 +0200 Subject: [PATCH 1/5] feat: Allow guests to contacts user via public profile "Talk to" link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonas Dreßler --- lib/Controller/PageController.php | 62 +++++++++++++++++++++++++++++-- lib/Profile/TalkAction.php | 8 ++-- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 537bd4339cc..fd232e16f5e 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -52,6 +52,7 @@ use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Template\PublicTemplateResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Http\TooManyRequestsResponse; use OCP\AppFramework\Services\IInitialState; use OCP\Collaboration\Reference\RenderReferenceEvent; use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent; @@ -64,9 +65,13 @@ use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUser; +use OCP\IUserManager; use OCP\IUserSession; +use OCP\L10N\IFactory; use OCP\Notification\IManager as INotificationManager; use OCP\Security\Bruteforce\IThrottler; +use OCP\Security\RateLimiting\ILimiter; +use OCP\Security\RateLimiting\IRateLimitExceededException; use Psr\Log\LoggerInterface; #[IgnoreOpenAPI] @@ -95,6 +100,9 @@ public function __construct( Config $talkConfig, IConfig $serverConfig, IGroupManager $groupManager, + protected IUserManager $userManager, + protected ILimiter $limiter, + protected IFactory $l10nFactory, ) { parent::__construct($appName, $request); $this->initialState = $initialState; @@ -148,7 +156,7 @@ public function duplicateSession(): Response { /** * @param string $token * @param string $callUser - * @return TemplateResponse|RedirectResponse + * @return TemplateResponse|RedirectResponse|TooManyRequestsResponse * @throws HintException */ #[NoCSRFRequired] @@ -162,18 +170,66 @@ public function index(string $token = '', string $callUser = ''): Response { return $this->pageHandler($token, $callUser); } + /** + * @param string $forUser + * @return ?Room + */ + protected function createPrivateRoom(string $forUser): ?Room { + $user = $this->userManager->get($forUser); + if (!$user instanceof IUser) { + return null; + } + + try { + $objectType = ''; + $objectId = ''; + $l = $this->l10nFactory->get('spreed', $this->l10nFactory->getUserLanguage($user)); + $room = $this->roomService->createConversation(Room::TYPE_PUBLIC, + $l->t('Contact request'), $user, $objectType, $objectId, + ); + } catch (\InvalidArgumentException $e) { + return null; + } + + return $room; + } + /** * @param string $token * @param string $callUser * @param string $password - * @return TemplateResponse|RedirectResponse + * @return TemplateResponse|RedirectResponse|TooManyRequestsResponse * @throws HintException */ protected function pageHandler(string $token = '', string $callUser = '', string $password = ''): Response { $bruteForceToken = $token; $user = $this->userSession->getUser(); if (!$user instanceof IUser) { - return $this->guestEnterRoom($token, $password); + if ($token === '') { + $room = $this->createPrivateRoom($callUser); + if ($room === null) { + $response = new TemplateResponse('core', '404-profile', [], 'guest'); + $response->throttle(['action' => 'callUser', 'callUser' => $callUser]); + + return $response; + } + + try { + $this->limiter->registerAnonRequest( + 'create-anonymous-conversation', + 5, // Five conversations + 60 * 60, // Per hour + $this->request->getRemoteAddress(), + ); + } catch (IRateLimitExceededException) { + return new TooManyRequestsResponse(); + } + + // FIXME: add rate limiting + return $this->redirectToConversation($room->getToken()); + } else { + return $this->guestEnterRoom($token, $password); + } } $throttle = false; diff --git a/lib/Profile/TalkAction.php b/lib/Profile/TalkAction.php index 943aed15b24..b922a873c8c 100644 --- a/lib/Profile/TalkAction.php +++ b/lib/Profile/TalkAction.php @@ -63,9 +63,10 @@ public function getDisplayId(): string { public function getTitle(): string { $visitingUser = $this->userSession->getUser(); - if (!$visitingUser || $visitingUser === $this->targetUser) { + if ($visitingUser === $this->targetUser) { return $this->l->t('Open Talk'); } + return $this->l->t('Talk to %s', [$this->targetUser->getDisplayName()]); } @@ -80,9 +81,8 @@ public function getIcon(): string { public function getTarget(): ?string { $visitingUser = $this->userSession->getUser(); if ( - !$visitingUser - || $this->config->isDisabledForUser($this->targetUser) - || $this->config->isDisabledForUser($visitingUser) + $this->config->isDisabledForUser($this->targetUser) + || ($visitingUser && $this->config->isDisabledForUser($visitingUser)) ) { return null; } From 5c721eb80c0369ff61bbd534ae030f2a377b12a4 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 23 Oct 2023 11:04:26 +0200 Subject: [PATCH 2/5] fix(profile): Move rate-limiting before the action Signed-off-by: Joas Schilling --- lib/Controller/PageController.php | 41 ++++++++++++++----------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index fd232e16f5e..9c8c0d080bc 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -170,24 +170,21 @@ public function index(string $token = '', string $callUser = ''): Response { return $this->pageHandler($token, $callUser); } - /** - * @param string $forUser - * @return ?Room - */ - protected function createPrivateRoom(string $forUser): ?Room { - $user = $this->userManager->get($forUser); + protected function createPrivateRoom(string $targetUserId): ?Room { + $user = $this->userManager->get($targetUserId); if (!$user instanceof IUser) { return null; } + $l = $this->l10nFactory->get('spreed', $this->l10nFactory->getUserLanguage($user)); + try { - $objectType = ''; - $objectId = ''; - $l = $this->l10nFactory->get('spreed', $this->l10nFactory->getUserLanguage($user)); - $room = $this->roomService->createConversation(Room::TYPE_PUBLIC, - $l->t('Contact request'), $user, $objectType, $objectId, + $room = $this->roomService->createConversation( + Room::TYPE_PUBLIC, + $l->t('Contact request'), + $user, ); - } catch (\InvalidArgumentException $e) { + } catch (\InvalidArgumentException) { return null; } @@ -206,14 +203,6 @@ protected function pageHandler(string $token = '', string $callUser = '', string $user = $this->userSession->getUser(); if (!$user instanceof IUser) { if ($token === '') { - $room = $this->createPrivateRoom($callUser); - if ($room === null) { - $response = new TemplateResponse('core', '404-profile', [], 'guest'); - $response->throttle(['action' => 'callUser', 'callUser' => $callUser]); - - return $response; - } - try { $this->limiter->registerAnonRequest( 'create-anonymous-conversation', @@ -225,11 +214,17 @@ protected function pageHandler(string $token = '', string $callUser = '', string return new TooManyRequestsResponse(); } - // FIXME: add rate limiting + $room = $this->createPrivateRoom($callUser); + if ($room === null) { + $response = new TemplateResponse('core', '404-profile', [], TemplateResponse::RENDER_AS_GUEST); + $response->throttle(['action' => 'callUser', 'callUser' => $callUser]); + return $response; + } + return $this->redirectToConversation($room->getToken()); - } else { - return $this->guestEnterRoom($token, $password); } + + return $this->guestEnterRoom($token, $password); } $throttle = false; From 7078ce64d7d37d2722e6958976d15ec74206b9d6 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 23 Oct 2023 12:38:06 +0200 Subject: [PATCH 3/5] fix(profile): Check profile config before creating room from guest Signed-off-by: Joas Schilling --- lib/Controller/PageController.php | 34 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 9c8c0d080bc..79cf7b23ca1 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -69,6 +69,7 @@ use OCP\IUserSession; use OCP\L10N\IFactory; use OCP\Notification\IManager as INotificationManager; +use OCP\Profile\IProfileManager; use OCP\Security\Bruteforce\IThrottler; use OCP\Security\RateLimiting\ILimiter; use OCP\Security\RateLimiting\IRateLimitExceededException; @@ -101,6 +102,7 @@ public function __construct( IConfig $serverConfig, IGroupManager $groupManager, protected IUserManager $userManager, + protected IProfileManager $profileManager, protected ILimiter $limiter, protected IFactory $l10nFactory, ) { @@ -170,25 +172,26 @@ public function index(string $token = '', string $callUser = ''): Response { return $this->pageHandler($token, $callUser); } - protected function createPrivateRoom(string $targetUserId): ?Room { + /** + * @throws \InvalidArgumentException + */ + protected function createPrivateRoom(string $targetUserId): Room { $user = $this->userManager->get($targetUserId); if (!$user instanceof IUser) { - return null; + throw new \InvalidArgumentException('user'); } - $l = $this->l10nFactory->get('spreed', $this->l10nFactory->getUserLanguage($user)); - - try { - $room = $this->roomService->createConversation( - Room::TYPE_PUBLIC, - $l->t('Contact request'), - $user, - ); - } catch (\InvalidArgumentException) { - return null; + if ($this->profileManager->isProfileFieldVisible('talk', $user, null)) { + throw new \InvalidArgumentException('profile'); } - return $room; + $l = $this->l10nFactory->get('spreed', $this->l10nFactory->getUserLanguage($user)); + + return $this->roomService->createConversation( + Room::TYPE_PUBLIC, + $l->t('Contact request'), + $user, + ); } /** @@ -214,8 +217,9 @@ protected function pageHandler(string $token = '', string $callUser = '', string return new TooManyRequestsResponse(); } - $room = $this->createPrivateRoom($callUser); - if ($room === null) { + try { + $room = $this->createPrivateRoom($callUser); + } catch (\InvalidArgumentException) { $response = new TemplateResponse('core', '404-profile', [], TemplateResponse::RENDER_AS_GUEST); $response->throttle(['action' => 'callUser', 'callUser' => $callUser]); return $response; From 777241c1d7fad0c900699d6d5136652348e28cf1 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 25 Oct 2023 16:33:35 +0200 Subject: [PATCH 4/5] fix(CI): Update OCP package Signed-off-by: Joas Schilling --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index de55963bc01..172118b9fc5 100644 --- a/composer.lock +++ b/composer.lock @@ -258,12 +258,12 @@ "source": { "type": "git", "url": "https://github.com/nextcloud-deps/ocp.git", - "reference": "a74e96645848edc4c29ed65629543ecec4cfaea1" + "reference": "fa7ed9794598fa62f13cc4a6c4cafd25af4b6c3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/a74e96645848edc4c29ed65629543ecec4cfaea1", - "reference": "a74e96645848edc4c29ed65629543ecec4cfaea1", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/fa7ed9794598fa62f13cc4a6c4cafd25af4b6c3e", + "reference": "fa7ed9794598fa62f13cc4a6c4cafd25af4b6c3e", "shasum": "" }, "require": { @@ -295,7 +295,7 @@ "issues": "https://github.com/nextcloud-deps/ocp/issues", "source": "https://github.com/nextcloud-deps/ocp/tree/master" }, - "time": "2023-10-20T00:31:29+00:00" + "time": "2023-10-24T00:31:30+00:00" }, { "name": "psr/clock", From 7bbf800a2ecb7b66e12f7acc84a3b8f0ddbb77b7 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 25 Oct 2023 16:48:15 +0200 Subject: [PATCH 5/5] fix(profile): Check if the user is allowed to create rooms Signed-off-by: Joas Schilling --- lib/Controller/PageController.php | 10 +++++++--- lib/Profile/TalkAction.php | 17 ++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 79cf7b23ca1..de5b348faaf 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -175,13 +175,17 @@ public function index(string $token = '', string $callUser = ''): Response { /** * @throws \InvalidArgumentException */ - protected function createPrivateRoom(string $targetUserId): Room { + protected function createContactRequestRoom(string $targetUserId): Room { $user = $this->userManager->get($targetUserId); if (!$user instanceof IUser) { throw new \InvalidArgumentException('user'); } - if ($this->profileManager->isProfileFieldVisible('talk', $user, null)) { + if ($this->talkConfig->isNotAllowedToCreateConversations($user)) { + throw new \InvalidArgumentException('config'); + } + + if (!$this->profileManager->isProfileFieldVisible('talk', $user, null)) { throw new \InvalidArgumentException('profile'); } @@ -218,7 +222,7 @@ protected function pageHandler(string $token = '', string $callUser = '', string } try { - $room = $this->createPrivateRoom($callUser); + $room = $this->createContactRequestRoom($callUser); } catch (\InvalidArgumentException) { $response = new TemplateResponse('core', '404-profile', [], TemplateResponse::RENDER_AS_GUEST); $response->throttle(['action' => 'callUser', 'callUser' => $callUser]); diff --git a/lib/Profile/TalkAction.php b/lib/Profile/TalkAction.php index b922a873c8c..c7a6dc8c646 100644 --- a/lib/Profile/TalkAction.php +++ b/lib/Profile/TalkAction.php @@ -79,16 +79,23 @@ public function getIcon(): string { } public function getTarget(): ?string { - $visitingUser = $this->userSession->getUser(); - if ( - $this->config->isDisabledForUser($this->targetUser) - || ($visitingUser && $this->config->isDisabledForUser($visitingUser)) - ) { + if ($this->config->isDisabledForUser($this->targetUser)) { return null; } + + $visitingUser = $this->userSession->getUser(); if ($visitingUser === $this->targetUser) { return $this->urlGenerator->linkToRouteAbsolute('spreed.Page.index'); } + + if ($visitingUser && $this->config->isDisabledForUser($visitingUser)) { + return null; + } + + if (!$visitingUser && $this->config->isNotAllowedToCreateConversations($this->targetUser)) { + return null; + } + return $this->urlGenerator->linkToRouteAbsolute('spreed.Page.index') . '?callUser=' . $this->targetUser->getUID(); } }