Skip to content

Commit

Permalink
Try to load a room by accessToken
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <coding@schilljs.com>
  • Loading branch information
nickvergessen committed Oct 10, 2023
1 parent dd75306 commit ef03ac4
Show file tree
Hide file tree
Showing 9 changed files with 299 additions and 27 deletions.
15 changes: 15 additions & 0 deletions lib/Controller/AEnvironmentAwareController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ abstract class AEnvironmentAwareController extends OCSController {
protected int $apiVersion = 1;
protected ?Room $room = null;
protected ?Participant $participant = null;
protected ?string $federationCloudId = null;
protected ?string $federationAccessToken = null;

public function setAPIVersion(int $apiVersion): void {
$this->apiVersion = $apiVersion;
Expand All @@ -61,6 +63,19 @@ public function getParticipant(): ?Participant {
return $this->participant;
}

public function setRemoteAccess(?string $actorId, ?string $accessToken): void {
$this->federationCloudId = $actorId;
$this->federationAccessToken = $accessToken;
}

public function getRemoteAccessCloudId(): ?string {
return $this->federationCloudId;
}

public function getRemoteAccessToken(): ?string {
return $this->federationAccessToken;
}

/**
* Following the logic of {@see Dispatcher::executeController}
* @return string Either 'json' or 'xml'
Expand Down
80 changes: 67 additions & 13 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ public function getBreakoutRooms(): DataResponse {
* 404: Room not found
*/
#[PublicPage]
#[BruteForceProtection(action: 'talkFederationAccess')]
#[BruteForceProtection(action: 'talkRoomToken')]
#[BruteForceProtection(action: 'talkSipBridgeSecret')]
public function getSingleRoom(string $token): DataResponse {
Expand All @@ -325,18 +326,38 @@ public function getSingleRoom(string $token): DataResponse {
$includeLastMessage = !$isSIPBridgeRequest;

try {
$sessionId = $this->session->getSessionForRoom($token);
$room = $this->manager->getRoomForUserByToken($token, $this->userId, $sessionId, $includeLastMessage, $isSIPBridgeRequest);

$action = 'talkRoomToken';
$participant = null;
try {
$participant = $this->participantService->getParticipant($room, $this->userId, $sessionId);
} catch (ParticipantNotFoundException $e) {

$isTalkFederation = $this->request->getHeader('X-Nextcloud-Federation');

if (!$isTalkFederation) {
$sessionId = $this->session->getSessionForRoom($token);
$room = $this->manager->getRoomForUserByToken($token, $this->userId, $sessionId, $includeLastMessage, $isSIPBridgeRequest);

try {
$participant = $this->participantService->getParticipantBySession($room, $sessionId);
$participant = $this->participantService->getParticipant($room, $this->userId, $sessionId);
} catch (ParticipantNotFoundException $e) {
try {
$participant = $this->participantService->getParticipantBySession($room, $sessionId);
} catch (ParticipantNotFoundException $e) {
}
}
} else {
$action = 'talkFederationAccess';
$room = $this->manager->getRoomByRemoteAccess(
$token,
Attendee::ACTOR_FEDERATED_USERS,
$this->getRemoteAccessCloudId(),
$this->getRemoteAccessToken(),
);
$participant = $this->participantService->getParticipantByActor(
$room,
Attendee::ACTOR_FEDERATED_USERS,
$this->getRemoteAccessCloudId(),
);
}

$statuses = [];
if ($this->userId !== null
&& $this->appManager->isEnabledForUser('user_status')) {
Expand All @@ -362,7 +383,7 @@ public function getSingleRoom(string $token): DataResponse {
* @var DataResponse<Http::STATUS_NOT_FOUND, null, array{}> $response
*/
$response = new DataResponse([], Http::STATUS_NOT_FOUND);
$response->throttle(['token' => $token, 'action' => 'talkRoomToken']);
$response->throttle(['token' => $token, 'action' => $action]);
return $response;
}
}
Expand Down Expand Up @@ -1341,15 +1362,30 @@ public function setPassword(string $password): DataResponse {
* 409: Session already exists
*/
#[PublicPage]
#[BruteForceProtection(action: 'talkFederationAccess')]
#[BruteForceProtection(action: 'talkRoomPassword')]
#[BruteForceProtection(action: 'talkRoomToken')]
public function joinRoom(string $token, string $password = '', bool $force = true): DataResponse {
$sessionId = $this->session->getSessionForRoom($token);
$isTalkFederation = $this->request->getHeader('X-Nextcloud-Federation');
try {
// The participant is just joining, so enforce to not load any session
$room = $this->manager->getRoomForUserByToken($token, $this->userId, null);
if (!$isTalkFederation) {
$action = 'talkRoomToken';
$room = $this->manager->getRoomForUserByToken($token, $this->userId, null);
} else {
$action = 'talkFederationAccess';
$room = $this->manager->getRoomByRemoteAccess(
$token,
Attendee::ACTOR_FEDERATED_USERS,
$this->getRemoteAccessCloudId(),
$this->getRemoteAccessToken(),
);
}
} catch (RoomNotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
$response = new DataResponse([], Http::STATUS_NOT_FOUND);
$response->throttle(['token' => $token, 'action' => $action]);
return $response;
}

/** @var Participant|null $previousSession */
Expand Down Expand Up @@ -1392,6 +1428,8 @@ public function joinRoom(string $token, string $password = '', bool $force = tru
if ($user instanceof IUser) {
$participant = $this->participantService->joinRoom($this->roomService, $room, $user, $password, $result['result']);
$this->participantService->generatePinForParticipant($room, $participant);
} elseif ($isTalkFederation) {
$participant = $this->participantService->joinRoomAsFederatedUser($room, Attendee::ACTOR_FEDERATED_USERS, $this->getRemoteAccessCloudId());
} else {
$participant = $this->participantService->joinRoomAsNewGuest($this->roomService, $room, $password, $result['result'], $previousParticipant);
}
Expand All @@ -1403,7 +1441,7 @@ public function joinRoom(string $token, string $password = '', bool $force = tru
return $response;
} catch (UnauthorizedException $e) {
$response = new DataResponse([], Http::STATUS_NOT_FOUND);
$response->throttle(['token' => $token, 'action' => 'talkRoomToken']);
$response->throttle(['token' => $token, 'action' => $action]);
return $response;
}

Expand Down Expand Up @@ -1523,8 +1561,24 @@ public function leaveRoom(string $token): DataResponse {
$this->session->removeSessionForRoom($token);

try {
$room = $this->manager->getRoomForUserByToken($token, $this->userId, $sessionId);
$participant = $this->participantService->getParticipantBySession($room, $sessionId);
$isTalkFederation = $this->request->getHeader('X-Nextcloud-Federation');
// The participant is just joining, so enforce to not load any session
if (!$isTalkFederation) {
$room = $this->manager->getRoomForUserByToken($token, $this->userId, $sessionId);
$participant = $this->participantService->getParticipantBySession($room, $sessionId);
} else {
$room = $this->manager->getRoomByRemoteAccess(
$token,
Attendee::ACTOR_FEDERATED_USERS,
$this->getRemoteAccessCloudId(),
$this->getRemoteAccessToken(),
);
$participant = $this->participantService->getParticipantByActor(
$room,
Attendee::ACTOR_FEDERATED_USERS,
$this->getRemoteAccessCloudId(),
);
}
$this->participantService->leaveRoomAsSession($room, $participant);
} catch (RoomNotFoundException $e) {
} catch (ParticipantNotFoundException $e) {
Expand Down
35 changes: 35 additions & 0 deletions lib/Events/BeforeFederatedUserJoinedRoomEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Talk\Events;

class BeforeFederatedUserJoinedRoomEvent extends FederatedUserJoinedRoomEvent {
protected bool $cancelJoin = false;

public function isJoinCanceled(): bool {
return $this->cancelJoin;
}
public function cancelJoin(): void {
$this->cancelJoin = true;
}
}
39 changes: 39 additions & 0 deletions lib/Events/FederatedUserJoinedRoomEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Talk\Events;

use OCA\Talk\Room;

class FederatedUserJoinedRoomEvent extends RoomEvent {
public function __construct(
Room $room,
protected string $cloudId,
) {
parent::__construct($room);
}

public function getCloudId(): string {
return $this->cloudId;
}
}
2 changes: 1 addition & 1 deletion lib/Federation/FederationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
class FederationManager {
public const TALK_ROOM_RESOURCE = 'talk-room';
public const TALK_PROTOCOL_NAME = 'nctalk';
public const TOKEN_LENGTH = 15;
public const TOKEN_LENGTH = 64;

public function __construct(
private IConfig $config,
Expand Down
56 changes: 55 additions & 1 deletion lib/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,6 @@ public function getRoomForUserByToken(string $token, ?string $userId, ?string $s
}

// never joined before but found in listing
$listable = (int)$row['listable'];
if ($this->isRoomListableByUser($room, $userId)) {
return $room;
}
Expand Down Expand Up @@ -732,6 +731,61 @@ public function getRoomByActor(string $token, string $actorType, string $actorId
return $room;
}

/**
* @param string $token
* @param string $actorType
* @param string $actorId
* @param string $remoteAccess
* @param ?string $sessionId
* @return Room
* @throws RoomNotFoundException
*/
public function getRoomByRemoteAccess(string $token, string $actorType, string $actorId, string $remoteAccess, ?string $sessionId = null): Room {
$query = $this->db->getQueryBuilder();
$helper = new SelectHelper();
$helper->selectRoomsTable($query);
$helper->selectAttendeesTable($query);
$query->from('talk_rooms', 'r')
->leftJoin('r', 'talk_attendees', 'a', $query->expr()->andX(
$query->expr()->eq('a.actor_type', $query->createNamedParameter($actorType)),
$query->expr()->eq('a.actor_id', $query->createNamedParameter($actorId)),
$query->expr()->eq('a.access_token', $query->createNamedParameter($remoteAccess)),
$query->expr()->eq('a.room_id', 'r.id')
))
->where($query->expr()->eq('r.token', $query->createNamedParameter($token)));

if ($sessionId !== null) {
$helper->selectSessionsTable($query);
$query->leftJoin('a', 'talk_sessions', 's', $query->expr()->andX(
$query->expr()->eq('s.session_id', $query->createNamedParameter($sessionId)),
$query->expr()->eq('a.id', 's.attendee_id')
));
}

$result = $query->executeQuery();
$row = $result->fetch();
$result->closeCursor();

if ($row === false) {
throw new RoomNotFoundException();
}

if ($row['token'] === null) {
// FIXME Temporary solution for the Talk6 release
throw new RoomNotFoundException();
}

$room = $this->createRoomObject($row);
if (isset($row['actor_id'])) {
$participant = $this->createParticipantObject($room, $row);
$this->participantService->cacheParticipant($room, $participant);
} else {
throw new RoomNotFoundException();
}

return $room;
}

/**
* @param string $token
* @param string|null $preloadUserId Load this participant's information if possible
Expand Down
Loading

0 comments on commit ef03ac4

Please sign in to comment.