-
Notifications
You must be signed in to change notification settings - Fork 509
Add support for external signaling federation #12604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
nickvergessen
merged 17 commits into
main
from
add-support-for-external-signaling-federation
Jul 24, 2024
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
9617cb9
feat: Add federation properties to signaling settings
danxuliu a857d77
fix: Adjust session id length in internal signaling messages
danxuliu 6f6498a
feat: Append cloud id to Nextcloud session for federated participants
danxuliu 2394fa4
test: Use real federated server in federation tests that join a room
danxuliu 668c91f
fix: Remove wrong comment
danxuliu 07084e3
fix: Remove unused handling of federated requests in join/leaveRoom
danxuliu 255794c
feat: Join and leave federated participants using their federated ses…
danxuliu 5697645
feat: Send federation settings when connecting to the signaling server
danxuliu 1f792eb
fix: Set signaling settings again after fetching them
danxuliu ad9c454
feat: Add "federation-v2" capability
danxuliu 2875b20
feat: Show active/inactive state of federated participants
danxuliu 4f5ddbc
refactor: Format if condition in several lines
danxuliu 2e445b0
feat: Set signaling server federation feature as mandatory
danxuliu 8d1dfa2
feat: Include cloud id of federated user in JWT token
danxuliu e8cff4f
refactor: Extract generalized getter with parameter
danxuliu 7056338
fix: Notify "roomlist" updates to federated users
danxuliu d891573
chore: Update OpenAPI
danxuliu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -1506,33 +1506,16 @@ 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->federationAuthenticator->isFederationRequest(); | ||||||||
| try { | ||||||||
| // The participant is just joining, so enforce to not load any session | ||||||||
| if (!$isTalkFederation) { | ||||||||
| $action = 'talkRoomToken'; | ||||||||
| $room = $this->manager->getRoomForUserByToken($token, $this->userId, null); | ||||||||
| } else { | ||||||||
| $action = 'talkFederationAccess'; | ||||||||
| try { | ||||||||
| $room = $this->federationAuthenticator->getRoom(); | ||||||||
| } catch (RoomNotFoundException) { | ||||||||
| $room = $this->manager->getRoomByRemoteAccess( | ||||||||
| $token, | ||||||||
| Attendee::ACTOR_FEDERATED_USERS, | ||||||||
| $this->federationAuthenticator->getCloudId(), | ||||||||
| $this->federationAuthenticator->getAccessToken(), | ||||||||
| ); | ||||||||
| } | ||||||||
| } | ||||||||
| $room = $this->manager->getRoomForUserByToken($token, $this->userId, null); | ||||||||
| } catch (RoomNotFoundException $e) { | ||||||||
| $response = new DataResponse([], Http::STATUS_NOT_FOUND); | ||||||||
| $response->throttle(['token' => $token, 'action' => $action]); | ||||||||
| $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); | ||||||||
| return $response; | ||||||||
| } | ||||||||
|
|
||||||||
|
|
@@ -1581,22 +1564,6 @@ public function joinRoom(string $token, string $password = '', bool $force = tru | |||||||
|
|
||||||||
| $headers = []; | ||||||||
| if ($room->isFederatedConversation()) { | ||||||||
| $participant = $this->participantService->getParticipant($room, $this->userId); | ||||||||
|
|
||||||||
| /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\RoomController $proxy */ | ||||||||
| $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\RoomController::class); | ||||||||
| $response = $proxy->joinFederatedRoom($room, $participant); | ||||||||
|
|
||||||||
| if ($response->getStatus() === Http::STATUS_NOT_FOUND) { | ||||||||
| $this->participantService->removeAttendee($room, $participant, AAttendeeRemovedEvent::REASON_REMOVED); | ||||||||
| return new DataResponse([], Http::STATUS_NOT_FOUND); | ||||||||
| } | ||||||||
|
|
||||||||
| $proxyHeaders = $response->getHeaders(); | ||||||||
| if (isset($proxyHeaders['X-Nextcloud-Talk-Proxy-Hash'])) { | ||||||||
| $headers['X-Nextcloud-Talk-Proxy-Hash'] = $proxyHeaders['X-Nextcloud-Talk-Proxy-Hash']; | ||||||||
| } | ||||||||
|
|
||||||||
| // Skip password checking | ||||||||
| $result = [ | ||||||||
| 'result' => true, | ||||||||
|
|
@@ -1610,8 +1577,6 @@ 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->federationAuthenticator->getCloudId()); | ||||||||
| } else { | ||||||||
| $participant = $this->participantService->joinRoomAsNewGuest($this->roomService, $room, $password, $result['result'], $previousParticipant); | ||||||||
| $this->session->setGuestActorIdForRoom($room->getToken(), $participant->getAttendee()->getActorId()); | ||||||||
|
|
@@ -1626,7 +1591,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' => $action]); | ||||||||
| $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); | ||||||||
| return $response; | ||||||||
| } | ||||||||
|
|
||||||||
|
|
@@ -1637,23 +1602,49 @@ public function joinRoom(string $token, string $password = '', bool $force = tru | |||||||
| $this->sessionService->updateLastPing($session, $this->timeFactory->getTime()); | ||||||||
| } | ||||||||
|
|
||||||||
| if ($room->isFederatedConversation()) { | ||||||||
| /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\RoomController $proxy */ | ||||||||
| $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\RoomController::class); | ||||||||
|
|
||||||||
| try { | ||||||||
| $response = $proxy->joinFederatedRoom($room, $participant); | ||||||||
| } catch (CannotReachRemoteException $e) { | ||||||||
| $this->participantService->leaveRoomAsSession($room, $participant); | ||||||||
|
|
||||||||
| throw $e; | ||||||||
| } | ||||||||
|
|
||||||||
| if ($response->getStatus() === Http::STATUS_NOT_FOUND) { | ||||||||
| $this->participantService->removeAttendee($room, $participant, AAttendeeRemovedEvent::REASON_REMOVED); | ||||||||
| return new DataResponse([], Http::STATUS_NOT_FOUND); | ||||||||
| } | ||||||||
|
|
||||||||
| $proxyHeaders = $response->getHeaders(); | ||||||||
| if (isset($proxyHeaders['X-Nextcloud-Talk-Proxy-Hash'])) { | ||||||||
| $headers['X-Nextcloud-Talk-Proxy-Hash'] = $proxyHeaders['X-Nextcloud-Talk-Proxy-Hash']; | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| return new DataResponse($this->formatRoom($room, $participant), Http::STATUS_OK, $headers); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Fake join a room on the host server to verify the federated user is still part of it | ||||||||
| * Join room on the host server using the session id of the federated user. | ||||||||
| * | ||||||||
| * The session id can be null only for requests from Talk < 20. | ||||||||
| * | ||||||||
| * @param string $token Token of the room | ||||||||
| * @param string $sessionId Federated session id to join with | ||||||||
| * @return DataResponse<Http::STATUS_OK, array<empty>, array{X-Nextcloud-Talk-Hash: string}>|DataResponse<Http::STATUS_NOT_FOUND, null, array{}> | ||||||||
| * | ||||||||
| * 200: Federated user is still part of the room | ||||||||
| * 200: Federated user joined the room | ||||||||
| * 404: Room not found | ||||||||
| */ | ||||||||
| #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] | ||||||||
| #[PublicPage] | ||||||||
| #[BruteForceProtection(action: 'talkRoomToken')] | ||||||||
| #[BruteForceProtection(action: 'talkFederationAccess')] | ||||||||
| public function joinFederatedRoom(string $token): DataResponse { | ||||||||
| public function joinFederatedRoom(string $token, ?string $sessionId): DataResponse { | ||||||||
| if (!$this->federationAuthenticator->isFederationRequest()) { | ||||||||
| $response = new DataResponse(null, Http::STATUS_NOT_FOUND); | ||||||||
| $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); | ||||||||
|
|
@@ -1662,22 +1653,26 @@ public function joinFederatedRoom(string $token): DataResponse { | |||||||
|
|
||||||||
| try { | ||||||||
| try { | ||||||||
| $this->federationAuthenticator->getRoom(); | ||||||||
| $room = $this->federationAuthenticator->getRoom(); | ||||||||
| } catch (RoomNotFoundException) { | ||||||||
| $this->manager->getRoomByRemoteAccess( | ||||||||
| $room = $this->manager->getRoomByRemoteAccess( | ||||||||
| $token, | ||||||||
| Attendee::ACTOR_FEDERATED_USERS, | ||||||||
| $this->federationAuthenticator->getCloudId(), | ||||||||
| $this->federationAuthenticator->getAccessToken(), | ||||||||
| ); | ||||||||
| } | ||||||||
|
|
||||||||
| if ($sessionId != null) { | ||||||||
| $participant = $this->participantService->joinRoomAsFederatedUser($room, Attendee::ACTOR_FEDERATED_USERS, $this->federationAuthenticator->getCloudId(), $sessionId); | ||||||||
| } | ||||||||
|
|
||||||||
| // Let the clients know if they need to reload capabilities | ||||||||
| $capabilities = $this->capabilities->getCapabilities(); | ||||||||
| return new DataResponse([], Http::STATUS_OK, [ | ||||||||
| 'X-Nextcloud-Talk-Hash' => sha1(json_encode($capabilities)), | ||||||||
| ]); | ||||||||
| } catch (RoomNotFoundException) { | ||||||||
| } catch (RoomNotFoundException|UnauthorizedException) { | ||||||||
| $response = new DataResponse(null, Http::STATUS_NOT_FOUND); | ||||||||
| $response->throttle(['token' => $token, 'action' => 'talkFederationAccess']); | ||||||||
| return $response; | ||||||||
|
|
@@ -1902,33 +1897,64 @@ public function leaveRoom(string $token): DataResponse { | |||||||
| $this->session->removeSessionForRoom($token); | ||||||||
|
|
||||||||
| try { | ||||||||
| // The participant is just joining, so enforce to not load any session | ||||||||
| if (!$this->federationAuthenticator->isFederationRequest()) { | ||||||||
| $room = $this->manager->getRoomForUserByToken($token, $this->userId, $sessionId); | ||||||||
| $participant = $this->participantService->getParticipantBySession($room, $sessionId); | ||||||||
| } else { | ||||||||
| try { | ||||||||
| $room = $this->federationAuthenticator->getRoom(); | ||||||||
| } catch (RoomNotFoundException) { | ||||||||
| $room = $this->manager->getRoomByRemoteAccess( | ||||||||
nickvergessen marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| $token, | ||||||||
| Attendee::ACTOR_FEDERATED_USERS, | ||||||||
| $this->federationAuthenticator->getCloudId(), | ||||||||
| $this->federationAuthenticator->getAccessToken(), | ||||||||
| ); | ||||||||
| } | ||||||||
| $room = $this->manager->getRoomForUserByToken($token, $this->userId, $sessionId); | ||||||||
| $participant = $this->participantService->getParticipantBySession($room, $sessionId); | ||||||||
|
|
||||||||
| try { | ||||||||
| $participant = $this->federationAuthenticator->getParticipant(); | ||||||||
| } catch (ParticipantNotFoundException) { | ||||||||
| $participant = $this->participantService->getParticipantByActor( | ||||||||
| $room, | ||||||||
| Attendee::ACTOR_FEDERATED_USERS, | ||||||||
| $this->federationAuthenticator->getCloudId(), | ||||||||
| ); | ||||||||
| $this->federationAuthenticator->authenticated($room, $participant); | ||||||||
| } | ||||||||
| if ($room->isFederatedConversation()) { | ||||||||
| /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\RoomController $proxy */ | ||||||||
| $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\RoomController::class); | ||||||||
| $response = $proxy->leaveFederatedRoom($room, $participant); | ||||||||
| } | ||||||||
|
|
||||||||
| $this->participantService->leaveRoomAsSession($room, $participant); | ||||||||
| } catch (RoomNotFoundException|ParticipantNotFoundException) { | ||||||||
| } | ||||||||
|
|
||||||||
| return new DataResponse(); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Leave room on the host server using the session id of the federated user. | ||||||||
| * | ||||||||
| * @param string $token Token of the room | ||||||||
| * @param string $sessionId Federated session id to leave with | ||||||||
| * @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_NOT_FOUND, null, array{}> | ||||||||
| * | ||||||||
| * 200: Successfully left the room | ||||||||
| * 404: Room not found (non-federation request) | ||||||||
| */ | ||||||||
| #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] | ||||||||
| #[PublicPage] | ||||||||
| #[BruteForceProtection(action: 'talkRoomToken')] | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| public function leaveFederatedRoom(string $token, string $sessionId): DataResponse { | ||||||||
| if (!$this->federationAuthenticator->isFederationRequest()) { | ||||||||
| $response = new DataResponse(null, Http::STATUS_NOT_FOUND); | ||||||||
| $response->throttle(['token' => $token, 'action' => 'talkRoomToken']); | ||||||||
| return $response; | ||||||||
| } | ||||||||
|
|
||||||||
| try { | ||||||||
| try { | ||||||||
| $room = $this->federationAuthenticator->getRoom(); | ||||||||
| } catch (RoomNotFoundException) { | ||||||||
| $room = $this->manager->getRoomByRemoteAccess( | ||||||||
| $token, | ||||||||
| Attendee::ACTOR_FEDERATED_USERS, | ||||||||
| $this->federationAuthenticator->getCloudId(), | ||||||||
| $this->federationAuthenticator->getAccessToken(), | ||||||||
| ); | ||||||||
| } | ||||||||
|
|
||||||||
| try { | ||||||||
| $participant = $this->federationAuthenticator->getParticipant(); | ||||||||
| } catch (ParticipantNotFoundException) { | ||||||||
| $participant = $this->participantService->getParticipantBySession( | ||||||||
| $room, | ||||||||
| $sessionId, | ||||||||
| ); | ||||||||
| $this->federationAuthenticator->authenticated($room, $participant); | ||||||||
| } | ||||||||
|
|
||||||||
| $this->participantService->leaveRoomAsSession($room, $participant); | ||||||||
| } catch (RoomNotFoundException|ParticipantNotFoundException) { | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Throttle with |
||||||||
| } | ||||||||
|
|
||||||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.