Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/Chat/MessageParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ protected function setActor(Message $message): void {
$displayName = $botName . ' (Bot)';
}
}
} elseif ($comment->getActorType() === Attendee::ACTOR_FEDERATED_USERS) {
// FIXME Read from some addressbooks?
$displayName = $actorId;
}

$message->setActor(
Expand Down
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
24 changes: 14 additions & 10 deletions lib/Controller/ChatController.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,27 @@ public function __construct(
parent::__construct($appName, $request);
}

/**
* @return list{0: Attendee::ACTOR_*, 1: string}
*/
protected function getActorInfo(string $actorDisplayName = ''): array {
if ($this->userId === null) {
$actorType = Attendee::ACTOR_GUESTS;
$actorId = $this->participant->getAttendee()->getActorId();
$remoteCloudId = $this->getRemoteAccessCloudId();
if ($remoteCloudId !== null) {
return [Attendee::ACTOR_FEDERATED_USERS, $remoteCloudId];
}

if ($this->userId === null) {
if ($actorDisplayName) {
$this->guestManager->updateName($this->room, $this->participant, $actorDisplayName);
}
} elseif ($this->userId === MatterbridgeManager::BRIDGE_BOT_USERID && $actorDisplayName) {
$actorType = Attendee::ACTOR_BRIDGED;
$actorId = str_replace(["/", "\""], "", $actorDisplayName);
} else {
$actorType = Attendee::ACTOR_USERS;
$actorId = $this->userId;
return [Attendee::ACTOR_GUESTS, $this->participant->getAttendee()->getActorId()];
}

if ($this->userId === MatterbridgeManager::BRIDGE_BOT_USERID && $actorDisplayName) {
return [Attendee::ACTOR_BRIDGED, str_replace(['/', '"'], '', $actorDisplayName)];
}

return [$actorType, $actorId];
return [Attendee::ACTOR_USERS, $this->userId];
}

/**
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 <[email protected]>
*
* @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 <[email protected]>
*
* @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