Skip to content

Commit

Permalink
signatories
Browse files Browse the repository at this point in the history
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
  • Loading branch information
ArtificialOwl committed Jul 2, 2024
1 parent ee7dda5 commit f45a2cb
Show file tree
Hide file tree
Showing 38 changed files with 1,795 additions and 63 deletions.
15 changes: 14 additions & 1 deletion apps/cloud_federation_api/lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,24 @@

namespace OCA\CloudFederationAPI;

use OC\OCM\OCMSignatoryManager;
use OC\Security\Signature\Model\Signatory;
use OCP\Capabilities\ICapability;
use OCP\IURLGenerator;
use OCP\OCM\Exceptions\OCMArgumentException;
use OCP\OCM\IOCMProvider;
use OCP\Security\Signature\Exceptions\SignatoryException;
use OCP\Security\Signature\Model\ISignatory;
use Psr\Log\LoggerInterface;

class Capabilities implements ICapability {
public const API_VERSION = '1.0-proposal1';
public const API_VERSION = '1.1'; // informative, real version.

public function __construct(
private IURLGenerator $urlGenerator,
private IOCMProvider $provider,
private readonly OCMSignatoryManager $ocmSignatoryManager,
private readonly LoggerInterface $logger,
) {
}

Expand Down Expand Up @@ -60,6 +67,12 @@ public function getCapabilities() {

$this->provider->addResourceType($resource);

try {
$this->provider->setSignatory($this->ocmSignatoryManager->getLocalSignatory());
} catch (SignatoryException $e) {
$this->logger->warning('cannot generate local signatory', ['exception' => $e]);
}

return ['ocm' => $this->provider->jsonSerialize()];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
namespace OCA\CloudFederationAPI\Controller;

use OC\OCM\OCMSignatoryManager;
use OCA\CloudFederationAPI\Config;
use OCA\CloudFederationAPI\ResponseDefinitions;
use OCP\AppFramework\Controller;
Expand All @@ -19,10 +20,16 @@
use OCP\Federation\ICloudFederationFactory;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\Federation\ICloudIdManager;
use OCP\IAppConfig;
use OCP\IGroupManager;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Security\Signature\Exceptions\IncomingRequestException;
use OCP\Security\Signature\Exceptions\SignatoryNotFoundException;
use OCP\Security\Signature\Exceptions\SignatureException;
use OCP\Security\Signature\ISignatureManager;
use OCP\Security\Signature\Model\IIncomingSignedRequest;
use OCP\Share\Exceptions\ShareNotFound;
use Psr\Log\LoggerInterface;

Expand All @@ -46,8 +53,11 @@ public function __construct(
private IURLGenerator $urlGenerator,
private ICloudFederationProviderManager $cloudFederationProviderManager,
private Config $config,
private readonly IAppConfig $appConfig,
private ICloudFederationFactory $factory,
private ICloudIdManager $cloudIdManager
private ICloudIdManager $cloudIdManager,
private readonly ISignatureManager $signatureManager,
private readonly OCMSignatoryManager $signatoryManager,
) {
parent::__construct($appName, $request);
}
Expand Down Expand Up @@ -77,6 +87,19 @@ public function __construct(
* 501: Share type or the resource type is not supported
*/
public function addShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $protocol, $shareType, $resourceType) {
try {
$this->logger->warning('___1');

$signedRequest = $this->getSignedRequest();
if ($signedRequest !== null && $signedRequest->getOrigin()) {
\OC::$server->getLogger()->log(3,' ### SIGNED REQUEST -- ' . $signedRequest->getBody());

Check notice

Code scanning / Psalm

DeprecatedMethod Note

The method OC\Server::getLogger has been marked as deprecated

Check notice

Code scanning / Psalm

DeprecatedMethod Note

The method OCP\ILogger::log has been marked as deprecated
}
} catch (IncomingRequestException $e) {
$this->logger->warning('incoming request exception', ['exception' => $e]);
return new JSONResponse(['message' => $e->getMessage(), 'validationErrors' => []],
Http::STATUS_BAD_REQUEST);
}

// check if all required parameters are set
if ($shareWith === null ||
$name === null ||
Expand All @@ -99,6 +122,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
);
}


$supportedShareTypes = $this->config->getSupportedShareTypes($resourceType);
if (!in_array($shareType, $supportedShareTypes)) {
return new JSONResponse(
Expand Down Expand Up @@ -279,4 +303,27 @@ private function mapUid($uid) {

return $uid;
}


/**
* @return IIncomingSignedRequest|null null if remote is not yet compatible with signed request and we ignore this fact
* @throws IncomingRequestException
*/
private function getSignedRequest(): ?IIncomingSignedRequest {
try {
return $this->signatureManager->getIncomingSignedRequest($this->signatoryManager);
} catch (SignatoryNotFoundException $e) {
// remote does not support signed request.
// currently we still accept unsigned request until lazy appconfig
// core.enforce_signed_ocm_request is set to true (default: false)
if ($this->appConfig->getValueBool('enforce_signed_ocm_request', false, lazy: true)) {

Check failure

Code scanning / Psalm

InvalidArgument Error

Argument 2 of OCP\IAppConfig::getValueBool cannot be false, string value expected

Check failure on line 319 in apps/cloud_federation_api/lib/Controller/RequestHandlerController.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/cloud_federation_api/lib/Controller/RequestHandlerController.php:319:69: InvalidArgument: Argument 2 of OCP\IAppConfig::getValueBool cannot be false, string value expected (see https://psalm.dev/004)
$this->logger->notice('ignored unsigned request', ['exception' => $e]);
throw new IncomingRequestException('Unsigned request');
}
} catch (SignatureException $e) {
$this->logger->notice('wrongly signed request', ['exception' => $e]);
throw new IncomingRequestException('Invalid signature');
}
return null;
}
}
5 changes: 4 additions & 1 deletion apps/files_sharing/lib/External/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ class Cache extends \OC\Files\Cache\Cache {
public function __construct($storage, ICloudId $cloudId) {
$this->cloudId = $cloudId;
$this->storage = $storage;
[, $remote] = explode('://', $cloudId->getRemote(), 2);
$remote = $cloudId->getRemote();
if (str_contains($remote, '://')) {
[, $remote] = explode('://', $cloudId->getRemote(), 2);

Check notice

Code scanning / Psalm

PossiblyUndefinedArrayOffset Note

Possibly undefined array key
}
$this->remote = $remote;
$this->remoteUser = $cloudId->getUser();
parent::__construct($storage);
Expand Down
11 changes: 6 additions & 5 deletions core/Controller/OCMController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\Capabilities\ICapability;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IRequest;
use OCP\Server;
Expand All @@ -29,7 +30,7 @@
class OCMController extends Controller {
public function __construct(
IRequest $request,
private IConfig $config,
private readonly IAppConfig $appConfig,
private LoggerInterface $logger
) {
parent::__construct('core', $request);
Expand All @@ -52,10 +53,10 @@ public function __construct(
public function discovery(): DataResponse {
try {
$cap = Server::get(
$this->config->getAppValue(
'core',
'ocm_providers',
'\OCA\CloudFederationAPI\Capabilities'
$this->appConfig->getValueString(
'core', 'ocm_providers',
\OCA\CloudFederationAPI\Capabilities::class,
lazy: true
)
);

Expand Down
105 changes: 105 additions & 0 deletions core/Migrations/Version30000Date20240101084401.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Core\Migrations;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

/**
*
*/
class Version30000Date20240101084401 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

if (!$schema->hasTable('sec_signatory')) {
$table = $schema->createTable('sec_signatory');
$table->addColumn('id', Types::BIGINT, [
'notnull' => true,
'length' => 64,
'autoincrement' => true,
'unsigned' => true,
]);
// key_id_sum will store a hash version of the key_id, more appropriate for search/index
$table->addColumn('key_id_sum', Types::STRING, [
'notnull' => true,
'length' => 127,
]);
$table->addColumn('key_id', Types::STRING, [
'notnull' => true,
'length' => 512
]);
// host/provider_id/account will help generate a unique entry, not based on key_id
// this way, a spoofed instance cannot publish a new key_id for same host+provider_id
// account will be used only to stored multiple keys for the same provider_id/host
$table->addColumn('host', Types::STRING, [
'notnull' => true,
'length' => 127
]);
$table->addColumn('provider_id', Types::STRING, [
'notnull' => true,
'length' => 31,
]);
$table->addColumn('account', Types::STRING, [
'notnull' => false,
'length' => 127,
'default' => ''
]);
$table->addColumn('public_key', Types::TEXT, [
'notnull' => true,
'default' => ''
]);
$table->addColumn('metadata', Types::TEXT, [
'notnull' => true,
'default' => '[]'
]);
// type+status are informative about the trustability of remote instance and status of the signatory
$table->addColumn('type', Types::SMALLINT, [
'notnull' => true,
'length' => 2,
'default' => 9
]);
$table->addColumn('status', Types::SMALLINT, [
'notnull' => true,
'length' => 2,
'default' => 0,
]);
$table->addColumn('creation', Types::INTEGER, [
'notnull' => false,
'length' => 4,
'default' => 0,
'unsigned' => true,
]);
$table->addColumn('last_updated', Types::INTEGER, [
'notnull' => false,
'length' => 4,
'default' => 0,
'unsigned' => true,
]);

$table->setPrimaryKey(['id'], 'sec_sig_id');
$table->addUniqueIndex(['provider_id', 'host', 'account'], 'sec_sig_unic');
$table->addIndex(['key_id_sum', 'provider_id'], 'sec_sig_key');

return $schema;
}

return null;
}
}
2 changes: 1 addition & 1 deletion lib/private/AppFramework/Http/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ public function getCookie(string $key) {
*
* @throws \LogicException
*/
protected function getContent() {
public function getContent() {
// If the content can't be parsed into an array then return a stream resource.
if ($this->isPutStreamContent()) {
if ($this->content === false) {
Expand Down
25 changes: 17 additions & 8 deletions lib/private/Federation/CloudFederationProviderManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace OC\Federation;

use OC\AppFramework\Http;
use OC\OCM\OCMSignatoryManager;
use OCP\App\IAppManager;
use OCP\Federation\Exceptions\ProviderDoesNotExistsException;
use OCP\Federation\ICloudFederationNotification;
Expand All @@ -21,6 +22,7 @@
use OCP\IConfig;
use OCP\OCM\Exceptions\OCMProviderException;
use OCP\OCM\IOCMDiscoveryService;
use OCP\Security\Signature\ISignatureManager;
use Psr\Log\LoggerInterface;

/**
Expand All @@ -40,7 +42,9 @@ public function __construct(
private IClientService $httpClientService,
private ICloudIdManager $cloudIdManager,
private IOCMDiscoveryService $discoveryService,
private LoggerInterface $logger
private readonly ISignatureManager $signatureManager,
private readonly OCMSignatoryManager $signatoryManager,
private LoggerInterface $logger,
) {
}

Expand Down Expand Up @@ -106,13 +110,18 @@ public function sendShare(ICloudFederationShare $share) {

$client = $this->httpClientService->newClient();
try {
$response = $client->post($ocmProvider->getEndPoint() . '/shares', [
'body' => json_encode($share->getShare()),
'headers' => ['content-type' => 'application/json'],
'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false),
'timeout' => 10,
'connect_timeout' => 10,
]);
$uri = $ocmProvider->getEndPoint() . '/shares';
$signedPayload = $this->signatureManager->signOutgoingRequestIClientPayload(
$this->signatoryManager,
[
'body' => json_encode($share->getShare()),
'headers' => ['content-type' => 'application/json'],
'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false),
'timeout' => 10,
'connect_timeout' => 10,
],
'post', $uri);
$response = $client->post($uri, $signedPayload);

if ($response->getStatusCode() === Http::STATUS_CREATED) {
$result = json_decode($response->getBody(), true);
Expand Down
Loading

0 comments on commit f45a2cb

Please sign in to comment.