From 70c0fb2e54759bec9d32b39bfc5256c4ff1e5ed1 Mon Sep 17 00:00:00 2001 From: netkas Date: Tue, 7 Jan 2025 15:25:32 -0500 Subject: [PATCH] Add authentication configuration and allowed methods logic --- src/Socialbox/Classes/Configuration.php | 25 ++ .../AuthenticationConfiguration.php | 36 ++ .../StandardMethods/GetAllowedMethods.php | 38 +++ src/Socialbox/Enums/StandardMethods.php | 315 ++++++++++++++---- 4 files changed, 349 insertions(+), 65 deletions(-) create mode 100644 src/Socialbox/Classes/Configuration/AuthenticationConfiguration.php create mode 100644 src/Socialbox/Classes/StandardMethods/GetAllowedMethods.php diff --git a/src/Socialbox/Classes/Configuration.php b/src/Socialbox/Classes/Configuration.php index cf3c621..74a6fca 100644 --- a/src/Socialbox/Classes/Configuration.php +++ b/src/Socialbox/Classes/Configuration.php @@ -2,6 +2,7 @@ namespace Socialbox\Classes; + use Socialbox\Classes\Configuration\AuthenticationConfiguration; use Socialbox\Classes\Configuration\CacheConfiguration; use Socialbox\Classes\Configuration\CryptographyConfiguration; use Socialbox\Classes\Configuration\DatabaseConfiguration; @@ -22,6 +23,7 @@ private static ?LoggingConfiguration $loggingConfiguration = null; private static ?CacheConfiguration $cacheConfiguration = null; private static ?RegistrationConfiguration $registrationConfiguration = null; + private static ?AuthenticationConfiguration $authenticationConfiguration = null; private static ?PoliciesConfiguration $policiesConfiguration = null; private static ?StorageConfiguration $storageConfiguration = null; @@ -134,6 +136,10 @@ $config->setDefault('registration.birthday_required', false); $config->setDefault('registration.image_captcha_verification_required', true); + // Authentication configuration + $config->setDefault('authentication.enabled', true); + $config->setDefault('authentication.image_captcha_verification_required', true); + // Server Policies // The maximum number of signing keys a peer can register onto the server at once $config->setDefault('policies.max_signing_keys', 20); @@ -160,6 +166,7 @@ self::$loggingConfiguration = new LoggingConfiguration(self::$configuration->getConfiguration()['logging']); self::$cacheConfiguration = new CacheConfiguration(self::$configuration->getConfiguration()['cache']); self::$registrationConfiguration = new RegistrationConfiguration(self::$configuration->getConfiguration()['registration']); + self::$authenticationConfiguration = new AuthenticationConfiguration(self::$configuration->getConfiguration()['authentication']); self::$policiesConfiguration = new PoliciesConfiguration(self::$configuration->getConfiguration()['policies']); self::$storageConfiguration = new StorageConfiguration(self::$configuration->getConfiguration()['storage']); } @@ -329,6 +336,24 @@ return self::$registrationConfiguration; } + /** + * Retrieves the authentication configuration. + * + * This method returns the current AuthenticationConfiguration instance. + * If the configuration has not been initialized yet, it initializes it first. + * + * @return AuthenticationConfiguration The authentication configuration instance. + */ + public static function getAuthenticationConfiguration(): AuthenticationConfiguration + { + if(self::$authenticationConfiguration === null) + { + self::initializeConfiguration(); + } + + return self::$authenticationConfiguration; + } + /** * Retrieves the policies configuration. * diff --git a/src/Socialbox/Classes/Configuration/AuthenticationConfiguration.php b/src/Socialbox/Classes/Configuration/AuthenticationConfiguration.php new file mode 100644 index 0000000..18fea9d --- /dev/null +++ b/src/Socialbox/Classes/Configuration/AuthenticationConfiguration.php @@ -0,0 +1,36 @@ +enabled = (bool)$data['enabled']; + $this->imageCaptchaVerificationRequired = (bool)$data['image_captcha_verification_required']; + } + + /** + * @return bool + */ + public function isEnabled(): bool + { + return $this->enabled; + } + + /** + * @return bool + */ + public function isImageCaptchaVerificationRequired(): bool + { + return $this->imageCaptchaVerificationRequired; + } + } \ No newline at end of file diff --git a/src/Socialbox/Classes/StandardMethods/GetAllowedMethods.php b/src/Socialbox/Classes/StandardMethods/GetAllowedMethods.php new file mode 100644 index 0000000..2baf0cd --- /dev/null +++ b/src/Socialbox/Classes/StandardMethods/GetAllowedMethods.php @@ -0,0 +1,38 @@ +value; + } + } + catch(DatabaseOperationException $e) + { + throw new StandardException('Failed to retrieve allowed methods due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e); + } + + return $rpcRequest->produceResponse($allowedMethods); + } + } \ No newline at end of file diff --git a/src/Socialbox/Enums/StandardMethods.php b/src/Socialbox/Enums/StandardMethods.php index a6b8b12..34c399f 100644 --- a/src/Socialbox/Enums/StandardMethods.php +++ b/src/Socialbox/Enums/StandardMethods.php @@ -6,32 +6,46 @@ use Socialbox\Classes\StandardMethods\AcceptCommunityGuidelines; use Socialbox\Classes\StandardMethods\AcceptPrivacyPolicy; use Socialbox\Classes\StandardMethods\AcceptTermsOfService; + use Socialbox\Classes\StandardMethods\GetAllowedMethods; use Socialbox\Classes\StandardMethods\GetCommunityGuidelines; use Socialbox\Classes\StandardMethods\GetPrivacyPolicy; use Socialbox\Classes\StandardMethods\GetSessionState; use Socialbox\Classes\StandardMethods\GetTermsOfService; use Socialbox\Classes\StandardMethods\Ping; use Socialbox\Classes\StandardMethods\SettingsAddSigningKey; + use Socialbox\Classes\StandardMethods\SettingsDeleteBirthday; use Socialbox\Classes\StandardMethods\SettingsDeleteDisplayName; use Socialbox\Classes\StandardMethods\SettingsDeleteDisplayPicture; + use Socialbox\Classes\StandardMethods\SettingsDeleteEmailAddress; use Socialbox\Classes\StandardMethods\SettingsDeletePassword; + use Socialbox\Classes\StandardMethods\SettingsDeletePhoneNumber; use Socialbox\Classes\StandardMethods\SettingsGetSigningKeys; + use Socialbox\Classes\StandardMethods\SettingsSetBirthday; use Socialbox\Classes\StandardMethods\SettingsSetDisplayName; use Socialbox\Classes\StandardMethods\SettingsSetDisplayPicture; + use Socialbox\Classes\StandardMethods\SettingsSetEmailAddress; use Socialbox\Classes\StandardMethods\SettingsSetPassword; + use Socialbox\Classes\StandardMethods\SettingsSetPhoneNumber; use Socialbox\Classes\StandardMethods\SettingsUpdatePassword; use Socialbox\Classes\StandardMethods\VerificationAnswerImageCaptcha; use Socialbox\Classes\StandardMethods\VerificationGetImageCaptcha; + use Socialbox\Classes\StandardMethods\VerificationOtpAuthentication; + use Socialbox\Classes\StandardMethods\VerificationPasswordAuthentication; use Socialbox\Enums\Flags\SessionFlags; + use Socialbox\Exceptions\DatabaseOperationException; use Socialbox\Exceptions\StandardException; use Socialbox\Interfaces\SerializableInterface; + use Socialbox\Managers\OneTimePasswordManager; + use Socialbox\Managers\PasswordManager; use Socialbox\Objects\ClientRequest; + use Socialbox\Objects\Database\SessionRecord; use Socialbox\Objects\RpcRequest; enum StandardMethods : string { case PING = 'ping'; case GET_SESSION_STATE = 'getSessionState'; + case GET_ALLOWED_METHODS = 'getAllowedMethods'; case GET_PRIVACY_POLICY = 'getPrivacyPolicy'; case ACCEPT_PRIVACY_POLICY = 'acceptPrivacyPolicy'; @@ -57,18 +71,25 @@ case VERIFICATION_GET_EXTERNAL_URL = 'verificationGetExternalUrl'; case VERIFICATION_ANSWER_EXTERNAL_URL = 'verificationAnswerExternalUrl'; + + case VERIFICATION_PASSWORD_AUTHENTICATION = 'verificationPasswordAuthentication'; + case VERIFICATION_OTP_AUTHENTICATION = 'verificationOtpAuthentication'; case SETTINGS_SET_PASSWORD = 'settingsSetPassword'; case SETTINGS_UPDATE_PASSWORD = 'settingsUpdatePassword'; case SETTINGS_DELETE_PASSWORD = 'settingsDeletePassword'; case SETTINGS_SET_OTP = 'settingsSetOtp'; + case SETTINGS_DELETE_OTP = 'settingsDeleteOtp'; case SETTINGS_SET_DISPLAY_NAME = 'settingsSetDisplayName'; case SETTINGS_DELETE_DISPLAY_NAME = 'settingsDeleteDisplayName'; case SETTINGS_SET_DISPLAY_PICTURE = 'settingsSetDisplayPicture'; case SETTINGS_DELETE_DISPLAY_PICTURE = 'settingsDeleteDisplayPicture'; case SETTINGS_SET_EMAIL = 'settingsSetEmail'; + case SETTINGS_DELETE_EMAIL = 'settingsDeleteEmail'; case SETTINGS_SET_PHONE = 'settingsSetPhone'; + case SETTINGS_DELETE_PHONE = 'settingsDeletePhone'; case SETTINGS_SET_BIRTHDAY = 'settingsSetBirthday'; + case SETTINGS_DELETE_BIRTHDAY = 'settingsDeleteBirthday'; case SETTINGS_ADD_SIGNING_KEY = 'settingsAddSigningKey'; case SETTINGS_GET_SIGNING_KEYS = 'settingsGetSigningKeys'; @@ -87,6 +108,7 @@ { self::PING => Ping::execute($request, $rpcRequest), self::GET_SESSION_STATE => GetSessionState::execute($request, $rpcRequest), + self::GET_ALLOWED_METHODS => GetAllowedMethods::execute($request, $rpcRequest), self::GET_PRIVACY_POLICY => GetPrivacyPolicy::execute($request, $rpcRequest), self::ACCEPT_PRIVACY_POLICY => AcceptPrivacyPolicy::execute($request, $rpcRequest), @@ -97,6 +119,9 @@ self::VERIFICATION_GET_IMAGE_CAPTCHA => VerificationGetImageCaptcha::execute($request, $rpcRequest), self::VERIFICATION_ANSWER_IMAGE_CAPTCHA => VerificationAnswerImageCaptcha::execute($request, $rpcRequest), + + self::VERIFICATION_PASSWORD_AUTHENTICATION => VerificationPasswordAuthentication::execute($request, $rpcRequest), + self::VERIFICATION_OTP_AUTHENTICATION => VerificationOtpAuthentication::execute($request, $rpcRequest), self::SETTINGS_SET_PASSWORD => SettingsSetPassword::execute($request, $rpcRequest), self::SETTINGS_UPDATE_PASSWORD => SettingsUpdatePassword::execute($request, $rpcRequest), @@ -105,6 +130,12 @@ self::SETTINGS_DELETE_DISPLAY_NAME => SettingsDeleteDisplayName::execute($request, $rpcRequest), self::SETTINGS_SET_DISPLAY_PICTURE => SettingsSetDisplayPicture::execute($request, $rpcRequest), self::SETTINGS_DELETE_DISPLAY_PICTURE => SettingsDeleteDisplayPicture::execute($request, $rpcRequest), + self::SETTINGS_SET_EMAIL => SettingsSetEmailAddress::execute($request, $rpcRequest), + self::SETTINGS_DELETE_EMAIL => SettingsDeleteEmailAddress::execute($request, $rpcRequest), + self::SETTINGS_SET_PHONE => SettingsSetPhoneNumber::execute($request, $rpcRequest), + self::SETTINGS_DELETE_PHONE => SettingsDeletePhoneNumber::execute($request, $rpcRequest), + self::SETTINGS_SET_BIRTHDAY => SettingsSetBirthday::execute($request, $rpcRequest), + self::SETTINGS_DELETE_BIRTHDAY => SettingsDeleteBirthday::execute($request, $rpcRequest), self::SETTINGS_ADD_SIGNING_KEY => SettingsAddSigningKey::execute($request, $rpcRequest), self::SETTINGS_GET_SIGNING_KEYS => SettingsGetSigningKeys::execute($request, $rpcRequest), @@ -118,6 +149,7 @@ * * @param ClientRequest $clientRequest The client request instance to check access against. * @return void + * @throws DatabaseOperationException If an error occurs while checking the database for session information. * @throws StandardException If the method is not allowed for the given client request. */ public function checkAccess(ClientRequest $clientRequest): void @@ -135,6 +167,7 @@ * * @param ClientRequest $clientRequest The client request for which allowed methods are determined. * @return array Returns an array of allowed methods for the provided client request. + * @throws DatabaseOperationException If an error occurs while checking the database for session information. */ public static function getAllowedMethods(ClientRequest $clientRequest): array { @@ -143,6 +176,7 @@ // Important methods self::PING, // Always allow the ping method self::GET_SESSION_STATE, // The session state should always be accessible + self::GET_ALLOWED_METHODS, // Client should always be able to get the allowed methods self::GET_PRIVACY_POLICY, // The user should always be able to get the privacy policy self::GET_TERMS_OF_SERVICE, // The user should always be able to get the terms of service self::GET_COMMUNITY_GUIDELINES, // The user should always be able to get the community guidelines @@ -150,86 +184,237 @@ $session = $clientRequest->getSession(); + if($session === null) + { + return $methods; + } + + try + { + $external = $session->isExternal(); + } + catch(DatabaseOperationException) + { + $external = false; + } + // If the session is external (eg; coming from a different server) // Servers will have their own access control mechanisms - if($session->isExternal()) + if($external) { - // TODO: Implement server access control + $methods = array_merge($methods, self::getExternalMethods($clientRequest)); } // If the session is authenticated, then allow additional method calls elseif($session->isAuthenticated()) { - // These methods are always allowed for authenticated users - $methods = array_merge($methods, [ - self::SETTINGS_ADD_SIGNING_KEY, - self::SETTINGS_GET_SIGNING_KEYS, - self::SETTINGS_SET_DISPLAY_NAME, - self::SETTINGS_SET_DISPLAY_PICTURE, - self::SETTINGS_SET_PASSWORD, - self::SETTINGS_UPDATE_PASSWORD, - ]); - - // Prevent the user from deleting their display name if it is required - if(!Configuration::getRegistrationConfiguration()->isDisplayNameRequired()) - { - $methods[] = self::SETTINGS_DELETE_DISPLAY_NAME; - } - - if(!Configuration::getRegistrationConfiguration()->isPasswordRequired()) - { - $methods[] = self::SETTINGS_DELETE_PASSWORD; - } - - if(!Configuration::getRegistrationConfiguration()->isDisplayPictureRequired()) - { - $methods[] = self::SETTINGS_DELETE_DISPLAY_PICTURE; - } + $methods = array_merge($methods, self::getAuthenticatedMethods()); } - // If the session isn't authenticated nor a host, a limited set of methods is available - else + // If the session isn't authenticated, check if it's a registering user + elseif($session->flagExists(SessionFlags::REGISTRATION_REQUIRED)) { - // If the flag `VER_PRIVACY_POLICY` is set, then the user can accept the privacy policy - if($session->flagExists(SessionFlags::VER_PRIVACY_POLICY)) - { - $methods[] = self::ACCEPT_PRIVACY_POLICY; - } + $methods = array_merge($methods, self::getRegistrationMethods($session)); + } + // If the user is a registering peer, check if it's an authenticating one + elseif($session->flagExists(SessionFlags::AUTHENTICATION_REQUIRED)) + { + $methods = array_merge($methods, self::getAuthenticationMethods($clientRequest)); + } - // If the flag `VER_TERMS_OF_SERVICE` is set, then the user can accept the terms of service - if($session->flagExists(SessionFlags::VER_TERMS_OF_SERVICE)) - { - $methods[] = self::ACCEPT_TERMS_OF_SERVICE; - } + return $methods; + } - // If the flag `VER_COMMUNITY_GUIDELINES` is set, then the user can accept the community guidelines - if($session->flagExists(SessionFlags::VER_COMMUNITY_GUIDELINES)) - { - $methods[] = self::ACCEPT_COMMUNITY_GUIDELINES; - } + /** + **/ + private static function getExternalMethods(ClientRequest $clientRequest): array + { + return []; + } - // If the flag `VER_IMAGE_CAPTCHA` is set, then the user has to get and answer an image captcha - if($session->flagExists(SessionFlags::VER_IMAGE_CAPTCHA)) - { - $methods[] = self::VERIFICATION_GET_IMAGE_CAPTCHA; - $methods[] = self::VERIFICATION_ANSWER_IMAGE_CAPTCHA; - } + /** + * Retrieves a list of authenticated user methods based on configuration settings. + * + * @return array An array of methods that are available to + */ + private static function getAuthenticatedMethods(): array + { - // If the flag `SET_PASSWORD` is set, then the user has to set a password - if($session->flagExists(SessionFlags::SET_PASSWORD)) - { - $methods[] = self::SETTINGS_SET_PASSWORD; - } + // These methods are always allowed for authenticated users + $methods = [ + self::SETTINGS_ADD_SIGNING_KEY, + self::SETTINGS_GET_SIGNING_KEYS, + self::SETTINGS_SET_DISPLAY_NAME, + self::SETTINGS_SET_DISPLAY_PICTURE, + self::SETTINGS_SET_PASSWORD, + self::SETTINGS_UPDATE_PASSWORD, + self::SETTINGS_SET_OTP, + self::SETTINGS_SET_EMAIL, + self::SETTINGS_SET_PHONE, + self::SETTINGS_SET_BIRTHDAY + ]; - // If the flag `SET_DISPLAY_NAME` is set, then the user has to set a display name - if($session->flagExists(SessionFlags::SET_DISPLAY_NAME)) - { - $methods[] = self::SETTINGS_SET_DISPLAY_NAME; - } + // Prevent the user from deleting their display name if it is required + if(!Configuration::getRegistrationConfiguration()->isDisplayNameRequired()) + { + $methods[] = self::SETTINGS_DELETE_DISPLAY_NAME; + } - // If the flag `SET_DISPLAY_PICTURE` is set, then the user has to set a display picture - if($session->flagExists(SessionFlags::SET_DISPLAY_PICTURE)) - { - $methods[] = self::SETTINGS_DELETE_DISPLAY_PICTURE; - } + // Prevent the user from deleting their password if it is required + if(!Configuration::getRegistrationConfiguration()->isPasswordRequired()) + { + $methods[] = self::SETTINGS_DELETE_PASSWORD; + } + + // Prevent the user from deleting their display picture if it is required + if(!Configuration::getRegistrationConfiguration()->isDisplayPictureRequired()) + { + $methods[] = self::SETTINGS_DELETE_DISPLAY_PICTURE; + } + + // Prevent the user from deleting their OTP if it is required + if(!Configuration::getRegistrationConfiguration()->isOtpRequired()) + { + $methods[] = self::SETTINGS_DELETE_OTP; + } + + // Prevent the user from deleting their Phone Number if it is required + if(!Configuration::getRegistrationConfiguration()->isPhoneNumberRequired()) + { + $methods[] = self::SETTINGS_DELETE_PHONE; + } + + // Prevent the user from deleting their email address if it is required + if(!Configuration::getRegistrationConfiguration()->isEmailAddressRequired()) + { + $methods[] = self::SETTINGS_DELETE_EMAIL; + } + + // Prevent the user from deleting their birthday if it is required + if(!Configuration::getRegistrationConfiguration()->isBirthdayRequired()) + { + $methods[] = self::SETTINGS_DELETE_BIRTHDAY; + } + + return $methods; + } + + /** + * Retrieves a list of registration methods based on the session flags. + * + * @param SessionRecord $session The session record containing flags that determine available registration methods. + * @return array An array of registration methods available for the session. + */ + private static function getRegistrationMethods(SessionRecord $session): array + { + // Don't allow registration methods if registration is disabled + if(!Configuration::getRegistrationConfiguration()->isRegistrationEnabled()) + { + return []; + } + + // If the flag `VER_PRIVACY_POLICY` is set, then the user can accept the privacy policy + if($session->flagExists(SessionFlags::VER_PRIVACY_POLICY)) + { + $methods[] = self::ACCEPT_PRIVACY_POLICY; + } + + // If the flag `VER_TERMS_OF_SERVICE` is set, then the user can accept the terms of service + if($session->flagExists(SessionFlags::VER_TERMS_OF_SERVICE)) + { + $methods[] = self::ACCEPT_TERMS_OF_SERVICE; + } + + // If the flag `VER_COMMUNITY_GUIDELINES` is set, then the user can accept the community guidelines + if($session->flagExists(SessionFlags::VER_COMMUNITY_GUIDELINES)) + { + $methods[] = self::ACCEPT_COMMUNITY_GUIDELINES; + } + + // If the flag `VER_IMAGE_CAPTCHA` is set, then the user has to get and answer an image captcha + if($session->flagExists(SessionFlags::VER_IMAGE_CAPTCHA)) + { + $methods[] = self::VERIFICATION_GET_IMAGE_CAPTCHA; + $methods[] = self::VERIFICATION_ANSWER_IMAGE_CAPTCHA; + } + + // If the flag `SET_PASSWORD` is set, then the user has to set a password + if($session->flagExists(SessionFlags::SET_PASSWORD)) + { + $methods[] = self::SETTINGS_SET_PASSWORD; + } + + // If the flag `SET_OTP` is set, then the user has to set an OTP + if($session->flagExists(SessionFLags::SET_OTP)) + { + $methods[] = self::SETTINGS_SET_OTP; + } + + // If the flag `SET_DISPLAY_NAME` is set, then the user has to set a display name + if($session->flagExists(SessionFlags::SET_DISPLAY_NAME)) + { + $methods[] = self::SETTINGS_SET_DISPLAY_NAME; + } + + // If the flag `SET_DISPLAY_PICTURE` is set, then the user has to set a display picture + if($session->flagExists(SessionFlags::SET_DISPLAY_PICTURE)) + { + $methods[] = self::SETTINGS_SET_DISPLAY_PICTURE; + } + + // If the flag `SET_EMAIL` is set, then the user has to set an email address + if($session->flagExists(SessionFlags::SET_EMAIL)) + { + $methods[] = self::SETTINGS_SET_EMAIL; + } + + // If the flag `SET_PHONE` is set, then the user has to set a phone number + if($session->flagExists(SessionFlags::SET_PHONE)) + { + $methods[] = self::SETTINGS_SET_PHONE; + } + + // If the flag `SET_BIRTHDAY` is set, then the user has to set a birthday + if($session->flagExists(SessionFlags::SET_BIRTHDAY)) + { + $methods[] = self::SETTINGS_SET_BIRTHDAY; + } + + return $methods; + } + + + /** + * Retrieves the list of authentication methods available for the given client request. + * + * @param ClientRequest $clientRequest The client request for which the authentication methods are determined. + * @return array The list of available authentication methods as an array of constants. + * @throws DatabaseOperationException If an error occurs while checking the database for authentication methods. + */ + private static function getAuthenticationMethods(ClientRequest $clientRequest): array + { + if(!Configuration::getAuthenticationConfiguration()->isEnabled()) + { + return []; + } + + $methods = []; + + if(Configuration::getAuthenticationConfiguration()->isImageCaptchaVerificationRequired()) + { + $methods[] = self::VERIFICATION_GET_IMAGE_CAPTCHA; + $methods[] = self::VERIFICATION_ANSWER_IMAGE_CAPTCHA; + } + + + $peer = $clientRequest->getPeer(); + + if(PasswordManager::usesPassword($peer)) + { + $methods[] = self::VERIFICATION_PASSWORD_AUTHENTICATION; + } + + if(OneTimePasswordManager::usesOtp($peer->getUuid())) + { + $methods[] = self::VERIFICATION_OTP_AUTHENTICATION; } return $methods;