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' => 'join#index', 'url' => '/b/{token}', '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\EventDispatcher\IEventDispatcher;
|
||||
use \OCA\BigBlueButton\Middleware\JoinMiddleware;
|
||||
use \OCA\BigBlueButton\Middleware\HookMiddleware;
|
||||
use \OCA\BigBlueButton\Event\RoomCreatedEvent;
|
||||
use \OCA\BigBlueButton\Event\RoomDeletedEvent;
|
||||
use \OCA\BigBlueButton\Activity\RoomListener;
|
||||
|
@ -35,6 +36,7 @@ class Application extends App {
|
|||
$dispatcher->addServiceListener(RoomShareDeletedEvent::class, RoomShareListener::class);
|
||||
|
||||
$container->registerMiddleWare(JoinMiddleware::class);
|
||||
$container->registerMiddleWare(HookMiddleware::class);
|
||||
|
||||
$config = $container->query(IConfig::class);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ use BigBlueButton\Parameters\DeleteRecordingsParameters;
|
|||
use BigBlueButton\Parameters\IsMeetingRunningParameters;
|
||||
use OCA\BigBlueButton\Db\Room;
|
||||
use OCA\BigBlueButton\Permission;
|
||||
use OCA\BigBlueButton\Crypto;
|
||||
use OCP\IConfig;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
|
@ -27,14 +28,19 @@ class API {
|
|||
/** @var BigBlueButton|null */
|
||||
private $server;
|
||||
|
||||
/** @var Crypto */
|
||||
private $crypto;
|
||||
|
||||
public function __construct(
|
||||
IConfig $config,
|
||||
IURLGenerator $urlGenerator,
|
||||
Permission $permission
|
||||
Permission $permission,
|
||||
Crypto $crypto
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->permission = $permission;
|
||||
$this->crypto = $crypto;
|
||||
}
|
||||
|
||||
private function getServer() {
|
||||
|
@ -101,6 +107,14 @@ class API {
|
|||
$createMeetingParams->setAllowStartStopRecording($room->record);
|
||||
$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]);
|
||||
$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