organization; $policy = $this->mergedPolicy($org, $room); $kind = $room->isConference() ? 'conference' : ($room->isSpace() ? 'room' : 'meeting'); if ($policy['org_only'] && ! $user) { return [false, "This {$kind} requires a Ladill account."]; } if ($policy['authenticated_only'] && ! $user) { return [false, "Please sign in to join this {$kind}."]; } if (! empty($policy['allowed_domains']) && $email) { $domain = Str($email)->after('@')->lower()->toString(); $allowed = collect($policy['allowed_domains'])->map(fn ($d) => strtolower(trim($d)))->filter(); if ($allowed->isNotEmpty() && ! $allowed->contains($domain)) { return [false, "Your email domain is not allowed to join this {$kind}."]; } } if ($room->isSpace() && $room->setting('invite_only', false) && ! $this->canJoinInviteOnlyRoom($room, $user, $email)) { return [false, 'This room is private. Ask the host for an invitation.']; } if ($room->passcode && ! session()->get("meet.passcode.{$room->uuid}")) { return [false, 'passcode_required']; } if ($room->activeSession()?->is_locked && ! $this->isHost($room, $user)) { return [false, "This {$kind} is locked."]; } if ($policy['recording_host_only'] && ! $this->isHost($room, $user)) { // enforced at recording start } return [true, null]; } public function canJoinInviteOnlyRoom(Room $room, ?User $user, ?string $email): bool { if ($user && $this->isHost($room, $user)) { return true; } if ($user && $room->organization_id) { $isMember = Member::query() ->where('organization_id', $room->organization_id) ->where('user_ref', $user->ownerRef()) ->exists(); if ($isMember) { return true; } $speakerRefs = (array) $room->setting('speaker_refs', []); if (in_array($user->ownerRef(), $speakerRefs, true)) { return true; } } $email = strtolower(trim((string) ($email ?? $user?->email ?? ''))); if ($email === '') { return false; } return $room->invitations() ->whereRaw('LOWER(email) = ?', [$email]) ->whereIn('status', ['pending', 'accepted']) ->exists(); } public function isHost(Room $room, ?User $user): bool { return $user && $user->ownerRef() === $room->host_user_ref; } /** * @param array $policy */ public function updateOrganizationPolicy(Organization $org, array $policy): Organization { $org->update([ 'security_policy' => array_merge($org->security_policy ?? [], $policy), ]); AuditLogger::record($org->owner_ref, 'organization.security_updated', $org->id, auth()->user()?->ownerRef()); return $org->fresh(); } /** * @return array */ protected function mergedPolicy(?Organization $org, Room $room): array { return array_merge( config('meet.security_defaults'), $org?->security_policy ?? [], $room->settings['security'] ?? [], ); } }