Refactored Peer Information to use InformationFields rather than being hard-coded into the peer record

This commit is contained in:
netkas 2025-01-24 15:10:20 -05:00
parent 75de51c910
commit f689e36378
45 changed files with 1422 additions and 1337 deletions

View file

@ -133,10 +133,14 @@
$config->setDefault('registration.password_required', true);
$config->setDefault('registration.otp_required', false);
$config->setDefault('registration.display_name_required', true);
$config->setDefault('registration.first_name_required', false);
$config->setDefault('registration.middle_name_required', false);
$config->setDefault('registration.last_name_required', false);
$config->setDefault('registration.display_picture_required', false);
$config->setDefault('registration.email_address_required', false);
$config->setDefault('registration.phone_number_required', false);
$config->setDefault('registration.birthday_required', false);
$config->setDefault('registration.url_required', false);
$config->setDefault('registration.image_captcha_verification_required', true);
// Authentication configuration
@ -162,6 +166,16 @@
// recommendation: 100
$config->setDefault('policies.get_contacts_limit', 100);
// Default privacy states for information fields associated with the peer
$config->setDefault('policies.default_display_picture_privacy', 'PUBLIC');
$config->setDefault('policies.default_first_name_privacy', 'CONTACTS');
$config->setDefault('policies.default_middle_name_privacy', 'PRIVATE');
$config->setDefault('policies.default_last_name_privacy', 'PRIVATE');
$config->setDefault('policies.default_email_address_privacy', 'CONTACTS');
$config->setDefault('policies.default_phone_number_privacy', 'CONTACTS');
$config->setDefault('policies.default_birthday_privacy', 'PRIVATE');
$config->setDefault('policies.default_url_privacy', 'PUBLIC');
// Storage configuration
$config->setDefault('storage.path', '/etc/socialbox'); // The main path for file storage
$config->setDefault('storage.user_display_images_path', 'user_profiles'); // eg; `/etc/socialbox/user_profiles`

View file

@ -2,6 +2,8 @@
namespace Socialbox\Classes\Configuration;
use Socialbox\Enums\PrivacyState;
class PoliciesConfiguration
{
private int $maxSigningKeys;
@ -9,7 +11,29 @@
private int $imageCaptchaExpires;
private int $peerSyncInterval;
private int $getContactsLimit;
private PrivacyState $defaultDisplayPicturePrivacy;
private PrivacyState $defaultFirstNamePrivacy;
private PrivacyState $defaultMiddleNamePrivacy;
private PrivacyState $defaultLastNamePrivacy;
private PrivacyState $defaultEmailAddressPrivacy;
private PrivacyState $defaultPhoneNumberPrivacy;
private PrivacyState $defaultBirthdayPrivacy;
private PrivacyState $defaultUrlPrivacy;
/**
* Constructor method for initializing the policies configuration
*
* @param array $data An associative array containing the following keys:
* 'max_signing_keys', 'session_inactivity_expires',
* 'image_captcha_expires', 'peer_sync_interval',
* 'get_contacts_limit', 'default_display_picture_privacy',
* 'default_first_name_privacy', 'default_middle_name_privacy',
* 'default_last_name_privacy', 'default_email_address_privacy',
* 'default_phone_number_privacy', 'default_birthday_privacy',
* 'default_url_privacy'.
*
* @return void
*/
public function __construct(array $data)
{
$this->maxSigningKeys = $data['max_signing_keys'];
@ -17,6 +41,14 @@
$this->imageCaptchaExpires = $data['image_captcha_expires'];
$this->peerSyncInterval = $data['peer_sync_interval'];
$this->getContactsLimit = $data['get_contacts_limit'];
$this->defaultDisplayPicturePrivacy = PrivacyState::tryFrom($data['default_display_picture_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultFirstNamePrivacy = PrivacyState::tryFrom($data['default_first_name_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultMiddleNamePrivacy = PrivacyState::tryFrom($data['default_middle_name_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultLastNamePrivacy = PrivacyState::tryFrom($data['default_last_name_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultEmailAddressPrivacy = PrivacyState::tryFrom($data['default_email_address_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultPhoneNumberPrivacy = PrivacyState::tryFrom($data['default_phone_number_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultBirthdayPrivacy = PrivacyState::tryFrom($data['default_birthday_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultUrlPrivacy = PrivacyState::tryFrom($data['default_url_privacy']) ?? PrivacyState::PRIVATE;
}
/**
@ -70,4 +102,84 @@
{
return $this->getContactsLimit;
}
/**
* Returns the default privacy state for the display picture
*
* @return PrivacyState
*/
public function getDefaultDisplayPicturePrivacy(): PrivacyState
{
return $this->defaultDisplayPicturePrivacy;
}
/**
* Returns the default privacy state for the first name
*
* @return PrivacyState
*/
public function getDefaultFirstNamePrivacy(): PrivacyState
{
return $this->defaultFirstNamePrivacy;
}
/**
* Returns the default privacy state for the middle name
*
* @return PrivacyState
*/
public function getDefaultMiddleNamePrivacy(): PrivacyState
{
return $this->defaultMiddleNamePrivacy;
}
/**
* Returns the default privacy state for the last name
*
* @return PrivacyState
*/
public function getDefaultLastNamePrivacy(): PrivacyState
{
return $this->defaultLastNamePrivacy;
}
/**
* Returns the default privacy state for the email address
*
* @return PrivacyState
*/
public function getDefaultEmailAddressPrivacy(): PrivacyState
{
return $this->defaultEmailAddressPrivacy;
}
/**
* Returns the default privacy state for the phone number
*
* @return PrivacyState
*/
public function getDefaultPhoneNumberPrivacy(): PrivacyState
{
return $this->defaultPhoneNumberPrivacy;
}
/**
* Returns the default privacy state for the birthday
*
* @return PrivacyState
*/
public function getDefaultBirthdayPrivacy(): PrivacyState
{
return $this->defaultBirthdayPrivacy;
}
/**
* Returns the default privacy state for the URL
*
* @return PrivacyState
*/
public function getDefaultUrlPrivacy(): PrivacyState
{
return $this->defaultUrlPrivacy;
}
}

View file

@ -17,10 +17,14 @@
private bool $passwordRequired;
private bool $otpRequired;
private bool $displayNameRequired;
private bool $firstNameRequired;
private bool $middleNameRequired;
private bool $lastNameRequired;
private bool $displayPictureRequired;
private bool $emailAddressRequired;
private bool $phoneNumberRequired;
private bool $birthdayRequired;
private bool $urlRequired;
private bool $imageCaptchaVerificationRequired;
/**
@ -49,10 +53,14 @@
$this->passwordRequired = (bool)$data['password_required'];
$this->otpRequired = (bool)$data['otp_required'];
$this->displayNameRequired = (bool)$data['display_name_required'];
$this->firstNameRequired = (bool)$data['first_name_required'];
$this->middleNameRequired = (bool)$data['middle_name_required'];
$this->lastNameRequired = (bool)$data['last_name_required'];
$this->displayPictureRequired = (bool)$data['display_picture_required'];
$this->emailAddressRequired = (bool)$data['email_address_required'];
$this->phoneNumberRequired = (bool)$data['phone_number_required'];
$this->birthdayRequired = (bool)$data['birthday_required'];
$this->urlRequired = (bool)$data['url_required'];
$this->imageCaptchaVerificationRequired = (bool)$data['image_captcha_verification_required'];
}
@ -186,6 +194,36 @@
return $this->displayNameRequired;
}
/**
* Checks if a first name is required.
*
* @return bool Returns true if a first name is required, false otherwise.
*/
public function isFirstNameRequired(): bool
{
return $this->firstNameRequired;
}
/**
* Checks if a middle name is required.
*
* @return bool Returns true if a middle name is required, false otherwise.
*/
public function isMiddleNameRequired(): bool
{
return $this->middleNameRequired;
}
/**
* Checks if a last name is required.
*
* @return bool Returns true if a last name is required, false otherwise.
*/
public function isLastNameRequired(): bool
{
return $this->lastNameRequired;
}
/**
* Checks if a display picture is required.
*
@ -226,6 +264,16 @@
return $this->birthdayRequired;
}
/**
* Determines if a URL is required.
*
* @return bool Returns true if a URL is required, false otherwise.
*/
public function isUrlRequired(): bool
{
return $this->urlRequired;
}
/**
* Determines if image CAPTCHA verification is required.
*

View file

@ -7,7 +7,7 @@ create table authentication_otp
constraint authentication_otp_peer_uuid_uindex
unique (peer_uuid) comment 'The Peer UUID unique Index',
constraint authentication_otp_registered_peers_uuid_fk
foreign key (peer_uuid) references registered_peers (uuid)
foreign key (peer_uuid) references peers (uuid)
on update cascade on delete cascade
)
comment 'Table for housing encrypted OTP secrets for for verification';

View file

@ -10,7 +10,7 @@ create table captcha_images
constraint captchas_peer_uuid_uindex
unique (peer_uuid) comment 'The Primary Unique Index for the peer UUID',
constraint captchas_registered_peers_uuid_fk
foreign key (peer_uuid) references registered_peers (uuid)
foreign key (peer_uuid) references peers (uuid)
on update cascade on delete cascade
);

View file

@ -0,0 +1,19 @@
create table peer_information
(
peer_uuid varchar(36) not null comment 'The Unique Universal Identifier for the peer',
property_name enum ('DISPLAY_NAME', 'DISPLAY_PICTURE', 'FIRST_NAME', 'MIDDLE_NAME', 'LAST_NAME', 'EMAIL_ADDRESS', 'PHONE_NUMBER', 'BIRTHDAY', 'URL') not null comment 'The name of the property',
property_value varchar(256) not null comment 'The value of the property associated with the peer',
privacy_state enum ('PUBLIC', 'PRIVATE', 'CONTACTS', 'TRUSTED') default 'PRIVATE' not null comment 'The privacy setting for the information property',
primary key (property_name, peer_uuid),
constraint peer_information_peer_uuid_property_name_uindex
unique (peer_uuid, property_name) comment 'The Unique Index for the the peer uuid & property name combination',
constraint peer_information_registered_peers_uuid_fk
foreign key (peer_uuid) references peers (uuid)
on update cascade on delete cascade
)
comment 'Table for housing peer information';
create index peer_information_peer_uuid_index
on peer_information (peer_uuid)
comment 'The index for the peer uuid';

View file

@ -0,0 +1,37 @@
create table peers
(
uuid varchar(36) default uuid() not null comment 'The Primary index for the peer uuid'
primary key,
username varchar(255) not null comment 'The Unique username associated with the peer',
server varchar(255) default 'host' not null comment 'The server that this peer is registered to',
flags text null comment 'Comma seprted flags associated with the peer',
enabled tinyint(1) default 0 not null comment 'Boolean column to indicate if this account is Enabled, by default it''s Disabled until the account is verified.',
updated timestamp default current_timestamp() not null comment 'The Timestamp for when this record was last updated',
created timestamp default current_timestamp() not null comment 'The Timestamp for when the peer was registered on the network',
constraint registered_peers_server_username_uindex
unique (server, username) comment 'The Unique Username + Server Index Pair',
constraint registered_peers_uuid_uindex
unique (uuid) comment 'The Primary index for the peer uuid'
)
comment 'Table for housing registered peers under this network';
create index registered_peers_enabled_index
on peers (enabled)
comment 'The index of the enabled column for registered peers';
create index registered_peers_registered_index
on peers (created)
comment 'The Index for the reigstered column of the peer';
create index registered_peers_server_index
on peers (server)
comment 'The Index for the peer''s server';
create index registered_peers_updated_index
on peers (updated)
comment 'The Index for the update column';
create index registered_peers_username_index
on peers (username)
comment 'The index for the registered username';

View file

@ -1,39 +0,0 @@
create table registered_peers
(
uuid varchar(36) default uuid() not null comment 'The Primary index for the peer uuid'
primary key,
username varchar(255) not null comment 'The Unique username associated with the peer',
server varchar(255) default 'host' not null comment 'The server that this peer is registered to',
display_name varchar(255) null comment 'Optional. The Non-Unique Display name of the peer',
display_picture varchar(36) null comment 'The UUID of the display picture that is used, null if none is set.',
flags text null comment 'Comma seprted flags associated with the peer',
enabled tinyint(1) default 0 not null comment 'Boolean column to indicate if this account is Enabled, by default it''s Disabled until the account is verified.',
updated timestamp default current_timestamp() not null comment 'The Timestamp for when this record was last updated',
created timestamp default current_timestamp() not null comment 'The Timestamp for when the peer was registered on the network',
constraint registered_peers_server_username_uindex
unique (server, username) comment 'The Unique Username + Server Index Pair',
constraint registered_peers_uuid_uindex
unique (uuid) comment 'The Primary index for the peer uuid'
)
comment 'Table for housing registered peers under this network';
create index registered_peers_enabled_index
on registered_peers (enabled)
comment 'The index of the enabled column for registered peers';
create index registered_peers_registered_index
on registered_peers (created)
comment 'The Index for the reigstered column of the peer';
create index registered_peers_server_index
on registered_peers (server)
comment 'The Index for the peer''s server';
create index registered_peers_updated_index
on registered_peers (updated)
comment 'The Index for the update column';
create index registered_peers_username_index
on registered_peers (username)
comment 'The index for the registered username';

View file

@ -20,7 +20,7 @@ create table sessions
constraint sessions_uuid_uindex
unique (uuid) comment 'The Unique Primary index for the session UUID',
constraint sessions_registered_peers_uuid_fk
foreign key (peer_uuid) references registered_peers (uuid)
foreign key (peer_uuid) references peers (uuid)
on update cascade on delete cascade
);

View file

@ -0,0 +1,147 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Enums\Flags\SessionFlags;
use Socialbox\Enums\PrivacyState;
use Socialbox\Enums\StandardError;
use Socialbox\Enums\Types\InformationFieldName;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\PeerInformationManager;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsAddInformationField extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
// Field parameter is required
if(!$rpcRequest->containsParameter('field'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The required field parameter is missing');
}
$fieldName = InformationFieldName::tryFrom(strtoupper($rpcRequest->getParameter('field')));
if($fieldName === null)
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided field parameter is invalid');
}
// Value parameter is required
if(!$rpcRequest->containsParameter('value'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The required value parameter is missing');
}
$value = $rpcRequest->getParameter('value');
if(!$fieldName->validate($value))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided value parameter is invalid');
}
// Privacy parameter is optional
$privacy = null;
if($rpcRequest->containsParameter('privacy') && $rpcRequest->getParameter('privacy') !== null)
{
$privacy = PrivacyState::tryFrom(strtoupper($rpcRequest->getParameter('privacy')));
if($privacy === null)
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided privacy parameter is invalid');
}
}
try
{
$peer = $request->getPeer();
}
catch (DatabaseOperationException $e)
{
throw new StandardException('Failed to retrieve current peer information', StandardError::INTERNAL_SERVER_ERROR, $e);
}
try
{
if (PeerInformationManager::fieldExists($peer, $fieldName))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided field parameter is already registered, use settingsUpdateInformationField or settingsUpdateInformationPrivacy instead');
}
PeerInformationManager::addField($peer, $fieldName, $value, $privacy);
}
catch (DatabaseOperationException $e)
{
throw new StandardException('Failed to add the information field', StandardError::INTERNAL_SERVER_ERROR, $e);
}
// Update the session flow if necessary
try
{
switch($fieldName)
{
case InformationFieldName::DISPLAY_NAME:
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_DISPLAY_NAME]);
break;
case InformationFieldName::FIRST_NAME:
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_FIRST_NAME]);
break;
case InformationFieldName::MIDDLE_NAME:
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_MIDDLE_NAME]);
break;
case InformationFieldName::LAST_NAME:
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_LAST_NAME]);
break;
case InformationFieldName::BIRTHDAY:
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_BIRTHDAY]);
break;
case InformationFieldName::PHONE_NUMBER:
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_PHONE]);
break;
case InformationFieldName::EMAIL_ADDRESS:
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_EMAIL]);
break;
case InformationFieldName::URL:
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_URL]);
break;
default:
break;
}
}
catch (Exception $e)
{
try
{
// Rollback the information field otherwise the peer will be stuck with an incomplete session flow
PeerInformationManager::deleteField($peer, $fieldName);
}
catch (DatabaseOperationException $e)
{
// Something is seriously wrong if we can't roll back the information field
throw new StandardException('Failed to rollback the information field', StandardError::INTERNAL_SERVER_ERROR, $e);
}
if($e instanceof StandardException)
{
throw $e;
}
throw new StandardException('Failed to update the session flow', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Configuration;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsDeleteBirthday extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(Configuration::getRegistrationConfiguration()->isBirthdayRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A birthday is required for this server');
}
try
{
RegisteredPeerManager::deleteBirthday($request->getPeer());
}
catch(Exception $e)
{
throw new StandardException('Failed to delete birthday ' . $e->getMessage(), StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Configuration;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsDeleteDisplayName extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(Configuration::getRegistrationConfiguration()->isDisplayNameRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A display name is required for this server');
}
try
{
RegisteredPeerManager::deleteDisplayName($request->getPeer());
}
catch(Exception $e)
{
throw new StandardException('Failed to delete display name due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Configuration;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsDeleteDisplayPicture extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(Configuration::getRegistrationConfiguration()->isDisplayPictureRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A display picture is required for this server');
}
try
{
RegisteredPeerManager::deleteDisplayPicture($request->getPeer());
}
catch(Exception $e)
{
throw new StandardException('Failed to delete display picture: ' . $e->getMessage(), StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Configuration;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsDeleteEmailAddress extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(Configuration::getRegistrationConfiguration()->isEmailAddressRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A email address is required for this server');
}
try
{
RegisteredPeerManager::deleteEmailAddress($request->getPeer());
}
catch(Exception $e)
{
throw new StandardException('Failed to delete email address: ' . $e->getMessage(), StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -0,0 +1,119 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Configuration;
use Socialbox\Enums\StandardError;
use Socialbox\Enums\Types\InformationFieldName;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\PeerInformationManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsDeleteInformationField extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
// Field parameter is required
if(!$rpcRequest->containsParameter('field'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The required field parameter is missing');
}
$fieldName = InformationFieldName::tryFrom(strtoupper($rpcRequest->getParameter('field')));
if($fieldName === null)
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided field parameter is invalid');
}
try
{
if(!PeerInformationManager::fieldExists($request->getPeer(), $fieldName))
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The information field does not exist');
}
}
catch(DatabaseOperationException $e)
{
throw new StandardException('Failed to check if the information field exists', StandardError::INTERNAL_SERVER_ERROR, $e);
}
switch($fieldName)
{
case InformationFieldName::DISPLAY_NAME:
if(Configuration::getRegistrationConfiguration()->isDisplayNameRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A display name is required for this server');
}
break;
case InformationFieldName::FIRST_NAME:
if(Configuration::getRegistrationConfiguration()->isFirstNameRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A first name is required for this server');
}
break;
case InformationFieldName::MIDDLE_NAME:
if(Configuration::getRegistrationConfiguration()->isMiddleNameRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A middle name is required for this server');
}
break;
case InformationFieldName::LAST_NAME:
if(Configuration::getRegistrationConfiguration()->isLastNameRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A last name is required for this server');
}
break;
case InformationFieldName::BIRTHDAY:
if(Configuration::getRegistrationConfiguration()->isBirthdayRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A birthday is required for this server');
}
break;
case InformationFieldName::PHONE_NUMBER:
if(Configuration::getRegistrationConfiguration()->isPhoneNumberRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A phone number is required for this server');
}
break;
case InformationFieldName::EMAIL_ADDRESS:
if(Configuration::getRegistrationConfiguration()->isEmailAddressRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'An email address is required for this server');
}
break;
case InformationFieldName::URL:
if(Configuration::getRegistrationConfiguration()->isUrlRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A URL is required for this server');
}
break;
default:
break;
}
try
{
PeerInformationManager::deleteField($request->getPeer(), $fieldName);
}
catch(DatabaseOperationException $e)
{
throw new StandardException('Failed to delete the information field', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Configuration;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsDeletePhoneNumber extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(Configuration::getRegistrationConfiguration()->isPhoneNumberRequired())
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'A phone number is required for this server');
}
try
{
RegisteredPeerManager::deletePhoneNumber($request->getPeer());
}
catch(Exception $e)
{
throw new StandardException('Failed to delete phone number: ' . $e->getMessage(), StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Socialbox\Abstracts\Method;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\PeerInformationManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsGetInformationFields extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
try
{
$fieldRecords = PeerInformationManager::getFields($request->getPeer());
}
catch(DatabaseOperationException $e)
{
throw new StandardException('Failed to retrieve existing information fields', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(array_map(fn($result) => $result->toInformationFieldState(), $fieldRecords));
}
}

View file

@ -1,63 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Validator;
use Socialbox\Enums\Flags\SessionFlags;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsSetBirthday extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(!$rpcRequest->containsParameter('month'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'month' parameter");
}
if(!$rpcRequest->containsParameter('day'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'day' parameter");
}
if(!$rpcRequest->containsParameter('year'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'year' parameter");
}
$month = $rpcRequest->getParameter('month');
$day = $rpcRequest->getParameter('day');
$year = $rpcRequest->getParameter('year');
if(!Validator::validateDate($month, $day, $year))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Invalid date provided, must be a valid gregorian calender date.");
}
try
{
// Set the password
RegisteredPeerManager::updateBirthday($request->getPeer(), $month, $day, $year);
// Check & update the session flow
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_BIRTHDAY]);
}
catch(Exception $e)
{
throw new StandardException('Failed to set birthday due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,48 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use ncc\ThirdParty\Symfony\Process\Exception\InvalidArgumentException;
use Socialbox\Abstracts\Method;
use Socialbox\Enums\Flags\SessionFlags;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsSetDisplayName extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(!$rpcRequest->containsParameter('name'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'name' parameter");
}
try
{
// Update the display name
RegisteredPeerManager::updateDisplayName($request->getPeer(), $rpcRequest->getParameter('name'));
// Check & update the session flow
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_DISPLAY_NAME]);
}
catch(InvalidArgumentException)
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Invalid display name');
}
catch(Exception $e)
{
throw new StandardException('Failed to set password due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,65 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Configuration;
use Socialbox\Classes\Utilities;
use Socialbox\Enums\Flags\SessionFlags;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsSetDisplayPicture extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(!$rpcRequest->containsParameter('image'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'image' parameter");
}
if(strlen($rpcRequest->getParameter('image')) > Configuration::getStorageConfiguration()->getUserDisplayImagesMaxSize())
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Image size exceeds the maximum allowed size of " . Configuration::getStorageConfiguration()->getUserDisplayImagesMaxSize() . " bytes");
}
try
{
$decodedImage = @base64_decode($rpcRequest->getParameter('image'));
if($decodedImage === false)
{
return $rpcRequest->produceError(StandardError::RPC_BAD_REQUEST, "Failed to decode JPEG image base64 data");
}
$sanitizedImage = Utilities::resizeImage(Utilities::sanitizeJpeg($decodedImage), 256, 256);
}
catch(Exception $e)
{
throw new StandardException('Failed to process JPEG image: ' . $e->getMessage(), StandardError::RPC_BAD_REQUEST, $e);
}
try
{
// Set the password
RegisteredPeerManager::updateDisplayPicture($request->getPeer(), $sanitizedImage);
// Check & update the session flow
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_DISPLAY_PICTURE]);
}
catch(Exception $e)
{
throw new StandardException('Failed to update display picture: ' . $e->getMessage(), StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,50 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Validator;
use Socialbox\Enums\Flags\SessionFlags;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class
SettingsSetEmailAddress extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(!$rpcRequest->containsParameter('email_address'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'email_address' parameter");
}
if(!Validator::validateEmailAddress($rpcRequest->getParameter('email_address')))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Invalid 'email_address' parameter, must be a valid email address");
}
try
{
// Set the password
RegisteredPeerManager::updateEmailAddress($request->getPeer(), $rpcRequest->getParameter('email_address'));
// Check & update the session flow
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_EMAIL]);
}
catch(Exception $e)
{
throw new StandardException('Failed to set email address due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -1,49 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Exception;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Validator;
use Socialbox\Enums\Flags\SessionFlags;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsSetPhoneNumber extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(!$rpcRequest->containsParameter('phone_number'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'phone_number' parameter");
}
if(!Validator::validatePhoneNumber($rpcRequest->getParameter('phone_number')))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Invalid 'phone_number' parameter, must be a valid phone number");
}
try
{
// Set the phone number
RegisteredPeerManager::updatePhoneNumber($request->getPeer(), $rpcRequest->getParameter('phone_number'));
// Check & update the session flow
SessionManager::updateFlow($request->getSession(), [SessionFlags::SET_PHONE]);
}
catch(Exception $e)
{
throw new StandardException('Failed to set phone number due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Socialbox\Abstracts\Method;
use Socialbox\Enums\StandardError;
use Socialbox\Enums\Types\InformationFieldName;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\PeerInformationManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsUpdateInformationField extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
// Field parameter is required
if(!$rpcRequest->containsParameter('field'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The required field parameter is missing');
}
$fieldName = InformationFieldName::tryFrom(strtoupper($rpcRequest->getParameter('field')));
if($fieldName === null)
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided field parameter is invalid');
}
// Value parameter is required
if(!$rpcRequest->containsParameter('value'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The required value parameter is missing');
}
$value = $rpcRequest->getParameter('value');
if(!$fieldName->validate($value))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided value parameter is invalid');
}
try
{
$peer = $request->getPeer();
if(!PeerInformationManager::fieldExists($peer, $fieldName))
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The information field does not exist');
}
PeerInformationManager::updateField($peer, $fieldName, $value);
}
catch(DatabaseOperationException $e)
{
throw new StandardException('Failed to update the information field', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Socialbox\Abstracts\Method;
use Socialbox\Enums\PrivacyState;
use Socialbox\Enums\StandardError;
use Socialbox\Enums\Types\InformationFieldName;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\PeerInformationManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
class SettingsUpdateInformationPrivacy extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
// Field parameter is required
if(!$rpcRequest->containsParameter('field'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The required field parameter is missing');
}
$fieldName = InformationFieldName::tryFrom(strtoupper($rpcRequest->getParameter('field')));
if($fieldName === null)
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided field parameter is invalid');
}
// Privacy parameter is required
$privacy = null;
if(!$rpcRequest->containsParameter('privacy'))
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The required privacy parameter is missing');
}
$privacy = PrivacyState::tryFrom(strtoupper($rpcRequest->getParameter('privacy')));
if($privacy === null)
{
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'The provided privacy parameter is invalid');
}
try
{
$peer = $request->getPeer();
if(!PeerInformationManager::fieldExists($peer, $fieldName))
{
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The information field does not exist');
}
PeerInformationManager::updatePrivacyState($peer, $fieldName, $privacy);
}
catch(DatabaseOperationException $e)
{
throw new StandardException('Failed to update the information field', StandardError::INTERNAL_SERVER_ERROR, $e);
}
return $rpcRequest->produceResponse(true);
}
}