Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
</background-jobs>

<commands>
<command>OCA\GlobalSiteSelector\Command\GlobalScaleDiscovery</command>
<command>OCA\GlobalSiteSelector\Command\UsersUpdate</command>
</commands>
</info>
8 changes: 8 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
return [
'ocs' => [
['name' => 'Slave#createAppToken', 'url' => '/v1/createapptoken', 'verb' => 'GET'],
['name' => 'Slave#discovery', 'url' => '/discovery', 'verb' => 'GET'],
['name' => 'Slave#sharedFile', 'url' => '/sharedfile', 'verb' => 'GET'],
],
'routes' => [
[
Expand All @@ -21,5 +23,11 @@
'url' => '/autologout',
'verb' => 'GET'
],
[
'name' => 'Slave#findFile',
'url' => '/gf/{token}/{fileId}',
'verb' => 'GET',
'root' => '',
],
],
];
10 changes: 5 additions & 5 deletions lib/BackgroundJobs/UpdateLookupServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,23 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\GlobalSiteSelector\BackgroundJobs;

use OCA\GlobalSiteSelector\GlobalSiteSelector;
use OCA\GlobalSiteSelector\Service\GlobalScaleService;
use OCA\GlobalSiteSelector\Slave;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJob;
use OCP\BackgroundJob\TimedJob;
use OCP\IConfig;

class UpdateLookupServer extends TimedJob {


public function __construct(
ITimeFactory $time,
IConfig $config,
private GlobalSiteSelector $globalSiteSelector,
private Slave $slave,
private readonly GlobalScaleService $globalScaleService,
private readonly GlobalSiteSelector $globalSiteSelector,
private readonly Slave $slave,
) {
parent::__construct($time);

Expand All @@ -36,6 +35,7 @@ protected function run($argument) {
return;
}

$this->globalScaleService->refreshTokenFromGlobalScale();
$this->slave->batchUpdate();
}
}
45 changes: 45 additions & 0 deletions lib/Command/GlobalScaleDiscovery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\GlobalSiteSelector\Command;

use OC\Core\Command\Base;
use OCA\GlobalSiteSelector\AppInfo\Application;
use OCA\GlobalSiteSelector\ConfigLexicon;
use OCA\GlobalSiteSelector\Service\GlobalScaleService;
use OCP\IAppConfig;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class GlobalScaleDiscovery extends Base {
public function __construct(
private readonly IAppConfig $appConfig,
private readonly GlobalScaleService $globalScaleService,
) {
parent::__construct();
}

protected function configure(): void {
parent::configure();
$this->setName('globalsiteselector:discovery')
->addOption('current', '', InputOption::VALUE_NONE, 'display current data')
->setDescription('run a discovery request over Global Scale to get details about each instances');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
if ($input->getOption('current')) {
$output->writeln(json_encode($this->appConfig->getValueArray(Application::APP_ID, ConfigLexicon::GS_TOKENS), JSON_PRETTY_PRINT));
Comment thread
ArtificialOwl marked this conversation as resolved.
return self::SUCCESS;
}

// currently, the only available data is a unique token that helps identify each instance
$this->globalScaleService->refreshTokenFromGlobalScale();
return self::SUCCESS;
}
}
41 changes: 41 additions & 0 deletions lib/ConfigLexicon.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\GlobalSiteSelector;

use OCP\Config\Lexicon\Entry;
use OCP\Config\Lexicon\ILexicon;
use OCP\Config\Lexicon\Strictness;
use OCP\Config\ValueType;

class ConfigLexicon implements ILexicon {
public const GS_TOKENS = 'globalScaleTokens';
public const LOCAL_TOKEN = 'localToken';

public function getStrictness(): Strictness {
return Strictness::IGNORE;
}

/**
* @inheritDoc
*/
public function getAppConfigs(): array {
return [
new Entry(key: self::GS_TOKENS, type: ValueType::ARRAY, defaultRaw: [], definition: 'list of token+host to navigate through GlobalScale', lazy: true),
new Entry(key: self::LOCAL_TOKEN, type: ValueType::STRING, defaultRaw: '', definition: 'local token to id instance within GlobalScale', lazy: true),
];
}

/**
* @inheritDoc
*/
public function getUserConfigs(): array {
return [
];
}
}
84 changes: 72 additions & 12 deletions lib/Controller/SlaveController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@

use OC\Authentication\Token\IToken;
use OCA\GlobalSiteSelector\AppInfo\Application;
use OCA\GlobalSiteSelector\Exceptions\LocalFederatedShareException;
use OCA\GlobalSiteSelector\Exceptions\MasterUrlException;
use OCA\GlobalSiteSelector\Exceptions\SharedFileException;
use OCA\GlobalSiteSelector\GlobalSiteSelector;
use OCA\GlobalSiteSelector\Model\LocalFile;
use OCA\GlobalSiteSelector\Service\GlobalScaleService;
use OCA\GlobalSiteSelector\Service\GlobalShareService;
use OCA\GlobalSiteSelector\Service\SlaveService;
use OCA\GlobalSiteSelector\Slave;
use OCA\GlobalSiteSelector\TokenHandler;
Expand All @@ -20,6 +25,9 @@
use OCA\GlobalSiteSelector\Vendor\Firebase\JWT\JWT;
use OCA\GlobalSiteSelector\Vendor\Firebase\JWT\Key;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\OCSController;
Expand All @@ -41,25 +49,77 @@
* @package OCA\GlobalSiteSelector\Controller
*/
class SlaveController extends OCSController {

public function __construct(
$appName,
IRequest $request,
private GlobalSiteSelector $gss,
private IUserSession $userSession,
private IURLGenerator $urlGenerator,
private ICrypto $crypto,
private TokenHandler $tokenHandler,
private IUserManager $userManager,
private UserBackend $userBackend,
private ISession $session,
private SlaveService $slaveService,
private IConfig $config,
private LoggerInterface $logger,
private readonly GlobalSiteSelector $gss,
private readonly IUserSession $userSession,
private readonly IURLGenerator $urlGenerator,
private readonly ICrypto $crypto,
private readonly TokenHandler $tokenHandler,
private readonly IUserManager $userManager,
private readonly UserBackend $userBackend,
private readonly ISession $session,
private readonly SlaveService $slaveService,
private readonly GlobalScaleService $globalScaleService,
private readonly GlobalShareService $globalShareService,
private readonly IConfig $config,
private readonly LoggerInterface $logger,
) {
parent::__construct($appName, $request);
}

#[PublicPage]
#[NoCSRFRequired]
public function discovery(): DataResponse {
// public data, reachable from outside.
return new DataResponse(['token' => $this->globalScaleService->getLocalToken()]);
}

/**
* return sharing details about a file.
* request must contain encoded jwt.
*/
#[NoAdminRequired]
#[NoCSRFRequired]
Comment thread
ArtificialOwl marked this conversation as resolved.
public function findFile(string $token, int $fileId): RedirectResponse {
return new RedirectResponse($this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $this->globalShareService->getNewFileId($token, $fileId) ?? 1]));
}

/**
* return sharing details about a file.
* request must contain encoded jwt.
*/
#[PublicPage]
#[NoCSRFRequired]
public function sharedFile(string $jwt): DataResponse {
$key = $this->gss->getJwtKey();
$decoded = (array)JWT::decode($jwt, new Key($key, Application::JWT_ALGORITHM));
// JWT store data as stdClass, not array
$decoded = json_decode(json_encode($decoded), true);
Comment thread
ArtificialOwl marked this conversation as resolved.

$this->logger->debug('decoded request', ['data' => $decoded]);

$fileId = (int)($decoded['fileId'] ?? 0);
$shareId = (int)($decoded['shareId'] ?? 0);
$instance = $decoded['instance'] ?? '';

$target = new LocalFile();
$target->import($decoded['target'] ?? []);

try {
// the file is local and returns shares related to it
return new DataResponse($this->globalShareService->getSharedFiles($fileId, $shareId, $instance, $target));
} catch (SharedFileException $e) {
// file not found
return new DataResponse(['message' => $e->getMessage()], Http::STATUS_NOT_FOUND);
} catch (LocalFederatedShareException $e) {
// the file is not local and returns the shared folder and the path to the file
return new DataResponse($e->getFederatedShare(), Http::STATUS_MOVED_PERMANENTLY);
}
}


/**
* @PublicPage
* @NoCSRFRequired
Expand Down
Loading
Loading