Fix anonymous guests inheriting the host participant record.
Deploy Ladill Meet / deploy (push) Successful in 35s

Guest joins without email were matching any active participant, overwriting
the host row and granting host controls; header now always shows room host.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-01 12:13:09 +00:00
co-authored by Cursor
parent ba77acdb9d
commit c651e2bcd4
4 changed files with 104 additions and 7 deletions
+32 -6
View File
@@ -91,12 +91,7 @@ class SessionService
?string $email = null,
): Participant {
$userRef = $user?->ownerRef();
$existing = $session->participants()
->when($userRef, fn ($q) => $q->where('user_ref', $userRef))
->when(! $userRef && $email, fn ($q) => $q->where('email', $email))
->whereIn('status', ['invited', 'waiting', 'joined'])
->first();
$existing = $this->findExistingParticipant($session, $userRef, $email);
if ($existing) {
$existing->update([
@@ -136,6 +131,37 @@ class SessionService
return $participant;
}
protected function findExistingParticipant(Session $session, ?string $userRef, ?string $email): ?Participant
{
$active = fn ($q) => $q->whereIn('status', ['invited', 'waiting', 'joined']);
if ($userRef) {
return $session->participants()
->where('user_ref', $userRef)
->where($active)
->first();
}
if ($email) {
return $session->participants()
->whereNull('user_ref')
->where('email', $email)
->where($active)
->first();
}
$guestUuid = (string) request()?->session()?->get("meet.participant.{$session->uuid}", '');
if ($guestUuid === '') {
return null;
}
return $session->participants()
->where('uuid', $guestUuid)
->whereNull('user_ref')
->where($active)
->first();
}
public function rememberParticipantSession(Request $request, Participant $participant, ?Session $session = null): void
{
$session ??= $participant->session;
+20 -1
View File
@@ -41,6 +41,7 @@ function meetRoom() {
connectionStatus: 'Connecting…',
participants: [],
sessionParticipants: [],
roomHost: null,
floatingReactions: [],
pollTimer: null,
config: {},
@@ -95,6 +96,12 @@ function meetRoom() {
this.sessionParticipants = [];
}
try {
this.roomHost = JSON.parse(el.dataset.roomHost || 'null');
} catch {
this.roomHost = null;
}
if (this.config.configured && this.config.token) {
this.connectLiveKit();
} else {
@@ -318,8 +325,20 @@ function meetRoom() {
hostParticipant() {
const hosts = this.sessionParticipants.filter((p) => p.role === 'host' || p.role === 'co_host');
if (hosts.length) {
return hosts[0];
}
return hosts[0] || this.sessionParticipants[0] || null;
if (this.roomHost?.user_ref) {
const inCall = this.sessionParticipants.find((p) => p.user_ref === this.roomHost.user_ref);
if (inCall) {
return inCall;
}
return this.roomHost;
}
return null;
},
participantCount() {
+8
View File
@@ -10,6 +10,7 @@
</head>
<body class="h-full overflow-hidden bg-slate-950 font-sans text-white" x-data="meetRoom()" x-init="init()">
@php
use App\Models\User;
use App\Services\Meet\ParticipantPresenter;
$joined = $session->participants->where('status', 'joined');
@@ -17,6 +18,12 @@
$joinedParticipants = $joined
->map(fn ($p) => ParticipantPresenter::toArray($p, $participantAvatars, $room->host_user_ref))
->values();
$roomHostUser = User::query()->where('public_id', $room->host_user_ref)->first();
$roomHost = [
'user_ref' => $room->host_user_ref,
'display_name' => $roomHostUser?->name ?? 'Host',
'avatar_url' => $roomHostUser?->avatarUrl(),
];
@endphp
<div id="meet-config"
data-token="{{ $mediaToken }}"
@@ -55,6 +62,7 @@
data-passcode="{{ $room->passcode ?? '' }}"
data-csrf="{{ csrf_token() }}"
data-initial-participants='@json($joinedParticipants)'
data-room-host='@json($roomHost)'
hidden></div>
@if (! $mediaConfigured)
+44
View File
@@ -152,6 +152,50 @@ class MeetWebTest extends TestCase
->assertSee('data-display-name="Guest User"', false);
}
public function test_anonymous_guest_does_not_take_over_host_participant(): void
{
$room = $this->createRoom([
'settings' => array_merge(config('meet.default_settings'), [
'join_before_host' => true,
]),
]);
$session = Session::create([
'owner_ref' => $this->user->public_id,
'room_id' => $room->id,
'media_room_name' => 'room-'.$room->uuid,
'status' => 'live',
'started_at' => now(),
]);
$room->update(['status' => 'live']);
$hostParticipant = \App\Models\Participant::create([
'owner_ref' => $this->user->public_id,
'session_id' => $session->id,
'user_ref' => $this->user->public_id,
'display_name' => $this->user->name,
'role' => 'host',
'status' => 'joined',
'joined_at' => now(),
]);
$this->post('/r/'.$room->uuid.'/enter', [
'display_name' => 'Guest User',
])->assertRedirect(route('meet.room', $session));
$hostParticipant->refresh();
$this->assertSame('host', $hostParticipant->role);
$this->assertSame($this->user->name, $hostParticipant->display_name);
$guest = \App\Models\Participant::query()
->where('session_id', $session->id)
->where('display_name', 'Guest User')
->first();
$this->assertNotNull($guest);
$this->assertSame('guest', $guest->role);
$this->assertNotSame($hostParticipant->uuid, $guest->uuid);
}
public function test_authenticated_user_can_join_live_meeting_without_display_name(): void
{
$room = $this->createRoom();