From b04de2f2a7bd033f94194294d3758b1d3e7abf61 Mon Sep 17 00:00:00 2001 From: netkas Date: Sat, 4 Jan 2025 15:32:42 -0500 Subject: [PATCH] Add methods for deleting and updating peer information --- .../SettingsDeleteDisplayName.php | 39 +++ src/Socialbox/Classes/Validator.php | 10 + src/Socialbox/Enums/StandardMethods.php | 3 + .../Managers/RegisteredPeerManager.php | 252 ++++++++++++++++-- .../Objects/Database/RegisteredPeerRecord.php | 64 ++++- 5 files changed, 343 insertions(+), 25 deletions(-) create mode 100644 src/Socialbox/Classes/StandardMethods/SettingsDeleteDisplayName.php diff --git a/src/Socialbox/Classes/StandardMethods/SettingsDeleteDisplayName.php b/src/Socialbox/Classes/StandardMethods/SettingsDeleteDisplayName.php new file mode 100644 index 0000000..51b4181 --- /dev/null +++ b/src/Socialbox/Classes/StandardMethods/SettingsDeleteDisplayName.php @@ -0,0 +1,39 @@ +isDisplayNameRequired()) + { + return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A display name is required for this server'); + } + + try + { + // Set the password + RegisteredPeerManager::deleteDisplayName($request->getPeer()); + } + catch(Exception $e) + { + throw new StandardException('Failed to set password due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e); + } + + return $rpcRequest->produceResponse(true); + } + } \ No newline at end of file diff --git a/src/Socialbox/Classes/Validator.php b/src/Socialbox/Classes/Validator.php index 9fe24a0..4d0bda5 100644 --- a/src/Socialbox/Classes/Validator.php +++ b/src/Socialbox/Classes/Validator.php @@ -34,4 +34,14 @@ class Validator return preg_match(self::USERNAME_PATTERN, $username) === 1; } + /** + * Validates whether a given phone number conforms to the required format. + * + * @param string $phoneNumber The phone number to validate. Must start with a "+" followed by 1 to 15 digits. + * @return bool Returns true if the phone number is valid according to the format, otherwise false. + */ + public static function validatePhoneNumber(string $phoneNumber): bool + { + return preg_match("/^\+[0-9]{1,15}$/", $phoneNumber) === 1; + } } \ No newline at end of file diff --git a/src/Socialbox/Enums/StandardMethods.php b/src/Socialbox/Enums/StandardMethods.php index 752c3e8..3ffb879 100644 --- a/src/Socialbox/Enums/StandardMethods.php +++ b/src/Socialbox/Enums/StandardMethods.php @@ -11,6 +11,7 @@ use Socialbox\Classes\StandardMethods\GetTermsOfService; use Socialbox\Classes\StandardMethods\Ping; use Socialbox\Classes\StandardMethods\SettingsAddSigningKey; + use Socialbox\Classes\StandardMethods\SettingsDeleteDisplayName; use Socialbox\Classes\StandardMethods\SettingsGetSigningKeys; use Socialbox\Classes\StandardMethods\SettingsSetDisplayName; use Socialbox\Classes\StandardMethods\SettingsSetPassword; @@ -55,6 +56,7 @@ case SETTINGS_SET_PASSWORD = 'settingsSetPassword'; case SETTINGS_SET_OTP = 'settingsSetOtp'; case SETTINGS_SET_DISPLAY_NAME = 'settingsSetDisplayName'; + case SETTINGS_DELETE_DISPLAY_NAME = 'settingsDeleteDisplayName'; case SETTINGS_SET_DISPLAY_PICTURE = 'settingsSetDisplayPicture'; case SETTINGS_SET_EMAIL = 'settingsSetEmail'; case SETTINGS_SET_PHONE = 'settingsSetPhone'; @@ -90,6 +92,7 @@ self::SETTINGS_SET_PASSWORD => SettingsSetPassword::execute($request, $rpcRequest), self::SETTINGS_SET_DISPLAY_NAME => SettingsSetDisplayName::execute($request, $rpcRequest), + self::SETTINGS_DELETE_DISPLAY_NAME => SettingsDeleteDisplayName::execute($request, $rpcRequest), self::SETTINGS_ADD_SIGNING_KEY => SettingsAddSigningKey::execute($request, $rpcRequest), self::SETTINGS_GET_SIGNING_KEYS => SettingsGetSigningKeys::execute($request, $rpcRequest), diff --git a/src/Socialbox/Managers/RegisteredPeerManager.php b/src/Socialbox/Managers/RegisteredPeerManager.php index 960ba4d..0924d45 100644 --- a/src/Socialbox/Managers/RegisteredPeerManager.php +++ b/src/Socialbox/Managers/RegisteredPeerManager.php @@ -2,7 +2,6 @@ namespace Socialbox\Managers; - use DateMalformedStringException; use Exception; use InvalidArgumentException; use PDO; @@ -10,10 +9,10 @@ use Socialbox\Classes\Configuration; use Socialbox\Classes\Database; use Socialbox\Classes\Logger; + use Socialbox\Classes\Validator; use Socialbox\Enums\Flags\PeerFlags; use Socialbox\Exceptions\DatabaseOperationException; use Socialbox\Objects\Database\RegisteredPeerRecord; - use Socialbox\Objects\Database\SecurePasswordRecord; use Socialbox\Objects\PeerAddress; use Symfony\Component\Uid\Uuid; @@ -177,7 +176,7 @@ return new RegisteredPeerRecord($result); } - catch(PDOException | DateMalformedStringException $e) + catch(Exception $e) { throw new DatabaseOperationException('Failed to get the peer from the database', $e); } @@ -365,6 +364,41 @@ } } + /** + * Deletes the display name of a registered peer identified by a unique identifier or RegisteredPeerRecord object. + * + * @param string|RegisteredPeerRecord $peer The unique identifier of the registered peer, or an instance of RegisteredPeerRecord. + * @return void + * @throws InvalidArgumentException If the peer is external and its display name cannot be deleted. + * @throws DatabaseOperationException If there is an error during the database operation. + */ + public static function deleteDisplayName(string|RegisteredPeerRecord $peer): void + { + if(is_string($peer)) + { + $peer = self::getPeer($peer); + } + + if($peer->isExternal()) + { + throw new InvalidArgumentException('Cannot delete the display name of an external peer'); + } + + Logger::getLogger()->verbose(sprintf("Deleting display name of peer %s", $peer->getUuid())); + + try + { + $statement = Database::getConnection()->prepare('UPDATE `registered_peers` SET display_name=NULL WHERE uuid=?'); + $uuid = $peer->getUuid(); + $statement->bindParam(1, $uuid); + $statement->execute(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to delete the display name of the peer in the database', $e); + } + } + /** * Updates the display picture of a registered peer in the database. * @@ -420,37 +454,211 @@ } /** - * Retrieves the password authentication record associated with the given unique peer identifier or a RegisteredPeerRecord object. + * Deletes the display picture of a registered peer based on the given unique identifier or RegisteredPeerRecord object. * - * @param string|RegisteredPeerRecord $peerUuid The unique identifier of the peer, or an instance of RegisteredPeerRecord. - * @return SecurePasswordRecord|null Returns a SecurePasswordRecord object if a password authentication record exists, otherwise null. + * @param string|RegisteredPeerRecord $peer The unique identifier of the registered peer, or an instance of RegisteredPeerRecord. + * @return void + * @throws InvalidArgumentException If the peer is external and its display picture cannot be deleted. * @throws DatabaseOperationException If there is an error during the database operation. */ - public static function getPasswordAuthentication(string|RegisteredPeerRecord $peerUuid): ?SecurePasswordRecord + public static function deleteDisplayPicture(string|RegisteredPeerRecord $peer): void { - if($peerUuid instanceof RegisteredPeerRecord) + if(is_string($peer)) { - $peerUuid = $peerUuid->getUuid(); + $peer = self::getPeer($peer); } + if($peer->isExternal()) + { + throw new InvalidArgumentException('Cannot delete the display picture of an external peer'); + } + + Logger::getLogger()->verbose(sprintf("Deleting display picture of peer %s", $peer->getUuid())); + try { - $statement = Database::getConnection()->prepare('SELECT * FROM `authentication_passwords` WHERE peer_uuid=?'); - $statement->bindParam(1, $peerUuid); + $statement = Database::getConnection()->prepare('UPDATE `registered_peers` SET display_picture=NULL WHERE uuid=?'); + $uuid = $peer->getUuid(); + $statement->bindParam(1, $uuid); $statement->execute(); - - $result = $statement->fetch(PDO::FETCH_ASSOC); - - if($result === false) - { - return null; - } - - return new SecurePasswordRecord($result); } - catch(PDOException | DateMalformedStringException $e) + catch(PDOException $e) { - throw new DatabaseOperationException('Failed to get the secure password record from the database', $e); + throw new DatabaseOperationException('Failed to delete the display picture of the peer in the database', $e); + } + } + + /** + * Updates the email address of a registered peer. + * + * @param string|RegisteredPeerRecord $peer The unique identifier of the peer, or an instance of RegisteredPeerRecord. + * @param string $emailAddress The new email address to be assigned to the peer. + * @return void + * @throws InvalidArgumentException If the email address is empty, exceeds 256 characters, is not a valid email format, or if the peer is external. + * @throws DatabaseOperationException If there is an error during the database operation. + */ + public static function updateEmailAddress(string|RegisteredPeerRecord $peer, string $emailAddress): void + { + if(empty($emailAddress)) + { + throw new InvalidArgumentException('The email address cannot be empty'); + } + + if(strlen($emailAddress) > 256) + { + throw new InvalidArgumentException('The email address cannot exceed 256 characters'); + } + + if(filter_var($emailAddress, FILTER_VALIDATE_EMAIL) === false) + { + throw new InvalidArgumentException('The email address is not valid'); + } + + if(is_string($peer)) + { + $peer = self::getPeer($peer); + } + + if($peer->isExternal()) + { + throw new InvalidArgumentException('Cannot update the email address of an external peer'); + } + + Logger::getLogger()->verbose(sprintf("Updating email address of peer %s to %s", $peer->getUuid(), $emailAddress)); + + try + { + $statement = Database::getConnection()->prepare('UPDATE `registered_peers` SET email_address=? WHERE uuid=?'); + $statement->bindParam(1, $emailAddress); + $uuid = $peer->getUuid(); + $statement->bindParam(2, $uuid); + $statement->execute(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to update the email address of the peer in the database', $e); + } + } + + /** + * Deletes the email address of a registered peer identified by either a unique identifier or a RegisteredPeerRecord object. + * + * @param string|RegisteredPeerRecord $peer The unique identifier of the registered peer, or an instance of RegisteredPeerRecord. + * @return void + * @throws InvalidArgumentException If the peer is external and its email address cannot be deleted. + * @throws DatabaseOperationException If there is an error during the database operation. + */ + public static function deleteEmailAddress(string|RegisteredPeerRecord $peer): void + { + if(is_string($peer)) + { + $peer = self::getPeer($peer); + } + + if($peer->isExternal()) + { + throw new InvalidArgumentException('Cannot delete the email address of an external peer'); + } + + Logger::getLogger()->verbose(sprintf("Deleting email address of peer %s", $peer->getUuid())); + + try + { + $statement = Database::getConnection()->prepare('UPDATE `registered_peers` SET email_address=NULL WHERE uuid=?'); + $uuid = $peer->getUuid(); + $statement->bindParam(1, $uuid); + $statement->execute(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to delete the email address of the peer in the database', $e); + } + } + + /** + * Updates the phone number of the specified registered peer. + * + * @param string|RegisteredPeerRecord $peer The unique identifier of the registered peer, or an instance of RegisteredPeerRecord. + * @param string $phoneNumber The new phone number to be set for the peer. + * @return void + * @throws InvalidArgumentException If the phone number is empty, exceeds 16 characters, is invalid, or if the peer is external. + * @throws DatabaseOperationException If there is an error during the database operation. + */ + public static function updatePhoneNumber(string|RegisteredPeerRecord $peer, string $phoneNumber): void + { + if(empty($phoneNumber)) + { + throw new InvalidArgumentException('The phone number cannot be empty'); + } + + if(strlen($phoneNumber) > 16) + { + throw new InvalidArgumentException('The phone number cannot exceed 16 characters'); + } + + if(!Validator::validatePhoneNumber($phoneNumber)) + { + throw new InvalidArgumentException('The phone number is not valid'); + } + + if(is_string($peer)) + { + $peer = self::getPeer($peer); + } + + if($peer->isExternal()) + { + throw new InvalidArgumentException('Cannot update the phone number of an external peer'); + } + + Logger::getLogger()->verbose(sprintf("Updating phone number of peer %s to %s", $peer->getUuid(), $phoneNumber)); + + try + { + $statement = Database::getConnection()->prepare('UPDATE `registered_peers` SET phone_number=? WHERE uuid=?'); + $statement->bindParam(1, $phoneNumber); + $uuid = $peer->getUuid(); + $statement->bindParam(2, $uuid); + $statement->execute(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to update the phone number of the peer in the database', $e); + } + } + + /** + * Deletes the phone number of a registered peer based on the given unique identifier or RegisteredPeerRecord object. + * + * @param string|RegisteredPeerRecord $peer The unique identifier of the registered peer, or an instance of RegisteredPeerRecord. + * @return void This method does not return a value. + * @throws InvalidArgumentException If the peer is external and its phone number cannot be deleted. + * @throws DatabaseOperationException If there is an error during the database operation. + */ + public static function deletePhoneNumber(string|RegisteredPeerRecord $peer): void + { + if(is_string($peer)) + { + $peer = self::getPeer($peer); + } + + if($peer->isExternal()) + { + throw new InvalidArgumentException('Cannot delete the phone number of an external peer'); + } + + Logger::getLogger()->verbose(sprintf("Deleting phone number of peer %s", $peer->getUuid())); + + try + { + $statement = Database::getConnection()->prepare('UPDATE `registered_peers` SET phone_number=NULL WHERE uuid=?'); + $uuid = $peer->getUuid(); + $statement->bindParam(1, $uuid); + $statement->execute(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to delete the phone number of the peer in the database', $e); } } } \ No newline at end of file diff --git a/src/Socialbox/Objects/Database/RegisteredPeerRecord.php b/src/Socialbox/Objects/Database/RegisteredPeerRecord.php index 4f6171e..56ad984 100644 --- a/src/Socialbox/Objects/Database/RegisteredPeerRecord.php +++ b/src/Socialbox/Objects/Database/RegisteredPeerRecord.php @@ -16,6 +16,9 @@ private string $server; private ?string $displayName; private ?string $displayPicture; + private ?string $emailAddress; + private ?string $phoneNumber; + private ?DateTime $birthday; /** * @var PeerFlags[] */ @@ -27,7 +30,6 @@ * Constructor for initializing class properties from provided data. * * @param array $data Array containing initialization data. - * @throws \DateMalformedStringException */ public function __construct(array $data) { @@ -36,6 +38,25 @@ $this->server = $data['server']; $this->displayName = $data['display_name'] ?? null; $this->displayPicture = $data['display_picture'] ?? null; + $this->emailAddress = $data['email_address'] ?? null; + $this->phoneNumber = $data['phone_number'] ?? null; + + if(is_int($data['birthday'])) + { + $this->birthday = (new DateTime())->setTimestamp($data['birthday']); + } + elseif(is_string($data['birthday'])) + { + $this->birthday = new DateTime($data['birthday']); + } + elseif($data['birthday'] instanceof DateTime) + { + $this->birthday = $data['birthday']; + } + else + { + $this->birthday = null; + } if($data['flags']) { @@ -48,13 +69,17 @@ $this->enabled = $data['enabled']; - if (is_string($data['created'])) + if(is_int($data['created'])) + { + $this->created = (new DateTime())->setTimestamp($data['created']); + } + elseif(is_string($data['created'])) { $this->created = new DateTime($data['created']); } else { - $this->created = $data['created']; + throw new \InvalidArgumentException("The created field must be a valid timestamp or date string."); } } @@ -118,6 +143,36 @@ return $this->displayPicture; } + /** + * Retrieves the email address. + * + * @return string|null The email address if set, or null otherwise. + */ + public function getEmailAddress(): ?string + { + return $this->emailAddress; + } + + /** + * Retrieves the phone number. + * + * @return string|null The phone number if set, or null otherwise. + */ + public function getPhoneNumber(): ?string + { + return $this->phoneNumber; + } + + /** + * Retrieves the birthday. + * + * @return DateTime|null The birthday if set, or null otherwise. + */ + public function getBirthday(): ?DateTime + { + return $this->birthday; + } + /** * Retrieves the flags. * @@ -221,6 +276,9 @@ 'server' => $this->server, 'display_name' => $this->displayName, 'display_picture' => $this->displayPicture, + 'email_address' => $this->emailAddress, + 'phone_number' => $this->phoneNumber, + 'birthday' => $this->birthday?->getTimestamp(), 'flags' => PeerFlags::toString($this->flags), 'enabled' => $this->enabled, 'created' => $this->created