1<?php 2 3declare(strict_types=1); 4/** 5 * @copyright Copyright (c) 2019 Joas Schilling <coding@schilljs.com> 6 * 7 * @license GNU AGPL version 3 or any later version 8 * 9 * This program is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU Affero General Public License as 11 * published by the Free Software Foundation, either version 3 of the 12 * License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU Affero General Public License for more details. 18 * 19 * You should have received a copy of the GNU Affero General Public License 20 * along with this program. If not, see <http://www.gnu.org/licenses/>. 21 * 22 */ 23 24namespace OCA\Talk\Middleware; 25 26use OCA\Talk\Controller\AEnvironmentAwareController; 27use OCA\Talk\Exceptions\ParticipantNotFoundException; 28use OCA\Talk\Exceptions\RoomNotFoundException; 29use OCA\Talk\Manager; 30use OCA\Talk\Middleware\Exceptions\LobbyException; 31use OCA\Talk\Middleware\Exceptions\NotAModeratorException; 32use OCA\Talk\Middleware\Exceptions\ReadOnlyException; 33use OCA\Talk\Model\Attendee; 34use OCA\Talk\Participant; 35use OCA\Talk\Room; 36use OCA\Talk\TalkSession; 37use OCA\Talk\Webinary; 38use OCP\AppFramework\Controller; 39use OCP\AppFramework\Http; 40use OCP\AppFramework\Http\Response; 41use OCP\AppFramework\Http\RedirectToDefaultAppResponse; 42use OCP\AppFramework\Middleware; 43use OCP\AppFramework\OCS\OCSException; 44use OCP\AppFramework\OCSController; 45use OCP\AppFramework\Utility\IControllerMethodReflector; 46use OCP\IRequest; 47 48class InjectionMiddleware extends Middleware { 49 50 /** @var IRequest */ 51 private $request; 52 /** @var IControllerMethodReflector */ 53 private $reflector; 54 /** @var TalkSession */ 55 private $talkSession; 56 /** @var Manager */ 57 private $manager; 58 /** @var ?string */ 59 private $userId; 60 61 public function __construct(IRequest $request, 62 IControllerMethodReflector $reflector, 63 TalkSession $talkSession, 64 Manager $manager, 65 ?string $userId) { 66 $this->request = $request; 67 $this->reflector = $reflector; 68 $this->talkSession = $talkSession; 69 $this->manager = $manager; 70 $this->userId = $userId; 71 } 72 73 /** 74 * @param Controller $controller 75 * @param string $methodName 76 * @throws RoomNotFoundException 77 * @throws ParticipantNotFoundException 78 * @throws NotAModeratorException 79 * @throws ReadOnlyException 80 * @throws LobbyException 81 */ 82 public function beforeController($controller, $methodName): void { 83 if (!$controller instanceof AEnvironmentAwareController) { 84 return; 85 } 86 87 $apiVersion = $this->request->getParam('apiVersion'); 88 $controller->setAPIVersion((int) substr($apiVersion, 1)); 89 90 if ($this->reflector->hasAnnotation('RequireLoggedInParticipant')) { 91 $this->getLoggedIn($controller, false); 92 } 93 94 if ($this->reflector->hasAnnotation('RequireLoggedInModeratorParticipant')) { 95 $this->getLoggedIn($controller, true); 96 } 97 98 if ($this->reflector->hasAnnotation('RequireParticipant')) { 99 $this->getLoggedInOrGuest($controller, false); 100 } 101 102 if ($this->reflector->hasAnnotation('RequireModeratorParticipant')) { 103 $this->getLoggedInOrGuest($controller, true); 104 } 105 106 if ($this->reflector->hasAnnotation('RequireRoom')) { 107 $this->getRoom($controller); 108 } 109 110 if ($this->reflector->hasAnnotation('RequireReadWriteConversation')) { 111 $this->checkReadOnlyState($controller); 112 } 113 114 if ($this->reflector->hasAnnotation('RequireModeratorOrNoLobby')) { 115 $this->checkLobbyState($controller); 116 } 117 } 118 119 /** 120 * @param AEnvironmentAwareController $controller 121 */ 122 protected function getRoom(AEnvironmentAwareController $controller): void { 123 $token = $this->request->getParam('token'); 124 $room = $this->manager->getRoomByToken($token); 125 $controller->setRoom($room); 126 } 127 128 /** 129 * @param AEnvironmentAwareController $controller 130 * @param bool $moderatorRequired 131 * @throws NotAModeratorException 132 */ 133 protected function getLoggedIn(AEnvironmentAwareController $controller, bool $moderatorRequired): void { 134 $token = $this->request->getParam('token'); 135 $sessionId = $this->talkSession->getSessionForRoom($token); 136 $room = $this->manager->getRoomForUserByToken($token, $this->userId, $sessionId); 137 $controller->setRoom($room); 138 139 $participant = $room->getParticipant($this->userId, $sessionId); 140 $controller->setParticipant($participant); 141 142 if ($moderatorRequired && !$participant->hasModeratorPermissions(false)) { 143 throw new NotAModeratorException(); 144 } 145 } 146 147 /** 148 * @param AEnvironmentAwareController $controller 149 * @param bool $moderatorRequired 150 * @throws NotAModeratorException 151 * @throws ParticipantNotFoundException 152 */ 153 protected function getLoggedInOrGuest(AEnvironmentAwareController $controller, bool $moderatorRequired): void { 154 $token = $this->request->getParam('token'); 155 $sessionId = $this->talkSession->getSessionForRoom($token); 156 $room = $this->manager->getRoomForUserByToken($token, $this->userId, $sessionId); 157 $controller->setRoom($room); 158 $participant = null; 159 160 if ($sessionId !== null) { 161 try { 162 $participant = $room->getParticipantBySession($sessionId); 163 } catch (ParticipantNotFoundException $e) { 164 // ignore and fall back in case a concurrent request might have 165 // invalidated the session 166 } 167 } 168 169 if ($participant === null) { 170 $participant = $room->getParticipant($this->userId); 171 } 172 173 $controller->setParticipant($participant); 174 175 if ($moderatorRequired && !$participant->hasModeratorPermissions()) { 176 throw new NotAModeratorException(); 177 } 178 } 179 180 /** 181 * @param AEnvironmentAwareController $controller 182 * @throws ReadOnlyException 183 */ 184 protected function checkReadOnlyState(AEnvironmentAwareController $controller): void { 185 $room = $controller->getRoom(); 186 if (!$room instanceof Room || $room->getReadOnly() === Room::READ_ONLY) { 187 throw new ReadOnlyException(); 188 } 189 } 190 191 /** 192 * @param AEnvironmentAwareController $controller 193 * @throws LobbyException 194 */ 195 protected function checkLobbyState(AEnvironmentAwareController $controller): void { 196 try { 197 $this->getLoggedInOrGuest($controller, true); 198 return; 199 } catch (NotAModeratorException $e) { 200 } catch (ParticipantNotFoundException $e) { 201 } 202 203 $participant = $controller->getParticipant(); 204 if ($participant instanceof Participant && 205 $participant->getPermissions() & Attendee::PERMISSIONS_LOBBY_IGNORE) { 206 return; 207 } 208 209 $room = $controller->getRoom(); 210 if (!$room instanceof Room || $room->getLobbyState() !== Webinary::LOBBY_NONE) { 211 throw new LobbyException(); 212 } 213 } 214 215 /** 216 * @param Controller $controller 217 * @param string $methodName 218 * @param \Exception $exception 219 * @throws \Exception 220 * @return Response 221 */ 222 public function afterException($controller, $methodName, \Exception $exception): Response { 223 if ($exception instanceof RoomNotFoundException || 224 $exception instanceof ParticipantNotFoundException) { 225 if ($controller instanceof OCSController) { 226 throw new OCSException('', Http::STATUS_NOT_FOUND); 227 } 228 229 return new RedirectToDefaultAppResponse(); 230 } 231 232 if ($exception instanceof LobbyException) { 233 if ($controller instanceof OCSController) { 234 throw new OCSException('', Http::STATUS_PRECONDITION_FAILED); 235 } 236 237 return new RedirectToDefaultAppResponse(); 238 } 239 240 if ($exception instanceof NotAModeratorException || 241 $exception instanceof ReadOnlyException) { 242 if ($controller instanceof OCSController) { 243 throw new OCSException('', Http::STATUS_FORBIDDEN); 244 } 245 246 return new RedirectToDefaultAppResponse(); 247 } 248 249 throw $exception; 250 } 251} 252