Refactor SessionFlags handling and improve test coverage
This commit is contained in:
parent
9d98659541
commit
cd12c1b987
4 changed files with 293 additions and 36 deletions
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace Socialbox\Enums\Flags;
|
namespace Socialbox\Enums\Flags;
|
||||||
|
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
|
|
||||||
enum SessionFlags : string
|
enum SessionFlags : string
|
||||||
{
|
{
|
||||||
// Session states
|
// Session states
|
||||||
|
@ -45,20 +47,20 @@
|
||||||
public static function getRegistrationFlags(): array
|
public static function getRegistrationFlags(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
self::SET_PASSWORD->value,
|
self::SET_PASSWORD,
|
||||||
self::SET_OTP->value,
|
self::SET_OTP,
|
||||||
self::SET_DISPLAY_NAME->value,
|
self::SET_DISPLAY_NAME,
|
||||||
self::SET_DISPLAY_PICTURE->value,
|
self::SET_DISPLAY_PICTURE,
|
||||||
self::SET_PHONE->value,
|
self::SET_PHONE,
|
||||||
self::SET_BIRTHDAY->value,
|
self::SET_BIRTHDAY,
|
||||||
self::SET_EMAIL->value,
|
self::SET_EMAIL,
|
||||||
self::VER_PRIVACY_POLICY->value,
|
self::VER_PRIVACY_POLICY,
|
||||||
self::VER_TERMS_OF_SERVICE->value,
|
self::VER_TERMS_OF_SERVICE,
|
||||||
self::VER_COMMUNITY_GUIDELINES->value,
|
self::VER_COMMUNITY_GUIDELINES,
|
||||||
self::VER_EMAIL->value,
|
self::VER_EMAIL,
|
||||||
self::VER_SMS->value,
|
self::VER_SMS,
|
||||||
self::VER_PHONE_CALL->value,
|
self::VER_PHONE_CALL,
|
||||||
self::VER_IMAGE_CAPTCHA->value
|
self::VER_IMAGE_CAPTCHA
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,10 +72,10 @@
|
||||||
public static function getAuthenticationFlags(): array
|
public static function getAuthenticationFlags(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
self::VER_IMAGE_CAPTCHA->value,
|
self::VER_IMAGE_CAPTCHA,
|
||||||
self::VER_PASSWORD->value,
|
self::VER_PASSWORD,
|
||||||
self::VER_OTP->value,
|
self::VER_OTP,
|
||||||
self::VER_AUTHENTICATION->value
|
self::VER_AUTHENTICATION
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,20 +114,35 @@
|
||||||
*/
|
*/
|
||||||
public static function isComplete(array $flags): bool
|
public static function isComplete(array $flags): bool
|
||||||
{
|
{
|
||||||
$flags = array_map(function ($flag) {return is_string($flag) ? SessionFlags::from($flag) : $flag;}, $flags);
|
// Map provided flags to their scalar values if they are enums
|
||||||
$flags = array_map(fn(SessionFlags $flag) => $flag->value, $flags);
|
$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;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
use Socialbox\Classes\Cryptography;
|
use Socialbox\Classes\Cryptography;
|
||||||
use Socialbox\Classes\Database;
|
use Socialbox\Classes\Database;
|
||||||
use Socialbox\Classes\Logger;
|
use Socialbox\Classes\Logger;
|
||||||
use Socialbox\Classes\Utilities;
|
|
||||||
use Socialbox\Enums\Flags\SessionFlags;
|
use Socialbox\Enums\Flags\SessionFlags;
|
||||||
use Socialbox\Enums\SessionState;
|
use Socialbox\Enums\SessionState;
|
||||||
use Socialbox\Enums\StandardError;
|
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 $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 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.
|
* @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.
|
* @return string The UUID of the newly created session.
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException If the provided public signing key or encryption key is invalid.
|
* @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.
|
* @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.
|
* Retrieves the flags associated with a specific session.
|
||||||
*
|
*
|
||||||
* @param string $uuid The UUID of the session to retrieve flags for.
|
* @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 StandardException If the specified session does not exist.
|
||||||
* @throws DatabaseOperationException If there
|
* @throws DatabaseOperationException If there
|
||||||
*/
|
*/
|
||||||
|
@ -359,7 +356,7 @@
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$statement = Database::getConnection()->prepare("UPDATE sessions SET flags=? WHERE uuid=?");
|
$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->bindParam(2, $uuid);
|
||||||
$statement->execute();
|
$statement->execute();
|
||||||
}
|
}
|
||||||
|
@ -381,15 +378,16 @@
|
||||||
{
|
{
|
||||||
Logger::getLogger()->verbose(sprintf("Removing flags from session %s", $uuid));
|
Logger::getLogger()->verbose(sprintf("Removing flags from session %s", $uuid));
|
||||||
|
|
||||||
$existingFlags = self::getFlags($uuid);
|
$existingFlags = array_map(fn(SessionFlags $flag) => $flag->value, self::getFlags($uuid));
|
||||||
$flagsToRemove = array_map(fn($flag) => $flag->value, $flags);
|
$flagsToRemove = array_map(fn(SessionFlags $flag) => $flag->value, $flags);
|
||||||
$updatedFlags = array_filter($existingFlags, fn($flag) => !in_array($flag->value, $flagsToRemove));
|
$updatedFlags = array_diff($existingFlags, $flagsToRemove);
|
||||||
$flags = SessionFlags::toString($updatedFlags);
|
$flagString = SessionFlags::toString(array_map(fn(string $value) => SessionFlags::from($value), $updatedFlags));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Update the session flags in the database
|
||||||
$statement = Database::getConnection()->prepare("UPDATE sessions SET flags=? WHERE uuid=?");
|
$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->bindParam(2, $uuid);
|
||||||
$statement->execute();
|
$statement->execute();
|
||||||
}
|
}
|
||||||
|
@ -436,7 +434,7 @@
|
||||||
public static function updateFlow(SessionRecord $session, array $flagsToRemove=[]): void
|
public static function updateFlow(SessionRecord $session, array $flagsToRemove=[]): void
|
||||||
{
|
{
|
||||||
// Don't do anything if the session is already authenticated
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -456,6 +454,7 @@
|
||||||
{
|
{
|
||||||
SessionManager::removeFlags($session->getUuid(), [SessionFlags::REGISTRATION_REQUIRED, SessionFlags::AUTHENTICATION_REQUIRED]); // Remove the registration/authentication flags
|
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
|
SessionManager::setAuthenticated($session->getUuid(), true); // Mark the session as authenticated
|
||||||
|
RegisteredPeerManager::enablePeer($session->getPeerUuid()); // Enable the peer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
180
tests/Socialbox/Enums/Flags/SessionFlagsTest.php
Normal file
180
tests/Socialbox/Enums/Flags/SessionFlagsTest.php
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Enums\Flags;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class SessionFlagsTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that fromString correctly converts a valid comma-separated string to an array of SessionFlags.
|
||||||
|
*/
|
||||||
|
public function testFromStringWithValidString()
|
||||||
|
{
|
||||||
|
$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 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);
|
||||||
|
}
|
||||||
|
}
|
61
tests/Socialbox/SocialClientTest.php
Normal file
61
tests/Socialbox/SocialClientTest.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Socialbox\Classes\ServerResolver;
|
||||||
|
use Socialbox\Enums\Flags\SessionFlags;
|
||||||
|
|
||||||
|
class SocialClientTest extends TestCase
|
||||||
|
{
|
||||||
|
private const string COFFEE_DOMAIN = 'coffee.com';
|
||||||
|
private const string TEAPOT_DOMAIN = 'teapot.com';
|
||||||
|
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
// Add mocked records for the test domains
|
||||||
|
ServerResolver::addMock('coffee.com', 'v=socialbox;sb-rpc=http://127.0.0.0:8086/;sb-key=sig:g59Cf8j1wmQmRg1MkveYbpdiZ-1-_hFU9eRRJmQAwmc;sb-exp=0');
|
||||||
|
ServerResolver::addMock('teapot.com', 'v=socialbox;sb-rpc=http://127.0.0.0:8087/;sb-key=sig:MDXUuripAo_IAv-EZTEoFhpIdhsXxfMLNunSnQzxYiY;sb-exp=0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random username based on the given domain.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain to be appended to the generated username.
|
||||||
|
* @return string Returns a randomly generated username in the format 'user<randomString>@<domain>'.
|
||||||
|
*/
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue