Page MenuHomePhorge

D2098.1775206802.diff
No OneTemporary

Authored By
Unknown
Size
22 KB
Referenced Files
None
Subscribers
None

D2098.1775206802.diff

diff --git a/src/app/Http/Controllers/API/V4/OpenViduController.php b/src/app/Http/Controllers/API/V4/OpenViduController.php
--- a/src/app/Http/Controllers/API/V4/OpenViduController.php
+++ b/src/app/Http/Controllers/API/V4/OpenViduController.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\API\V4;
use App\Http\Controllers\Controller;
+use App\OpenVidu\Connection;
use App\OpenVidu\Room;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@@ -115,22 +116,22 @@
*/
public function dismissConnection($id, $conn)
{
- $room = Room::where('name', $id)->first();
+ $connection = Connection::where('id', $conn)->first();
- // This isn't a room, bye bye
- if (!$room) {
- return $this->errorResponse(404, \trans('meet.room-not-found'));
+ // There's no such connection, bye bye
+ if (!$connection || $connection->room->name != $id) {
+ return $this->errorResponse(404, \trans('meet.connection-not-found'));
}
$user = Auth::guard()->user();
- // Only the room owner can do it
- if (!$user || $user->id != $room->user_id) {
+ // Only the room owner can do it (for now)
+ if (!$user || $user->id != $connection->room->user_id) {
return $this->errorResponse(403);
}
- if (!$room->closeOVConnection($conn)) {
- return $this->errorResponse(500, \trans('meet.session-dismiss-connection-error'));
+ if (!$connection->dismiss()) {
+ return $this->errorResponse(500, \trans('meet.connection-dismiss-error'));
}
return response()->json(['status' => 'success']);
@@ -281,28 +282,29 @@
if ($init) {
// Choose the connection role
$canPublish = !empty(request()->input('canPublish'));
- $reqRole = $canPublish ? Room::ROLE_PUBLISHER : Room::ROLE_SUBSCRIBER;
- $role = $isOwner ? Room::ROLE_MODERATOR : $reqRole;
+ $role = $canPublish ? Room::ROLE_PUBLISHER : Room::ROLE_SUBSCRIBER;
+ if ($isOwner) {
+ $role |= Room::ROLE_MODERATOR;
+ $role |= Room::ROLE_OWNER;
+ }
// Create session token for the current user/connection
- $response = $room->getSessionToken($role, ['canPublish' => $canPublish]);
+ $response = $room->getSessionToken($role);
if (empty($response)) {
return $this->errorResponse(500, \trans('meet.session-join-error'));
}
// Create session token for screen sharing connection
- if ($role != Room::ROLE_SUBSCRIBER && !empty(request()->input('screenShare'))) {
- $add_token = $room->getSessionToken(Room::ROLE_PUBLISHER, ['canPublish' => true]);
+ if (($role & Room::ROLE_PUBLISHER) && !empty(request()->input('screenShare'))) {
+ $add_token = $room->getSessionToken(Room::ROLE_SCREEN);
$response['shareToken'] = $add_token['token'];
}
$response_code = 200;
$response['role'] = $role;
- $response['owner'] = $isOwner;
$response['config'] = $config;
- $response['canPublish'] = $canPublish;
} else {
$response_code = 422;
$response['code'] = 322;
@@ -393,6 +395,9 @@
$room->save();
}
+ // Remove all connections
+ Connection::where('session_id', $sessionId)->delete();
+
break;
}
diff --git a/src/app/OpenVidu/Connection.php b/src/app/OpenVidu/Connection.php
new file mode 100644
--- /dev/null
+++ b/src/app/OpenVidu/Connection.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace App\OpenVidu;
+
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * The eloquent definition of a Connection.
+ *
+ * @property string $id OpenVidu connection identifier
+ * @property array $metadata Connection metadata
+ * @property int $role Connection role
+ * @property int $room_id Room identifier
+ * @property string $session_id OpenVidu session identifier
+ */
+class Connection extends Model
+{
+ protected $table = 'openvidu_connections';
+
+ public $incrementing = false;
+ protected $keyType = 'string';
+
+ /**
+ * The attributes that should be cast.
+ *
+ * @var array
+ */
+ protected $casts = [
+ 'metadata' => 'array',
+ ];
+
+ /**
+ * Dismiss (close) the connection.
+ *
+ * @return bool True on success, False on failure
+ */
+ public function dismiss()
+ {
+ if ($this->room->closeOVConnection($this->id)) {
+ $this->delete();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * The room to which this connection belongs.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ */
+ public function room()
+ {
+ return $this->belongsTo(Room::class, 'room_id', 'id');
+ }
+}
diff --git a/src/app/OpenVidu/Room.php b/src/app/OpenVidu/Room.php
--- a/src/app/OpenVidu/Room.php
+++ b/src/app/OpenVidu/Room.php
@@ -18,13 +18,19 @@
{
use SettingsTrait;
- public const ROLE_MODERATOR = 'MODERATOR';
- public const ROLE_PUBLISHER = 'PUBLISHER';
- public const ROLE_SUBSCRIBER = 'SUBSCRIBER';
+ public const ROLE_SUBSCRIBER = 1 << 0;
+ public const ROLE_PUBLISHER = 1 << 1;
+ public const ROLE_MODERATOR = 1 << 2;
+ public const ROLE_SCREEN = 1 << 3;
+ public const ROLE_OWNER = 1 << 4;
public const REQUEST_ACCEPTED = 'accepted';
public const REQUEST_DENIED = 'denied';
+ private const OV_ROLE_MODERATOR = 'MODERATOR';
+ private const OV_ROLE_PUBLISHER = 'PUBLISHER';
+ private const OV_ROLE_SUBSCRIBER = 'SUBSCRIBER';
+
protected $fillable = [
'user_id',
'name'
@@ -169,14 +175,12 @@
/**
* Create a OpenVidu session (connection) token
*
- * @param string $role User role
- * @param array $data User data to attach to the connection.
- * It will be available client-side for everybody.
+ * @param int $role User role (see self::ROLE_* constants)
*
* @return array|null Token data on success, NULL otherwise
* @throws \Exception if session does not exist
*/
- public function getSessionToken($role = self::ROLE_PUBLISHER, $data = []): ?array
+ public function getSessionToken($role = self::ROLE_SUBSCRIBER): ?array
{
if (!$this->session_id) {
throw new \Exception("The room session does not exist");
@@ -186,16 +190,12 @@
// to make it visible for everyone in a room. So, for example we can
// handle/style subscribers/publishers/moderators differently on the
// client-side. Is this a security issue?
- if (!empty($data)) {
- $data += ['role' => $role];
- } else {
- $data = ['role' => $role];
- }
+ $data = ['role' => $role];
$url = 'sessions/' . $this->session_id . '/connection';
$post = [
'json' => [
- 'role' => $role,
+ 'role' => self::OV_ROLE_PUBLISHER,
'data' => json_encode($data)
]
];
@@ -205,11 +205,26 @@
if ($response->getStatusCode() == 200) {
$json = json_decode($response->getBody(), true);
+ // Extract the 'token' part of the token, it will be used to authenticate the connection.
+ // It will be needed in next iterations e.g. to authenticate moderators that aren't
+ // Kolab4 users (or are just not logged in to Kolab4).
+ // FIXME: we could as well generate our own token for auth purposes
+ parse_str(parse_url($json['token'], PHP_URL_QUERY), $url);
+
+ // Create the connection reference in our database
+ $conn = new Connection();
+ $conn->id = $json['id'];
+ $conn->session_id = $this->session_id;
+ $conn->room_id = $this->id;
+ $conn->role = $role;
+ $conn->metadata = ['token' => $url['token']];
+ $conn->save();
+
return [
'session' => $this->session_id,
'token' => $json['token'],
- 'role' => $json['role'],
'connectionId' => $json['id'],
+ 'role' => $role,
];
}
@@ -324,10 +339,10 @@
/**
* Send a OpenVidu signal to the session participants (connections)
*
- * @param string $name Signal name (type)
- * @param array $data Signal data array
- * @param array|string $target List of target connections, Null for all connections.
- * It can be also a participant role.
+ * @param string $name Signal name (type)
+ * @param array $data Signal data array
+ * @param null|int|string[] $target List of target connections, Null for all connections.
+ * It can be also a participant role.
*
* @return bool True on success, False on failure
* @throws \Exception if session does not exist
@@ -345,26 +360,12 @@
];
// Get connection IDs by participant role
- if (is_string($target)) {
- // TODO: We should probably store this in our database/redis. I foresee a use-case
- // for such a connections store on our side, e.g. keeping participant
- // metadata, e.g. selected language, extra roles like a "language interpreter", etc.
-
- $response = $this->client()->request('GET', 'sessions/' . $this->session_id);
-
- if ($response->getStatusCode() !== 200) {
- return false;
- }
-
- $json = json_decode($response->getBody(), true);
- $connections = [];
-
- foreach ($json['connections']['content'] as $connection) {
- if ($connection['role'] === $target) {
- $connections[] = $connection['id'];
- break;
- }
- }
+ if (is_int($target)) {
+ $connections = Connection::where('room_id', $this->id)
+ ->where('session_id', $this->session_id)
+ ->whereRaw("(role & $target)")
+ ->pluck('id')
+ ->all();
if (empty($connections)) {
return false;
diff --git a/src/database/migrations/2021_01_13_120000_create_openvidu_connections_table.php b/src/database/migrations/2021_01_13_120000_create_openvidu_connections_table.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2021_01_13_120000_create_openvidu_connections_table.php
@@ -0,0 +1,45 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// phpcs:ignore
+class CreateOpenviduConnectionsTable extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::create(
+ 'openvidu_connections',
+ function (Blueprint $table) {
+ // I'm not sure about the max. length of the OpenVidu identifiers
+ // In examples they have 14 characters, so 16 should be enough, but
+ // let's be on the safe side with 24.
+ $table->string('id', 24);
+ $table->string('session_id', 24);
+ $table->bigInteger('room_id')->unsigned();
+ $table->smallInteger('role')->default(0);
+ $table->text('metadata')->nullable(); // should be json, but mariadb
+ $table->timestamps();
+
+ $table->primary('id');
+ $table->foreign('room_id')->references('id')->on('openvidu_rooms')->onDelete('cascade');
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('openvidu_connections');
+ }
+}
diff --git a/src/resources/js/meet/app.js b/src/resources/js/meet/app.js
--- a/src/resources/js/meet/app.js
+++ b/src/resources/js/meet/app.js
@@ -2,6 +2,13 @@
import { library } from '@fortawesome/fontawesome-svg-core'
import { OpenVidu } from 'openvidu-browser'
+class Roles {
+ static get SUBSCRIBER() { return 1 << 0; }
+ static get PUBLISHER() { return 1 << 1; }
+ static get MODERATOR() { return 1 << 2; }
+ static get SCREEN() { return 1 << 3; }
+ static get OWNER() { return 1 << 4; }
+}
function Meet(container)
{
@@ -14,7 +21,6 @@
let audioSource = '' // Currently selected microphone
let videoSource = '' // Currently selected camera
let sessionData // Room session metadata
- let role // Current user role
let screenOV // OpenVidu object to initialize a screen sharing session
let screenSession // Session object where the user will connect for screen sharing
@@ -67,12 +73,11 @@
this.switchVideo = switchVideo
this.updateSession = updateSession
-
/**
* Join the room session
*
- * @param data Session metadata and event handlers (session, token, shareToken, nickname,
- * canPublish, chatElement, menuElement, onDestroy, onJoinRequest)
+ * @param data Session metadata and event handlers (session, token, shareToken, nickname, role,
+ * chatElement, menuElement, onDestroy, onJoinRequest)
*/
function joinRoom(data) {
resize();
@@ -97,7 +102,6 @@
session.on('connectionCreated', event => {
// Ignore the current user connection
if (event.connection.role) {
- role = event.connection.role
return
}
@@ -182,7 +186,7 @@
session.connect(data.token, data.params)
.then(() => {
let wrapper
- let params = { self: true, canPublish: data.canPublish, audioActive, videoActive }
+ let params = { self: true, role: data.role, audioActive, videoActive }
params = Object.assign({}, data.params, params)
publisher.on('videoElementCreated', event => {
@@ -196,7 +200,7 @@
wrapper = participantCreate(params)
- if (data.canPublish) {
+ if (data.role & Roles.PUBLISHER) {
publisher.createVideoElement(wrapper, 'PREPEND')
session.publish(publisher)
}
@@ -654,7 +658,7 @@
}
/**
- * Create a participant element in the matrix. Depending on the `canPublish`
+ * Create a participant element in the matrix. Depending on the connection role
* parameter it will be a video element wrapper inside the matrix or a simple
* tag-like element on the subscribers list.
*
@@ -663,7 +667,7 @@
* @return The element
*/
function participantCreate(params) {
- if (params.canPublish) {
+ if (params.role & Roles.PUBLISHER || params.role & Roles.SCREEN) {
return publisherCreate(params)
}
@@ -760,7 +764,7 @@
$element.addClass('self')
}
- if (role == 'MODERATOR') {
+ if (sessionData.role & Roles.MODERATOR) {
$element.addClass('moderated')
}
}
@@ -827,7 +831,7 @@
return false
}
})
- } else if (role == 'MODERATOR') {
+ } else if (sessionData.role & Roles.MODERATOR) {
nickname.attr({title: 'Options', 'data-toggle': 'dropdown'})
.dropdown({boundary: container})
@@ -1071,4 +1075,4 @@
}
}
-export default Meet
+export { Meet, Roles }
diff --git a/src/resources/lang/en/meet.php b/src/resources/lang/en/meet.php
--- a/src/resources/lang/en/meet.php
+++ b/src/resources/lang/en/meet.php
@@ -13,6 +13,8 @@
|
*/
+ 'connection-not-found' => 'The connection does not exist.',
+ 'connection-dismiss-error' => 'Failed to dismiss the connection.',
'room-not-found' => 'The room does not exist.',
'room-setconfig-success' => 'Room configuration updated successfully.',
'room-unsupported-option-error' => 'Invalid room configuration option.',
@@ -21,7 +23,6 @@
'session-join-error' => 'Failed to join the session.',
'session-close-error' => 'Failed to close the session.',
'session-close-success' => 'The session has been closed successfully.',
- 'session-dismiss-connection-error' => 'Failed to dismiss the connection.',
'session-password-error' => 'Failed to join the session. Invalid password.',
'session-request-accept-error' => 'Failed to accept the join request.',
'session-request-deny-error' => 'Failed to deny the join request.',
diff --git a/src/resources/vue/Meet/Room.vue b/src/resources/vue/Meet/Room.vue
--- a/src/resources/vue/Meet/Room.vue
+++ b/src/resources/vue/Meet/Room.vue
@@ -20,7 +20,7 @@
<button class="btn btn-link link-fullscreen open hidden" @click="switchFullscreen" title="Full screen">
<svg-icon icon="compress"></svg-icon>
</button>
- <button class="btn btn-link link-security" v-if="session && session.owner" @click="securityOptions" title="Security options">
+ <button class="btn btn-link link-security" v-if="isRoomOwner()" @click="securityOptions" title="Security options">
<svg-icon icon="shield-alt"></svg-icon>
</button>
<button class="btn btn-link link-logout" @click="logout" title="Leave session">
@@ -123,7 +123,7 @@
</template>
<script>
- import Meet from '../../js/meet/app.js'
+ import { Meet, Roles } from '../../js/meet/app.js'
import StatusMessage from '../Widgets/StatusMessage'
import LogonForm from '../Login'
import SessionSecurityOptions from './SessionSecurityOptions'
@@ -317,7 +317,10 @@
}
},
isPublisher() {
- return this.session && this.session.canPublish
+ return !!this.session.role && (this.session.role & Roles.PUBLISHER) > 0
+ },
+ isRoomOwner() {
+ return !!this.session.role && (this.session.role & Roles.OWNER) > 0
},
isRoomReady() {
return ['ready', 322, 324, 325, 326, 327].includes(this.roomState)
@@ -402,7 +405,7 @@
this.session.onDestroy = event => {
// TODO: Display different message for each reason: forceDisconnectByUser,
// forceDisconnectByServer, sessionClosedByServer?
- if (event.reason != 'disconnect' && event.reason != 'networkDisconnect' && !this.session.owner) {
+ if (event.reason != 'disconnect' && event.reason != 'networkDisconnect' && !this.isRoomOwner()) {
$('#leave-dialog').on('hide.bs.modal', () => {
// FIXME: Where exactly the user should land? Currently he'll land
// on dashboard (if he's logged in) or login form (if he's not).
@@ -414,7 +417,7 @@
this.session.onDismiss = connId => { this.dismissParticipant(connId) }
- if (this.session.owner) {
+ if (this.isRoomOwner()) {
this.session.onJoinRequest = data => { this.joinRequest(data) }
}
@@ -427,7 +430,7 @@
this.$router.push({ name: 'dashboard' })
}
- if (this.session.owner) {
+ if (this.isRoomOwner()) {
axios.post('/api/v4/openvidu/rooms/' + this.room + '/close').then(logout)
} else {
logout()
diff --git a/src/tests/Feature/Controller/OpenViduTest.php b/src/tests/Feature/Controller/OpenViduTest.php
--- a/src/tests/Feature/Controller/OpenViduTest.php
+++ b/src/tests/Feature/Controller/OpenViduTest.php
@@ -114,9 +114,8 @@
$session_id = $room->fresh()->session_id;
- $this->assertSame(Room::ROLE_MODERATOR, $json['role']);
+ $this->assertSame(Room::ROLE_SUBSCRIBER | Room::ROLE_MODERATOR | Room::ROLE_OWNER, $json['role']);
$this->assertSame($session_id, $json['session']);
- $this->assertFalse($json['canPublish']);
$this->assertTrue(is_string($session_id) && !empty($session_id));
$this->assertTrue(strpos($json['token'], 'wss://') === 0);
$this->assertTrue(!array_key_exists('shareToken', $json));
@@ -140,7 +139,6 @@
$json = $response->json();
$this->assertSame(Room::ROLE_SUBSCRIBER, $json['role']);
- $this->assertFalse($json['canPublish']);
$this->assertSame($session_id, $json['session']);
$this->assertTrue(strpos($json['token'], 'wss://') === 0);
$this->assertTrue($json['token'] != $john_token);
@@ -155,7 +153,6 @@
$this->assertSame(Room::ROLE_PUBLISHER, $json['role']);
$this->assertSame($session_id, $json['session']);
- $this->assertTrue($json['canPublish']);
$this->assertTrue(strpos($json['token'], 'wss://') === 0);
$this->assertTrue($json['token'] != $john_token);
$this->assertTrue(!array_key_exists('shareToken', $json));
@@ -331,7 +328,6 @@
$json = $response->json();
$this->assertSame(Room::ROLE_PUBLISHER, $json['role']);
- $this->assertTrue($json['canPublish']);
$this->assertTrue(strpos($json['token'], 'wss://') === 0);
// TODO: Test a scenario where both password and lock are enabled
@@ -359,7 +355,6 @@
$json = $response->json();
$this->assertSame(Room::ROLE_PUBLISHER, $json['role']);
- $this->assertTrue($json['canPublish']);
$this->assertSame($room->session_id, $json['session']);
$this->assertTrue(strpos($json['token'], 'wss://') === 0);
$this->assertTrue(strpos($json['shareToken'], 'wss://') === 0);
@@ -460,13 +455,13 @@
// Non-existing connection
$response = $this->actingAs($john)->post("api/v4/openvidu/rooms/{$room->name}/connections/123/dismiss");
- $response->assertStatus(500);
+ $response->assertStatus(404);
$json = $response->json();
$this->assertCount(2, $json);
$this->assertSame('error', $json['status']);
- $this->assertSame('Failed to dismiss the connection.', $json['message']);
+ $this->assertSame('The connection does not exist.', $json['message']);
// Non-owner access
$response = $this->actingAs($jack)->post("api/v4/openvidu/rooms/{$room->name}/connections/{$conn_id}/dismiss");

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 3, 9:00 AM (14 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18823107
Default Alt Text
D2098.1775206802.diff (22 KB)

Event Timeline