2021-09-15 15:19:48 +00:00
|
|
|
<?php
|
2023-10-11 19:55:03 +00:00
|
|
|
declare(strict_types=1);
|
|
|
|
// SPDX-FileCopyrightText: BVSC e.V. <no@example.com>
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
2021-09-15 15:19:48 +00:00
|
|
|
|
|
|
|
namespace OCA\UPschooling\Service;
|
|
|
|
|
|
|
|
use Aryess\PhpMatrixSdk\Exceptions\MatrixException;
|
2021-09-19 14:55:30 +00:00
|
|
|
use Aryess\PhpMatrixSdk\Exceptions\MatrixHttpLibException;
|
2021-09-18 17:28:10 +00:00
|
|
|
use Aryess\PhpMatrixSdk\Exceptions\MatrixRequestException;
|
2021-09-19 14:55:30 +00:00
|
|
|
use Aryess\PhpMatrixSdk\Exceptions\ValidationException;
|
2021-09-15 15:19:48 +00:00
|
|
|
use Aryess\PhpMatrixSdk\MatrixClient;
|
|
|
|
use Aryess\PhpMatrixSdk\Room;
|
2021-09-19 14:55:30 +00:00
|
|
|
use OCA\UPschooling\Db\MatrixUser;
|
|
|
|
use OCA\UPschooling\Exceptions\RoomNotFoundException;
|
2021-10-10 18:21:42 +00:00
|
|
|
use OCP\IConfig;
|
2021-09-18 17:28:10 +00:00
|
|
|
use Psr\Log\LoggerInterface;
|
2021-09-15 15:19:48 +00:00
|
|
|
|
|
|
|
class MatrixService
|
|
|
|
{
|
|
|
|
|
2021-09-19 14:55:30 +00:00
|
|
|
/** @var LoggerInterface */
|
|
|
|
private $logger;
|
2021-09-15 15:19:48 +00:00
|
|
|
|
2021-10-10 18:21:42 +00:00
|
|
|
/** @var IConfig */
|
|
|
|
private $config;
|
|
|
|
|
2021-09-19 14:55:30 +00:00
|
|
|
/** @var MatrixClient */
|
|
|
|
private $client;
|
2021-09-15 15:19:48 +00:00
|
|
|
|
2021-09-19 14:55:30 +00:00
|
|
|
/** @var string */
|
2021-10-10 18:21:42 +00:00
|
|
|
private $registrationSecret;
|
|
|
|
|
|
|
|
/** @var string Matrix server URL */
|
|
|
|
private $serverUrl;
|
|
|
|
|
|
|
|
/** @var string Matrix server part */
|
|
|
|
private $server;
|
|
|
|
|
|
|
|
/** @var string Matrix admin user */
|
|
|
|
private $superuser;
|
|
|
|
|
|
|
|
/** @var string Matrix authentication token */
|
|
|
|
private $token;
|
2021-09-15 15:19:48 +00:00
|
|
|
|
2021-09-19 14:55:30 +00:00
|
|
|
/**
|
|
|
|
* @throws MatrixRequestException
|
|
|
|
* @throws MatrixHttpLibException
|
|
|
|
* @throws ValidationException
|
|
|
|
* @throws MatrixException
|
|
|
|
*/
|
2021-10-10 18:21:42 +00:00
|
|
|
public function __construct(IConfig $config, LoggerInterface $logger)
|
2021-09-19 14:55:30 +00:00
|
|
|
{
|
|
|
|
$this->logger = $logger;
|
2021-10-10 18:21:42 +00:00
|
|
|
$this->config = $config;
|
|
|
|
$this->registrationSecret = $this->config->getSystemValueString(
|
|
|
|
"upschooling.matrix_registration_secret",
|
|
|
|
"oyYh_iEJ7Aim.iB+ye.Xk;Gl3iHFab5*8K,zv~IulT85P=c-38"
|
|
|
|
);
|
|
|
|
$this->serverUrl = $this->config->getSystemValueString(
|
|
|
|
"upschooling.matrix_server_url",
|
2022-02-26 18:40:21 +00:00
|
|
|
"http://localhost:8008"
|
2021-10-10 18:21:42 +00:00
|
|
|
);
|
|
|
|
$this->server = $this->config->getSystemValueString(
|
|
|
|
"upschooling.matrix_server",
|
|
|
|
"synapse"
|
|
|
|
);
|
|
|
|
$this->superuser = $this->config->getSystemValueString(
|
|
|
|
"upschooling.matrix_superuser",
|
|
|
|
"upschooling"
|
|
|
|
);
|
|
|
|
$this->token = $this->config->getSystemValueString("upschooling.matrix_auth_token");
|
|
|
|
if ($this->token != "") {
|
|
|
|
$this->client = new MatrixClient($this->serverUrl, $this->token);
|
|
|
|
$this->logger->debug("Using previous login as " . $this->superuser . " on server " . $this->serverUrl);
|
|
|
|
} else {
|
|
|
|
$this->client = new MatrixClient($this->serverUrl);
|
|
|
|
$token = $this->client->login($this->superuser, "secret", true);
|
|
|
|
$this->logger->debug("Logged in as " . $this->superuser . " on server " . $this->serverUrl);
|
|
|
|
$this->config->setSystemValue("upschooling.matrix_auth_token", $token);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->checkRateLimit();
|
2021-09-19 14:55:30 +00:00
|
|
|
}
|
2021-09-15 15:19:48 +00:00
|
|
|
|
2021-09-19 14:55:30 +00:00
|
|
|
/**
|
|
|
|
* @param string $roomId a room id of an existing and joined room.
|
|
|
|
* @param string $eventType a unique property identifier with reverse domain notation, e.g. com.example.property.
|
|
|
|
* @param array $content the contents as a JSON serializable array.
|
|
|
|
* @throws RoomNotFoundException
|
|
|
|
* @throws MatrixException
|
|
|
|
*/
|
|
|
|
public function setProperty(string $roomId, string $eventType, array $content)
|
|
|
|
{
|
|
|
|
$room = $this->findRoom($roomId);
|
|
|
|
$room->sendStateEvent($eventType, $content);
|
|
|
|
$this->logger->debug(
|
|
|
|
"Set property " . $eventType . " on room " . $roomId,
|
|
|
|
array("room" => $roomId, "key" => $eventType, "value" => $content)
|
|
|
|
);
|
|
|
|
}
|
2021-09-15 15:19:48 +00:00
|
|
|
|
2021-09-19 14:55:30 +00:00
|
|
|
/**
|
|
|
|
* @param string $roomId a room id of an existing and joined room.
|
|
|
|
* @param string $eventType a unique property identifier with reverse domain notation, e.g. com.example.property.
|
|
|
|
* @return array the contents of the room state.
|
|
|
|
* @throws MatrixException
|
|
|
|
* @throws RoomNotFoundException
|
|
|
|
*/
|
|
|
|
public function getProperty(string $roomId, string $eventType): array
|
|
|
|
{
|
|
|
|
$this->findRoom($roomId); // make sure the room exists/is joined
|
|
|
|
$content = $this->client->api()->getStateEvent($roomId, $eventType);
|
|
|
|
$this->logger->debug(
|
|
|
|
"Got property " . $eventType . " from room " . $roomId,
|
|
|
|
array("room" => $roomId, "key" => $eventType, "value" => $content)
|
|
|
|
);
|
|
|
|
return $content;
|
|
|
|
}
|
2021-09-15 15:19:48 +00:00
|
|
|
|
2021-09-19 14:55:30 +00:00
|
|
|
/**
|
|
|
|
* @param string $roomId a room id of an existing and joined room.
|
|
|
|
* @throws RoomNotFoundException
|
|
|
|
* @returns int the origin server timestamp of the most recent event.
|
|
|
|
*/
|
|
|
|
public function getLastEventDate(string $roomId): int
|
|
|
|
{
|
|
|
|
$room = $this->findRoom($roomId);
|
|
|
|
$events = $room->getEvents();
|
|
|
|
if (count($events) === 0) {
|
|
|
|
$this->logger->debug("Did not have any events for room " . $roomId);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
$timestamp = array_get($events[0], 'origin_server_ts', 1);
|
|
|
|
if ($timestamp === 1) {
|
|
|
|
$this->logger->debug("Could not find origin_server_ts in last event of room " . $roomId);
|
|
|
|
} else {
|
|
|
|
$this->logger->debug("Last event in room " . $roomId . " was at " . $timestamp);
|
|
|
|
}
|
|
|
|
return $timestamp;
|
|
|
|
}
|
|
|
|
}
|
2021-09-15 15:19:48 +00:00
|
|
|
|
2021-09-19 14:55:30 +00:00
|
|
|
/**
|
|
|
|
* Registers a new matrix user with the local matrix server.
|
|
|
|
*
|
|
|
|
* @throws MatrixException
|
|
|
|
* @throws MatrixHttpLibException
|
|
|
|
* @throws MatrixRequestException
|
|
|
|
* @return MatrixUser user object without id or Nextcloud user id set.
|
|
|
|
*/
|
|
|
|
public function registerNewUser(): MatrixUser
|
|
|
|
{
|
|
|
|
$nonceResponse = $this->client->api()->send(
|
|
|
|
'GET',
|
|
|
|
'/register',
|
|
|
|
null,
|
|
|
|
[],
|
|
|
|
[],
|
2021-09-21 18:22:56 +00:00
|
|
|
'/_synapse/admin/v1',
|
|
|
|
true
|
2021-09-19 14:55:30 +00:00
|
|
|
);
|
|
|
|
$randUsername = trim(str_replace(["/", "+"], ".", base64_encode(random_bytes(6))), "=");
|
|
|
|
$username = "upschooling_" . $randUsername;
|
|
|
|
$password = base64_encode(random_bytes(32));
|
|
|
|
$hmacData = $nonceResponse["nonce"] . "\x00" . $username . "\x00" . $password . "\x00notadmin";
|
|
|
|
$hmac = hash_hmac("sha1", $hmacData, $this->registrationSecret, false);
|
|
|
|
$registrationResponse = $this->client->api()->send(
|
|
|
|
'POST',
|
|
|
|
'/register',
|
|
|
|
array(
|
|
|
|
"nonce" => $nonceResponse["nonce"],
|
|
|
|
"username" => $username,
|
|
|
|
"password" => $password,
|
|
|
|
"displayname" => "UPschooling Support User " . $randUsername,
|
|
|
|
"admin" => false,
|
|
|
|
"mac" => $hmac,
|
|
|
|
),
|
|
|
|
[],
|
|
|
|
[],
|
2021-09-21 18:22:56 +00:00
|
|
|
'/_synapse/admin/v1',
|
|
|
|
true
|
2021-09-19 14:55:30 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
$matrixUser = new MatrixUser();
|
|
|
|
$matrixUser->setMatrixUser($registrationResponse["user_id"]);
|
|
|
|
$matrixUser->setMatrixToken($registrationResponse["access_token"]);
|
|
|
|
$this->logger->debug("Created a new user: " . $matrixUser->getMatrixUser());
|
|
|
|
return $matrixUser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $roomId a room id of an existing and joined room.
|
|
|
|
* @throws RoomNotFoundException
|
|
|
|
* @returns Room the room object, if found.
|
|
|
|
*/
|
|
|
|
private function findRoom(string $roomId): Room
|
|
|
|
{
|
|
|
|
foreach ($this->client->getRooms() as $room) {
|
|
|
|
if ($room->getRoomId() === $roomId) {
|
|
|
|
$this->logger->debug("Found room " . $roomId . " on matrix client");
|
|
|
|
return $room;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->logger->error("Room " . $roomId . " was not found on matrix client");
|
|
|
|
throw new RoomNotFoundException();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new room.
|
|
|
|
*
|
|
|
|
* @throws MatrixException
|
|
|
|
* @return string the room id of the newly created room.
|
|
|
|
*/
|
|
|
|
public function createRoom(): string
|
|
|
|
{
|
|
|
|
$roomId = $this->client->createRoom()->getRoomId();
|
|
|
|
$this->logger->debug("Created a new room: " . $roomId);
|
|
|
|
return $roomId;
|
|
|
|
}
|
2021-09-15 15:19:48 +00:00
|
|
|
|
2022-02-26 18:40:21 +00:00
|
|
|
/**
|
|
|
|
* @return string the public matrix server url.
|
|
|
|
*/
|
|
|
|
public function getServerUrl(): string
|
|
|
|
{
|
|
|
|
return $this->serverUrl;
|
|
|
|
}
|
|
|
|
|
2021-10-10 18:21:42 +00:00
|
|
|
private function checkRateLimit()
|
|
|
|
{
|
|
|
|
$fullSuperuserId = "@" . $this->superuser . ":" . $this->server;
|
|
|
|
$rateLimitResponse = $this->client->api()->send(
|
|
|
|
'GET',
|
|
|
|
'/users/' . rawurlencode($fullSuperuserId) . '/override_ratelimit',
|
|
|
|
null,
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
'/_synapse/admin/v1',
|
|
|
|
true
|
|
|
|
);
|
|
|
|
if (array_has($rateLimitResponse, "messages_per_second") &&
|
|
|
|
array_has($rateLimitResponse, "burst_count")) {
|
|
|
|
$this->logger->debug("Ratelimit setting found for " . $this->superuser);
|
|
|
|
} else {
|
|
|
|
$this->client->api()->send(
|
|
|
|
'POST',
|
|
|
|
'/users/' . rawurlencode($fullSuperuserId) . '/override_ratelimit',
|
|
|
|
array(
|
|
|
|
"messages_per_second" => 0,
|
|
|
|
"burst_count" => 0,
|
|
|
|
),
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
'/_synapse/admin/v1',
|
|
|
|
true
|
|
|
|
);
|
|
|
|
$this->logger->debug("No ratelimiting for " . $this->superuser);
|
|
|
|
}
|
|
|
|
}
|
2022-04-23 14:55:05 +00:00
|
|
|
|
|
|
|
public function inviteUser(string $roomId, string $matrixUserId)
|
|
|
|
{
|
|
|
|
$room = $this->client->joinRoom($roomId);
|
|
|
|
$room->inviteUser($matrixUserId);
|
|
|
|
}
|
2021-09-15 15:19:48 +00:00
|
|
|
}
|