diff --git a/src/Socialbox/Enums/Flags/SessionFlags.php b/src/Socialbox/Enums/Flags/SessionFlags.php index 01c37bf..ae47c2a 100644 --- a/src/Socialbox/Enums/Flags/SessionFlags.php +++ b/src/Socialbox/Enums/Flags/SessionFlags.php @@ -2,6 +2,8 @@ namespace Socialbox\Enums\Flags; + use Socialbox\Classes\Logger; + enum SessionFlags : string { // Session states @@ -45,20 +47,20 @@ public static function getRegistrationFlags(): array { return [ - self::SET_PASSWORD->value, - self::SET_OTP->value, - self::SET_DISPLAY_NAME->value, - self::SET_DISPLAY_PICTURE->value, - self::SET_PHONE->value, - self::SET_BIRTHDAY->value, - self::SET_EMAIL->value, - self::VER_PRIVACY_POLICY->value, - self::VER_TERMS_OF_SERVICE->value, - self::VER_COMMUNITY_GUIDELINES->value, - self::VER_EMAIL->value, - self::VER_SMS->value, - self::VER_PHONE_CALL->value, - self::VER_IMAGE_CAPTCHA->value + self::SET_PASSWORD, + self::SET_OTP, + self::SET_DISPLAY_NAME, + self::SET_DISPLAY_PICTURE, + self::SET_PHONE, + self::SET_BIRTHDAY, + self::SET_EMAIL, + self::VER_PRIVACY_POLICY, + self::VER_TERMS_OF_SERVICE, + self::VER_COMMUNITY_GUIDELINES, + self::VER_EMAIL, + self::VER_SMS, + self::VER_PHONE_CALL, + self::VER_IMAGE_CAPTCHA ]; } @@ -70,10 +72,10 @@ public static function getAuthenticationFlags(): array { return [ - self::VER_IMAGE_CAPTCHA->value, - self::VER_PASSWORD->value, - self::VER_OTP->value, - self::VER_AUTHENTICATION->value + self::VER_IMAGE_CAPTCHA, + self::VER_PASSWORD, + self::VER_OTP, + self::VER_AUTHENTICATION ]; } @@ -112,20 +114,35 @@ */ public static function isComplete(array $flags): bool { - $flags = array_map(function ($flag) {return is_string($flag) ? SessionFlags::from($flag) : $flag;}, $flags); - $flags = array_map(fn(SessionFlags $flag) => $flag->value, $flags); + // Map provided flags to their scalar values if they are enums + $flagValues = array_map(fn($flag) => $flag instanceof SessionFlags ? $flag->value : $flag, $flags); - if (in_array(SessionFlags::REGISTRATION_REQUIRED->value, $flags)) + if (in_array(SessionFlags::REGISTRATION_REQUIRED, $flags, true)) { - return !array_intersect(self::getRegistrationFlags(), $flags); // Check if the intersection is empty + Logger::getLogger()->info('Checking registration flags'); + // Compare values instead of objects + return empty(array_intersect(self::getScalarValues(self::getRegistrationFlags()), $flagValues)); } - if (in_array(SessionFlags::AUTHENTICATION_REQUIRED->value, $flags)) + if (in_array(SessionFlags::AUTHENTICATION_REQUIRED, $flags, true)) { - return !array_intersect(self::getAuthenticationFlags(), $flags); // Check if the intersection is empty - + Logger::getLogger()->info('Checking authentication flags'); + // Compare values instead of objects + return empty(array_intersect(self::getScalarValues(self::getAuthenticationFlags()), $flagValues)); } + Logger::getLogger()->info('Neither registration nor authentication flags found'); return true; } + + /** + * Helper method: Converts an array of SessionFlags enums to their scalar values (strings) + * + * @param array $flagEnums Array of SessionFlags objects + * @return array Array of scalar values corresponding to the flags + */ + private static function getScalarValues(array $flagEnums): array + { + return array_map(fn(SessionFlags $flag) => $flag->value, $flagEnums); + } } diff --git a/src/Socialbox/Managers/SessionManager.php b/src/Socialbox/Managers/SessionManager.php index 2c2e848..6bf5071 100644 --- a/src/Socialbox/Managers/SessionManager.php +++ b/src/Socialbox/Managers/SessionManager.php @@ -11,7 +11,6 @@ use Socialbox\Classes\Cryptography; use Socialbox\Classes\Database; use Socialbox\Classes\Logger; - use Socialbox\Classes\Utilities; use Socialbox\Enums\Flags\SessionFlags; use Socialbox\Enums\SessionState; use Socialbox\Enums\StandardError; @@ -33,9 +32,7 @@ * @param string $clientPublicSigningKey The client's public signing key, which must be a valid Ed25519 key. * @param string $clientPublicEncryptionKey The client's public encryption key, which must be a valid X25519 key. * @param KeyPair $serverEncryptionKeyPair The server's key pair for encryption, including both public and private keys. - * * @return string The UUID of the newly created session. - * * @throws InvalidArgumentException If the provided public signing key or encryption key is invalid. * @throws DatabaseOperationException If there is an error during the session creation in the database. */ @@ -310,7 +307,7 @@ * Retrieves the flags associated with a specific session. * * @param string $uuid The UUID of the session to retrieve flags for. - * @return array An array of flags associated with the specified session. + * @return SessionFlags[] An array of flags associated with the specified session. * @throws StandardException If the specified session does not exist. * @throws DatabaseOperationException If there */ @@ -359,7 +356,7 @@ try { $statement = Database::getConnection()->prepare("UPDATE sessions SET flags=? WHERE uuid=?"); - $statement->bindValue(1, Utilities::serializeList($flags)); + $statement->bindValue(1, SessionFlags::toString($flags)); $statement->bindParam(2, $uuid); $statement->execute(); } @@ -381,15 +378,16 @@ { Logger::getLogger()->verbose(sprintf("Removing flags from session %s", $uuid)); - $existingFlags = self::getFlags($uuid); - $flagsToRemove = array_map(fn($flag) => $flag->value, $flags); - $updatedFlags = array_filter($existingFlags, fn($flag) => !in_array($flag->value, $flagsToRemove)); - $flags = SessionFlags::toString($updatedFlags); + $existingFlags = array_map(fn(SessionFlags $flag) => $flag->value, self::getFlags($uuid)); + $flagsToRemove = array_map(fn(SessionFlags $flag) => $flag->value, $flags); + $updatedFlags = array_diff($existingFlags, $flagsToRemove); + $flagString = SessionFlags::toString(array_map(fn(string $value) => SessionFlags::from($value), $updatedFlags)); try { + // Update the session flags in the database $statement = Database::getConnection()->prepare("UPDATE sessions SET flags=? WHERE uuid=?"); - $statement->bindValue(1, $flags); // Directly use the toString() result + $statement->bindValue(1, $flagString); // Use the stringified updated flags $statement->bindParam(2, $uuid); $statement->execute(); } @@ -436,7 +434,7 @@ public static function updateFlow(SessionRecord $session, array $flagsToRemove=[]): void { // Don't do anything if the session is already authenticated - if(!in_array(SessionFlags::REGISTRATION_REQUIRED, $session->getFlags()) || !in_array(SessionFlags::AUTHENTICATION_REQUIRED, $session->getFlags())) + if (!$session->flagExists(SessionFlags::AUTHENTICATION_REQUIRED) && !$session->flagExists(SessionFlags::REGISTRATION_REQUIRED)) { return; } @@ -456,6 +454,7 @@ { SessionManager::removeFlags($session->getUuid(), [SessionFlags::REGISTRATION_REQUIRED, SessionFlags::AUTHENTICATION_REQUIRED]); // Remove the registration/authentication flags SessionManager::setAuthenticated($session->getUuid(), true); // Mark the session as authenticated + RegisteredPeerManager::enablePeer($session->getPeerUuid()); // Enable the peer } } } \ No newline at end of file diff --git a/tests/Socialbox/Enums/Flags/SessionFlagsTest.php b/tests/Socialbox/Enums/Flags/SessionFlagsTest.php new file mode 100644 index 0000000..46f4974 --- /dev/null +++ b/tests/Socialbox/Enums/Flags/SessionFlagsTest.php @@ -0,0 +1,180 @@ +assertEquals($expectedFlags, $result); + } + + /** + * Test that fromString handles an empty string correctly by returning an empty array. + */ + public function testFromStringWithEmptyString() + { + $flagString = ''; + $result = SessionFlags::fromString($flagString); + + $this->assertEquals([], $result); + } + + /** + * Test that fromString correctly trims whitespace from flag values. + */ + public function testFromStringWithWhitespace() + { + $flagString = ' SET_PASSWORD , SET_EMAIL , VER_SMS '; + $expectedFlags = [ + SessionFlags::SET_PASSWORD, + SessionFlags::SET_EMAIL, + SessionFlags::VER_SMS, + ]; + + $result = SessionFlags::fromString($flagString); + + $this->assertEquals($expectedFlags, $result); + } + + /** + * Test that fromString throws an error for invalid values. + */ + public function testFromStringWithInvalidValues() + { + $this->expectException(\ValueError::class); + + $flagString = 'INVALID_FLAG'; + SessionFlags::fromString($flagString); + } + + /** + * Test that fromString works for a single valid flag. + */ + public function testFromStringWithSingleValue() + { + $flagString = 'SET_PASSWORD'; + $expectedFlags = [SessionFlags::SET_PASSWORD]; + + $result = SessionFlags::fromString($flagString); + + $this->assertEquals($expectedFlags, $result); + } + + /** + * Test that fromString correctly handles duplicate flag values in the input string. + */ + public function testFromStringWithDuplicateValues() + { + $flagString = 'SET_EMAIL,SET_EMAIL,VER_SMS'; + $expectedFlags = [ + SessionFlags::SET_EMAIL, + SessionFlags::SET_EMAIL, + SessionFlags::VER_SMS, + ]; + + $result = SessionFlags::fromString($flagString); + + $this->assertEquals($expectedFlags, $result); + } + + /** + * Test that isComplete returns true for an empty array of flags. + */ + public function testIsCompleteWithEmptyFlags() + { + $flags = []; + + $result = SessionFlags::isComplete($flags); + + $this->assertTrue($result); + } + + /** + * Test that isComplete returns false when registration flags are incomplete. + */ + public function testIsCompleteWithIncompleteRegistrationFlags() + { + $flags = [ + SessionFlags::REGISTRATION_REQUIRED, + SessionFlags::SET_PASSWORD, + ]; + + $result = SessionFlags::isComplete($flags); + + $this->assertFalse($result); + } + + /** + * Test that isComplete returns false when authentication flags are incomplete. + */ + public function testIsCompleteWithIncompleteAuthenticationFlags() + { + $flags = [ + SessionFlags::AUTHENTICATION_REQUIRED, + SessionFlags::VER_PASSWORD, + ]; + + $result = SessionFlags::isComplete($flags); + + $this->assertFalse($result); + } + + /** + * Test that isComplete returns true when registration flags are complete. + */ + public function testIsCompleteWithCompleteRegistrationFlags() + { + $flags = [ + SessionFlags::REGISTRATION_REQUIRED, + ]; + + $result = SessionFlags::isComplete($flags); + + $this->assertTrue($result); + } + + /** + * Test that isComplete returns true when authentication flags are complete. + */ + public function testIsCompleteWithCompleteAuthenticationFlags() + { + $flags = [ + SessionFlags::AUTHENTICATION_REQUIRED, + ]; + + $result = SessionFlags::isComplete($flags); + + $this->assertTrue($result); + } + + /** + * Test that isComplete ignores non-relevant flags while processing. + */ + public function testIsCompleteIgnoringNonRelevantFlags() + { + $flags = [ + SessionFlags::RATE_LIMITED, + SessionFlags::AUTHENTICATION_REQUIRED, + ]; + + $result = SessionFlags::isComplete($flags); + + $this->assertTrue($result); + } + } diff --git a/tests/Socialbox/SocialClientTest.php b/tests/Socialbox/SocialClientTest.php new file mode 100644 index 0000000..0089677 --- /dev/null +++ b/tests/Socialbox/SocialClientTest.php @@ -0,0 +1,61 @@ +@'. + */ + private static function generateUsername(string $domain): string + { + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + $randomString = ''; + + for ($i = 0; $i < 16; $i++) + { + $randomString .= $characters[rand(0, $charactersLength - 1)]; + } + + return 'user' . $randomString . '@' . $domain; + } + + public function testConnection() :void + { + $coffeeClient = new SocialClient(self::generateUsername('intvo.id')); + + // Check initial session state + $this->assertFalse($coffeeClient->getSessionState()->isAuthenticated()); + $this->assertTrue($coffeeClient->getSessionState()->containsFlag(SessionFlags::REGISTRATION_REQUIRED)); + $this->assertTrue($coffeeClient->getSessionState()->containsFlag(SessionFlags::SET_PASSWORD)); + $this->assertTrue($coffeeClient->getSessionState()->containsFlag(SessionFlags::SET_DISPLAY_NAME)); + + // Check progressive session state + $this->assertTrue($coffeeClient->settingsSetPassword('coffeePassword')); + $this->assertFalse($coffeeClient->getSessionState()->containsFlag(SessionFlags::SET_PASSWORD)); + $this->assertTrue($coffeeClient->settingsSetDisplayName('Coffee User')); + $this->assertFalse($coffeeClient->getSessionState()->containsFlag(SessionFlags::SET_DISPLAY_NAME)); + + $this->assertFalse($coffeeClient->getSessionState()->containsFlag(SessionFlags::REGISTRATION_REQUIRED)); + $this->assertTrue($coffeeClient->getSessionState()->isAuthenticated()); + } + }