diff --git a/appinfo/info.xml b/appinfo/info.xml index e685fa28db..e9f87176e3 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -17,7 +17,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m ]]> - 5.99.2 + 5.99.3 agpl Daniel Calviño Sánchez diff --git a/appinfo/routes.php b/appinfo/routes.php index 302d7dea4a..63255e4d44 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -146,7 +146,7 @@ return [ 'requirements' => ['apiVersion' => 'v1'], ], [ - 'name' => 'Room#getRoom', + 'name' => 'Room#getSingleRoom', 'url' => '/api/{apiVersion}/room/{token}', 'verb' => 'GET', 'requirements' => [ diff --git a/docs/api-v1.md b/docs/api-v1.md index bf887772d7..ab4794189b 100644 --- a/docs/api-v1.md +++ b/docs/api-v1.md @@ -45,6 +45,10 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1` * `2` group * `3` public +### Read-only states +* `0` read-write +* `1` read-only + ### Participant types * `1` owner * `2` moderator @@ -84,6 +88,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1` ### 6.0 * `locked-one-to-one-rooms` - One-to-one conversations are now locked to the users. Neither guests nor other participants can be added, so the options to do that should be hidden as well. Also a user can only leave a one-to-one room (not delete). It will be deleted when the other participant left too. If the other participant posts a new chat message or starts a call, the left-participant will be re-added. +* `read-only-rooms` - Rooms can be in `read-only` mode which means people can not do calls or write chat messages. ## Room management @@ -137,6 +142,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1` `participantType` | int | Permissions level of the current user `participantInCall` | bool | Flag if the current user is in the call (deprecated, use `participantFlags` instead) `participantFlags` | int | Flags of the current user (only available with `in-call-flags` capability) + `readOnly` | int | Read-only state for the current user (only available with `read-only-rooms` capability) `count` | int | Number of active users `numGuests` | int | Number of active guests `lastPing` | int | Timestamp of the last ping of the current user (should be used for sorting) diff --git a/lib/Capabilities.php b/lib/Capabilities.php index 60b6a9f412..f264379455 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -66,6 +66,7 @@ class Capabilities implements IPublicCapability { 'notification-levels', 'invite-groups-and-mails', 'locked-one-to-one-rooms', + 'read-only-rooms', ], ], ]; diff --git a/lib/Controller/AEnvironmentAwareController.php b/lib/Controller/AEnvironmentAwareController.php new file mode 100644 index 0000000000..6a2cda446a --- /dev/null +++ b/lib/Controller/AEnvironmentAwareController.php @@ -0,0 +1,56 @@ + + * @copyright Copyright (c) 2016 Joas Schilling + * + * @author Lukas Reschke + * @author Joas Schilling + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Spreed\Controller; + +use OCA\Spreed\Participant; +use OCA\Spreed\Room; +use OCP\AppFramework\OCSController; + +abstract class AEnvironmentAwareController extends OCSController { + + /** @var Room */ + protected $room; + /** @var Participant */ + protected $participant; + + public function setRoom(Room $room): void { + $this->room = $room; + } + + public function getRoom(): ?Room { + return $this->room; + } + + public function setParticipant(Participant $participant): void { + $this->participant = $participant; + } + + public function getParticipant(): ?Participant { + return $this->participant; + } + +} diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index 30c192b72f..ed2437ecc8 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -28,12 +28,10 @@ use OCA\Spreed\Chat\AutoComplete\Sorter; use OCA\Spreed\Chat\ChatManager; use OCA\Spreed\Chat\MessageParser; use OCA\Spreed\GuestManager; -use OCA\Spreed\Participant; use OCA\Spreed\Room; use OCA\Spreed\TalkSession; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; -use OCP\AppFramework\OCSController; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Collaboration\AutoComplete\IManager; use OCP\Collaboration\Collaborators\ISearchResult; @@ -43,7 +41,7 @@ use OCP\IL10N; use OCP\IRequest; use OCP\IUserManager; -class ChatController extends OCSController { +class ChatController extends AEnvironmentAwareController { /** @var string */ private $userId; @@ -80,11 +78,6 @@ class ChatController extends OCSController { /** @var ITimeFactory */ protected $timeFactory; - /** @var Room */ - private $room; - /** @var Participant */ - private $participant; - public function __construct(string $appName, ?string $UserId, IRequest $request, @@ -113,17 +106,10 @@ class ChatController extends OCSController { $this->l = $l; } - public function setRoom(Room $room): void { - $this->room = $room; - } - - public function setParticipant(Participant $participant): void { - $this->participant = $participant; - } - /** * @PublicPage * @RequireParticipant + * @RequireReadWriteConversation * * Sends a new chat message to the given room. * @@ -280,6 +266,7 @@ class ChatController extends OCSController { /** * @PublicPage * @RequireParticipant + * @RequireReadWriteConversation * * @param string $search * @param int $limit diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index a244142a32..2299bbf3e5 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -39,18 +39,16 @@ use OCA\Spreed\Room; use OCA\Spreed\TalkSession; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; -use OCP\AppFramework\OCSController; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Comments\IComment; use OCP\IL10N; -use OCP\ILogger; use OCP\IRequest; use OCP\IUser; use OCP\IUserManager; use OCP\IGroup; use OCP\IGroupManager; -class RoomController extends OCSController { +class RoomController extends AEnvironmentAwareController { /** @var string|null */ private $userId; /** @var TalkSession */ @@ -72,11 +70,6 @@ class RoomController extends OCSController { /** @var IL10N */ private $l10n; - /** @var Room */ - private $room; - /** @var Participant */ - private $participant; - public function __construct(string $appName, ?string $UserId, IRequest $request, @@ -102,14 +95,6 @@ class RoomController extends OCSController { $this->l10n = $l10n; } - public function setRoom(Room $room): void { - $this->room = $room; - } - - public function setParticipant(Participant $participant): void { - $this->participant = $participant; - } - /** * Get all currently existent rooms which the user has joined * @@ -138,7 +123,7 @@ class RoomController extends OCSController { * @param string $token * @return DataResponse */ - public function getRoom(string $token): DataResponse { + public function getSingleRoom(string $token): DataResponse { try { $room = $this->manager->getRoomForParticipantByToken($token, $this->userId, true); @@ -177,6 +162,7 @@ class RoomController extends OCSController { // Deprecated, use participantFlags instead. 'participantInCall' => false, 'participantFlags' => Participant::FLAG_DISCONNECTED, + 'readOnly' => Room::READ_WRITE, 'count' => 0, 'hasPassword' => $room->hasPassword(), 'hasCall' => false, @@ -213,6 +199,7 @@ class RoomController extends OCSController { // Deprecated, use participantFlags instead. 'participantInCall' => ($currentParticipant->getInCallFlags() & Participant::FLAG_IN_CALL) !== 0, 'participantFlags' => $currentParticipant->getInCallFlags(), + 'readOnly' => $room->getReadOnly(), 'count' => $room->getNumberOfParticipants(false, $this->timeFactory->getTime() - 30), 'hasCall' => $room->getActiveSince() instanceof \DateTimeInterface, 'lastActivity' => $lastActivity, diff --git a/lib/Manager.php b/lib/Manager.php index 6dbcebcbfa..559b8e1262 100644 --- a/lib/Manager.php +++ b/lib/Manager.php @@ -119,7 +119,7 @@ class Manager { ])); } - return new Room($this, $this->db, $this->secureRandom, $this->dispatcher, $this->timeFactory, $this->hasher, (int) $row['id'], (int) $row['type'], $row['token'], $row['name'], $row['password'], (int) $row['active_guests'], $activeSince, $lastActivity, $lastMessage, (string) $row['object_type'], (string) $row['object_id']); + return new Room($this, $this->db, $this->secureRandom, $this->dispatcher, $this->timeFactory, $this->hasher, (int) $row['id'], (int) $row['type'], (int) $row['read_only'], $row['token'], $row['name'], $row['password'], (int) $row['active_guests'], $activeSince, $lastActivity, $lastMessage, (string) $row['object_type'], (string) $row['object_id']); } /** diff --git a/lib/Middleware/Exceptions/ReadOnlyException.php b/lib/Middleware/Exceptions/ReadOnlyException.php new file mode 100644 index 0000000000..83ea3df9f9 --- /dev/null +++ b/lib/Middleware/Exceptions/ReadOnlyException.php @@ -0,0 +1,31 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Spreed\Middleware\Exceptions; + +use OCP\AppFramework\Http; + +class ReadOnlyException extends \Exception { + public function __construct() { + parent::__construct('The conversation is read-only', Http::STATUS_FORBIDDEN); + } +} diff --git a/lib/Middleware/InjectionMiddleware.php b/lib/Middleware/InjectionMiddleware.php index 23666ceea6..738da0ebaf 100644 --- a/lib/Middleware/InjectionMiddleware.php +++ b/lib/Middleware/InjectionMiddleware.php @@ -23,10 +23,14 @@ declare(strict_types=1); namespace OCA\Spreed\Middleware; use OC\AppFramework\Utility\ControllerMethodReflector; +use OCA\Spreed\Controller\AEnvironmentAwareController; +use OCA\Spreed\Controller\EnvironmentAwareTrait; use OCA\Spreed\Exceptions\ParticipantNotFoundException; use OCA\Spreed\Exceptions\RoomNotFoundException; use OCA\Spreed\Manager; use OCA\Spreed\Middleware\Exceptions\NotAModeratorException; +use OCA\Spreed\Middleware\Exceptions\ReadOnlyException; +use OCA\Spreed\Room; use OCA\Spreed\TalkSession; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; @@ -69,8 +73,13 @@ class InjectionMiddleware extends Middleware { * @throws RoomNotFoundException * @throws ParticipantNotFoundException * @throws NotAModeratorException + * @throws ReadOnlyException */ public function beforeController($controller, $methodName): void { + if (!$controller instanceof AEnvironmentAwareController) { + return; + } + if ($this->reflector->hasAnnotation('RequireLoggedInParticipant')) { $this->getLoggedIn($controller, false); } @@ -86,9 +95,18 @@ class InjectionMiddleware extends Middleware { if ($this->reflector->hasAnnotation('RequireModeratorParticipant')) { $this->getLoggedInOrGuest($controller, true); } + + if ($this->reflector->hasAnnotation('RequireReadWriteConversation')) { + $this->checkReadOnlyState($controller); + } } - protected function getLoggedIn(Controller $controller, bool $moderatorRequired): void { + /** + * @param AEnvironmentAwareController $controller + * @param bool $moderatorRequired + * @throws NotAModeratorException + */ + protected function getLoggedIn(AEnvironmentAwareController $controller, bool $moderatorRequired): void { $token = $this->request->getParam('token'); $room = $this->manager->getRoomForParticipantByToken($token, $this->userId); $participant = $room->getParticipant($this->userId); @@ -101,7 +119,12 @@ class InjectionMiddleware extends Middleware { $controller->setParticipant($participant); } - protected function getLoggedInOrGuest(Controller $controller, bool $moderatorRequired): void { + /** + * @param AEnvironmentAwareController $controller + * @param bool $moderatorRequired + * @throws NotAModeratorException + */ + protected function getLoggedInOrGuest(AEnvironmentAwareController $controller, bool $moderatorRequired): void { $token = $this->request->getParam('token'); $room = $this->manager->getRoomForParticipantByToken($token, $this->userId); if ($this->userId !== null) { @@ -119,6 +142,17 @@ class InjectionMiddleware extends Middleware { $controller->setParticipant($participant); } + /** + * @param AEnvironmentAwareController $controller + * @throws ReadOnlyException + */ + protected function checkReadOnlyState(AEnvironmentAwareController $controller): void { + $room = $controller->getRoom(); + if (!$room instanceof Room || $room->getReadOnly() === Room::READ_ONLY) { + throw new ReadOnlyException(); + } + } + /** * @param Controller $controller * @param string $methodName @@ -136,7 +170,8 @@ class InjectionMiddleware extends Middleware { return new RedirectToDefaultAppResponse(); } - if ($exception instanceof NotAModeratorException) { + if ($exception instanceof NotAModeratorException || + $exception instanceof ReadOnlyException) { if ($controller instanceof OCSController) { throw new OCSException('', Http::STATUS_FORBIDDEN); } diff --git a/lib/Migration/Version5099Date20190319134820.php b/lib/Migration/Version5099Date20190319134820.php new file mode 100644 index 0000000000..94428dcf5a --- /dev/null +++ b/lib/Migration/Version5099Date20190319134820.php @@ -0,0 +1,58 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Spreed\Migration; + +use Closure; +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\SimpleMigrationStep; +use OCP\Migration\IOutput; + +class Version5099Date20190319134820 extends SimpleMigrationStep { + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if ($schema->hasTable('talk_rooms')) { + $table = $schema->getTable('talk_rooms'); + + if (!$table->hasColumn('read_only')) { + $table->addColumn('read_only', Type::INTEGER, [ + 'notnull' => true, + 'length' => 6, + 'default' => 0, + ]); + } + } + + return $schema; + } + +} diff --git a/lib/Room.php b/lib/Room.php index 6d4d3ab723..cc8d1c04c5 100644 --- a/lib/Room.php +++ b/lib/Room.php @@ -45,6 +45,9 @@ class Room { public const GROUP_CALL = 2; public const PUBLIC_CALL = 3; + public const READ_WRITE = 0; + public const READ_ONLY = 1; + public const PARTICIPANT_REMOVED = 'remove'; public const PARTICIPANT_LEFT = 'leave'; @@ -65,6 +68,8 @@ class Room { private $id; /** @var int */ private $type; + /** @var int */ + private $readOnly; /** @var string */ private $token; /** @var string */ @@ -97,6 +102,7 @@ class Room { IHasher $hasher, int $id, int $type, + int $readOnly, string $token, string $name, string $password, @@ -114,6 +120,7 @@ class Room { $this->hasher = $hasher; $this->id = $id; $this->type = $type; + $this->readOnly = $readOnly; $this->token = $token; $this->name = $name; $this->password = $password; @@ -133,6 +140,10 @@ class Room { return $this->type; } + public function getReadOnly(): int { + return $this->readOnly; + } + public function getToken(): string { return $this->token; } diff --git a/tests/php/CapabilitiesTest.php b/tests/php/CapabilitiesTest.php index 56bfc3b50f..1fe580d92d 100644 --- a/tests/php/CapabilitiesTest.php +++ b/tests/php/CapabilitiesTest.php @@ -78,6 +78,7 @@ class CapabilitiesTest extends TestCase { 'notification-levels', 'invite-groups-and-mails', 'locked-one-to-one-rooms', + 'read-only-rooms', ], ], ], $capabilities->getCapabilities()); @@ -119,6 +120,7 @@ class CapabilitiesTest extends TestCase { 'notification-levels', 'invite-groups-and-mails', 'locked-one-to-one-rooms', + 'read-only-rooms', ], ], ], $capabilities->getCapabilities());