circles/lib/Service/RemoteDownstreamService.php
Maxence Lange 7c0e514d04 sync more data on GS
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2021-05-03 11:33:48 -01:00

296 lines
7.5 KiB
PHP

<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @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\Circles\Service;
use daita\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
use daita\MySmallPhpTools\Traits\TAsync;
use Exception;
use OCA\Circles\Db\CircleRequest;
use OCA\Circles\Db\MemberRequest;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedEventDSyncException;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class RemoteDownstreamService
*
* @package OCA\Circles\Service
*/
class RemoteDownstreamService {
use TNC22Logger;
use TAsync;
/** @var CircleRequest */
private $circleRequest;
/** @var MemberRequest */
private $memberRequest;
/** @var FederatedEventService */
private $federatedEventService;
/** @var ConfigService */
private $configService;
/**
* RemoteDownstreamService constructor.
*
* @param CircleRequest $circleRequest
* @param MemberRequest $memberRequest
* @param FederatedEventService $federatedEventService
* @param ConfigService $configService
*/
public function __construct(
CircleRequest $circleRequest,
MemberRequest $memberRequest,
FederatedEventService $federatedEventService,
ConfigService $configService
) {
$this->setup('app', 'circles');
$this->circleRequest = $circleRequest;
$this->memberRequest = $memberRequest;
$this->federatedEventService = $federatedEventService;
$this->configService = $configService;
}
//
//
// /**
// * @param GSEvent $event
// *
// * @throws CircleDoesNotExistException
// * @throws ConfigNoCircleAvailableException
// * @throws GSKeyException
// * @throws GlobalScaleDSyncException
// * @throws GlobalScaleEventException
// */
// public function statusEvent(GSEvent $event) {
// $this->globalScaleService->checkEvent($event);
//
// $gs = $this->globalScaleService->getGlobalScaleEvent($event);
// $gs->verify($event, false);
// $gs->manage($event);
// }
//
/**
* @param FederatedEvent $event
*
* @throws FederatedEventDSyncException
* @throws FederatedEventException
* @throws InitiatorNotConfirmedException
* @throws OwnerNotFoundException
* @throws FederatedItemException
*/
public function requestedEvent(FederatedEvent $event): void {
$item = $this->federatedEventService->getFederatedItem($event, true);
if (!$this->configService->isLocalInstance($event->getCircle()->getInstance())) {
throw new FederatedEventDSyncException('Circle is not from this instance');
}
if ($event->isLimitedToInstanceWithMember()) {
$instances = $this->memberRequest->getMemberInstances($event->getCircle()->getSingleId());
if (!in_array($event->getIncomingOrigin(), $instances)) {
throw new FederatedEventException('Instance have no members in this Circle');
}
}
$this->federatedEventService->confirmInitiator($event, false);
$this->confirmContent($event, true);
$item->verify($event);
if ($event->isDataRequestOnly()) {
return;
}
$filter = [];
if (!$event->isAsync()) {
$item->manage($event);
// we dont filter anymore as some data might be change during the remote verify()
// $filter[] = $event->getIncomingOrigin();
}
$this->federatedEventService->initBroadcast($event, $filter);
}
/**
* @param FederatedEvent $event
*/
public function incomingEvent(FederatedEvent $event): void {
try {
$gs = $this->federatedEventService->getFederatedItem($event, false);
$this->confirmOriginEvent($event);
$this->confirmContent($event, false);
$gs->manage($event);
} catch (Exception $e) {
$this->e($e, ['event' => $event]);
}
}
/**
* @param FederatedEvent $event
* @param bool $full
*
* @throws FederatedEventDSyncException
* @throws OwnerNotFoundException
*/
private function confirmContent(FederatedEvent $event, bool $full = true): void {
$this->confirmCircle($event);
$this->confirmMember($event, $full);
}
/**
* @param FederatedEvent $event
*
* @throws OwnerNotFoundException
* @throws FederatedEventDSyncException
*/
private function confirmCircle(FederatedEvent $event): void {
if ($event->canBypass(FederatedEvent::BYPASS_LOCALCIRCLECHECK) || $this->verifyCircle($event)) {
return;
}
throw new FederatedEventDSyncException('Could not verify Circle');
}
/**
* @param FederatedEvent $event
*
* @return bool
* @throws OwnerNotFoundException
* @throws RequestBuilderException
*/
private function verifyCircle(FederatedEvent $event): bool {
$circle = $event->getCircle();
try {
$localCircle = $this->circleRequest->getCircle($circle->getSingleId(), null, null, 0);
} catch (CircleNotFoundException $e) {
return false;
}
if (!$localCircle->compareWith($circle)) {
$this->debug(
'failed to compare Circles',
['localCircle' => json_encode($localCircle), 'circle' => json_encode($circle)]
);
return false;
}
return true;
}
/**
* @param FederatedEvent $event
* @param bool $full
* // TODO: Check IFederatedItemMember*
*
* @throws FederatedEventDSyncException
*/
private function confirmMember(FederatedEvent $event, bool $full): void {
if ($event->canBypass(FederatedEvent::BYPASS_LOCALMEMBERCHECK) || !$event->hasMember()
|| $this->verifyMember($event, $full)) {
return;
}
throw new FederatedEventDSyncException('Could not verify Member');
}
/**
* @param FederatedEvent $event
* @param bool $full
*
* @return bool
*/
private function verifyMember(FederatedEvent $event, bool $full): bool {
$this->debug('verifyMember()', ['event' => $event]);
$member = $event->getMember();
try {
$localMember = $this->memberRequest->getMember($member->getId());
} catch (MemberNotFoundException $e) {
$this->debug('Member not found', ['member' => $member]);
return false;
}
if (!$localMember->compareWith($member, $full)) {
$this->debug(
'failed to compare Members',
['localMember' => json_encode($localMember), 'member' => json_encode($member)]
);
return false;
}
return true;
}
/**
* @param FederatedEvent $event
*
* @throws FederatedEventException
* @throws OwnerNotFoundException
*/
private function confirmOriginEvent(FederatedEvent $event): void {
if ($event->getIncomingOrigin() !== $event->getCircle()->getInstance()) {
$this->debug('invalid origin', ['event' => $event]);
throw new FederatedEventException('invalid origin');
}
}
}