From 330e7f876b02aed52cbb87935b2f4415a87d6eca Mon Sep 17 00:00:00 2001 From: netkas Date: Thu, 30 Jan 2025 15:20:11 -0500 Subject: [PATCH] Added the ability to trust signing keys & resolve signing keys for peers, minor improvements and added new standard error "CONFLICT" --- src/Socialbox/Classes/Configuration.php | 1 + .../Configuration/PoliciesConfiguration.php | 7 + .../Resources/database/signing_keys.sql | 2 +- .../AddressBook/AddressBookTrustSignature.php | 52 ++++- .../AddressBookUpdateRelationship.php | 10 +- .../Settings/SettingsAddSignature.php | 10 +- src/Socialbox/Enums/StandardError.php | 3 +- src/Socialbox/Managers/ContactManager.php | 198 ++++++++++++++++++ src/Socialbox/Managers/SigningKeysManager.php | 17 +- .../Database/ContactKnownKeyRecord.php | 138 +++++++++++- .../Objects/Database/SigningKeyRecord.php | 8 +- src/Socialbox/Objects/Standard/SigningKey.php | 4 +- src/Socialbox/SocialClient.php | 11 +- src/Socialbox/Socialbox.php | 8 +- 14 files changed, 427 insertions(+), 42 deletions(-) diff --git a/src/Socialbox/Classes/Configuration.php b/src/Socialbox/Classes/Configuration.php index 653ab2e..af43b69 100644 --- a/src/Socialbox/Classes/Configuration.php +++ b/src/Socialbox/Classes/Configuration.php @@ -150,6 +150,7 @@ // Server Policies // The maximum number of signing keys a peer can register onto the server at once $config->setDefault('policies.max_signing_keys', 20); + $config->setDefault('policies.max_contact_signing_keys', 50); // The amount of time in seconds it takes before a session is considered expired due to inactivity // Default: 12hours $config->setDefault('policies.session_inactivity_expires', 43200); diff --git a/src/Socialbox/Classes/Configuration/PoliciesConfiguration.php b/src/Socialbox/Classes/Configuration/PoliciesConfiguration.php index 7a11247..329b92a 100644 --- a/src/Socialbox/Classes/Configuration/PoliciesConfiguration.php +++ b/src/Socialbox/Classes/Configuration/PoliciesConfiguration.php @@ -7,6 +7,7 @@ class PoliciesConfiguration { private int $maxSigningKeys; + private int $maxContactSigningKeys; private int $sessionInactivityExpires; private int $imageCaptchaExpires; private int $peerSyncInterval; @@ -37,6 +38,7 @@ public function __construct(array $data) { $this->maxSigningKeys = $data['max_signing_keys']; + $this->maxContactSigningKeys = $data['max_contact_signing_keys']; $this->sessionInactivityExpires = $data['session_inactivity_expires']; $this->imageCaptchaExpires = $data['image_captcha_expires']; $this->peerSyncInterval = $data['peer_sync_interval']; @@ -61,6 +63,11 @@ return $this->maxSigningKeys; } + public function getMaxContactSigningKeys(): int + { + return $this->maxContactSigningKeys; + } + /** * Returns the maximum amount of seconds before the session is considered expired due to inactivity * diff --git a/src/Socialbox/Classes/Resources/database/signing_keys.sql b/src/Socialbox/Classes/Resources/database/signing_keys.sql index 3853718..9184a57 100644 --- a/src/Socialbox/Classes/Resources/database/signing_keys.sql +++ b/src/Socialbox/Classes/Resources/database/signing_keys.sql @@ -2,7 +2,7 @@ create table signing_keys ( peer_uuid varchar(36) not null comment 'The UUID of the peer', uuid varchar(36) default uuid() not null comment 'The UUID of the key record', - name varchar(64) null comment 'Optional. User provided name for the key', + name varchar(64) not null comment 'Optional. User provided name for the key', public_key varchar(64) not null comment 'The Public Signature Key', state enum ('ACTIVE', 'EXPIRED') default 'ACTIVE' not null comment 'The state of the public key', expires timestamp null comment 'The Timestamp for when this key expires, null = Never expires', diff --git a/src/Socialbox/Classes/StandardMethods/AddressBook/AddressBookTrustSignature.php b/src/Socialbox/Classes/StandardMethods/AddressBook/AddressBookTrustSignature.php index 1c6ce68..c04a2a9 100644 --- a/src/Socialbox/Classes/StandardMethods/AddressBook/AddressBookTrustSignature.php +++ b/src/Socialbox/Classes/StandardMethods/AddressBook/AddressBookTrustSignature.php @@ -4,14 +4,18 @@ use InvalidArgumentException; use Socialbox\Abstracts\Method; + use Socialbox\Classes\Configuration; use Socialbox\Enums\StandardError; use Socialbox\Exceptions\DatabaseOperationException; + use Socialbox\Exceptions\Standard\InvalidRpcArgumentException; + use Socialbox\Exceptions\Standard\MissingRpcArgumentException; use Socialbox\Exceptions\Standard\StandardRpcException; use Socialbox\Interfaces\SerializableInterface; use Socialbox\Managers\ContactManager; use Socialbox\Objects\ClientRequest; use Socialbox\Objects\PeerAddress; use Socialbox\Objects\RpcRequest; + use Socialbox\Socialbox; use Symfony\Component\Uid\Uuid; class AddressBookTrustSignature extends Method @@ -23,7 +27,7 @@ { if(!$rpcRequest->containsParameter('peer')) { - return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing required peer parameter'); + throw new MissingRpcArgumentException('peer'); } try @@ -32,12 +36,12 @@ } catch(InvalidArgumentException $e) { - throw new StandardRpcException('Invalid peer address', StandardError::RPC_INVALID_ARGUMENTS, $e); + throw new InvalidRpcArgumentException('peer', $e->getMessage()); } if(!$rpcRequest->containsParameter('uuid')) { - return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'uuid' parameter"); + throw new MissingRpcArgumentException('uuid'); } try @@ -46,24 +50,54 @@ } catch(InvalidArgumentException $e) { - throw new StandardRpcException('Invalid UUID', StandardError::RPC_INVALID_ARGUMENTS, $e); + throw new InvalidRpcArgumentException('uuid', $e->getMessage()); } + $signingKey = Socialbox::resolvePeerSignature($address, $uuid); + try { // Check if the contact already exists $peer = $request->getPeer(); - if(ContactManager::isContact($peer, $address)) + if(!ContactManager::isContact($peer, $address)) { - + ContactManager::createContact($peer, $address); } - // Create the contact - ContactManager::updateContactRelationship($peer, $address, $relationship); + $contact = ContactManager::getContact($peer, $address); + + if(ContactManager::contactGetSigningKeysCount($contact) > Configuration::getPoliciesConfiguration()->getMaxContactSigningKeys()) + { + return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The contact has exceeded the maximum amount of trusted signatures'); + } } catch (DatabaseOperationException $e) { - throw new StandardRpcException('Failed to update contact relationship', StandardError::INTERNAL_SERVER_ERROR, $e); + throw new StandardRpcException('Failed to check contact state with calling peer', StandardError::INTERNAL_SERVER_ERROR, $e); + } + + if($signingKey === null) + { + return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested signature key was not found'); + } + + try + { + if(ContactManager::contactSigningKeyUuidExists($contact, $signingKey->getUuid())) + { + return $rpcRequest->produceResponse(false); + } + + if(ContactManager::contactSigningKeyExists($contact, $signingKey->getPublicKey())) + { + return $rpcRequest->produceResponse(false); + } + + ContactManager::addContactSigningKey($contact, $signingKey); + } + catch (DatabaseOperationException $e) + { + throw new StandardRpcException('Failed to trust contact signature', StandardError::INTERNAL_SERVER_ERROR, $e); } // Return success diff --git a/src/Socialbox/Classes/StandardMethods/AddressBook/AddressBookUpdateRelationship.php b/src/Socialbox/Classes/StandardMethods/AddressBook/AddressBookUpdateRelationship.php index e30669a..cf217c0 100644 --- a/src/Socialbox/Classes/StandardMethods/AddressBook/AddressBookUpdateRelationship.php +++ b/src/Socialbox/Classes/StandardMethods/AddressBook/AddressBookUpdateRelationship.php @@ -7,6 +7,8 @@ use Socialbox\Enums\StandardError; use Socialbox\Enums\Types\ContactRelationshipType; use Socialbox\Exceptions\DatabaseOperationException; + use Socialbox\Exceptions\Standard\InvalidRpcArgumentException; + use Socialbox\Exceptions\Standard\MissingRpcArgumentException; use Socialbox\Exceptions\Standard\StandardRpcException; use Socialbox\Interfaces\SerializableInterface; use Socialbox\Managers\ContactManager; @@ -23,7 +25,7 @@ { if(!$rpcRequest->containsParameter('peer')) { - return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing required peer parameter'); + throw new MissingRpcArgumentException('peer'); } try @@ -32,17 +34,17 @@ } catch(InvalidArgumentException $e) { - throw new StandardRpcException('Invalid peer address', StandardError::RPC_INVALID_ARGUMENTS, $e); + throw new InvalidRpcArgumentException('peer', 'Invalid peer address'); } if(!$rpcRequest->containsParameter('relationship')) { - return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing required relationship parameter'); + throw new MissingRpcArgumentException('relationship'); } $relationship = ContactRelationshipType::tryFrom(strtoupper($rpcRequest->getParameter('relationship'))); if($relationship === null) { - throw new StandardRpcException('Invalid relationship type', StandardError::RPC_INVALID_ARGUMENTS); + throw new InvalidRpcArgumentException('relationship', 'Invalid relationship type'); } try diff --git a/src/Socialbox/Classes/StandardMethods/Settings/SettingsAddSignature.php b/src/Socialbox/Classes/StandardMethods/Settings/SettingsAddSignature.php index 796f6a2..5c7fb3c 100644 --- a/src/Socialbox/Classes/StandardMethods/Settings/SettingsAddSignature.php +++ b/src/Socialbox/Classes/StandardMethods/Settings/SettingsAddSignature.php @@ -7,6 +7,7 @@ use Socialbox\Abstracts\Method; use Socialbox\Classes\Configuration; use Socialbox\Enums\StandardError; + use Socialbox\Exceptions\Standard\MissingRpcArgumentException; use Socialbox\Exceptions\Standard\StandardRpcException; use Socialbox\Interfaces\SerializableInterface; use Socialbox\Managers\SigningKeysManager; @@ -22,7 +23,7 @@ { if(!$rpcRequest->containsParameter('public_key')) { - return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'public_key' parameter"); + throw new MissingRpcArgumentException('public_key'); } $expires = null; @@ -31,6 +32,11 @@ $expires = (int)$rpcRequest->getParameter('expires'); } + if(!$rpcRequest->containsParameter('name')) + { + throw new MissingRpcArgumentException('name'); + } + $name = null; if($rpcRequest->containsParameter('name') && $rpcRequest->getParameter('name') !== null) { @@ -46,7 +52,7 @@ return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The maximum number of signing keys has been reached'); } - $uuid = SigningKeysManager::addSigningKey($peerUuid, $rpcRequest->getParameter('public_key'), $expires, $name); + $uuid = SigningKeysManager::addSigningKey($peerUuid, $rpcRequest->getParameter('public_key'), $name, $expires); } catch(InvalidArgumentException $e) { diff --git a/src/Socialbox/Enums/StandardError.php b/src/Socialbox/Enums/StandardError.php index 8824d54..ad2721f 100644 --- a/src/Socialbox/Enums/StandardError.php +++ b/src/Socialbox/Enums/StandardError.php @@ -15,7 +15,8 @@ case UNAUTHORIZED = -104; case NOT_FOUND = -105; case RESOLUTION_FAILED = -106; - case CRYPTOGRAPHIC_ERROR = -107; + case CONFLICT = -107; + case CRYPTOGRAPHIC_ERROR = -108; // RPC Errors case RPC_METHOD_NOT_FOUND = -1000; diff --git a/src/Socialbox/Managers/ContactManager.php b/src/Socialbox/Managers/ContactManager.php index c8ad1a0..4bd00e1 100644 --- a/src/Socialbox/Managers/ContactManager.php +++ b/src/Socialbox/Managers/ContactManager.php @@ -9,7 +9,9 @@ use Socialbox\Enums\Types\ContactRelationshipType; use Socialbox\Exceptions\DatabaseOperationException; use Socialbox\Objects\Database\ContactDatabaseRecord; + use Socialbox\Objects\Database\ContactKnownKeyRecord; use Socialbox\Objects\PeerAddress; + use Socialbox\Objects\Standard\SigningKey; class ContactManager { @@ -278,4 +280,200 @@ } return $contacts; } + + /** + * Adds a signing key to a contact in the database. + * + * @param string|ContactDatabaseRecord $contactUuid The unique identifier of the contact to add the signing key to. + * @param SigningKey $signingKey The signing key to add to the contact. + * @return void + * @throws DatabaseOperationException If the database query fails. + */ + public static function addContactSigningKey(string|ContactDatabaseRecord $contactUuid, SigningKey $signingKey): void + { + if($contactUuid instanceof ContactDatabaseRecord) + { + $contactUuid = $contactUuid->getUuid(); + } + + try + { + $statement = Database::getConnection()->prepare('INSERT INTO contacts_known_keys (contact_uuid, signature_uuid, signature_name, signature_key, expires, created, trusted_on) VALUES (:contact_uuid, :signature_uuid, :signature_name, :signature_key, :expires, :created, :trusted_on)'); + + $statement->bindParam(':contact_uuid', $contactUuid); + $signatureUuid = $signingKey->getUuid(); + $statement->bindParam(':signature_uuid', $signatureUuid); + $signatureName = $signingKey->getName(); + $statement->bindParam(':signature_name', $signatureName); + $signatureKey = $signingKey->getPublicKey(); + $statement->bindParam(':signature_key', $signatureKey); + $expires = $signingKey->getExpires(); + $statement->bindParam(':expires', $expires); + $created = $signingKey->getCreated(); + $statement->bindParam(':created', $created); + $trustedOn = (new \DateTime())->format('Y-m-d H:i:s'); + $statement->bindParam(':trusted_on', $trustedOn); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to add a signing key to a contact in the database', $e); + } + } + + /** + * Determines if a signing key UUID exists for a contact in the database. + * + * @param string|ContactDatabaseRecord $contactUuid The unique identifier of the contact to check. + * @param string $signatureUuid The UUID of the signing key to check. + * @return bool Returns true if the signing key UUID exists for the contact; otherwise, returns false. + * @throws DatabaseOperationException If the database query fails. + */ + public static function contactSigningKeyUuidExists(string|ContactDatabaseRecord $contactUuid, string $signatureUuid): bool + { + if($contactUuid instanceof ContactDatabaseRecord) + { + $contactUuid = $contactUuid->getUuid(); + } + + try + { + $statement = Database::getConnection()->prepare('SELECT COUNT(*) FROM contacts_known_keys WHERE contact_uuid=:contact_uuid AND signature_uuid=:signature_uuid'); + $statement->bindParam(':contact_uuid', $contactUuid); + $statement->bindParam(':signature_uuid', $signatureUuid); + $statement->execute(); + return $statement->fetchColumn() > 0; + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to check if a signing key UUID exists for a contact in the database', $e); + } + } + + /** + * Determines if a signing key exists for a contact in the database. + * + * @param string|ContactDatabaseRecord $contactUuid The unique identifier of the contact to check. + * @param string $signatureKey The public key of the signing key to check. + * @return bool Returns true if the signing key exists for the contact; otherwise, returns false. + * @throws DatabaseOperationException If the database query fails. + */ + public static function contactSigningKeyExists(string|ContactDatabaseRecord $contactUuid, string $signatureKey): bool + { + if($contactUuid instanceof ContactDatabaseRecord) + { + $contactUuid = $contactUuid->getUuid(); + } + + try + { + $statement = Database::getConnection()->prepare('SELECT COUNT(*) FROM contacts_known_keys WHERE contact_uuid=:contact_uuid AND signature_key=:signature_key'); + $statement->bindParam(':contact_uuid', $contactUuid); + $statement->bindParam(':signature_key', $signatureKey); + $statement->execute(); + return $statement->fetchColumn() > 0; + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to check if a signing key exists for a contact in the database', $e); + } + } + + /** + * Retrieves a signing key for a contact from the database. + * + * @param string|ContactDatabaseRecord $contactUuid The unique identifier of the contact to retrieve the signing key for. + * @param string $signatureUuid The UUID of the signing key to retrieve. + * @return ContactKnownKeyRecord|null The retrieved ContactKnownKeyRecord instance if found, or null if no matching signing key exists. + * @throws DatabaseOperationException If the database query fails. + */ + public static function contactGetSigningKey(string|ContactDatabaseRecord $contactUuid, string $signatureUuid): ?ContactKnownKeyRecord + { + if($contactUuid instanceof ContactDatabaseRecord) + { + $contactUuid = $contactUuid->getUuid(); + } + + try + { + $statement = Database::getConnection()->prepare('SELECT * FROM contacts_known_keys WHERE contact_uuid=:contact_uuid AND signature_uuid=:signature_uuid LIMIT 1'); + $statement->bindParam(':contact_uuid', $contactUuid); + $statement->bindParam(':signature_uuid', $signatureUuid); + $statement->execute(); + $result = $statement->fetch(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to get a signing key for a contact from the database', $e); + } + + if($result === false) + { + return null; + } + + return ContactKnownKeyRecord::fromArray($result); + } + + /** + * Retrieves all signing keys for a contact from the database. + * + * @param string|ContactDatabaseRecord $contactUuid The unique identifier of the contact to retrieve the signing keys for. + * @return ContactKnownKeyRecord[] An array of ContactKnownKeyRecord instances representing the signing keys for the contact. + * @throws DatabaseOperationException If the database query fails. + */ + public static function contactGetSigningKeys(string|ContactDatabaseRecord $contactUuid): array + { + if($contactUuid instanceof ContactDatabaseRecord) + { + $contactUuid = $contactUuid->getUuid(); + } + + $signingKeys = []; + + try + { + $statement = Database::getConnection()->prepare('SELECT * FROM contacts_known_keys WHERE contact_uuid=:contact_uuid'); + $statement->bindParam(':contact_uuid', $contactUuid); + $statement->execute(); + $results = $statement->fetchAll(PDO::FETCH_ASSOC); + + foreach($results as $result) + { + $signingKeys[] = ContactKnownKeyRecord::fromArray($result); + } + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to get signing keys for a contact from the database', $e); + } + + return $signingKeys; + } + + /** + * Retrieves the number of signing keys for a contact from the database. + * + * @param string|ContactDatabaseRecord $contactUuid The unique identifier of the contact to retrieve the signing keys count for. + * @return int The number of signing keys for the contact. + * @throws DatabaseOperationException If the database query fails. + */ + public static function contactGetSigningKeysCount(string|ContactDatabaseRecord $contactUuid): int + { + if($contactUuid instanceof ContactDatabaseRecord) + { + $contactUuid = $contactUuid->getUuid(); + } + + try + { + $statement = Database::getConnection()->prepare('SELECT COUNT(*) FROM contacts_known_keys WHERE contact_uuid=:contact_uuid'); + $statement->bindParam(':contact_uuid', $contactUuid); + $statement->execute(); + return $statement->fetchColumn(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to get the number of signing keys for a contact from the database', $e); + } + } } \ No newline at end of file diff --git a/src/Socialbox/Managers/SigningKeysManager.php b/src/Socialbox/Managers/SigningKeysManager.php index 4d30e1c..2b78fa7 100644 --- a/src/Socialbox/Managers/SigningKeysManager.php +++ b/src/Socialbox/Managers/SigningKeysManager.php @@ -44,28 +44,29 @@ * * @param string $peerUuid The unique identifier of the peer associated with the signing key. * @param string $publicKey The public signing key to be added. Must be valid according to the Cryptography::validatePublicSigningKey method. + * @param string $name Optional name associated with the signing key. Must not exceed 64 characters in length. * @param int|null $expires Optional expiration timestamp for the signing key. Can be null if the key does not expire. - * @param string|null $name Optional name associated with the signing key. Must not exceed 64 characters in length. - * @throws DatabaseOperationException If the operation to add the signing key to the database fails. * @return string The UUID of the newly added signing key. + * @throws DatabaseOperationException If the operation to add the signing key to the database fails. */ - public static function addSigningKey(string $peerUuid, string $publicKey, ?int $expires=null, ?string $name=null): string + public static function addSigningKey(string $peerUuid, string $publicKey, string $name, ?int $expires=null): string { if(!Cryptography::validatePublicSigningKey($publicKey)) { throw new InvalidArgumentException('The public key is invalid'); } + + if(empty($name)) + { + throw new InvalidArgumentException('The name cannot be empty'); + } + if(strlen($name) > 64) { throw new InvalidArgumentException('The name is too long'); } - if($name !== null && empty($name)) - { - throw new InvalidArgumentException('The name cannot be empty'); - } - if($expires !== null) { if($expires === 0) diff --git a/src/Socialbox/Objects/Database/ContactKnownKeyRecord.php b/src/Socialbox/Objects/Database/ContactKnownKeyRecord.php index c553c02..1dd8de4 100644 --- a/src/Socialbox/Objects/Database/ContactKnownKeyRecord.php +++ b/src/Socialbox/Objects/Database/ContactKnownKeyRecord.php @@ -2,17 +2,141 @@ namespace Socialbox\Objects\Database; + use DateTime; + use InvalidArgumentException; use Socialbox\Interfaces\SerializableInterface; class ContactKnownKeyRecord implements SerializableInterface { + private string $contactUuid; + private string $signatureUuid; + private string $signatureName; + private string $signatureKey; + private ?DateTime $expires; + private DateTime $created; + private DateTime $trustedOn; + + public function __construct(array $data) + { + $this->contactUuid = $data['contact_uuid']; + $this->signatureUuid = $data['signature_uuid']; + $this->signatureName = $data['signature_name']; + $this->signatureKey = $data['signature_key']; + + if(!isset($data['expires'])) + { + $this->expires = null; + } + else + { + if(is_string($data['expires'])) + { + $this->expires = new DateTime($data['expires']); + } + elseif(is_int($data['expires'])) + { + $this->expires = (new DateTime())->setTimestamp($data['expires']); + } + elseif($data['expires'] instanceof DateTime) + { + $this->expires = $data['expires']; + } + else + { + throw new InvalidArgumentException('Invalid expires property, got type ' . gettype($data['expires'])); + } + } + + if(!isset($data['created'])) + { + throw new InvalidArgumentException('Missing created property'); + } + else + { + if(is_string($data['created'])) + { + $this->created = new DateTime($data['created']); + } + elseif(is_int($data['created'])) + { + $this->created = (new DateTime())->setTimestamp($data['created']); + } + elseif($data['created'] instanceof DateTime) + { + $this->created = $data['created']; + } + else + { + throw new InvalidArgumentException('Invalid created property, got type ' . gettype($data['created'])); + } + } + + if(!isset($data['trusted_on'])) + { + throw new InvalidArgumentException('Missing trusted_on property'); + } + else + { + if(is_string($data['trusted_on'])) + { + $this->trustedOn = new DateTime($data['trusted_on']); + } + elseif(is_int($data['trusted_on'])) + { + $this->trustedOn = (new DateTime())->setTimestamp($data['trusted_on']); + } + elseif($data['trusted_on'] instanceof DateTime) + { + $this->trustedOn = $data['trusted_on']; + } + else + { + throw new InvalidArgumentException('Invalid trusted_on property, got type ' . gettype($data['trusted_on'])); + } + } + } + + public function getContactUuid(): string + { + return $this->contactUuid; + } + + public function getSignatureUuid(): string + { + return $this->signatureUuid; + } + + public function getSignatureName(): string + { + return $this->signatureName; + } + + public function getSignatureKey(): string + { + return $this->signatureKey; + } + + public function getExpires(): ?DateTime + { + return $this->expires; + } + + public function getCreated(): DateTime + { + return $this->created; + } + + public function getTrustedOn(): DateTime + { + return $this->trustedOn; + } /** * @inheritDoc */ - public static function fromArray(array $data): object + public static function fromArray(array $data): ContactKnownKeyRecord { - // TODO: Implement fromArray() method. + return new self($data); } /** @@ -20,6 +144,14 @@ */ public function toArray(): array { - // TODO: Implement toArray() method. + return [ + 'contact_uuid' => $this->contactUuid, + 'signature_uuid' => $this->signatureUuid, + 'signature_name' => $this->signatureName, + 'signature_key' => $this->signatureKey, + 'expires' => $this->expires?->getTimestamp(), + 'created' => $this->created->getTimestamp(), + 'trusted_on' => $this->trustedOn->getTimestamp() + ]; } } \ No newline at end of file diff --git a/src/Socialbox/Objects/Database/SigningKeyRecord.php b/src/Socialbox/Objects/Database/SigningKeyRecord.php index 62ac8af..2ca95f5 100644 --- a/src/Socialbox/Objects/Database/SigningKeyRecord.php +++ b/src/Socialbox/Objects/Database/SigningKeyRecord.php @@ -12,7 +12,7 @@ { private string $peerUuid; private string $uuid; - private ?string $name; + private string $name; private string $publicKey; private SigningKeyState $state; private int $expires; @@ -35,7 +35,7 @@ { $this->peerUuid = $data['peer_uuid']; $this->uuid = $data['uuid']; - $this->name = $data['name'] ?? null; + $this->name = $data['name']; $this->publicKey = $data['public_key']; $this->state = SigningKeyState::tryFrom($data['state']); @@ -115,9 +115,9 @@ /** * Retrieves the name. * - * @return string|null The name, or null if not set. + * @return string The name, or null if not set. */ - public function getName(): ?string + public function getName(): string { return $this->name; } diff --git a/src/Socialbox/Objects/Standard/SigningKey.php b/src/Socialbox/Objects/Standard/SigningKey.php index fdf2ecb..cfc216e 100644 --- a/src/Socialbox/Objects/Standard/SigningKey.php +++ b/src/Socialbox/Objects/Standard/SigningKey.php @@ -11,7 +11,7 @@ class SigningKey implements SerializableInterface { private string $uuid; - private ?string $name; + private string $name; private string $publicKey; private SigningKeyState $state; private int $expires; @@ -35,7 +35,7 @@ public function __construct(array $data) { $this->uuid = $data['uuid']; - $this->name = $data['name'] ?? null; + $this->name = $data['name']; $this->publicKey = $data['public_key']; $this->state = SigningKeyState::from($data['state']); diff --git a/src/Socialbox/SocialClient.php b/src/Socialbox/SocialClient.php index 0295c0b..57c99e6 100644 --- a/src/Socialbox/SocialClient.php +++ b/src/Socialbox/SocialClient.php @@ -729,21 +729,24 @@ * * @param string|PeerAddress $peerAddress The peer address as a string or an instance of PeerAddress. * @param string $signatureUuid The UUID of the signature to resolve. - * @return SigningKey The resolved signing key. + * @return SigningKey|null The resolved signing key. Null if the resolved key was not found * @throws RpcException Thrown if the RPC request fails. */ - public function resolvePeerSignature(string|PeerAddress $peerAddress, string $signatureUuid): SigningKey + public function resolvePeerSignature(string|PeerAddress $peerAddress, string $signatureUuid): ?SigningKey { if($peerAddress instanceof PeerAddress) { $peerAddress = $peerAddress->getAddress(); } - return SigningKey::fromArray($this->sendRequest( + $result = $this->sendRequest( new RpcRequest(StandardMethods::RESOLVE_PEER_SIGNATURE, Utilities::randomCrc32(), [ 'peer' => $peerAddress, 'uuid' => $signatureUuid ]) - )->getResponse()->getResult()); + )->getResponse()->getResult(); + + // Conditional null-return + return $result ? SigningKey::fromArray($result) : null; } } \ No newline at end of file diff --git a/src/Socialbox/Socialbox.php b/src/Socialbox/Socialbox.php index 1b4d0b3..e5dc7ea 100644 --- a/src/Socialbox/Socialbox.php +++ b/src/Socialbox/Socialbox.php @@ -754,10 +754,10 @@ * * @param PeerAddress|string $peerAddress The peer address or string identifier to be resolved. * @param string $signatureUuid The UUID of the signature key to be resolved. - * @return SigningKey The resolved signing key for the peer. + * @return SigningKey|null The resolved signing key for the peer. Null if not found * @throws StandardRpcException If there was an error while resolving the peer signature key. */ - public static function resolvePeerSignature(PeerAddress|string $peerAddress, string $signatureUuid): SigningKey + public static function resolvePeerSignature(PeerAddress|string $peerAddress, string $signatureUuid): ?SigningKey { // Convert string peer address to object PeerAddress if(is_string($peerAddress)) @@ -788,13 +788,13 @@ if($peer === null || !$peer?->isEnabled()) { // Fail if the peer is not found or enabled - throw new StandardRpcException(sprintf('The peer %s does not exist', $peerAddress), StandardError::PEER_NOT_FOUND); + return null; } $signingKey = SigningKeysManager::getSigningKey($peer->getUuid(), $signatureUuid); if($signingKey === null) { - throw new StandardRpcException(sprintf('The requested signing key %s was not found', $signatureUuid), StandardError::NOT_FOUND); + return null; } } catch(StandardRpcException $e)