0
0
Fork 0
mirror of https://github.com/nextcloud/server.git synced 2025-02-12 12:09:14 +00:00
nextcloud_server/lib/unstable/Security/Signature/Model/Signatory.php
Joas Schilling 280b492919
fix(federation): Allow Oracles empty strings
Signed-off-by: Joas Schilling <coding@schilljs.com>
2024-12-10 11:20:45 +01:00

200 lines
5.5 KiB
PHP

<?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 NCU\Security\Signature\Exceptions\IdentityNotFoundException;
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
*
* @experimental 31.0.0
*
* @method void setProviderId(string $providerId)
* @method string getProviderId()
* @method string getKeyId()
* @method void setKeyIdSum(string $keyIdSum)
* @method string getKeyIdSum()
* @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 int getType()
* @method void setType(int $type)
* @method int getStatus()
* @method void setStatus(int $status)
* @method void setAccount(?string $account)
* @method void setMetadata(array $metadata)
* @method ?array getMetadata()
* @method void setCreation(int $creation)
* @method int getCreation()
* @method void setLastUpdated(int $creation)
* @method int getLastUpdated()
* @psalm-suppress PropertyNotSetInConstructor
*/
class Signatory extends Entity implements JsonSerializable {
protected string $keyId = '';
protected string $keyIdSum = '';
protected string $providerId = '';
protected string $host = '';
protected string $publicKey = '';
protected string $privateKey = '';
protected ?string $account = '';
protected int $type = 9;
protected int $status = 1;
protected ?array $metadata = null;
protected int $creation = 0;
protected int $lastUpdated = 0;
/**
* @param bool $local only set to TRUE when managing local signatory
*
* @experimental 31.0.0
*/
public function __construct(
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');
}
/**
* @param string $keyId
*
* @experimental 31.0.0
* @throws IdentityNotFoundException if identity cannot be extracted from keyId
*/
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->setter('keyId', [$keyId]); // needed to trigger the update in database
$this->setKeyIdSum(hash('sha256', $keyId));
$this->setHost(self::extractIdentityFromUri($this->getKeyId()));
}
/**
* @param SignatoryType $type
* @experimental 31.0.0
*/
public function setSignatoryType(SignatoryType $type): void {
$this->setType($type->value);
}
/**
* @return SignatoryType
* @experimental 31.0.0
*/
public function getSignatoryType(): SignatoryType {
return SignatoryType::from($this->getType());
}
/**
* @param SignatoryStatus $status
* @experimental 31.0.0
*/
public function setSignatoryStatus(SignatoryStatus $status): void {
$this->setStatus($status->value);
}
/**
* @return SignatoryStatus
* @experimental 31.0.0
*/
public function getSignatoryStatus(): SignatoryStatus {
return SignatoryStatus::from($this->getStatus());
}
/**
* @experimental 31.0.0
*/
public function getAccount(): string {
return $this->account ?? '';
}
/**
* update an entry in metadata
*
* @param string $key
* @param string|int|float|bool|array $value
* @experimental 31.0.0
*/
public function setMetaValue(string $key, string|int|float|bool|array $value): void {
$this->metadata[$key] = $value;
$this->setter('metadata', [$this->metadata]);
}
/**
* @return array
* @experimental 31.0.0
*/
public function jsonSerialize(): array {
return [
'keyId' => $this->getKeyId(),
'publicKeyPem' => $this->getPublicKey()
];
}
/**
* static is needed to make this easily callable from outside the model
*
* @param string $uri
*
* @return string
* @throws IdentityNotFoundException if identity cannot be extracted
* @experimental 31.0.0
*/
public static function extractIdentityFromUri(string $uri): string {
$identity = parse_url($uri, PHP_URL_HOST);
$port = parse_url($uri, PHP_URL_PORT);
if ($identity === null || $identity === false) {
throw new IdentityNotFoundException('cannot extract identity from ' . $uri);
}
if ($port !== null && $port !== false) {
$identity .= ':' . $port;
}
return $identity;
}
}