diff --git a/src/app/Http/Controllers/API/V4/OpenViduController.php b/src/app/Http/Controllers/API/V4/OpenViduController.php index 24ce2c12..c0e71349 100644 --- a/src/app/Http/Controllers/API/V4/OpenViduController.php +++ b/src/app/Http/Controllers/API/V4/OpenViduController.php @@ -1,68 +1,69 @@ user(); - $room = \App\OpenVidu\Room::where('name', $id)->first(); + $room = Room::where('name', $id)->first(); // This isn't a room, bye bye if (!$room) { return $this->errorResponse(404, \trans('meet.roomnotfound')); } // There's no existing session if (!$room->hasSession()) { // Only the room owner can create the session if ($user->id != $room->user_id) { return $this->errorResponse(423, \trans('meet.sessionnotfound')); } $session = $room->createSession(); if (empty($session)) { return $this->errorResponse(500, \trans('meet.sessioncreateerror')); } } // Create session token for the current user/connection $response = $room->getSessionToken('PUBLISHER'); if (empty($response)) { return $this->errorResponse(500, \trans('meet.sessionjoinerror')); } if (!empty(request()->input('screenShare'))) { $add_token = $room->getSessionToken('PUBLISHER'); $response['shareToken'] = $add_token['token']; } return response()->json($response, 200); } /** * Webhook as triggered from OpenVidu server * * @param \Illuminate\Http\Request $request The API request. * * @return \Illuminate\Http\Response The response */ public function webhook(Request $request) { return response('Success', 200); } } diff --git a/src/resources/vue/Meet/Room.vue b/src/resources/vue/Meet/Room.vue index a94182cb..650571cc 100644 --- a/src/resources/vue/Meet/Room.vue +++ b/src/resources/vue/Meet/Room.vue @@ -1,185 +1,186 @@ Set up your session Microphone None {{ mic.label }} Camera None {{ cam.label }} Nickname JOIN diff --git a/src/routes/api.php b/src/routes/api.php index e1c25970..007d23fa 100644 --- a/src/routes/api.php +++ b/src/routes/api.php @@ -1,125 +1,118 @@ 'api', 'prefix' => 'auth' ], function ($router) { Route::post('login', 'API\AuthController@login'); Route::group( ['middleware' => 'auth:api'], function ($router) { Route::get('info', 'API\AuthController@info'); Route::post('logout', 'API\AuthController@logout'); Route::post('refresh', 'API\AuthController@refresh'); } ); } ); Route::group( [ 'domain' => \config('app.domain'), 'middleware' => 'api', 'prefix' => 'auth' ], function ($router) { Route::post('password-reset/init', 'API\PasswordResetController@init'); Route::post('password-reset/verify', 'API\PasswordResetController@verify'); Route::post('password-reset', 'API\PasswordResetController@reset'); Route::get('signup/plans', 'API\SignupController@plans'); Route::post('signup/init', 'API\SignupController@init'); Route::post('signup/verify', 'API\SignupController@verify'); Route::post('signup', 'API\SignupController@signup'); } ); Route::group( [ 'domain' => \config('app.domain'), 'middleware' => 'auth:api', 'prefix' => 'v4' ], function () { Route::apiResource('domains', API\V4\DomainsController::class); Route::get('domains/{id}/confirm', 'API\V4\DomainsController@confirm'); Route::get('domains/{id}/status', 'API\V4\DomainsController@status'); Route::apiResource('entitlements', API\V4\EntitlementsController::class); Route::apiResource('packages', API\V4\PackagesController::class); Route::apiResource('skus', API\V4\SkusController::class); Route::apiResource('users', API\V4\UsersController::class); Route::get('users/{id}/status', 'API\V4\UsersController@status'); Route::apiResource('wallets', API\V4\WalletsController::class); Route::get('wallets/{id}/transactions', 'API\V4\WalletsController@transactions'); Route::get('wallets/{id}/receipts', 'API\V4\WalletsController@receipts'); Route::get('wallets/{id}/receipts/{receipt}', 'API\V4\WalletsController@receiptDownload'); Route::post('payments', 'API\V4\PaymentsController@store'); Route::get('payments/mandate', 'API\V4\PaymentsController@mandate'); Route::post('payments/mandate', 'API\V4\PaymentsController@mandateCreate'); Route::put('payments/mandate', 'API\V4\PaymentsController@mandateUpdate'); Route::delete('payments/mandate', 'API\V4\PaymentsController@mandateDelete'); - } -); -Route::group( - [ - 'domain' => \config('app.domain'), - 'middleware' => 'api', - 'prefix' => 'v4' - ], - function () { + // For now we require authenticated users + // TODO: Allow guests/external users Route::get('meet/openvidu/{id}', 'API\V4\OpenViduController@joinOrCreate'); } ); Route::group( [ 'domain' => \config('app.domain'), ], function () { Route::post('webhooks/payment/{provider}', 'API\V4\PaymentsController@webhook'); Route::post('webhooks/meet/openvidu', 'API\V4\OpenViduController@webhook'); } ); Route::group( [ 'domain' => 'admin.' . \config('app.domain'), 'middleware' => ['auth:api', 'admin'], 'prefix' => 'v4', ], function () { Route::apiResource('domains', API\V4\Admin\DomainsController::class); Route::get('domains/{id}/confirm', 'API\V4\Admin\DomainsController@confirm'); Route::apiResource('entitlements', API\V4\Admin\EntitlementsController::class); Route::apiResource('packages', API\V4\Admin\PackagesController::class); Route::apiResource('skus', API\V4\Admin\SkusController::class); Route::apiResource('users', API\V4\Admin\UsersController::class); Route::post('users/{id}/suspend', 'API\V4\Admin\UsersController@suspend'); Route::post('users/{id}/unsuspend', 'API\V4\Admin\UsersController@unsuspend'); Route::apiResource('wallets', API\V4\Admin\WalletsController::class); Route::post('wallets/{id}/one-off', 'API\V4\Admin\WalletsController@oneOff'); Route::get('wallets/{id}/transactions', 'API\V4\Admin\WalletsController@transactions'); Route::apiResource('discounts', API\V4\Admin\DiscountsController::class); } ); diff --git a/src/tests/Feature/Controller/DomainsTest.php b/src/tests/Feature/Controller/DomainsTest.php index cf2f1a31..bbc3e616 100644 --- a/src/tests/Feature/Controller/DomainsTest.php +++ b/src/tests/Feature/Controller/DomainsTest.php @@ -1,243 +1,242 @@ deleteTestUser('test1@domainscontroller.com'); $this->deleteTestDomain('domainscontroller.com'); } public function tearDown(): void { $this->deleteTestUser('test1@domainscontroller.com'); $this->deleteTestDomain('domainscontroller.com'); parent::tearDown(); } /** * Test domain confirm request */ public function testConfirm(): void { $sku_domain = Sku::where('title', 'domain-hosting')->first(); $john = $this->getTestUser('john@kolab.org'); $ned = $this->getTestUser('ned@kolab.org'); $user = $this->getTestUser('test1@domainscontroller.com'); $domain = $this->getTestDomain('domainscontroller.com', [ 'status' => Domain::STATUS_NEW, 'type' => Domain::TYPE_EXTERNAL, ]); Entitlement::create([ 'wallet_id' => $user->wallets()->first()->id, 'sku_id' => $sku_domain->id, 'entitleable_id' => $domain->id, 'entitleable_type' => Domain::class ]); $response = $this->actingAs($user)->get("api/v4/domains/{$domain->id}/confirm"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(2, $json); $this->assertEquals('error', $json['status']); $this->assertEquals('Domain ownership verification failed.', $json['message']); $domain->status |= Domain::STATUS_CONFIRMED; $domain->save(); $response = $this->actingAs($user)->get("api/v4/domains/{$domain->id}/confirm"); $response->assertStatus(200); $json = $response->json(); $this->assertEquals('success', $json['status']); $this->assertEquals('Domain verified successfully.', $json['message']); $this->assertTrue(is_array($json['statusInfo'])); // Not authorized access $response = $this->actingAs($john)->get("api/v4/domains/{$domain->id}/confirm"); $response->assertStatus(403); // Authorized access by additional account controller $domain = $this->getTestDomain('kolab.org'); $response = $this->actingAs($ned)->get("api/v4/domains/{$domain->id}/confirm"); $response->assertStatus(200); } /** * Test fetching domains list */ public function testIndex(): void { // User with no domains $user = $this->getTestUser('test1@domainscontroller.com'); $response = $this->actingAs($user)->get("api/v4/domains"); $response->assertStatus(200); $json = $response->json(); $this->assertSame([], $json); // User with custom domain(s) $john = $this->getTestUser('john@kolab.org'); $ned = $this->getTestUser('ned@kolab.org'); $response = $this->actingAs($john)->get("api/v4/domains"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(1, $json); $this->assertSame('kolab.org', $json[0]['namespace']); // Values below are tested by Unit tests $this->assertArrayHasKey('isConfirmed', $json[0]); $this->assertArrayHasKey('isDeleted', $json[0]); $this->assertArrayHasKey('isVerified', $json[0]); $this->assertArrayHasKey('isSuspended', $json[0]); $this->assertArrayHasKey('isActive', $json[0]); $this->assertArrayHasKey('isLdapReady', $json[0]); $response = $this->actingAs($ned)->get("api/v4/domains"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(1, $json); $this->assertSame('kolab.org', $json[0]['namespace']); } /** * Test fetching domain info */ public function testShow(): void { $sku_domain = Sku::where('title', 'domain-hosting')->first(); $user = $this->getTestUser('test1@domainscontroller.com'); $domain = $this->getTestDomain('domainscontroller.com', [ 'status' => Domain::STATUS_NEW, 'type' => Domain::TYPE_EXTERNAL, ]); Entitlement::create([ 'wallet_id' => $user->wallets()->first()->id, 'sku_id' => $sku_domain->id, 'entitleable_id' => $domain->id, 'entitleable_type' => Domain::class ]); $response = $this->actingAs($user)->get("api/v4/domains/{$domain->id}"); $response->assertStatus(200); $json = $response->json(); $this->assertEquals($domain->id, $json['id']); $this->assertEquals($domain->namespace, $json['namespace']); $this->assertEquals($domain->status, $json['status']); $this->assertEquals($domain->type, $json['type']); $this->assertSame($domain->hash(Domain::HASH_TEXT), $json['hash_text']); $this->assertSame($domain->hash(Domain::HASH_CNAME), $json['hash_cname']); $this->assertSame($domain->hash(Domain::HASH_CODE), $json['hash_code']); $this->assertCount(4, $json['config']); $this->assertTrue(strpos(implode("\n", $json['config']), $domain->namespace) !== false); $this->assertCount(8, $json['dns']); $this->assertTrue(strpos(implode("\n", $json['dns']), $domain->namespace) !== false); $this->assertTrue(strpos(implode("\n", $json['dns']), $domain->hash()) !== false); $this->assertTrue(is_array($json['statusInfo'])); // Values below are tested by Unit tests $this->assertArrayHasKey('isConfirmed', $json); $this->assertArrayHasKey('isDeleted', $json); $this->assertArrayHasKey('isVerified', $json); $this->assertArrayHasKey('isSuspended', $json); $this->assertArrayHasKey('isActive', $json); $this->assertArrayHasKey('isLdapReady', $json); $john = $this->getTestUser('john@kolab.org'); $ned = $this->getTestUser('ned@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); // Not authorized - Other account domain $response = $this->actingAs($john)->get("api/v4/domains/{$domain->id}"); $response->assertStatus(403); $domain = $this->getTestDomain('kolab.org'); // Ned is an additional controller on kolab.org's wallet $response = $this->actingAs($ned)->get("api/v4/domains/{$domain->id}"); $response->assertStatus(200); // Jack has no entitlement/control over kolab.org $response = $this->actingAs($jack)->get("api/v4/domains/{$domain->id}"); $response->assertStatus(403); } /** * Test fetching domain status (GET /api/v4/domains//status) * and forcing setup process update (?refresh=1) * * @group dns */ public function testStatus(): void { $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $domain = $this->getTestDomain('kolab.org'); // Test unauthorized access $response = $this->actingAs($jack)->get("/api/v4/domains/{$domain->id}/status"); $response->assertStatus(403); $domain->status = Domain::STATUS_NEW | Domain::STATUS_ACTIVE | Domain::STATUS_LDAP_READY; $domain->save(); // Get domain status $response = $this->actingAs($john)->get("/api/v4/domains/{$domain->id}/status"); $response->assertStatus(200); $json = $response->json(); $this->assertFalse($json['isVerified']); $this->assertFalse($json['isReady']); $this->assertCount(4, $json['process']); $this->assertSame('domain-verified', $json['process'][2]['label']); $this->assertSame(false, $json['process'][2]['state']); $this->assertTrue(empty($json['status'])); $this->assertTrue(empty($json['message'])); // Now "reboot" the process and verify the domain $response = $this->actingAs($john)->get("/api/v4/domains/{$domain->id}/status?refresh=1"); $response->assertStatus(200); $json = $response->json(); $this->assertTrue($json['isVerified']); $this->assertFalse($json['isReady']); $this->assertCount(4, $json['process']); $this->assertSame('domain-verified', $json['process'][2]['label']); $this->assertSame(true, $json['process'][2]['state']); $this->assertSame('domain-confirmed', $json['process'][3]['label']); $this->assertSame(false, $json['process'][3]['state']); $this->assertSame('error', $json['status']); $this->assertSame('Failed to verify an ownership of a domain.', $json['message']); // TODO: Test completing all process steps } } diff --git a/src/tests/Feature/Controller/OpenViduTest.php b/src/tests/Feature/Controller/OpenViduTest.php new file mode 100644 index 00000000..3526eec7 --- /dev/null +++ b/src/tests/Feature/Controller/OpenViduTest.php @@ -0,0 +1,79 @@ +getTestUser('john@kolab.org'); + $jack = $this->getTestUser('jack@kolab.org'); + $room = Room::where('name', 'john')->first(); + $room->session_id = null; + $room->save(); + + // Unauth access not allowed (yet) + $response = $this->get("api/v4/meet/openvidu/{$room->name}"); + $response->assertStatus(401); + + // Non-existing room name + $response = $this->actingAs($john)->get("api/v4/meet/openvidu/non-existing"); + $response->assertStatus(404); + + // Non-owner, no session yet + $response = $this->actingAs($jack)->get("api/v4/meet/openvidu/{$room->name}"); + $response->assertStatus(423); + + // Room owner + $response = $this->actingAs($john)->get("api/v4/meet/openvidu/{$room->name}"); + $response->assertStatus(200); + + $json = $response->json(); + + $session_id = $room->fresh()->session_id; + + $this->assertSame('PUBLISHER', $json['role']); + $this->assertSame($session_id, $json['session']); + $this->assertTrue(is_string($session_id) && !empty($session_id)); + $this->assertTrue(strpos($json['token'], 'wss://') === 0); + $this->assertTrue(!array_key_exists('shareToken', $json)); + + $john_token = $json['token']; + + // Non-owner, now the session exists + $response = $this->actingAs($jack)->get("api/v4/meet/openvidu/{$room->name}"); + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertSame('PUBLISHER', $json['role']); + $this->assertSame($session_id, $json['session']); + $this->assertTrue(strpos($json['token'], 'wss://') === 0); + $this->assertTrue($json['token'] != $john_token); + $this->assertTrue(!array_key_exists('shareToken', $json)); + + $jack_token = $json['token']; + + // request with screenShare token + $response = $this->actingAs($john)->get("api/v4/meet/openvidu/{$room->name}?screenShare=1"); + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertSame('PUBLISHER', $json['role']); + $this->assertSame($session_id, $json['session']); + $this->assertTrue(strpos($json['token'], 'wss://') === 0); + $this->assertTrue($json['token'] != $john_token); + $this->assertTrue(strpos($json['shareToken'], 'wss://') === 0); + $this->assertTrue($json['shareToken'] != $john_token && $json['shareToken'] != $json['token']); + } +} diff --git a/src/tests/Feature/Controller/PackagesTest.php b/src/tests/Feature/Controller/PackagesTest.php index 1b12b6d4..4435021e 100644 --- a/src/tests/Feature/Controller/PackagesTest.php +++ b/src/tests/Feature/Controller/PackagesTest.php @@ -1,53 +1,52 @@ get("api/v4/packages"); $response->assertStatus(401); $user = $this->getTestUser('john@kolab.org'); $package_domain = Package::where('title', 'domain-hosting')->first(); $package_kolab = Package::where('title', 'kolab')->first(); $package_lite = Package::where('title', 'lite')->first(); $response = $this->actingAs($user)->get("api/v4/packages"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(3, $json); $this->assertSame($package_domain->id, $json[0]['id']); $this->assertSame($package_domain->title, $json[0]['title']); $this->assertSame($package_domain->name, $json[0]['name']); $this->assertSame($package_domain->description, $json[0]['description']); $this->assertSame($package_domain->isDomain(), $json[0]['isDomain']); $this->assertSame($package_domain->cost(), $json[0]['cost']); $this->assertSame($package_kolab->id, $json[1]['id']); $this->assertSame($package_kolab->title, $json[1]['title']); $this->assertSame($package_kolab->name, $json[1]['name']); $this->assertSame($package_kolab->description, $json[1]['description']); $this->assertSame($package_kolab->isDomain(), $json[1]['isDomain']); $this->assertSame($package_kolab->cost(), $json[1]['cost']); $this->assertSame($package_lite->id, $json[2]['id']); $this->assertSame($package_lite->title, $json[2]['title']); $this->assertSame($package_lite->name, $json[2]['name']); $this->assertSame($package_lite->description, $json[2]['description']); $this->assertSame($package_lite->isDomain(), $json[2]['isDomain']); $this->assertSame($package_lite->cost(), $json[2]['cost']); } }