mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-17 21:12:20 +01:00
feat(email): Recognize guests invited via email
Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
parent
dbe4b80f90
commit
5e276f5dc2
7 changed files with 140 additions and 14 deletions
|
|
@ -142,7 +142,7 @@ class MessageParser {
|
|||
$displayName = $this->guestNames[$actorId];
|
||||
} else {
|
||||
try {
|
||||
$participant = $this->participantService->getParticipantByActor($message->getRoom(), Attendee::ACTOR_GUESTS, $actorId);
|
||||
$participant = $this->participantService->getParticipantByActor($message->getRoom(), str_contains($actorId, '@') ? Attendee::ACTOR_EMAILS : Attendee::ACTOR_GUESTS, $actorId);
|
||||
$displayName = $participant->getAttendee()->getDisplayName();
|
||||
} catch (ParticipantNotFoundException) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use OCA\Talk\Config;
|
|||
use OCA\Talk\Exceptions\ParticipantNotFoundException;
|
||||
use OCA\Talk\Exceptions\RoomNotFoundException;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
|
|
@ -50,6 +51,7 @@ use OCP\IUserSession;
|
|||
use OCP\Notification\IManager as INotificationManager;
|
||||
use OCP\Security\Bruteforce\IThrottler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use SensitiveParameter;
|
||||
|
||||
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
|
||||
class PageController extends Controller {
|
||||
|
|
@ -96,9 +98,9 @@ class PageController extends Controller {
|
|||
#[PublicPage]
|
||||
#[UseSession]
|
||||
#[BruteForceProtection(action: 'talkRoomToken')]
|
||||
public function showCall(string $token): Response {
|
||||
public function showCall(string $token, string $email = '', string $access = ''): Response {
|
||||
// This is the entry point from the `/call/{token}` URL which is hardcoded in the server.
|
||||
return $this->index($token);
|
||||
return $this->pageHandler($token, email: $email, accessToken: $access);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -113,7 +115,7 @@ class PageController extends Controller {
|
|||
#[BruteForceProtection(action: 'talkRoomPassword')]
|
||||
public function authenticatePassword(string $token, string $password = ''): Response {
|
||||
// This is the entry point from the `/call/{token}` URL which is hardcoded in the server.
|
||||
return $this->pageHandler($token, '', $password);
|
||||
return $this->pageHandler($token, password: $password);
|
||||
}
|
||||
|
||||
#[NoCSRFRequired]
|
||||
|
|
@ -152,11 +154,18 @@ class PageController extends Controller {
|
|||
* @return TemplateResponse|RedirectResponse
|
||||
* @throws HintException
|
||||
*/
|
||||
protected function pageHandler(string $token = '', string $callUser = '', string $password = ''): Response {
|
||||
protected function pageHandler(
|
||||
string $token = '',
|
||||
string $callUser = '',
|
||||
string $password = '',
|
||||
string $email = '',
|
||||
#[SensitiveParameter]
|
||||
string $accessToken = '',
|
||||
): Response {
|
||||
$bruteForceToken = $token;
|
||||
$user = $this->userSession->getUser();
|
||||
if (!$user instanceof IUser) {
|
||||
return $this->guestEnterRoom($token, $password);
|
||||
return $this->guestEnterRoom($token, $password, $email, $accessToken);
|
||||
}
|
||||
|
||||
$throttle = false;
|
||||
|
|
@ -332,12 +341,23 @@ class PageController extends Controller {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @param string $password
|
||||
* @return TemplateResponse|RedirectResponse
|
||||
* @throws HintException
|
||||
*/
|
||||
protected function guestEnterRoom(string $token, string $password): Response {
|
||||
protected function guestEnterRoom(
|
||||
string $token,
|
||||
string $password,
|
||||
string $email,
|
||||
#[SensitiveParameter]
|
||||
string $accessToken,
|
||||
): Response {
|
||||
if ($email && $accessToken) {
|
||||
return $this->invitedEmail(
|
||||
$token,
|
||||
$email,
|
||||
$accessToken,
|
||||
);
|
||||
}
|
||||
try {
|
||||
$room = $this->manager->getRoomByToken($token);
|
||||
if ($room->getType() !== Room::TYPE_PUBLIC) {
|
||||
|
|
@ -405,6 +425,62 @@ class PageController extends Controller {
|
|||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TemplateResponse|RedirectResponse
|
||||
* @throws HintException
|
||||
*/
|
||||
protected function invitedEmail(
|
||||
string $token,
|
||||
string $email,
|
||||
#[SensitiveParameter]
|
||||
string $accessToken,
|
||||
): Response {
|
||||
try {
|
||||
$this->manager->getRoomByAccessToken(
|
||||
$token,
|
||||
Attendee::ACTOR_EMAILS,
|
||||
$email,
|
||||
$accessToken,
|
||||
);
|
||||
$this->talkSession->renewSessionId();
|
||||
$this->talkSession->setAuthedEmailActorIdForRoom($token, $email);
|
||||
} catch (RoomNotFoundException) {
|
||||
$redirectUrl = $this->url->linkToRoute('spreed.Page.index');
|
||||
if ($token) {
|
||||
$redirectUrl = $this->url->linkToRoute('spreed.Page.showCall', ['token' => $token]);
|
||||
}
|
||||
$response = new RedirectResponse($this->url->linkToRoute('core.login.showLoginForm', [
|
||||
'redirect_url' => $redirectUrl,
|
||||
]));
|
||||
$response->throttle(['token' => $token, 'action' => 'talkRoomToken']);
|
||||
return $response;
|
||||
}
|
||||
|
||||
$this->publishInitialStateForGuest();
|
||||
$this->eventDispatcher->dispatchTyped(new RenderReferenceEvent());
|
||||
|
||||
$response = new PublicTemplateResponse($this->appName, 'index', [
|
||||
'id-app-content' => '#content-vue',
|
||||
'id-app-navigation' => null,
|
||||
]);
|
||||
|
||||
$response->setFooterVisible(false);
|
||||
$csp = new ContentSecurityPolicy();
|
||||
$csp->addAllowedConnectDomain('*');
|
||||
$csp->addAllowedMediaDomain('blob:');
|
||||
$csp->addAllowedWorkerSrcDomain('blob:');
|
||||
$csp->addAllowedWorkerSrcDomain("'self'");
|
||||
$csp->addAllowedChildSrcDomain('blob:');
|
||||
$csp->addAllowedChildSrcDomain("'self'");
|
||||
$csp->addAllowedScriptDomain('blob:');
|
||||
$csp->addAllowedScriptDomain("'self'");
|
||||
$csp->addAllowedConnectDomain('blob:');
|
||||
$csp->addAllowedConnectDomain("'self'");
|
||||
$csp->addAllowedImageDomain('https://*.tile.openstreetmap.org');
|
||||
$response->setContentSecurityPolicy($csp);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return RedirectResponse
|
||||
|
|
|
|||
|
|
@ -1076,6 +1076,8 @@ class RoomController extends AEnvironmentAwareController {
|
|||
$result['displayName'] = $participant->getAttendee()->getDisplayName();
|
||||
} elseif ($participant->getAttendee()->getActorType() === Attendee::ACTOR_CIRCLES) {
|
||||
$result['displayName'] = $participant->getAttendee()->getDisplayName();
|
||||
} elseif ($participant->getAttendee()->getActorType() === Attendee::ACTOR_EMAILS) {
|
||||
$result['displayName'] = $participant->getAttendee()->getDisplayName();
|
||||
} elseif ($participant->getAttendee()->getActorType() === Attendee::ACTOR_FEDERATED_USERS) {
|
||||
if ($participant->getSession() instanceof Session && $participant->getSession()->getLastPing() <= $maxPingAge) {
|
||||
$this->participantService->leaveRoomAsSession($this->room, $participant);
|
||||
|
|
@ -1643,8 +1645,10 @@ class RoomController extends AEnvironmentAwareController {
|
|||
}
|
||||
}
|
||||
|
||||
$authenticatedEmailGuest = $this->session->getAuthedEmailActorIdForRoom($token);
|
||||
|
||||
$headers = [];
|
||||
if ($room->isFederatedConversation()) {
|
||||
if ($authenticatedEmailGuest !== null || $room->isFederatedConversation()) {
|
||||
// Skip password checking
|
||||
$result = [
|
||||
'result' => true,
|
||||
|
|
@ -1659,6 +1663,12 @@ class RoomController extends AEnvironmentAwareController {
|
|||
$participant = $this->participantService->joinRoom($this->roomService, $room, $user, $password, $result['result']);
|
||||
$this->participantService->generatePinForParticipant($room, $participant);
|
||||
} else {
|
||||
if ($authenticatedEmailGuest !== null && $previousParticipant === null) {
|
||||
try {
|
||||
$previousParticipant = $this->participantService->getParticipantByActor($room, Attendee::ACTOR_EMAILS, $authenticatedEmailGuest);
|
||||
} catch (ParticipantNotFoundException $e) {
|
||||
}
|
||||
}
|
||||
$participant = $this->participantService->joinRoomAsNewGuest($this->roomService, $room, $password, $result['result'], $previousParticipant);
|
||||
$this->session->setGuestActorIdForRoom($room->getToken(), $participant->getAttendee()->getActorId());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class GuestManager {
|
|||
$event = new BeforeEmailInvitationSentEvent($room, $participant->getAttendee());
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
|
||||
$link = $this->url->linkToRouteAbsolute('spreed.Page.showCall', ['token' => $room->getToken()]);
|
||||
$link = $this->url->linkToRouteAbsolute('spreed.Page.showCall', ['token' => $room->getToken(), 'email' => $email, 'access' => $participant->getAttendee()->getAccessToken()]);
|
||||
|
||||
$message = $this->mailer->createMessage();
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ use OCP\IUserManager;
|
|||
use OCP\Security\IHasher;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use OCP\Server;
|
||||
use SensitiveParameter;
|
||||
|
||||
class Manager {
|
||||
|
||||
|
|
@ -746,7 +747,34 @@ class Manager {
|
|||
* @return Room
|
||||
* @throws RoomNotFoundException
|
||||
*/
|
||||
public function getRoomByRemoteAccess(string $token, string $actorType, string $actorId, string $remoteAccess, ?string $sessionId = null): Room {
|
||||
public function getRoomByRemoteAccess(
|
||||
string $token,
|
||||
string $actorType,
|
||||
string $actorId,
|
||||
#[SensitiveParameter]
|
||||
string $remoteAccess,
|
||||
?string $sessionId = null,
|
||||
): Room {
|
||||
return $this->getRoomByAccessToken($token, $actorType, $actorId, $remoteAccess, $sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @param string $actorType
|
||||
* @param string $actorId
|
||||
* @param string $remoteAccess
|
||||
* @param ?string $sessionId
|
||||
* @return Room
|
||||
* @throws RoomNotFoundException
|
||||
*/
|
||||
public function getRoomByAccessToken(
|
||||
string $token,
|
||||
string $actorType,
|
||||
string $actorId,
|
||||
#[SensitiveParameter]
|
||||
string $accessToken,
|
||||
?string $sessionId = null,
|
||||
): Room {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$helper = new SelectHelper();
|
||||
$helper->selectRoomsTable($query);
|
||||
|
|
@ -755,7 +783,7 @@ class Manager {
|
|||
->leftJoin('r', 'talk_attendees', 'a', $query->expr()->andX(
|
||||
$query->expr()->eq('a.actor_type', $query->createNamedParameter($actorType)),
|
||||
$query->expr()->eq('a.actor_id', $query->createNamedParameter($actorId)),
|
||||
$query->expr()->eq('a.access_token', $query->createNamedParameter($remoteAccess)),
|
||||
$query->expr()->eq('a.access_token', $query->createNamedParameter($accessToken)),
|
||||
$query->expr()->eq('a.room_id', 'r.id')
|
||||
))
|
||||
->where($query->expr()->eq('r.token', $query->createNamedParameter($token)));
|
||||
|
|
@ -946,7 +974,7 @@ class Manager {
|
|||
throw new RoomNotFoundException();
|
||||
}
|
||||
} else {
|
||||
if ($row['actor_type'] !== Attendee::ACTOR_GUESTS) {
|
||||
if ($row['actor_type'] !== Attendee::ACTOR_GUESTS && $row['actor_type'] !== Attendee::ACTOR_EMAILS) {
|
||||
throw new RoomNotFoundException();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -821,6 +821,10 @@ class ParticipantService {
|
|||
$attendee->setRoomId($room->getId());
|
||||
$attendee->setActorType(Attendee::ACTOR_EMAILS);
|
||||
$attendee->setActorId($email);
|
||||
$attendee->setAccessToken($this->secureRandom->generate(
|
||||
FederationManager::TOKEN_LENGTH,
|
||||
ISecureRandom::CHAR_HUMAN_READABLE
|
||||
));
|
||||
|
||||
if ($room->getSIPEnabled() !== Webinary::SIP_DISABLED
|
||||
&& $this->talkConfig->isSIPConfigured()) {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,14 @@ class TalkSession {
|
|||
$this->removeValue('spreed-guest-id', $token);
|
||||
}
|
||||
|
||||
public function getAuthedEmailActorIdForRoom(string $token): ?string {
|
||||
return $this->getValue('spreed-authed-email', $token);
|
||||
}
|
||||
|
||||
public function setAuthedEmailActorIdForRoom(string $token, string $actorId): void {
|
||||
$this->setValue('spreed-authed-email', $token, $actorId);
|
||||
}
|
||||
|
||||
public function getFileShareTokenForRoom(string $roomToken): ?string {
|
||||
return $this->getValue('spreed-file-share-token', $roomToken);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue