feat: add meeting ended event

and recording ready event
pull/77/head
sualko 2020-09-22 12:19:48 +02:00
parent ece86b2fd6
commit 6902b50795
8 changed files with 231 additions and 1 deletions

View File

@ -14,5 +14,7 @@ return [
['name' => 'server#delete_record', 'url' => '/server/record/{recordId}', 'verb' => 'DELETE'], ['name' => 'server#delete_record', 'url' => '/server/record/{recordId}', 'verb' => 'DELETE'],
['name' => 'join#index', 'url' => '/b/{token}', 'verb' => 'GET'], ['name' => 'join#index', 'url' => '/b/{token}', 'verb' => 'GET'],
['name' => 'restriction#user', 'url' => '/restrictions/user', 'verb' => 'GET'], ['name' => 'restriction#user', 'url' => '/restrictions/user', 'verb' => 'GET'],
['name' => 'hook#meetingEnded', 'url' => '/hook/ended/{token}/{mac}', 'verb' => 'GET'],
['name' => 'hook#recordingReady', 'url' => '/hook/recording/{token}/{mac}', 'verb' => 'GET'],
] ]
]; ];

View File

@ -7,6 +7,7 @@ use \OCP\Settings\IManager as ISettingsManager;
use \OCP\AppFramework\App; use \OCP\AppFramework\App;
use \OCP\EventDispatcher\IEventDispatcher; use \OCP\EventDispatcher\IEventDispatcher;
use \OCA\BigBlueButton\Middleware\JoinMiddleware; use \OCA\BigBlueButton\Middleware\JoinMiddleware;
use \OCA\BigBlueButton\Middleware\HookMiddleware;
use \OCA\BigBlueButton\Event\RoomCreatedEvent; use \OCA\BigBlueButton\Event\RoomCreatedEvent;
use \OCA\BigBlueButton\Event\RoomDeletedEvent; use \OCA\BigBlueButton\Event\RoomDeletedEvent;
use \OCA\BigBlueButton\Activity\RoomListener; use \OCA\BigBlueButton\Activity\RoomListener;
@ -35,6 +36,7 @@ class Application extends App {
$dispatcher->addServiceListener(RoomShareDeletedEvent::class, RoomShareListener::class); $dispatcher->addServiceListener(RoomShareDeletedEvent::class, RoomShareListener::class);
$container->registerMiddleWare(JoinMiddleware::class); $container->registerMiddleWare(JoinMiddleware::class);
$container->registerMiddleWare(HookMiddleware::class);
$config = $container->query(IConfig::class); $config = $container->query(IConfig::class);

View File

@ -11,6 +11,7 @@ use BigBlueButton\Parameters\DeleteRecordingsParameters;
use BigBlueButton\Parameters\IsMeetingRunningParameters; use BigBlueButton\Parameters\IsMeetingRunningParameters;
use OCA\BigBlueButton\Db\Room; use OCA\BigBlueButton\Db\Room;
use OCA\BigBlueButton\Permission; use OCA\BigBlueButton\Permission;
use OCA\BigBlueButton\Crypto;
use OCP\IConfig; use OCP\IConfig;
use OCP\IURLGenerator; use OCP\IURLGenerator;
@ -27,14 +28,19 @@ class API {
/** @var BigBlueButton|null */ /** @var BigBlueButton|null */
private $server; private $server;
/** @var Crypto */
private $crypto;
public function __construct( public function __construct(
IConfig $config, IConfig $config,
IURLGenerator $urlGenerator, IURLGenerator $urlGenerator,
Permission $permission Permission $permission,
Crypto $crypto
) { ) {
$this->config = $config; $this->config = $config;
$this->urlGenerator = $urlGenerator; $this->urlGenerator = $urlGenerator;
$this->permission = $permission; $this->permission = $permission;
$this->crypto = $crypto;
} }
private function getServer() { private function getServer() {
@ -101,6 +107,14 @@ class API {
$createMeetingParams->setAllowStartStopRecording($room->record); $createMeetingParams->setAllowStartStopRecording($room->record);
$createMeetingParams->setLogoutUrl($this->urlGenerator->getBaseUrl()); $createMeetingParams->setLogoutUrl($this->urlGenerator->getBaseUrl());
$mac = $this->crypto->calculateHMAC($room->uid);
$endMeetingUrl = $this->urlGenerator->linkToRouteAbsolute('bbb.hook.meetingEnded', ['token' => $room->uid, 'mac' => $mac]);
$createMeetingParams->setEndCallbackUrl($endMeetingUrl);
$recordingReadyUrl = $this->urlGenerator->linkToRouteAbsolute('bbb.hook.recordingReady', ['token' => $room->uid, 'mac' => $mac]);
$createMeetingParams->setRecordingReadyCallbackUrl($recordingReadyUrl);
$invitationUrl = $this->urlGenerator->linkToRouteAbsolute('bbb.join.index', ['token' => $room->uid]); $invitationUrl = $this->urlGenerator->linkToRouteAbsolute('bbb.join.index', ['token' => $room->uid]);
$createMeetingParams->setModeratorOnlyMessage('To invite someone to the meeting, send them this link: ' . $invitationUrl); $createMeetingParams->setModeratorOnlyMessage('To invite someone to the meeting, send them this link: ' . $invitationUrl);

View File

@ -0,0 +1,74 @@
<?php
namespace OCA\BigBlueButton\Controller;
use OCA\BigBlueButton\Db\Room;
use OCP\IRequest;
use OCP\EventDispatcher\IEventDispatcher;
use OCA\BigBlueButton\Service\RoomService;
use OCA\BigBlueButton\Event\RoomEndedEvent;
use OCA\BigBlueButton\Event\RecordingReadyEvent;
use OCP\AppFramework\Controller;
class HookController extends Controller {
/** @var string */
protected $token;
/** @var Room|null */
protected $room;
/** @var RoomService */
private $service;
/** @var IEventDispatcher */
private $eventDispatcher;
public function __construct(
string $appName,
IRequest $request,
RoomService $service,
IEventDispatcher $eventDispatcher
) {
parent::__construct($appName, $request);
$this->service = $service;
$this->eventDispatcher = $eventDispatcher;
}
public function setToken(string $token) {
$this->token = $token;
$this->room = null;
}
public function isValidToken(): bool {
$room = $this->getRoom();
return $room !== null;
}
/**
* @PublicPage
* @NoCSRFRequired
*/
public function meetingEnded($recordingmarks = false) {
$recordingmarks = \boolval($recordingmarks);
$this->eventDispatcher->dispatch(RoomEndedEvent::class, new RoomEndedEvent($this->getRoom(), $recordingmarks));
}
/**
* @PublicPage
* @NoCSRFRequired
*/
public function recordingReady() {
$this->eventDispatcher->dispatch(RecordingReadyEvent::class, new RecordingReadyEvent($this->getRoom()));
}
private function getRoom(): ?Room {
if ($this->room === null) {
$this->room = $this->service->findByUid($this->token);
}
return $this->room;
}
}

44
lib/Crypto.php Normal file
View File

@ -0,0 +1,44 @@
<?php
namespace OCA\BigBlueButton;
use OCP\Security\ICrypto;
class Crypto {
/** @var ICrypto */
private $crypto;
public function __construct(
ICrypto $crypto
) {
$this->crypto = $crypto;
}
public function calculateHMAC(string $message): string {
if ($message === null) {
throw new \InvalidArgumentException();
}
return $this->encodeBase64UrlSafe(\sha1($this->crypto->calculateHMAC($message), true));
}
public function verifyHMAC(string $message, string $mac) {
if ($message === null || $mac === null) {
return false;
}
$validMac = $this->encodeBase64UrlSafe(\sha1($this->crypto->calculateHMAC($message), true));
return $validMac === $mac;
}
private function encodeBase64UrlSafe($data) {
$b64 = \base64_encode($data);
if ($b64 === false) {
return false;
}
return \rtrim(\strtr($b64, '+/', '-_'), '=');
}
}

View File

@ -0,0 +1,6 @@
<?php
namespace OCA\BigBlueButton\Event;
class RecordingReadyEvent extends RoomEvent {
}

View File

@ -0,0 +1,19 @@
<?php
namespace OCA\BigBlueButton\Event;
use OCA\BigBlueButton\Db\Room;
abstract class RoomEndedEvent extends RoomEvent {
private $recordingMarks = false;
public function __construct(Room $room, bool $recordingMarks) {
parent::__construct($room);
$this->recordingMarks = $recordingMarks;
}
public function hasRecordingMarks(): bool {
return $this->recordingMarks;
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace OCA\BigBlueButton\Middleware;
use OCA\BigBlueButton\Controller\HookController;
use OCA\BigBlueButton\NoPermissionException;
use OCA\BigBlueButton\NotFoundException;
use OCA\BigBlueButton\Crypto;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http;
use OCP\AppFramework\Middleware;
use OCP\IRequest;
class HookMiddleware extends Middleware {
/** @var IRequest */
private $request;
/** @var Crypto */
private $crypto;
public function __construct(IRequest $request, Crypto $crypto) {
$this->request = $request;
$this->crypto = $crypto;
}
public function beforeController($controller, $methodName) {
if (!($controller instanceof HookController)) {
return;
}
$token = $this->request->getParam('token');
if ($token === null) {
throw new NotFoundException();
}
$mac = $this->request->getParam('mac');
if ($mac === null) {
throw new NoPermissionException();
}
if (!$this->crypto->verifyHMAC($token, $mac)) {
throw new NoPermissionException();
}
$controller->setToken($token);
if ($controller->isValidToken()) {
return;
}
throw new NotFoundException();
}
public function afterException($controller, $methodName, \Exception $exception) {
if (!($controller instanceof HookController)) {
throw $exception;
}
if ($exception instanceof NotFoundException) {
return new JSONResponse(null, Http::STATUS_NOT_FOUND);
}
if ($exception instanceof NoPermissionException) {
return new JSONResponse(null, Http::STATUS_FORBIDDEN);
}
throw $exception;
}
}