use NCU\Security\Signature\Exceptions\SignatoryNotFoundException;
use NCU\Security\Signature\Exceptions\SignatureException;
use NCU\Security\Signature\Exceptions\SignatureNotFoundException;
+use NCU\Security\Signature\IIncomingSignedRequest;
use NCU\Security\Signature\ISignatureManager;
-use NCU\Security\Signature\Model\IIncomingSignedRequest;
use OC\OCM\OCMSignatoryManager;
use OCA\CloudFederationAPI\Config;
use OCA\CloudFederationAPI\ResponseDefinitions;
if ($signedRequest === null) {
$instance = $this->getHostFromFederationId($value);
try {
- $this->signatureManager->searchSignatory($instance);
+ $this->signatureManager->getSignatory($instance);
throw new IncomingRequestException('instance is supposed to sign its request');
} catch (SignatoryNotFoundException) {
return;
$instance = $this->getHostFromFederationId($entry);
if ($signedRequest === null) {
try {
- $this->signatureManager->searchSignatory($instance);
+ $this->signatureManager->getSignatory($instance);
throw new IncomingRequestException('instance is supposed to sign its request');
} catch (SignatoryNotFoundException) {
return;
'NCU\\Config\\Exceptions\\UnknownKeyException' => $baseDir . '/lib/unstable/Config/Exceptions/UnknownKeyException.php',
'NCU\\Config\\IUserConfig' => $baseDir . '/lib/unstable/Config/IUserConfig.php',
'NCU\\Config\\ValueType' => $baseDir . '/lib/unstable/Config/ValueType.php',
+ 'NCU\\Security\\Signature\\Enum\\SignatoryStatus' => $baseDir . '/lib/unstable/Security/Signature/Enum/SignatoryStatus.php',
+ 'NCU\\Security\\Signature\\Enum\\SignatoryType' => $baseDir . '/lib/unstable/Security/Signature/Enum/SignatoryType.php',
+ 'NCU\\Security\\Signature\\Enum\\SignatureAlgorithm' => $baseDir . '/lib/unstable/Security/Signature/Enum/SignatureAlgorithm.php',
'NCU\\Security\\Signature\\Exceptions\\IdentityNotFoundException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/IdentityNotFoundException.php',
'NCU\\Security\\Signature\\Exceptions\\IncomingRequestException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/IncomingRequestException.php',
'NCU\\Security\\Signature\\Exceptions\\InvalidKeyOriginException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/InvalidKeyOriginException.php',
'NCU\\Security\\Signature\\Exceptions\\SignatureElementNotFoundException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/SignatureElementNotFoundException.php',
'NCU\\Security\\Signature\\Exceptions\\SignatureException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/SignatureException.php',
'NCU\\Security\\Signature\\Exceptions\\SignatureNotFoundException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/SignatureNotFoundException.php',
+ 'NCU\\Security\\Signature\\IIncomingSignedRequest' => $baseDir . '/lib/unstable/Security/Signature/IIncomingSignedRequest.php',
+ 'NCU\\Security\\Signature\\IOutgoingSignedRequest' => $baseDir . '/lib/unstable/Security/Signature/IOutgoingSignedRequest.php',
'NCU\\Security\\Signature\\ISignatoryManager' => $baseDir . '/lib/unstable/Security/Signature/ISignatoryManager.php',
'NCU\\Security\\Signature\\ISignatureManager' => $baseDir . '/lib/unstable/Security/Signature/ISignatureManager.php',
- 'NCU\\Security\\Signature\\Model\\IIncomingSignedRequest' => $baseDir . '/lib/unstable/Security/Signature/Model/IIncomingSignedRequest.php',
- 'NCU\\Security\\Signature\\Model\\IOutgoingSignedRequest' => $baseDir . '/lib/unstable/Security/Signature/Model/IOutgoingSignedRequest.php',
- 'NCU\\Security\\Signature\\Model\\ISignatory' => $baseDir . '/lib/unstable/Security/Signature/Model/ISignatory.php',
- 'NCU\\Security\\Signature\\Model\\ISignedRequest' => $baseDir . '/lib/unstable/Security/Signature/Model/ISignedRequest.php',
- 'NCU\\Security\\Signature\\Model\\SignatoryStatus' => $baseDir . '/lib/unstable/Security/Signature/Model/SignatoryStatus.php',
- 'NCU\\Security\\Signature\\Model\\SignatoryType' => $baseDir . '/lib/unstable/Security/Signature/Model/SignatoryType.php',
- 'NCU\\Security\\Signature\\SignatureAlgorithm' => $baseDir . '/lib/unstable/Security/Signature/SignatureAlgorithm.php',
+ 'NCU\\Security\\Signature\\ISignedRequest' => $baseDir . '/lib/unstable/Security/Signature/ISignedRequest.php',
+ 'NCU\\Security\\Signature\\Model\\Signatory' => $baseDir . '/lib/unstable/Security/Signature/Model/Signatory.php',
'OCP\\Accounts\\IAccount' => $baseDir . '/lib/public/Accounts/IAccount.php',
'OCP\\Accounts\\IAccountManager' => $baseDir . '/lib/public/Accounts/IAccountManager.php',
'OCP\\Accounts\\IAccountProperty' => $baseDir . '/lib/public/Accounts/IAccountProperty.php',
'OC\\Security\\RateLimiting\\Limiter' => $baseDir . '/lib/private/Security/RateLimiting/Limiter.php',
'OC\\Security\\RemoteHostValidator' => $baseDir . '/lib/private/Security/RemoteHostValidator.php',
'OC\\Security\\SecureRandom' => $baseDir . '/lib/private/Security/SecureRandom.php',
+ 'OC\\Security\\Signature\\Db\\SignatoryMapper' => $baseDir . '/lib/private/Security/Signature/Db/SignatoryMapper.php',
'OC\\Security\\Signature\\Model\\IncomingSignedRequest' => $baseDir . '/lib/private/Security/Signature/Model/IncomingSignedRequest.php',
'OC\\Security\\Signature\\Model\\OutgoingSignedRequest' => $baseDir . '/lib/private/Security/Signature/Model/OutgoingSignedRequest.php',
- 'OC\\Security\\Signature\\Model\\Signatory' => $baseDir . '/lib/private/Security/Signature/Model/Signatory.php',
'OC\\Security\\Signature\\Model\\SignedRequest' => $baseDir . '/lib/private/Security/Signature/Model/SignedRequest.php',
'OC\\Security\\Signature\\SignatureManager' => $baseDir . '/lib/private/Security/Signature/SignatureManager.php',
'OC\\Security\\TrustedDomainHelper' => $baseDir . '/lib/private/Security/TrustedDomainHelper.php',
'NCU\\Config\\Exceptions\\UnknownKeyException' => __DIR__ . '/../../..' . '/lib/unstable/Config/Exceptions/UnknownKeyException.php',
'NCU\\Config\\IUserConfig' => __DIR__ . '/../../..' . '/lib/unstable/Config/IUserConfig.php',
'NCU\\Config\\ValueType' => __DIR__ . '/../../..' . '/lib/unstable/Config/ValueType.php',
+ 'NCU\\Security\\Signature\\Enum\\SignatoryStatus' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Enum/SignatoryStatus.php',
+ 'NCU\\Security\\Signature\\Enum\\SignatoryType' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Enum/SignatoryType.php',
+ 'NCU\\Security\\Signature\\Enum\\SignatureAlgorithm' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Enum/SignatureAlgorithm.php',
'NCU\\Security\\Signature\\Exceptions\\IdentityNotFoundException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/IdentityNotFoundException.php',
'NCU\\Security\\Signature\\Exceptions\\IncomingRequestException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/IncomingRequestException.php',
'NCU\\Security\\Signature\\Exceptions\\InvalidKeyOriginException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/InvalidKeyOriginException.php',
'NCU\\Security\\Signature\\Exceptions\\SignatureElementNotFoundException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/SignatureElementNotFoundException.php',
'NCU\\Security\\Signature\\Exceptions\\SignatureException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/SignatureException.php',
'NCU\\Security\\Signature\\Exceptions\\SignatureNotFoundException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/SignatureNotFoundException.php',
+ 'NCU\\Security\\Signature\\IIncomingSignedRequest' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/IIncomingSignedRequest.php',
+ 'NCU\\Security\\Signature\\IOutgoingSignedRequest' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/IOutgoingSignedRequest.php',
'NCU\\Security\\Signature\\ISignatoryManager' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/ISignatoryManager.php',
'NCU\\Security\\Signature\\ISignatureManager' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/ISignatureManager.php',
- 'NCU\\Security\\Signature\\Model\\IIncomingSignedRequest' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Model/IIncomingSignedRequest.php',
- 'NCU\\Security\\Signature\\Model\\IOutgoingSignedRequest' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Model/IOutgoingSignedRequest.php',
- 'NCU\\Security\\Signature\\Model\\ISignatory' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Model/ISignatory.php',
- 'NCU\\Security\\Signature\\Model\\ISignedRequest' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Model/ISignedRequest.php',
- 'NCU\\Security\\Signature\\Model\\SignatoryStatus' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Model/SignatoryStatus.php',
- 'NCU\\Security\\Signature\\Model\\SignatoryType' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Model/SignatoryType.php',
- 'NCU\\Security\\Signature\\SignatureAlgorithm' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/SignatureAlgorithm.php',
+ 'NCU\\Security\\Signature\\ISignedRequest' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/ISignedRequest.php',
+ 'NCU\\Security\\Signature\\Model\\Signatory' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Model/Signatory.php',
'OCP\\Accounts\\IAccount' => __DIR__ . '/../../..' . '/lib/public/Accounts/IAccount.php',
'OCP\\Accounts\\IAccountManager' => __DIR__ . '/../../..' . '/lib/public/Accounts/IAccountManager.php',
'OCP\\Accounts\\IAccountProperty' => __DIR__ . '/../../..' . '/lib/public/Accounts/IAccountProperty.php',
'OC\\Security\\RateLimiting\\Limiter' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Limiter.php',
'OC\\Security\\RemoteHostValidator' => __DIR__ . '/../../..' . '/lib/private/Security/RemoteHostValidator.php',
'OC\\Security\\SecureRandom' => __DIR__ . '/../../..' . '/lib/private/Security/SecureRandom.php',
+ 'OC\\Security\\Signature\\Db\\SignatoryMapper' => __DIR__ . '/../../..' . '/lib/private/Security/Signature/Db/SignatoryMapper.php',
'OC\\Security\\Signature\\Model\\IncomingSignedRequest' => __DIR__ . '/../../..' . '/lib/private/Security/Signature/Model/IncomingSignedRequest.php',
'OC\\Security\\Signature\\Model\\OutgoingSignedRequest' => __DIR__ . '/../../..' . '/lib/private/Security/Signature/Model/OutgoingSignedRequest.php',
- 'OC\\Security\\Signature\\Model\\Signatory' => __DIR__ . '/../../..' . '/lib/private/Security/Signature/Model/Signatory.php',
'OC\\Security\\Signature\\Model\\SignedRequest' => __DIR__ . '/../../..' . '/lib/private/Security/Signature/Model/SignedRequest.php',
'OC\\Security\\Signature\\SignatureManager' => __DIR__ . '/../../..' . '/lib/private/Security/Signature/SignatureManager.php',
'OC\\Security\\TrustedDomainHelper' => __DIR__ . '/../../..' . '/lib/private/Security/TrustedDomainHelper.php',
namespace OC\OCM\Model;
-use NCU\Security\Signature\Model\ISignatory;
-use OC\Security\Signature\Model\Signatory;
+use NCU\Security\Signature\Model\Signatory;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\OCM\Events\ResourceTypeRegisterEvent;
use OCP\OCM\Exceptions\OCMArgumentException;
private string $endPoint = '';
/** @var IOCMResource[] */
private array $resourceTypes = [];
- private ?ISignatory $signatory = null;
+ private ?Signatory $signatory = null;
private bool $emittedEvent = false;
public function __construct(
throw new OCMArgumentException('resource not found');
}
- public function setSignatory(ISignatory $signatory): void {
+ public function setSignatory(Signatory $signatory): void {
$this->signatory = $signatory;
}
- public function getSignatory(): ?ISignatory {
+ public function getSignatory(): ?Signatory {
return $this->signatory;
}
* enabled: bool,
* apiVersion: '1.0-proposal1',
* endPoint: string,
- * publicKey: ISignatory|null,
+ * publicKey: Signatory|null,
* resourceTypes: list<array{
* name: string,
* shareTypes: list<string>,
namespace OC\OCM;
+use NCU\Security\Signature\Enum\SignatoryType;
use NCU\Security\Signature\Exceptions\IdentityNotFoundException;
use NCU\Security\Signature\ISignatoryManager;
use NCU\Security\Signature\ISignatureManager;
-use NCU\Security\Signature\Model\ISignatory;
-use NCU\Security\Signature\Model\SignatoryType;
+use NCU\Security\Signature\Model\Signatory;
use OC\Security\IdentityProof\Manager;
-use OC\Security\Signature\Model\Signatory;
use OCP\IAppConfig;
use OCP\IURLGenerator;
use OCP\OCM\Exceptions\OCMProviderException;
/**
* @inheritDoc
*
- * @return ISignatory
+ * @return Signatory
* @throws IdentityNotFoundException
* @since 31.0.0
*/
- public function getLocalSignatory(): ISignatory {
+ public function getLocalSignatory(): Signatory {
/**
* TODO: manage multiple identity (external, internal, ...) to allow a limitation
* based on the requested interface (ie. only accept shares from globalscale)
*
* @param string $remote
*
- * @return ISignatory|null must be NULL if no signatory is found
+ * @return Signatory|null must be NULL if no signatory is found
* @since 31.0.0
*/
- public function getRemoteSignatory(string $remote): ?ISignatory {
+ public function getRemoteSignatory(string $remote): ?Signatory {
try {
return $this->getRemoteSignatoryFromHost($remote);
} catch (OCMProviderException $e) {
*
* @param string $host
*
- * @return ISignatory|null
+ * @return Signatory|null
* @throws OCMProviderException on fail to discover ocm services
* @since 31.0.0
*/
- public function getRemoteSignatoryFromHost(string $host): ?ISignatory {
+ public function getRemoteSignatoryFromHost(string $host): ?Signatory {
$ocmProvider = $this->ocmDiscoveryService->discover($host, true);
$signatory = $ocmProvider->getSignatory();
public function hasAppKey(string $app, string $name): bool {
$id = $this->generateAppKeyId($app, $name);
try {
- $this->appData->getFolder($id);
- return true;
+ $folder = $this->appData->getFolder($id);
+ return ($folder->fileExists('public') && $folder->fileExists('private'));
} catch (NotFoundException) {
return false;
}
public function deleteAppKey(string $app, string $name): bool {
try {
$folder = $this->appData->getFolder($this->generateAppKeyId($app, $name));
+ $folder->delete();
+ return true;
} catch (NotFoundException) {
return false;
}
- $folder->delete();
- return true;
}
private function generateAppKeyId(string $app, string $name): string {
--- /dev/null
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\Security\Signature\Db;
+
+use NCU\Security\Signature\Exceptions\SignatoryNotFoundException;
+use NCU\Security\Signature\Model\Signatory;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Db\QBMapper;
+use OCP\DB\Exception;
+use OCP\IDBConnection;
+
+/**
+ * @template-extends QBMapper<Signatory>
+ */
+class SignatoryMapper extends QBMapper {
+ public const TABLE = 'sec_signatory';
+
+ public function __construct(
+ IDBConnection $db,
+ ) {
+ parent::__construct($db, self::TABLE, Signatory::class);
+ }
+
+ /**
+ *
+ */
+ public function getByHost(string $host, string $account = ''): Signatory {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('*')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('host', $qb->createNamedParameter($host)))
+ ->andWhere($qb->expr()->eq('account', $qb->createNamedParameter($account)));
+
+ try {
+ return $this->findEntity($qb);
+ } catch (DoesNotExistException) {
+ throw new SignatoryNotFoundException('no signatory found');
+ }
+ }
+
+ /**
+ */
+ public function getByKeyId(string $keyId): Signatory {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('*')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('key_id_sum', $qb->createNamedParameter($this->hashKeyId($keyId))));
+
+ try {
+ return $this->findEntity($qb);
+ } catch (DoesNotExistException) {
+ throw new SignatoryNotFoundException('no signatory found');
+ }
+ }
+
+ /**
+ * @param string $keyId
+ *
+ * @return int
+ * @throws Exception
+ */
+ public function deleteByKeyId(string $keyId): int {
+ $qb = $this->db->getQueryBuilder();
+ $qb->delete($this->getTableName())
+ ->where($qb->expr()->eq('key_id_sum', $qb->createNamedParameter($this->hashKeyId($keyId))));
+
+ return $qb->executeStatement();
+ }
+
+ /**
+ * @param Signatory $signatory
+ *
+ * @return int
+ */
+ public function updateMetadata(Signatory $signatory): int {
+ $qb = $this->db->getQueryBuilder();
+ $qb->update($this->getTableName())
+ ->set('metadata', $qb->createNamedParameter(json_encode($signatory->getMetadata())))
+ ->set('last_updated', $qb->createNamedParameter(time()));
+ $qb->where($qb->expr()->eq('key_id_sum', $qb->createNamedParameter($this->hashKeyId($signatory->getKeyId()))));
+
+ return $qb->executeStatement();
+ }
+
+ /**
+ * @param Signatory $signator
+ */
+ public function updatePublicKey(Signatory $signatory): int {
+ $qb = $this->db->getQueryBuilder();
+ $qb->update($this->getTableName())
+ ->set('signatory', $qb->createNamedParameter($signatory->getPublicKey()))
+ ->set('last_updated', $qb->createNamedParameter(time()));
+ $qb->where($qb->expr()->eq('key_id_sum', $qb->createNamedParameter($this->hashKeyId($signatory->getKeyId()))));
+
+ return $qb->executeStatement();
+ }
+
+ /**
+ * returns a hash version for keyId for better index in the database
+ *
+ * @param string $keyId
+ *
+ * @return string
+ */
+ private function hashKeyId(string $keyId): string {
+ return hash('sha256', $keyId);
+ }
+}
use NCU\Security\Signature\Exceptions\SignatoryException;
use NCU\Security\Signature\Exceptions\SignatureElementNotFoundException;
use NCU\Security\Signature\Exceptions\SignatureNotFoundException;
+use NCU\Security\Signature\IIncomingSignedRequest;
use NCU\Security\Signature\ISignatureManager;
-use NCU\Security\Signature\Model\IIncomingSignedRequest;
-use NCU\Security\Signature\Model\ISignatory;
+use NCU\Security\Signature\Model\Signatory;
use OC\Security\Signature\SignatureManager;
use OCP\IRequest;
/**
* @inheritDoc
*
- * @param ISignatory $signatory
+ * @param Signatory $signatory
*
* @return $this
* @throws IdentityNotFoundException
* @throws SignatoryException
* @since 31.0.0
*/
- public function setSignatory(ISignatory $signatory): self {
+ public function setSignatory(Signatory $signatory): self {
$identity = \OCP\Server::get(ISignatureManager::class)->extractIdentityFromUri($signatory->getKeyId());
if ($identity !== $this->getOrigin()) {
throw new SignatoryException('keyId from provider is different from the one from signed request');
namespace OC\Security\Signature\Model;
use JsonSerializable;
+use NCU\Security\Signature\Enum\SignatureAlgorithm;
+use NCU\Security\Signature\IOutgoingSignedRequest;
use NCU\Security\Signature\ISignatoryManager;
use NCU\Security\Signature\ISignatureManager;
-use NCU\Security\Signature\Model\IOutgoingSignedRequest;
-use NCU\Security\Signature\SignatureAlgorithm;
use OC\Security\Signature\SignatureManager;
/**
$signing = $headerList = [];
foreach ($headers as $element => $value) {
- $value = $headers[$element];
$signing[] = $element . ': ' . $value;
$headerList[] = $element;
if ($element !== '(request-target)') {
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-/**
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-namespace OC\Security\Signature\Model;
-
-use JsonSerializable;
-use NCU\Security\Signature\Model\ISignatory;
-use NCU\Security\Signature\Model\SignatoryStatus;
-use NCU\Security\Signature\Model\SignatoryType;
-
-class Signatory implements ISignatory, JsonSerializable {
- private string $providerId = '';
- private string $account = '';
- private SignatoryType $type = SignatoryType::STATIC;
- private SignatoryStatus $status = SignatoryStatus::SYNCED;
- private array $metadata = [];
- private int $creation = 0;
- private int $lastUpdated = 0;
-
- public function __construct(
- private string $keyId,
- private readonly string $publicKey,
- private readonly string $privateKey = '',
- readonly bool $local = false,
- ) {
- // if set as local (for current instance), we apply some filters.
- if ($local) {
- // to avoid conflict with duplicate key pairs (ie generated url from the occ command), we enforce https as prefix
- if (str_starts_with($keyId, 'http://')) {
- $keyId = 'https://' . substr($keyId, 7);
- }
-
- // removing /index.php from generated url
- $path = parse_url($keyId, PHP_URL_PATH);
- if (str_starts_with($path, '/index.php/')) {
- $pos = strpos($keyId, '/index.php');
- if ($pos !== false) {
- $keyId = substr_replace($keyId, '', $pos, 10);
- }
- }
-
- $this->keyId = $keyId;
- }
- }
-
- public function setProviderId(string $providerId): self {
- $this->providerId = $providerId;
- return $this;
- }
-
- public function getProviderId(): string {
- return $this->providerId;
- }
-
- public function setAccount(string $account): self {
- $this->account = $account;
- return $this;
- }
-
- public function getAccount(): string {
- return $this->account;
- }
-
- public function getKeyId(): string {
- return $this->keyId;
- }
-
- public function getPublicKey(): string {
- return $this->publicKey;
- }
-
- public function getPrivateKey(): string {
- return $this->privateKey;
- }
-
- public function setMetadata(array $metadata): self {
- $this->metadata = $metadata;
- return $this;
- }
-
- public function getMetadata(): array {
- return $this->metadata;
- }
-
- public function setMetaValue(string $key, string|int $value): self {
- $this->metadata[$key] = $value;
- return $this;
- }
-
- public function setType(SignatoryType $type): self {
- $this->type = $type;
- return $this;
- }
- public function getType(): SignatoryType {
- return $this->type;
- }
-
- public function setStatus(SignatoryStatus $status): self {
- $this->status = $status;
- return $this;
- }
-
- public function getStatus(): SignatoryStatus {
- return $this->status;
- }
-
- public function setCreation(int $creation): self {
- $this->creation = $creation;
- return $this;
- }
-
- public function getCreation(): int {
- return $this->creation;
- }
-
- public function setLastUpdated(int $lastUpdated): self {
- $this->lastUpdated = $lastUpdated;
- return $this;
- }
-
- public function getLastUpdated(): int {
- return $this->lastUpdated;
- }
-
- public function importFromDatabase(array $row): self {
- $this->setProviderId($row['provider_id'] ?? '')
- ->setAccount($row['account'] ?? '')
- ->setMetadata(json_decode($row['metadata'], true) ?? [])
- ->setType(SignatoryType::from($row['type'] ?? 9))
- ->setStatus(SignatoryStatus::from($row['status'] ?? 1))
- ->setCreation($row['creation'] ?? 0)
- ->setLastUpdated($row['last_updated'] ?? 0);
- return $this;
- }
-
- public function jsonSerialize(): array {
- return [
- 'keyId' => $this->getKeyId(),
- 'publicKeyPem' => $this->getPublicKey()
- ];
- }
-}
use JsonSerializable;
use NCU\Security\Signature\Exceptions\SignatoryNotFoundException;
use NCU\Security\Signature\Exceptions\SignatureElementNotFoundException;
-use NCU\Security\Signature\Model\ISignatory;
-use NCU\Security\Signature\Model\ISignedRequest;
+use NCU\Security\Signature\ISignedRequest;
+use NCU\Security\Signature\Model\Signatory;
/**
* @inheritDoc
private array $signatureElements = [];
private string $clearSignature = '';
private string $signedSignature = '';
- private ?ISignatory $signatory = null;
+ private ?Signatory $signatory = null;
public function __construct(
private readonly string $body,
/**
* @inheritDoc
*
- * @param ISignatory $signatory
+ * @param Signatory $signatory
* @return ISignedRequest
* @since 31.0.0
*/
- public function setSignatory(ISignatory $signatory): ISignedRequest {
+ public function setSignatory(Signatory $signatory): ISignedRequest {
$this->signatory = $signatory;
return $this;
}
/**
* @inheritDoc
*
- * @return ISignatory
+ * @return Signatory
* @throws SignatoryNotFoundException
* @since 31.0.0
*/
- public function getSignatory(): ISignatory {
+ public function getSignatory(): Signatory {
if ($this->signatory === null) {
throw new SignatoryNotFoundException();
}
namespace OC\Security\Signature;
+use NCU\Security\Signature\Enum\SignatoryType;
+use NCU\Security\Signature\Enum\SignatureAlgorithm;
use NCU\Security\Signature\Exceptions\IdentityNotFoundException;
use NCU\Security\Signature\Exceptions\IncomingRequestException;
use NCU\Security\Signature\Exceptions\InvalidKeyOriginException;
use NCU\Security\Signature\Exceptions\SignatureElementNotFoundException;
use NCU\Security\Signature\Exceptions\SignatureException;
use NCU\Security\Signature\Exceptions\SignatureNotFoundException;
+use NCU\Security\Signature\IIncomingSignedRequest;
+use NCU\Security\Signature\IOutgoingSignedRequest;
use NCU\Security\Signature\ISignatoryManager;
use NCU\Security\Signature\ISignatureManager;
-use NCU\Security\Signature\Model\IIncomingSignedRequest;
-use NCU\Security\Signature\Model\IOutgoingSignedRequest;
-use NCU\Security\Signature\Model\ISignatory;
-use NCU\Security\Signature\Model\SignatoryType;
-use NCU\Security\Signature\SignatureAlgorithm;
+use NCU\Security\Signature\Model\Signatory;
+use OC\Security\Signature\Db\SignatoryMapper;
use OC\Security\Signature\Model\IncomingSignedRequest;
use OC\Security\Signature\Model\OutgoingSignedRequest;
-use OC\Security\Signature\Model\Signatory;
use OCP\DB\Exception as DBException;
use OCP\IAppConfig;
-use OCP\IDBConnection;
use OCP\IRequest;
use Psr\Log\LoggerInterface;
public const DATE_HEADER = 'D, d M Y H:i:s T';
public const DATE_TTL = 300;
public const SIGNATORY_TTL = 86400 * 3;
- public const TABLE_SIGNATORIES = 'sec_signatory';
public const BODY_MAXSIZE = 50000; // max size of the payload of the request
public const APPCONFIG_IDENTITY = 'security.signature.identity';
public function __construct(
private readonly IRequest $request,
- private readonly IDBConnection $connection,
+ private readonly SignatoryMapper $mapper,
private readonly IAppConfig $appConfig,
private readonly LoggerInterface $logger,
) {
* @param string $account linked account, should be used when multiple signature can exist for the same
* host
*
- * @return ISignatory
+ * @return Signatory
* @throws SignatoryNotFoundException if entry does not exist in local database
* @since 31.0.0
*/
- public function searchSignatory(string $host, string $account = ''): ISignatory {
- $qb = $this->connection->getQueryBuilder();
- $qb->select(
- 'id', 'provider_id', 'host', 'account', 'key_id', 'key_id_sum', 'public_key', 'metadata', 'type',
- 'status', 'creation', 'last_updated'
- );
- $qb->from(self::TABLE_SIGNATORIES);
- $qb->where($qb->expr()->eq('host', $qb->createNamedParameter($host)));
- $qb->andWhere($qb->expr()->eq('account', $qb->createNamedParameter($account)));
-
- $result = $qb->executeQuery();
- $row = $result->fetch();
- $result->closeCursor();
-
- if (!$row) {
- throw new SignatoryNotFoundException('no signatory found');
- }
-
- $signature = new Signatory($row['key_id'], $row['public_key']);
-
- return $signature->importFromDatabase($row);
+ public function getSignatory(string $host, string $account = ''): Signatory {
+ return $this->mapper->getByHost($host, $account);
}
* @param ISignatoryManager $signatoryManager
* @param IIncomingSignedRequest $signedRequest
*
- * @return ISignatory
+ * @return Signatory
* @throws InvalidKeyOriginException
* @throws SignatoryNotFoundException
* @see ISignatoryManager::getRemoteSignatory
private function getSaneRemoteSignatory(
ISignatoryManager $signatoryManager,
IIncomingSignedRequest $signedRequest,
- ): ISignatory {
+ ): Signatory {
$signatory = $signatoryManager->getRemoteSignatory($signedRequest->getOrigin());
if ($signatory === null) {
throw new SignatoryNotFoundException('empty result from getRemoteSignatory');
} catch (SignatureElementNotFoundException) {
throw new InvalidKeyOriginException('missing keyId');
}
+ $signatory->setProviderId($signatoryManager->getProviderId());
- return $signatory->setProviderId($signatoryManager->getProviderId());
+ return $signatory;
}
/**
* @param IIncomingSignedRequest $signedRequest
*
- * @return void
* @throws SignatureException
* @throws SignatoryNotFoundException
*/
/**
* @param string $keyId
*
- * @return ISignatory
+ * @return Signatory
* @throws SignatoryNotFoundException
*/
- private function getStoredSignatory(string $keyId): ISignatory {
- $qb = $this->connection->getQueryBuilder();
- $qb->select(
- 'id', 'provider_id', 'host', 'account', 'key_id', 'key_id_sum', 'public_key', 'metadata', 'type',
- 'status', 'creation', 'last_updated'
- );
- $qb->from(self::TABLE_SIGNATORIES);
- $qb->where($qb->expr()->eq('key_id_sum', $qb->createNamedParameter($this->hashKeyId($keyId))));
-
- $result = $qb->executeQuery();
- $row = $result->fetch();
- $result->closeCursor();
-
- if (!$row) {
- throw new SignatoryNotFoundException('no signatory found in local');
- }
-
- $signature = new Signatory($row['key_id'], $row['public_key']);
- $signature->importFromDatabase($row);
-
- return $signature;
+ private function getStoredSignatory(string $keyId): Signatory {
+ return $this->mapper->getByKeyId($keyId);
}
/**
- * @param ISignatory $signatory
+ * @param Signatory $signatory
*/
- private function storeSignatory(ISignatory $signatory): void {
+ private function storeSignatory(Signatory $signatory): void {
try {
$this->insertSignatory($signatory);
} catch (DBException $e) {
}
/**
- * @param ISignatory $signatory
+ * @param Signatory $signatory
* @throws DBException
*/
- private function insertSignatory(ISignatory $signatory): void {
- $qb = $this->connection->getQueryBuilder();
- $qb->insert(self::TABLE_SIGNATORIES)
- ->setValue('provider_id', $qb->createNamedParameter($signatory->getProviderId()))
- ->setValue('host', $qb->createNamedParameter($this->extractIdentityFromUri($signatory->getKeyId())))
- ->setValue('account', $qb->createNamedParameter($signatory->getAccount()))
- ->setValue('key_id', $qb->createNamedParameter($signatory->getKeyId()))
- ->setValue('key_id_sum', $qb->createNamedParameter($this->hashKeyId($signatory->getKeyId())))
- ->setValue('public_key', $qb->createNamedParameter($signatory->getPublicKey()))
- ->setValue('metadata', $qb->createNamedParameter(json_encode($signatory->getMetadata())))
- ->setValue('type', $qb->createNamedParameter($signatory->getType()->value))
- ->setValue('status', $qb->createNamedParameter($signatory->getStatus()->value))
- ->setValue('creation', $qb->createNamedParameter(time()))
- ->setValue('last_updated', $qb->createNamedParameter(time()));
-
- $qb->executeStatement();
+ private function insertSignatory(Signatory $signatory): void {
+ $this->mapper->insert($signatory);
}
/**
- * @param ISignatory $signatory
+ * @param Signatory $signatory
*
* @throws SignatoryNotFoundException
* @throws SignatoryConflictException
*/
- private function updateKnownSignatory(ISignatory $signatory): void {
+ private function updateKnownSignatory(Signatory $signatory): void {
$knownSignatory = $this->getStoredSignatory($signatory->getKeyId());
switch ($signatory->getType()) {
case SignatoryType::FORGIVABLE:
/**
* This is called when a remote signatory does not exist anymore
*
- * @param ISignatory|null $knownSignatory NULL is not known
+ * @param Signatory|null $knownSignatory NULL is not known
*
* @throws SignatoryConflictException
* @throws SignatoryNotFoundException
*/
- private function manageDeprecatedSignatory(?ISignatory $knownSignatory): void {
+ private function manageDeprecatedSignatory(?Signatory $knownSignatory): void {
switch ($knownSignatory?->getType()) {
case null: // unknown in local database
case SignatoryType::FORGIVABLE: // who cares ?
}
- private function updateSignatoryPublicKey(ISignatory $signatory): void {
- $qb = $this->connection->getQueryBuilder();
- $qb->update(self::TABLE_SIGNATORIES)
- ->set('signatory', $qb->createNamedParameter($signatory->getPublicKey()))
- ->set('last_updated', $qb->createNamedParameter(time()));
-
- $qb->where(
- $qb->expr()->eq('key_id_sum', $qb->createNamedParameter($this->hashKeyId($signatory->getKeyId())))
- );
- $qb->executeStatement();
+ private function updateSignatoryPublicKey(Signatory $signatory): void {
+ $this->mapper->updatePublicKey($signatory);
}
- private function updateSignatoryMetadata(ISignatory $signatory): void {
- $qb = $this->connection->getQueryBuilder();
- $qb->update(self::TABLE_SIGNATORIES)
- ->set('metadata', $qb->createNamedParameter(json_encode($signatory->getMetadata())))
- ->set('last_updated', $qb->createNamedParameter(time()));
-
- $qb->where(
- $qb->expr()->eq('key_id_sum', $qb->createNamedParameter($this->hashKeyId($signatory->getKeyId())))
- );
- $qb->executeStatement();
+ private function updateSignatoryMetadata(Signatory $signatory): void {
+ $this->mapper->updateMetadata($signatory);
}
private function deleteSignatory(string $keyId): void {
- $qb = $this->connection->getQueryBuilder();
- $qb->delete(self::TABLE_SIGNATORIES)
- ->where($qb->expr()->eq('key_id_sum', $qb->createNamedParameter($this->hashKeyId($keyId))));
- $qb->executeStatement();
- }
-
- private function hashKeyId(string $keyId): string {
- return hash('sha256', $keyId);
+ $this->mapper->deleteByKeyId($keyId);
}
}
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCP\OCM;
use JsonSerializable;
-use NCU\Security\Signature\Model\ISignatory;
+use NCU\Security\Signature\Model\Signatory;
use OCP\OCM\Exceptions\OCMArgumentException;
use OCP\OCM\Exceptions\OCMProviderException;
/**
* store signatory (public/private key pair) to sign outgoing/incoming request
*
- * @param ISignatory $signatory
+ * @param Signatory $signatory
* @since 31.0.0
*/
- public function setSignatory(ISignatory $signatory): void;
+ public function setSignatory(Signatory $signatory): void;
/**
* signatory (public/private key pair) used to sign outgoing/incoming request
*
- * @return ISignatory|null returns null if no ISignatory available
+ * @return Signatory|null returns null if no Signatory available
* @since 31.0.0
*/
- public function getSignatory(): ?ISignatory;
+ public function getSignatory(): ?Signatory;
/**
* import data from an array
* enabled: bool,
* apiVersion: '1.0-proposal1',
* endPoint: string,
- * publicKey: ISignatory|null,
+ * publicKey: Signatory|null,
* resourceTypes: list<array{
* name: string,
* shareTypes: list<string>,
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace NCU\Security\Signature\Enum;
+
+/**
+ * current status of signatory. is it trustable or not ?
+ *
+ * - SYNCED = the remote instance is trustable.
+ * - BROKEN = the remote instance does not use the same key pairs than previously
+ *
+ * @experimental 31.0.0
+ * @since 31.0.0
+ */
+enum SignatoryStatus: int {
+ /** @since 31.0.0 */
+ case SYNCED = 1;
+ /** @since 31.0.0 */
+ case BROKEN = 9;
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace NCU\Security\Signature\Enum;
+
+/**
+ * type of link between local and remote instance
+ *
+ * - FORGIVABLE = the keypair can be deleted and refreshed anytime; silently
+ * - REFRESHABLE = the keypair can be refreshed but a notice will be generated
+ * - TRUSTED = any changes of keypair will require human interaction, warning will be issued
+ * - STATIC = error will be issued on conflict, assume keypair cannot be reset.
+ *
+ * @experimental 31.0.0
+ * @since 31.0.0
+ */
+enum SignatoryType: int {
+ /** @since 31.0.0 */
+ case FORGIVABLE = 1; // no notice on refresh
+ /** @since 31.0.0 */
+ case REFRESHABLE = 4; // notice on refresh
+ /** @since 31.0.0 */
+ case TRUSTED = 8; // warning on refresh
+ /** @since 31.0.0 */
+ case STATIC = 9; // error on refresh
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace NCU\Security\Signature\Enum;
+
+/**
+ * list of available algorithm when signing payload
+ *
+ * @experimental 31.0.0
+ * @since 31.0.0
+ */
+enum SignatureAlgorithm: string {
+ /** @since 31.0.0 */
+ case SHA256 = 'sha256';
+ /** @since 31.0.0 */
+ case SHA512 = 'sha512';
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace NCU\Security\Signature;
+
+use NCU\Security\Signature\Exceptions\SignatureElementNotFoundException;
+use OCP\IRequest;
+
+/**
+ * model wrapping an actual incoming request, adding details about the signature and the
+ * authenticity of the origin of the request.
+ *
+ * @see ISignatureManager for details on signature
+ * @experimental 31.0.0
+ * @since 31.0.0
+ */
+interface IIncomingSignedRequest extends ISignedRequest {
+ /**
+ * returns the base IRequest
+ *
+ * @return IRequest
+ * @since 31.0.0
+ */
+ public function getRequest(): IRequest;
+
+ /**
+ * set the hostname at the source of the request,
+ * based on the keyId defined in the signature header.
+ *
+ * @param string $origin
+ * @return IIncomingSignedRequest
+ * @since 31.0.0
+ */
+ public function setOrigin(string $origin): IIncomingSignedRequest;
+
+ /**
+ * get the hostname at the source of the base request.
+ * based on the keyId defined in the signature header.
+ *
+ * @return string
+ * @since 31.0.0
+ */
+ public function getOrigin(): string;
+
+ /**
+ * returns the keyId extracted from the signature headers.
+ * keyId is a mandatory entry in the headers of a signed request.
+ *
+ * @return string
+ * @throws SignatureElementNotFoundException
+ * @since 31.0.0
+ */
+ public function getKeyId(): string;
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace NCU\Security\Signature;
+
+use NCU\Security\Signature\Enum\SignatureAlgorithm;
+
+/**
+ * extends ISignedRequest to add info requested at the generation of the signature
+ *
+ * @see ISignatureManager for details on signature
+ * @experimental 31.0.0
+ * @since 31.0.0
+ */
+interface IOutgoingSignedRequest extends ISignedRequest {
+ /**
+ * set the host of the recipient of the request.
+ *
+ * @param string $host
+ * @return IOutgoingSignedRequest
+ * @since 31.0.0
+ */
+ public function setHost(string $host): IOutgoingSignedRequest;
+
+ /**
+ * get the host of the recipient of the request.
+ * - on incoming request, this is the local hostname of current instance.
+ * - on outgoing request, this is the remote instance.
+ *
+ * @return string
+ * @since 31.0.0
+ */
+ public function getHost(): string;
+
+ /**
+ * add a key/value pair to the headers of the request
+ *
+ * @param string $key
+ * @param string|int|float $value
+ *
+ * @return IOutgoingSignedRequest
+ * @since 31.0.0
+ */
+ public function addHeader(string $key, string|int|float $value): IOutgoingSignedRequest;
+
+ /**
+ * returns list of headers value that will be added to the base request
+ *
+ * @return array
+ * @since 31.0.0
+ */
+ public function getHeaders(): array;
+
+ /**
+ * set the ordered list of used headers in the Signature
+ *
+ * @param list<string> $list
+ *
+ * @return IOutgoingSignedRequest
+ * @since 31.0.0
+ */
+ public function setHeaderList(array $list): IOutgoingSignedRequest;
+
+ /**
+ * returns ordered list of used headers in the Signature
+ *
+ * @return list<string>
+ * @since 31.0.0
+ */
+ public function getHeaderList(): array;
+
+ /**
+ * set algorithm to be used to sign the signature
+ *
+ * @param SignatureAlgorithm $algorithm
+ *
+ * @return IOutgoingSignedRequest
+ * @since 31.0.0
+ */
+ public function setAlgorithm(SignatureAlgorithm $algorithm): IOutgoingSignedRequest;
+
+ /**
+ * returns the algorithm set to sign the signature
+ *
+ * @return SignatureAlgorithm
+ * @since 31.0.0
+ */
+ public function getAlgorithm(): SignatureAlgorithm;
+}
*/
namespace NCU\Security\Signature;
-use NCU\Security\Signature\Model\ISignatory;
+use NCU\Security\Signature\Model\Signatory;
/**
* ISignatoryManager contains a group of method that will help
*
* Used to sign outgoing request
*
- * @return ISignatory
+ * @return Signatory
* @since 31.0.0
*/
- public function getLocalSignatory(): ISignatory;
+ public function getLocalSignatory(): Signatory;
/**
* retrieve details and generate signatory from remote instance.
*
* @param string $remote
*
- * @return ISignatory|null must be NULL if no signatory is found
+ * @return Signatory|null must be NULL if no signatory is found
* @since 31.0.0
*/
- public function getRemoteSignatory(string $remote): ?ISignatory;
+ public function getRemoteSignatory(string $remote): ?Signatory;
}
use NCU\Security\Signature\Exceptions\SignatoryNotFoundException;
use NCU\Security\Signature\Exceptions\SignatureException;
use NCU\Security\Signature\Exceptions\SignatureNotFoundException;
-use NCU\Security\Signature\Model\IIncomingSignedRequest;
-use NCU\Security\Signature\Model\IOutgoingSignedRequest;
-use NCU\Security\Signature\Model\ISignatory;
+use NCU\Security\Signature\Model\Signatory;
/**
* ISignatureManager is a service integrated to core that provide tools
* @param string $host remote host
* @param string $account linked account, should be used when multiple signature can exist for the same host
*
- * @return ISignatory
+ * @return Signatory
* @throws SignatoryNotFoundException if entry does not exist in local database
* @since 31.0.0
*/
- public function searchSignatory(string $host, string $account = ''): ISignatory;
+ public function getSignatory(string $host, string $account = ''): Signatory;
/**
* returns a fully formatted keyId, based on a fix hostname and path
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace NCU\Security\Signature;
+
+use NCU\Security\Signature\Exceptions\SignatoryNotFoundException;
+use NCU\Security\Signature\Exceptions\SignatureElementNotFoundException;
+use NCU\Security\Signature\Model\Signatory;
+
+/**
+ * model that store data related to a possible signature.
+ * those details will be used:
+ * - to confirm authenticity of a signed incoming request
+ * - to sign an outgoing request
+ *
+ * @experimental 31.0.0
+ * @since 31.0.0
+ */
+interface ISignedRequest {
+ /**
+ * payload of the request
+ *
+ * @return string
+ * @since 31.0.0
+ */
+ public function getBody(): string;
+
+ /**
+ * checksum of the payload of the request
+ *
+ * @return string
+ * @since 31.0.0
+ */
+ public function getDigest(): string;
+
+ /**
+ * set the list of headers related to the signature of the request
+ *
+ * @param array $elements
+ *
+ * @return ISignedRequest
+ * @since 31.0.0
+ */
+ public function setSignatureElements(array $elements): ISignedRequest;
+
+ /**
+ * get the list of elements in the Signature header of the request
+ *
+ * @return array
+ * @since 31.0.0
+ */
+ public function getSignatureElements(): array;
+
+ /**
+ * @param string $key
+ *
+ * @return string
+ * @throws SignatureElementNotFoundException
+ * @since 31.0.0
+ */
+ public function getSignatureElement(string $key): string;
+
+ /**
+ * store a clear version of the signature
+ *
+ * @param string $clearSignature
+ *
+ * @return ISignedRequest
+ * @since 31.0.0
+ */
+ public function setClearSignature(string $clearSignature): ISignedRequest;
+
+ /**
+ * returns the clear version of the signature
+ *
+ * @return string
+ * @since 31.0.0
+ */
+ public function getClearSignature(): string;
+
+ /**
+ * set the signed version of the signature
+ *
+ * @param string $signedSignature
+ * @return ISignedRequest
+ * @since 31.0.0
+ */
+ public function setSignedSignature(string $signedSignature): ISignedRequest;
+
+ /**
+ * get the signed version of the signature
+ *
+ * @return string
+ * @since 31.0.0
+ */
+ public function getSignedSignature(): string;
+
+ /**
+ * set the signatory, containing keys and details, related to this request
+ *
+ * @param Signatory $signatory
+ * @return ISignedRequest
+ * @since 31.0.0
+ */
+ public function setSignatory(Signatory $signatory): ISignedRequest;
+
+ /**
+ * get the signatory, containing keys and details, related to this request
+ *
+ * @return Signatory
+ * @throws SignatoryNotFoundException
+ * @since 31.0.0
+ */
+ public function getSignatory(): Signatory;
+
+ /**
+ * returns if a signatory related to this request have been found and defined
+ *
+ * @return bool
+ * @since 31.0.0
+ */
+ public function hasSignatory(): bool;
+}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-/**
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-namespace NCU\Security\Signature\Model;
-
-use NCU\Security\Signature\Exceptions\SignatureElementNotFoundException;
-use NCU\Security\Signature\ISignatureManager;
-use OCP\IRequest;
-
-/**
- * model wrapping an actual incoming request, adding details about the signature and the
- * authenticity of the origin of the request.
- *
- * @see ISignatureManager for details on signature
- * @experimental 31.0.0
- * @since 31.0.0
- */
-interface IIncomingSignedRequest extends ISignedRequest {
- /**
- * returns the base IRequest
- *
- * @return IRequest
- * @since 31.0.0
- */
- public function getRequest(): IRequest;
-
- /**
- * set the hostname at the source of the request,
- * based on the keyId defined in the signature header.
- *
- * @param string $origin
- * @return IIncomingSignedRequest
- * @since 31.0.0
- */
- public function setOrigin(string $origin): IIncomingSignedRequest;
-
- /**
- * get the hostname at the source of the base request.
- * based on the keyId defined in the signature header.
- *
- * @return string
- * @since 31.0.0
- */
- public function getOrigin(): string;
-
- /**
- * returns the keyId extracted from the signature headers.
- * keyId is a mandatory entry in the headers of a signed request.
- *
- * @return string
- * @throws SignatureElementNotFoundException
- * @since 31.0.0
- */
- public function getKeyId(): string;
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-/**
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-namespace NCU\Security\Signature\Model;
-
-use NCU\Security\Signature\ISignatureManager;
-use NCU\Security\Signature\SignatureAlgorithm;
-
-/**
- * extends ISignedRequest to add info requested at the generation of the signature
- *
- * @see ISignatureManager for details on signature
- * @experimental 31.0.0
- * @since 31.0.0
- */
-interface IOutgoingSignedRequest extends ISignedRequest {
- /**
- * set the host of the recipient of the request.
- *
- * @param string $host
- * @return IOutgoingSignedRequest
- * @since 31.0.0
- */
- public function setHost(string $host): IOutgoingSignedRequest;
-
- /**
- * get the host of the recipient of the request.
- * - on incoming request, this is the local hostname of current instance.
- * - on outgoing request, this is the remote instance.
- *
- * @return string
- * @since 31.0.0
- */
- public function getHost(): string;
-
- /**
- * add a key/value pair to the headers of the request
- *
- * @param string $key
- * @param string|int|float $value
- *
- * @return IOutgoingSignedRequest
- * @since 31.0.0
- */
- public function addHeader(string $key, string|int|float $value): IOutgoingSignedRequest;
-
- /**
- * returns list of headers value that will be added to the base request
- *
- * @return array
- * @since 31.0.0
- */
- public function getHeaders(): array;
-
- /**
- * set the ordered list of used headers in the Signature
- *
- * @param list<string> $list
- *
- * @return IOutgoingSignedRequest
- * @since 31.0.0
- */
- public function setHeaderList(array $list): IOutgoingSignedRequest;
-
- /**
- * returns ordered list of used headers in the Signature
- *
- * @return list<string>
- * @since 31.0.0
- */
- public function getHeaderList(): array;
-
- /**
- * set algorithm to be used to sign the signature
- *
- * @param SignatureAlgorithm $algorithm
- *
- * @return IOutgoingSignedRequest
- * @since 31.0.0
- */
- public function setAlgorithm(SignatureAlgorithm $algorithm): IOutgoingSignedRequest;
-
- /**
- * returns the algorithm set to sign the signature
- *
- * @return SignatureAlgorithm
- * @since 31.0.0
- */
- public function getAlgorithm(): SignatureAlgorithm;
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-/**
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-namespace NCU\Security\Signature\Model;
-
-use NCU\Security\Signature\ISignatoryManager;
-
-/**
- * model that store keys and details related to host and in use protocol
- * mandatory details are providerId, host, keyId and public key.
- * private key is only used for local signatory, used to sign outgoing request
- *
- * the pair providerId+host is unique, meaning only one signatory can exist for each host
- * and protocol
- *
- * @since 31.0.0
- * @experimental 31.0.0
- */
-interface ISignatory {
- /**
- * unique string, related to the ISignatoryManager
- *
- * @see ISignatoryManager::getProviderId
- * @param string $providerId
- *
- * @return ISignatory
- * @since 31.0.0
- */
- public function setProviderId(string $providerId): ISignatory;
-
- /**
- * returns the provider id, unique string related to the ISignatoryManager
- *
- * @return string
- * @since 31.0.0
- */
- public function getProviderId(): string;
-
- /**
- * set account, in case your ISignatoryManager needs to manage multiple keys from same host
- *
- * @param string $account
- *
- * @return ISignatory
- * @since 31.0.0
- */
- public function setAccount(string $account): ISignatory;
-
- /**
- * return account name, empty string if not set
- *
- * @return string
- * @since 31.0.0
- */
- public function getAccount(): string;
-
- /**
- * returns key id
- *
- * @return string
- * @since 31.0.0
- */
- public function getKeyId(): string;
-
- /**
- * returns public key
- *
- * @return string
- * @since 31.0.0
- */
- public function getPublicKey(): string;
-
- /**
- * returns private key, if available
- *
- * @return string
- * @since 31.0.0
- */
- public function getPrivateKey(): string;
-
- /**
- * set metadata
- *
- * @param array $metadata
- *
- * @return ISignatory
- * @since 31.0.0
- */
- public function setMetadata(array $metadata): ISignatory;
-
- /**
- * returns metadata
- *
- * @return array
- * @since 31.0.0
- */
- public function getMetadata(): array;
-
- /**
- * update an entry in metadata
- *
- * @param string $key
- * @param string|int $value
- *
- * @return ISignatory
- * @since 31.0.0
- */
- public function setMetaValue(string $key, string|int $value): ISignatory;
-
- /**
- * set SignatoryType
- *
- * @param SignatoryType $type
- *
- * @return ISignatory
- * @since 31.0.0
- */
- public function setType(SignatoryType $type): ISignatory;
-
- /**
- * returns SignatoryType
- *
- * @return SignatoryType
- * @since 31.0.0
- */
- public function getType(): SignatoryType;
-
- /**
- * set SignatoryStatus
- *
- * @param SignatoryStatus $status
- *
- * @see SignatoryStatus
- * @return ISignatory
- * @since 31.0.0
- */
- public function setStatus(SignatoryStatus $status): ISignatory;
-
- /**
- * get SignatoryStatus
- *
- * @see SignatoryStatus
- * @return SignatoryStatus
- * @since 31.0.0
- */
- public function getStatus(): SignatoryStatus;
-
- /**
- * get last timestamp this entry has been updated
- *
- * @return int
- * @since 31.0.0
- */
- public function getLastUpdated(): int;
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-/**
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-namespace NCU\Security\Signature\Model;
-
-use NCU\Security\Signature\Exceptions\SignatoryNotFoundException;
-use NCU\Security\Signature\Exceptions\SignatureElementNotFoundException;
-
-/**
- * model that store data related to a possible signature.
- * those details will be used:
- * - to confirm authenticity of a signed incoming request
- * - to sign an outgoing request
- *
- * @experimental 31.0.0
- * @since 31.0.0
- */
-interface ISignedRequest {
- /**
- * payload of the request
- *
- * @return string
- * @since 31.0.0
- */
- public function getBody(): string;
-
- /**
- * checksum of the payload of the request
- *
- * @return string
- * @since 31.0.0
- */
- public function getDigest(): string;
-
- /**
- * set the list of headers related to the signature of the request
- *
- * @param array $elements
- *
- * @return ISignedRequest
- * @since 31.0.0
- */
- public function setSignatureElements(array $elements): ISignedRequest;
-
- /**
- * get the list of elements in the Signature header of the request
- *
- * @return array
- * @since 31.0.0
- */
- public function getSignatureElements(): array;
-
- /**
- * @param string $key
- *
- * @return string
- * @throws SignatureElementNotFoundException
- * @since 31.0.0
- */
- public function getSignatureElement(string $key): string;
-
- /**
- * store a clear version of the signature
- *
- * @param string $clearSignature
- *
- * @return ISignedRequest
- * @since 31.0.0
- */
- public function setClearSignature(string $clearSignature): ISignedRequest;
-
- /**
- * returns the clear version of the signature
- *
- * @return string
- * @since 31.0.0
- */
- public function getClearSignature(): string;
-
- /**
- * set the signed version of the signature
- *
- * @param string $signedSignature
- * @return ISignedRequest
- * @since 31.0.0
- */
- public function setSignedSignature(string $signedSignature): ISignedRequest;
-
- /**
- * get the signed version of the signature
- *
- * @return string
- * @since 31.0.0
- */
- public function getSignedSignature(): string;
-
- /**
- * set the signatory, containing keys and details, related to this request
- *
- * @param ISignatory $signatory
- * @return ISignedRequest
- * @since 31.0.0
- */
- public function setSignatory(ISignatory $signatory): ISignedRequest;
-
- /**
- * get the signatory, containing keys and details, related to this request
- *
- * @return ISignatory
- * @throws SignatoryNotFoundException
- * @since 31.0.0
- */
- public function getSignatory(): ISignatory;
-
- /**
- * returns if a signatory related to this request have been found and defined
- *
- * @return bool
- * @since 31.0.0
- */
- public function hasSignatory(): bool;
-}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace NCU\Security\Signature\Model;
+
+use JsonSerializable;
+use NCU\Security\Signature\Enum\SignatoryStatus;
+use NCU\Security\Signature\Enum\SignatoryType;
+use OCP\AppFramework\Db\Entity;
+
+/**
+ * model that store keys and details related to host and in use protocol
+ * mandatory details are providerId, host, keyId and public key.
+ * private key is only used for local signatory, used to sign outgoing request
+ *
+ * the pair providerId+host is unique, meaning only one signatory can exist for each host
+ * and protocol
+ *
+ * @since 31.0.0
+ * @experimental 31.0.0
+ *
+ * @method void setProviderId(string $providerId)
+ * @method string getProviderId()
+ * @method string getKeyId()
+ * @method void setPublicKey(string $publicKey)
+ * @method string getPublicKey()
+ * @method void setPrivateKey(string $privateKey)
+ * @method string getPrivateKey()
+ * @method void setHost(string $host)
+ * @method string getHost()
+ * @method void setAccount(string $account)
+ * @method string getAccount()
+ * @method void setMetadata(array $metadata)
+ * @method array getMetadata()
+ * @method void setCreation(int $creation)
+ * @method int getCreation()
+ * @method void setLastUpdated(int $creation)
+ * @method int getLastUpdated()
+ */
+class Signatory extends Entity implements JsonSerializable {
+ protected string $keyId = '';
+ protected string $keyIdSum = '';
+ protected string $providerId = '';
+ protected string $host = '';
+ protected string $account = '';
+ protected int $type = 9;
+ protected int $status = 1;
+ protected array $metadata = [];
+ protected int $creation = 0;
+ protected int $lastUpdated = 0;
+
+ /**
+ * @param string $keyId
+ * @param string $publicKey
+ * @param string $privateKey
+ * @param bool $local
+ *
+ * @since 31.0.0
+ */
+ public function __construct(
+ string $keyId = '',
+ protected string $publicKey = '',
+ protected string $privateKey = '',
+ private readonly bool $local = false,
+ ) {
+ $this->addType('providerId', 'string');
+ $this->addType('host', 'string');
+ $this->addType('account', 'string');
+ $this->addType('keyId', 'string');
+ $this->addType('keyIdSum', 'string');
+ $this->addType('publicKey', 'string');
+ $this->addType('metadata', 'json');
+ $this->addType('type', 'integer');
+ $this->addType('status', 'integer');
+ $this->addType('creation', 'integer');
+ $this->addType('lastUpdated', 'integer');
+
+ $this->setKeyId($keyId);
+ }
+
+ /**
+ * @param string $keyId
+ *
+ * @since 31.0.0
+ */
+ public function setKeyId(string $keyId): void {
+ // if set as local (for current instance), we apply some filters.
+ if ($this->local) {
+ // to avoid conflict with duplicate key pairs (ie generated url from the occ command), we enforce https as prefix
+ if (str_starts_with($keyId, 'http://')) {
+ $keyId = 'https://' . substr($keyId, 7);
+ }
+
+ // removing /index.php from generated url
+ $path = parse_url($keyId, PHP_URL_PATH);
+ if (str_starts_with($path, '/index.php/')) {
+ $pos = strpos($keyId, '/index.php');
+ if ($pos !== false) {
+ $keyId = substr_replace($keyId, '', $pos, 10);
+ }
+ }
+ }
+ $this->keyId = $keyId;
+ $this->keyIdSum = hash('sha256', $keyId);
+ }
+
+ /**
+ * @param SignatoryType $type
+ * @since 31.0.0
+ */
+ public function setType(SignatoryType $type): void {
+ $this->type = $type->value;
+ }
+
+ /**
+ * @return SignatoryType
+ * @since 31.0.0
+ */
+ public function getType(): SignatoryType {
+ return SignatoryType::from($this->type);
+ }
+
+ /**
+ * @param SignatoryStatus $status
+ * @since 31.0.0
+ */
+ public function setStatus(SignatoryStatus $status): void {
+ $this->status = $status->value;
+ }
+
+ /**
+ * @return SignatoryStatus
+ * @since 31.0.0
+ */
+ public function getStatus(): SignatoryStatus {
+ return SignatoryStatus::from($this->status);
+ }
+
+ /**
+ * update an entry in metadata
+ *
+ * @param string $key
+ * @param string|int|float|bool|array $value
+ * @since 31.0.0
+ */
+ public function setMetaValue(string $key, string|int|float|bool|array $value): void {
+ $this->metadata[$key] = $value;
+ }
+
+ /**
+ * @return array
+ * @since 31.0.0
+ */
+ public function jsonSerialize(): array {
+ return [
+ 'keyId' => $this->getKeyId(),
+ 'publicKeyPem' => $this->getPublicKey()
+ ];
+ }
+}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-/**
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-namespace NCU\Security\Signature\Model;
-
-/**
- * current status of signatory. is it trustable or not ?
- *
- * - SYNCED = the remote instance is trustable.
- * - BROKEN = the remote instance does not use the same key pairs than previously
- *
- * @experimental 31.0.0
- * @since 31.0.0
- */
-enum SignatoryStatus: int {
- /** @since 31.0.0 */
- case SYNCED = 1;
- /** @since 31.0.0 */
- case BROKEN = 9;
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-/**
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-namespace NCU\Security\Signature\Model;
-
-/**
- * type of link between local and remote instance
- *
- * - FORGIVABLE = the keypair can be deleted and refreshed anytime and silently
- * - REFRESHABLE = the keypair can be refreshed but a notice will be generated
- * - TRUSTED = any changes of keypair will require human interaction, warning will be issued
- * - STATIC = error will be issued on conflict, assume keypair cannot be reset.
- *
- * @experimental 31.0.0
- * @since 31.0.0
- */
-enum SignatoryType: int {
- /** @since 31.0.0 */
- case FORGIVABLE = 1; // no notice on refresh
- /** @since 31.0.0 */
- case REFRESHABLE = 4; // notice on refresh
- /** @since 31.0.0 */
- case TRUSTED = 8; // warning on refresh
- /** @since 31.0.0 */
- case STATIC = 9; // error on refresh
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-/**
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-namespace NCU\Security\Signature;
-
-/**
- * list of available algorithm when signing payload
- *
- * @experimental 31.0.0
- * @since 31.0.0
- */
-enum SignatureAlgorithm: string {
- /** @since 31.0.0 */
- case SHA256 = 'sha256';
- /** @since 31.0.0 */
- case SHA512 = 'sha512';
-}