Skip to content

Commit

Permalink
Implement CloudFederationProvider
Browse files Browse the repository at this point in the history
Signed-off-by: Gary Kim <[email protected]>
  • Loading branch information
gary-kim committed Jun 18, 2021
1 parent 8f3e737 commit cb64a33
Show file tree
Hide file tree
Showing 7 changed files with 369 additions and 18 deletions.
21 changes: 21 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,27 @@
],
],

/**
* Remote
*/

[
'name' => 'Remote#acceptShare',
'url' => 'api/{apiVersion}/remote/pending/{id}',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1',
],
],
[
'name' => 'Remote#rejectShare',
'url' => 'api/{apiVersion}/remote/pending/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v1',
],
],

/**
* PublicShareAuth
*/
Expand Down
52 changes: 52 additions & 0 deletions lib/Controller/FederationController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2021, Gary Kim <[email protected]>
*
* @author Gary Kim <[email protected]>
*
* @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\Controller;

use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;

class FederationController extends OCSController {
public function __construct(string $appName, IRequest $request) {
parent::__construct($appName, $request);
}

/**
* @param string $id
* @return DataResponse
*/
public function acceptShare(string $id): DataResponse {

}

/**
* @param string $id
* @return DataResponse
*/
public function rejectShare(string $id): DataResponse {

}
}
144 changes: 144 additions & 0 deletions lib/Federation/CloudFederationProviderTalk.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2021, Gary Kim <[email protected]>
*
* @author Gary Kim <[email protected]>
*
* @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\Federation;

use OC\AppFramework\Http;
use OC\HintException;
use OCA\FederatedFileSharing\AddressHandler;
use OCA\Talk\AppInfo\Application;
use OCA\Talk\Federation\FederationManager;
use OCP\DB\Exception as DBException;
use OCP\Federation\Exceptions\ProviderCouldNotAddShareException;
use OCP\Federation\ICloudFederationProvider;
use OCP\Federation\ICloudFederationShare;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Notification\IManager as INotificationManager;
use OCP\Share\IShare;

class CloudFederationProviderTalk implements ICloudFederationProvider {

/** @var IUserManager */
private IUserManager $userManager;

/** @var AddressHandler */
private AddressHandler $addressHandler;

/** @var FederationManager */
private FederationManager $federationManager;

/** @var INotificationManager */
private INotificationManager $notificationManager;

/** @var IURLGenerator */
private IURLGenerator $urlGenerator;

public function __construct(
IUserManager $userManager,
AddressHandler $addressHandler,
FederationManager $federationManager,
INotificationManager $notificationManager,
IURLGenerator $urlGenerator
) {
$this->userManager = $userManager;
$this->addressHandler = $addressHandler;
$this->federationManager = $federationManager;
$this->notificationManager = $notificationManager;
$this->urlGenerator = $urlGenerator;
}

/**
* @inheritDoc
*/
public function getShareType(): string {
return 'talk-room';
}

/**
* @inheritDoc
* @throws HintException
* @throws DBException
*/
public function shareReceived(ICloudFederationShare $share): string {
if (!$this->federationManager->isEnabled()) {
throw new ProviderCouldNotAddShareException('Server does not support talk federation', '', Http::STATUS_SERVICE_UNAVAILABLE);
}
if ($share->getShareType() !== 'user') {
throw new ProviderCouldNotAddShareException('support for sharing with non-groups not implemented yet', '', Http::STATUS_NOT_IMPLEMENTED);
}

$shareSecret = $share->getShareSecret();
$shareWith = $share->getShareWith();
$roomToken = $share->getProviderId();
[, $remote] = $this->addressHandler->splitUserRemote($share->getOwner());

if ($remote && $shareSecret && $shareWith) {
$shareWith = $this->userManager->get($shareWith);
if ($shareWith === null) {
throw new ProviderCouldNotAddShareException('User does not exist', '',Http::STATUS_BAD_REQUEST);
}

$shareId = $this->federationManager->addRemoteRoom($shareWith, $roomToken, $remote, $shareSecret);
return (string) $shareId;
}
throw new ProviderCouldNotAddShareException('required request data not found', '', Http::STATUS_BAD_REQUEST);
// TODO: Finish implementing shareReceived() method.
}

/**
* @inheritDoc
*/
public function notificationReceived($notificationType, $providerId, array $notification) {

// TODO: Implement notificationReceived() method.
}

private function notifyAboutNewShare(string $shareWith, string $shareId, string $ownerFederatedId, string $sharedByFederatedId, string $name) {
$notification = $this->notificationManager->createNotification();
$notification->setApp(Application::APP_ID)
->setUser($shareWith)
->setDateTime(new \DateTime());

$declineAction = $notification->createAction();
$declineAction->setLabel('decline')
->setLink($this->urlGenerator->linkToOCSRouteAbsolute('spreed.Remote.rejectShare', ['id' => $shareId]), 'DELETE');
$notification->addAction($declineAction);

$acceptAction = $notification->createAction();
$acceptAction->setLabel('accept')
->setLink($this->urlGenerator->linkToOCSRouteAbsolute('spreed.Remote.acceptShare', ['id' => $shareId]), 'POST');
$notification->addAction($acceptAction);

$this->notificationManager->notify($notification);
}

/**
* @inheritDoc
*/
public function getSupportedShareTypes() {
return ['user'];
}
}
137 changes: 137 additions & 0 deletions lib/Federation/FederationManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2021, Gary Kim <[email protected]>
*
* @author Gary Kim <[email protected]>
*
* @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\Federation;

use OCA\Talk\Exceptions\UnauthorizedException;
use OCA\Talk\Room;
use OCP\DB\Exception as DBException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IUser;

class FederationManager {
/** @var IDBConnection */
private $db;
/** @var IConfig */
private $config;

public function __construct (
IDBConnection $db,
IConfig $config
) {
$this->db = $db;
$this->config = $config;
}

/**
* Determine if Talk federation is enabled on this instance
* @return bool
*/
public function isEnabled(): bool {
// TODO: Set to true once implementation is complete
return $this->config->getSystemValueBool('talk_federation_enabled', false);
}

/**
* @param IUser $user
* @param string $roomToken
* @param string $remoteUrl
* @param string $sharedSecret
* @return int share id for this specific remote room share
* @throws DBException
*/
public function addRemoteRoom(IUser $user, string $roomToken, string $remoteUrl, string $sharedSecret): int {
$qb = $this->db->getQueryBuilder();

$query = $qb->insert('talk_rooms_external')
->values([
'user_id' => $qb->createNamedParameter($user->getUID(), IQueryBuilder::PARAM_STR),
'room_id' => $qb->createNamedParameter($roomToken, IQueryBuilder::PARAM_STR),
'remote_url' => $qb->createNamedParameter($remoteUrl, IQueryBuilder::PARAM_STR),
'shareToken' => $qb->createNamedParameter($sharedSecret, IQueryBuilder::PARAM_STR),
])
->executeQuery();

$row = $query->fetch();
return (int)($row['id']);
}

/**
* @throws DBException
* @throws UnauthorizedException
*/
public function acceptRemoteRoomShare(IUser $user, int $shareId) {
$share = this->$this->getShare($shareId);
if ($share['user_id'] !== $user->getUID()) {
throw new UnauthorizedException();
}

$qb = $this->db->getQueryBuilder();

$qb->update('talk_rooms_external')
->set('accepted', true)
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($shareId, IQueryBuilder::PARAM_INT))
)
->executeQuery();
}

/**
* @throws DBException
* @throws UnauthorizedException
*/
public function rejectRemoteRoomShare(IUser $user, int $shareId) {
$share = this->$this->getShare($shareId);
if ($share['user_id'] !== $user->getUID()) {
throw new UnauthorizedException();
}

$qb = $this->db->getQueryBuilder();

$qb->delete('talk_rooms_external')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($shareId, IQueryBuilder::PARAM_INT))
)
->executeQuery();
}

/**
* @throws DBException
*/
public function getShare(int $shareId): array {
$qb = $this->db->getQueryBuilder();

$query = $qb->select('*')
->from('talk_rooms_external')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($shareId, IQueryBuilder::PARAM_INT))
)
->executeQuery();

return $query->fetch();
}
}
Loading

0 comments on commit cb64a33

Please sign in to comment.