mirror of https://github.com/sualko/cloud_bbb
parent
ece86b2fd6
commit
6902b50795
|
@ -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'],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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, '+/', '-_'), '=');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\BigBlueButton\Event;
|
||||||
|
|
||||||
|
class RecordingReadyEvent extends RoomEvent {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue