1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
|
<?php
declare(strict_types=1);
/**
*
* @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com)
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Talk\PublicShareAuth;
use OCA\Talk\Events\AddParticipantsEvent;
use OCA\Talk\Events\JoinRoomGuestEvent;
use OCA\Talk\Events\JoinRoomUserEvent;
use OCA\Talk\Events\RoomEvent;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCP\EventDispatcher\IEventDispatcher;
/**
* Custom behaviour for rooms to request the password for a share.
*
* The rooms to request the password for a share are temporary, short-lived
* rooms intended to give the sharer the chance to verify the identity of the
* sharee before granting her access to the share. They are always created by a
* guest or user (the sharee) who then waits for the sharer (who will be the
* owner of the room) to join and provide her the password.
*
* These rooms are associated to a "share:password" object, and their custom
* behaviour is provided by calling the methods of this class as a response to
* different room events.
*/
class Listener {
public static function register(IEventDispatcher $dispatcher): void {
$listener = static function (JoinRoomUserEvent $event) {
self::preventExtraUsersFromJoining($event->getRoom(), $event->getUser()->getUID());
};
$dispatcher->addListener(Room::EVENT_BEFORE_ROOM_CONNECT, $listener);
$listener = static function (JoinRoomGuestEvent $event) {
self::preventExtraGuestsFromJoining($event->getRoom());
};
$dispatcher->addListener(Room::EVENT_BEFORE_GUEST_CONNECT, $listener);
$listener = static function (AddParticipantsEvent $event) {
self::preventExtraUsersFromBeingAdded($event->getRoom(), $event->getParticipants());
};
$dispatcher->addListener(Room::EVENT_BEFORE_USERS_ADD, $listener);
$listener = static function (RoomEvent $event) {
self::destroyRoomOnParticipantLeave($event->getRoom());
};
$dispatcher->addListener(Room::EVENT_AFTER_USER_REMOVE, $listener);
$dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_REMOVE, $listener);
$dispatcher->addListener(Room::EVENT_AFTER_ROOM_DISCONNECT, $listener);
$dispatcher->addListener(Room::EVENT_AFTER_GUESTS_CLEAN, $listener);
}
/**
* Prevents other users from joining if there is already another participant
* in the room besides the owner.
*
* This method should be called before a user joins a room.
*
* @param Room $room
* @param string $userId
* @throws \OverflowException
*/
public static function preventExtraUsersFromJoining(Room $room, string $userId): void {
if ($room->getObjectType() !== 'share:password') {
return;
}
try {
$participant = $room->getParticipant($userId);
if ($participant->getAttendee()->getParticipantType() === Participant::OWNER) {
return;
}
} catch (ParticipantNotFoundException $e) {
}
$participantService = \OC::$server->get(ParticipantService::class);
if ($participantService->getNumberOfActors($room) > 1) {
throw new \OverflowException('Only the owner and another participant are allowed in rooms to request the password for a share');
}
}
/**
* Prevents other guests from joining if there is already another
* participant in the room besides the owner.
*
* This method should be called before a guest joins a room.
*
* @param Room $room
* @throws \OverflowException
*/
public static function preventExtraGuestsFromJoining(Room $room): void {
if ($room->getObjectType() !== 'share:password') {
return;
}
$participantService = \OC::$server->get(ParticipantService::class);
if ($participantService->getNumberOfActors($room) > 1) {
throw new \OverflowException('Only the owner and another participant are allowed in rooms to request the password for a share');
}
}
/**
* Prevents other users from being added to the room (as they will not be
* able to join).
*
* This method should be called before a user is added to a room.
*
* @param Room $room
* @param array[] $participants
* @throws \OverflowException
*/
public static function preventExtraUsersFromBeingAdded(Room $room, array $participants): void {
if ($room->getObjectType() !== 'share:password') {
return;
}
if (empty($participants)) {
return;
}
// Events with more than one participant can be directly aborted, as
// when the owner is added during room creation or a user self-joins the
// event will always have just one participant.
if (count($participants) > 1) {
throw new \OverflowException('Only the owner and another participant are allowed in rooms to request the password for a share');
}
$participant = $participants[0];
if ($participant['participantType'] !== Participant::OWNER && $participant['participantType'] !== Participant::USER_SELF_JOINED) {
throw new \OverflowException('Only the owner and another participant are allowed in rooms to request the password for a share');
}
}
/**
* Destroys the PublicShareAuth room as soon as one of the participant
* leaves the room.
*
* This method should be called after a user or guest leaves a room for any
* reason (no matter if the user or guest removed herself, was removed or
* timed out).
*
* @param Room $room
*/
public static function destroyRoomOnParticipantLeave(Room $room): void {
if ($room->getObjectType() !== 'share:password') {
return;
}
$room->deleteRoom();
}
}
|