Blame | Letzte Änderung | Log anzeigen | RSS feed
<?phpnamespace Illuminate\Broadcasting\Broadcasters;use Ably\AblyRest;use Ably\Exceptions\AblyException;use Ably\Models\Message as AblyMessage;use Illuminate\Broadcasting\BroadcastException;use Illuminate\Support\Str;use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;/*** @author Matthew Hall (matthall28@gmail.com)* @author Taylor Otwell (taylor@laravel.com)*/class AblyBroadcaster extends Broadcaster{/*** The AblyRest SDK instance.** @var \Ably\AblyRest*/protected $ably;/*** Create a new broadcaster instance.** @param \Ably\AblyRest $ably* @return void*/public function __construct(AblyRest $ably){$this->ably = $ably;}/*** Authenticate the incoming request for a given channel.** @param \Illuminate\Http\Request $request* @return mixed** @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException*/public function auth($request){$channelName = $this->normalizeChannelName($request->channel_name);if (empty($request->channel_name) ||($this->isGuardedChannel($request->channel_name) &&! $this->retrieveUser($request, $channelName))) {throw new AccessDeniedHttpException;}return parent::verifyUserCanAccessChannel($request, $channelName);}/*** Return the valid authentication response.** @param \Illuminate\Http\Request $request* @param mixed $result* @return mixed*/public function validAuthenticationResponse($request, $result){if (str_starts_with($request->channel_name, 'private')) {$signature = $this->generateAblySignature($request->channel_name, $request->socket_id);return ['auth' => $this->getPublicToken().':'.$signature];}$channelName = $this->normalizeChannelName($request->channel_name);$user = $this->retrieveUser($request, $channelName);$broadcastIdentifier = method_exists($user, 'getAuthIdentifierForBroadcasting')? $user->getAuthIdentifierForBroadcasting(): $user->getAuthIdentifier();$signature = $this->generateAblySignature($request->channel_name,$request->socket_id,$userData = array_filter(['user_id' => (string) $broadcastIdentifier,'user_info' => $result,]));return ['auth' => $this->getPublicToken().':'.$signature,'channel_data' => json_encode($userData),];}/*** Generate the signature needed for Ably authentication headers.** @param string $channelName* @param string $socketId* @param array|null $userData* @return string*/public function generateAblySignature($channelName, $socketId, $userData = null){return hash_hmac('sha256',sprintf('%s:%s%s', $socketId, $channelName, $userData ? ':'.json_encode($userData) : ''),$this->getPrivateToken(),);}/*** Broadcast the given event.** @param array $channels* @param string $event* @param array $payload* @return void** @throws \Illuminate\Broadcasting\BroadcastException*/public function broadcast(array $channels, $event, array $payload = []){try {foreach ($this->formatChannels($channels) as $channel) {$this->ably->channels->get($channel)->publish($this->buildAblyMessage($event, $payload));}} catch (AblyException $e) {throw new BroadcastException(sprintf('Ably error: %s', $e->getMessage()));}}/*** Build an Ably message object for broadcasting.** @param string $event* @param array $payload* @return \Ably\Models\Message*/protected function buildAblyMessage($event, array $payload = []){return tap(new AblyMessage, function ($message) use ($event, $payload) {$message->name = $event;$message->data = $payload;$message->connectionKey = data_get($payload, 'socket');});}/*** Return true if the channel is protected by authentication.** @param string $channel* @return bool*/public function isGuardedChannel($channel){return Str::startsWith($channel, ['private-', 'presence-']);}/*** Remove prefix from channel name.** @param string $channel* @return string*/public function normalizeChannelName($channel){if ($this->isGuardedChannel($channel)) {return str_starts_with($channel, 'private-')? Str::replaceFirst('private-', '', $channel): Str::replaceFirst('presence-', '', $channel);}return $channel;}/*** Format the channel array into an array of strings.** @param array $channels* @return array*/protected function formatChannels(array $channels){return array_map(function ($channel) {$channel = (string) $channel;if (Str::startsWith($channel, ['private-', 'presence-'])) {return str_starts_with($channel, 'private-')? Str::replaceFirst('private-', 'private:', $channel): Str::replaceFirst('presence-', 'presence:', $channel);}return 'public:'.$channel;}, $channels);}/*** Get the public token value from the Ably key.** @return mixed*/protected function getPublicToken(){return Str::before($this->ably->options->key, ':');}/*** Get the private token value from the Ably key.** @return mixed*/protected function getPrivateToken(){return Str::after($this->ably->options->key, ':');}/*** Get the underlying Ably SDK instance.** @return \Ably\AblyRest*/public function getAbly(){return $this->ably;}}