Remove unused StandardMethods and improve session logic
This commit is contained in:
parent
86435a3d0b
commit
701acfde35
30 changed files with 1032 additions and 704 deletions
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
\Socialbox\Socialbox::handleRpc();
|
\Socialbox\Socialbox::handleRequest();
|
||||||
}
|
}
|
||||||
catch(Exception $e)
|
catch(Exception $e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@ use Socialbox\Exceptions\StandardException;
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
use Socialbox\Managers\SessionManager;
|
use Socialbox\Managers\SessionManager;
|
||||||
use Socialbox\Objects\ClientRequest;
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\ClientRequestOld;
|
||||||
use Socialbox\Objects\Database\SessionRecord;
|
use Socialbox\Objects\Database\SessionRecord;
|
||||||
use Socialbox\Objects\RpcRequest;
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
@ -22,27 +23,4 @@ abstract class Method
|
||||||
* @throws StandardException If a standard exception is thrown, it will be handled by the engine.
|
* @throws StandardException If a standard exception is thrown, it will be handled by the engine.
|
||||||
*/
|
*/
|
||||||
public static abstract function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface;
|
public static abstract function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ClientRequest $request The client request object
|
|
||||||
* @return SessionRecord|null Returns null if the client has not provided a Session UUID
|
|
||||||
* @throws StandardException Thrown if standard exceptions are to be thrown regarding this
|
|
||||||
*/
|
|
||||||
protected static function getSession(ClientRequest $request): ?SessionRecord
|
|
||||||
{
|
|
||||||
if($request->getSessionUuid() === null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// NOTE: If the session UUID was provided, it has already been validated up until this point
|
|
||||||
return SessionManager::getSession($request->getSessionUuid());
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
throw new StandardException("There was an error while retrieving the session from the server", StandardError::INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -35,7 +35,7 @@ class Configuration
|
||||||
$config->setDefault('instance.domain', null);
|
$config->setDefault('instance.domain', null);
|
||||||
$config->setDefault('instance.rpc_endpoint', null);
|
$config->setDefault('instance.rpc_endpoint', null);
|
||||||
$config->setDefault('instance.encryption_keys_count', 5);
|
$config->setDefault('instance.encryption_keys_count', 5);
|
||||||
$config->setDefault('instance.encryption_record_count', 5);
|
$config->setDefault('instance.encryption_records_count', 5);
|
||||||
$config->setDefault('instance.private_key', null);
|
$config->setDefault('instance.private_key', null);
|
||||||
$config->setDefault('instance.public_key', null);
|
$config->setDefault('instance.public_key', null);
|
||||||
$config->setDefault('instance.encryption_keys', null);
|
$config->setDefault('instance.encryption_keys', null);
|
||||||
|
|
|
@ -16,6 +16,7 @@ class Cryptography
|
||||||
private const int PADDING = OPENSSL_PKCS1_OAEP_PADDING;
|
private const int PADDING = OPENSSL_PKCS1_OAEP_PADDING;
|
||||||
private const string PEM_PRIVATE_HEADER = 'PRIVATE';
|
private const string PEM_PRIVATE_HEADER = 'PRIVATE';
|
||||||
private const string PEM_PUBLIC_HEADER = 'PUBLIC';
|
private const string PEM_PUBLIC_HEADER = 'PUBLIC';
|
||||||
|
private const string TRANSPORT_ENCRYPTION = 'aes-256-cbc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new public-private key pair.
|
* Generates a new public-private key pair.
|
||||||
|
@ -307,4 +308,74 @@ class Cryptography
|
||||||
|
|
||||||
return $keys;
|
return $keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function generateEncryptionKey(): string
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return base64_encode(random_bytes(32));
|
||||||
|
}
|
||||||
|
catch (RandomException $e)
|
||||||
|
{
|
||||||
|
throw new CryptographyException('Failed to generate encryption key: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the given content for transport using the provided encryption key.
|
||||||
|
*
|
||||||
|
* @param string $content The content to be encrypted.
|
||||||
|
* @param string $encryptionKey The encryption key used for encrypting the content.
|
||||||
|
* @return string The Base64 encoded string containing the IV and the encrypted content.
|
||||||
|
* @throws CryptographyException If the IV generation or encryption process fails.
|
||||||
|
*/
|
||||||
|
public static function encryptTransport(string $content, string $encryptionKey): string
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$iv = random_bytes(openssl_cipher_iv_length('aes-256-cbc'));
|
||||||
|
}
|
||||||
|
catch (RandomException $e)
|
||||||
|
{
|
||||||
|
throw new CryptographyException('Failed to generate IV: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$encrypted = openssl_encrypt($content, self::TRANSPORT_ENCRYPTION, base64_decode($encryptionKey), OPENSSL_RAW_DATA, $iv);
|
||||||
|
|
||||||
|
if($encrypted === false)
|
||||||
|
{
|
||||||
|
throw new CryptographyException('Failed to encrypt transport content: ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64_encode($iv . $encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the given encrypted transport content using the provided encryption key.
|
||||||
|
*
|
||||||
|
* @param string $encryptedContent The Base64 encoded encrypted content to be decrypted.
|
||||||
|
* @param string $encryptionKey The Base64 encoded encryption key used for decryption.
|
||||||
|
* @return string The decrypted content as a string.
|
||||||
|
* @throws CryptographyException If the decryption process fails.
|
||||||
|
*/
|
||||||
|
public static function decryptTransport(string $encryptedContent, string $encryptionKey): string
|
||||||
|
{
|
||||||
|
$decodedData = base64_decode($encryptedContent);
|
||||||
|
$ivLength = openssl_cipher_iv_length(self::TRANSPORT_ENCRYPTION);
|
||||||
|
|
||||||
|
// Perform decryption
|
||||||
|
$decryption = openssl_decrypt(substr($decodedData, $ivLength),
|
||||||
|
self::TRANSPORT_ENCRYPTION,
|
||||||
|
base64_decode($encryptionKey),
|
||||||
|
OPENSSL_RAW_DATA,
|
||||||
|
substr($decodedData, 0, $ivLength)
|
||||||
|
);
|
||||||
|
|
||||||
|
if($decryption === false)
|
||||||
|
{
|
||||||
|
throw new CryptographyException('Failed to decrypt transport content: ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $decryption;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,10 +7,11 @@ use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Socialbox\Enums\StandardHeaders;
|
use Socialbox\Enums\StandardHeaders;
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RequestException;
|
||||||
use Socialbox\Exceptions\RpcException;
|
use Socialbox\Exceptions\RpcException;
|
||||||
use Socialbox\Exceptions\StandardException;
|
use Socialbox\Exceptions\StandardException;
|
||||||
use Socialbox\Managers\SessionManager;
|
use Socialbox\Managers\SessionManager;
|
||||||
use Socialbox\Objects\ClientRequest;
|
use Socialbox\Objects\ClientRequestOld;
|
||||||
use Socialbox\Objects\RpcRequest;
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
class RpcHandler
|
class RpcHandler
|
||||||
|
@ -19,16 +20,12 @@ class RpcHandler
|
||||||
* Gets the incoming ClientRequest object, validates if the request is valid & if a session UUID is provided
|
* Gets the incoming ClientRequest object, validates if the request is valid & if a session UUID is provided
|
||||||
* checks if the request signature matches the client's provided public key.
|
* checks if the request signature matches the client's provided public key.
|
||||||
*
|
*
|
||||||
* @return ClientRequest The parsed ClientRequest object
|
* @return ClientRequestOld The parsed ClientRequest object
|
||||||
|
* @throws RequestException
|
||||||
* @throws RpcException Thrown if the request is invalid
|
* @throws RpcException Thrown if the request is invalid
|
||||||
*/
|
*/
|
||||||
public static function getClientRequest(): ClientRequest
|
public static function getClientRequest(): ClientRequestOld
|
||||||
{
|
{
|
||||||
if($_SERVER['REQUEST_METHOD'] !== 'POST')
|
|
||||||
{
|
|
||||||
throw new RpcException('Invalid Request Method, expected POST', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$headers = Utilities::getRequestHeaders();
|
$headers = Utilities::getRequestHeaders();
|
||||||
|
@ -36,7 +33,7 @@ class RpcHandler
|
||||||
{
|
{
|
||||||
if (!isset($headers[$header]))
|
if (!isset($headers[$header]))
|
||||||
{
|
{
|
||||||
throw new RpcException("Missing required header: $header", 400);
|
throw new RequestException("Missing required header: $header", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the headers
|
// Validate the headers
|
||||||
|
@ -73,7 +70,7 @@ class RpcHandler
|
||||||
throw new RpcException("Failed to parse request: " . $e->getMessage(), 400, $e);
|
throw new RpcException("Failed to parse request: " . $e->getMessage(), 400, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
$clientRequest = new ClientRequest($headers, self::getRpcRequests(), self::getRequestHash());
|
$clientRequest = new ClientRequestOld($headers, self::getRpcRequests(), self::getRequestHash());
|
||||||
|
|
||||||
// Verify the session & request signature
|
// Verify the session & request signature
|
||||||
if($clientRequest->getSessionUuid() !== null)
|
if($clientRequest->getSessionUuid() !== null)
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes\StandardMethods;
|
|
||||||
|
|
||||||
use Socialbox\Abstracts\Method;
|
|
||||||
use Socialbox\Enums\FirstLevelAuthentication;
|
|
||||||
use Socialbox\Enums\StandardError;
|
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
|
||||||
use Socialbox\Objects\ClientRequest;
|
|
||||||
use Socialbox\Objects\RpcRequest;
|
|
||||||
use Socialbox\Objects\RpcResponse;
|
|
||||||
|
|
||||||
class Authenticate extends Method
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
|
||||||
{
|
|
||||||
if(!isset($rpcRequest->getParameters()['type']))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing required parameter \'type\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strlen($rpcRequest->getParameters()['type']) == 0)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Parameter \'type\' cannot be empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
return match (FirstLevelAuthentication::tryFrom($rpcRequest->getParameters()['type']))
|
|
||||||
{
|
|
||||||
FirstLevelAuthentication::PASSWORD => self::handlePassword($request),
|
|
||||||
|
|
||||||
default => $rpcRequest->produceError(StandardError::UNSUPPORTED_AUTHENTICATION_TYPE,
|
|
||||||
sprintf('Unsupported authentication type: %s', $rpcRequest->getParameters()['type'])
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the password authentication phase for the peer
|
|
||||||
*
|
|
||||||
* @param ClientRequest $request
|
|
||||||
* @return SerializableInterface
|
|
||||||
*/
|
|
||||||
private static function handlePassword(ClientRequest $request): SerializableInterface
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes\StandardMethods;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Socialbox\Abstracts\Method;
|
|
||||||
use Socialbox\Enums\StandardError;
|
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
|
||||||
use Socialbox\Managers\SessionManager;
|
|
||||||
use Socialbox\Objects\ClientRequest;
|
|
||||||
use Socialbox\Objects\RpcRequest;
|
|
||||||
|
|
||||||
class CreateSession extends Method
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Executes the session creation process based on the provided public key.
|
|
||||||
*
|
|
||||||
* @param ClientRequest $request The client request object.
|
|
||||||
* @param RpcRequest $rpcRequest The RPC request containing parameters for execution.
|
|
||||||
* @return SerializableInterface|null Returns a response with the session UUID or an error.
|
|
||||||
*/
|
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
|
||||||
{
|
|
||||||
if(!$rpcRequest->containsParameter('public_key'))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing parameter \'public_key\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$uuid = SessionManager::createSession($rpcRequest->getParameter('public_key'));
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR, 'There was an error while trying to create a new session: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
catch(InvalidArgumentException $e)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rpcRequest->produceResponse($uuid);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes\StandardMethods;
|
|
||||||
|
|
||||||
use Socialbox\Abstracts\Method;
|
|
||||||
use Socialbox\Classes\Logger;
|
|
||||||
use Socialbox\Enums\StandardError;
|
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
|
||||||
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 GetMe extends Method
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
|
||||||
{
|
|
||||||
// Check if the request has a Session UUID
|
|
||||||
if($request->getSessionUuid() === null)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::SESSION_REQUIRED);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Get the session and check if it's already authenticated
|
|
||||||
$session = SessionManager::getSession($request->getSessionUuid());
|
|
||||||
if($session->getPeerUuid() === null)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::AUTHENTICATION_REQUIRED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the peer and return it
|
|
||||||
return $rpcRequest->produceResponse(RegisteredPeerManager::getPeer($session->getPeerUuid())->toSelfUser());
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
throw new StandardException("There was an unexpected error while trying to register", StandardError::INTERNAL_SERVER_ERROR, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
<?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\RegisteredPeerManager;
|
|
||||||
use Socialbox\Managers\SessionManager;
|
|
||||||
use Socialbox\Objects\ClientRequest;
|
|
||||||
use Socialbox\Objects\RpcRequest;
|
|
||||||
|
|
||||||
class GetSession extends Method
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
|
||||||
{
|
|
||||||
if($request->getSessionUuid() === null)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::SESSION_REQUIRED);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Get the session
|
|
||||||
$session = SessionManager::getSession($request->getSessionUuid());
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
throw new StandardException("There was an unexpected error while trying to retrieve the session", StandardError::INTERNAL_SERVER_ERROR, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes\StandardMethods;
|
|
||||||
|
|
||||||
use Socialbox\Abstracts\Method;
|
|
||||||
use Socialbox\Classes\Configuration;
|
|
||||||
use Socialbox\Classes\Validator;
|
|
||||||
use Socialbox\Enums\StandardError;
|
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
|
||||||
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 Identify extends Method
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
|
||||||
{
|
|
||||||
// Check if the username parameter exists
|
|
||||||
if(!$rpcRequest->containsParameter('username'))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing parameter \'username\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the username is valid
|
|
||||||
if(!Validator::validateUsername($rpcRequest->getParameter('username')))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::INVALID_USERNAME, StandardError::INVALID_USERNAME->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the request has a Session UUID
|
|
||||||
if($request->getSessionUuid() === null)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::SESSION_REQUIRED);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Get the session and check if it's already authenticated
|
|
||||||
$session = SessionManager::getSession($request->getSessionUuid());
|
|
||||||
|
|
||||||
// If the session is already authenticated, return an error
|
|
||||||
if($session->getPeerUuid() !== null)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::ALREADY_AUTHENTICATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the username does not exist, return an error
|
|
||||||
if(!RegisteredPeerManager::usernameExists($rpcRequest->getParameter('username')))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::NOT_REGISTERED, StandardError::NOT_REGISTERED->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create session to be identified as the provided username
|
|
||||||
SessionManager::updatePeer($session->getUuid(), $rpcRequest->getParameter('username'));
|
|
||||||
|
|
||||||
// Set the required session flags
|
|
||||||
$initialFlags = [];
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
throw new StandardException("There was an unexpected error while trying to register", StandardError::INTERNAL_SERVER_ERROR, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true to indicate the operation was a success
|
|
||||||
return $rpcRequest->produceResponse(true);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Socialbox\Classes\StandardMethods;
|
namespace Socialbox\Classes\StandardMethods;
|
||||||
|
|
||||||
use Socialbox\Abstracts\Method;
|
use Socialbox\Abstracts\Method;
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
use Socialbox\Objects\ClientRequest;
|
use Socialbox\Objects\ClientRequest;
|
||||||
use Socialbox\Objects\RpcRequest;
|
use Socialbox\Objects\ClientRequestOld;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
class Ping extends Method
|
class Ping extends Method
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
@ -16,4 +17,4 @@ class Ping extends Method
|
||||||
{
|
{
|
||||||
return $rpcRequest->produceResponse(true);
|
return $rpcRequest->produceResponse(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,80 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes\StandardMethods;
|
|
||||||
|
|
||||||
use Socialbox\Abstracts\Method;
|
|
||||||
use Socialbox\Classes\Configuration;
|
|
||||||
use Socialbox\Classes\Validator;
|
|
||||||
use Socialbox\Enums\StandardError;
|
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
|
||||||
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 Register extends Method
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
|
||||||
{
|
|
||||||
if(!Configuration::getRegistrationConfiguration()->isRegistrationEnabled())
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::REGISTRATION_DISABLED, StandardError::REGISTRATION_DISABLED->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the username parameter exists
|
|
||||||
if(!$rpcRequest->containsParameter('username'))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing parameter \'username\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the username is valid
|
|
||||||
if(!Validator::validateUsername($rpcRequest->getParameter('username')))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::INVALID_USERNAME, StandardError::INVALID_USERNAME->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the username exists already
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (RegisteredPeerManager::usernameExists($rpcRequest->getParameter('username')))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::USERNAME_ALREADY_EXISTS, StandardError::USERNAME_ALREADY_EXISTS->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
throw new StandardException("There was an unexpected error while trying to check the username existence", StandardError::INTERNAL_SERVER_ERROR, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the request has a Session UUID
|
|
||||||
if($request->getSessionUuid() === null)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::SESSION_REQUIRED);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Get the session and check if it's already authenticated
|
|
||||||
$session = SessionManager::getSession($request->getSessionUuid());
|
|
||||||
if($session->getPeerUuid() !== null)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::ALREADY_AUTHENTICATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the peer & set the current's session authenticated peer as the newly created peer
|
|
||||||
SessionManager::updatePeer($session->getUuid(), RegisteredPeerManager::createPeer($rpcRequest->getParameter('username')));
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
throw new StandardException("There was an unexpected error while trying to register", StandardError::INTERNAL_SERVER_ERROR, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true to indicate the operation was a success
|
|
||||||
return $rpcRequest->produceResponse(true);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ use Socialbox\Interfaces\SerializableInterface;
|
||||||
use Socialbox\Managers\CaptchaManager;
|
use Socialbox\Managers\CaptchaManager;
|
||||||
use Socialbox\Managers\RegisteredPeerManager;
|
use Socialbox\Managers\RegisteredPeerManager;
|
||||||
use Socialbox\Managers\SessionManager;
|
use Socialbox\Managers\SessionManager;
|
||||||
use Socialbox\Objects\ClientRequest;
|
use Socialbox\Objects\ClientRequestOld;
|
||||||
use Socialbox\Objects\RpcRequest;
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
class VerificationAnswerImageCaptcha extends Method
|
class VerificationAnswerImageCaptcha extends Method
|
||||||
|
@ -20,7 +20,7 @@ class VerificationAnswerImageCaptcha extends Method
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
public static function execute(ClientRequestOld $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
{
|
{
|
||||||
// Check if the request has a Session UUID
|
// Check if the request has a Session UUID
|
||||||
if($request->getSessionUuid() === null)
|
if($request->getSessionUuid() === null)
|
||||||
|
|
|
@ -13,7 +13,7 @@ use Socialbox\Interfaces\SerializableInterface;
|
||||||
use Socialbox\Managers\CaptchaManager;
|
use Socialbox\Managers\CaptchaManager;
|
||||||
use Socialbox\Managers\RegisteredPeerManager;
|
use Socialbox\Managers\RegisteredPeerManager;
|
||||||
use Socialbox\Managers\SessionManager;
|
use Socialbox\Managers\SessionManager;
|
||||||
use Socialbox\Objects\ClientRequest;
|
use Socialbox\Objects\ClientRequestOld;
|
||||||
use Socialbox\Objects\RpcRequest;
|
use Socialbox\Objects\RpcRequest;
|
||||||
use Socialbox\Objects\Standard\ImageCaptcha;
|
use Socialbox\Objects\Standard\ImageCaptcha;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class VerificationGetImageCaptcha extends Method
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
public static function execute(ClientRequestOld $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
{
|
{
|
||||||
// Check if the request has a Session UUID
|
// Check if the request has a Session UUID
|
||||||
if($request->getSessionUuid() === null)
|
if($request->getSessionUuid() === null)
|
||||||
|
|
|
@ -7,6 +7,7 @@ use InvalidArgumentException;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Socialbox\Enums\StandardHeaders;
|
use Socialbox\Enums\StandardHeaders;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
class Utilities
|
class Utilities
|
||||||
|
|
|
@ -13,15 +13,6 @@ enum PeerFlags : string
|
||||||
// General Flags
|
// General Flags
|
||||||
case VERIFIED = 'VERIFIED';
|
case VERIFIED = 'VERIFIED';
|
||||||
|
|
||||||
// Verification Flags
|
|
||||||
case VER_SET_PASSWORD = 'VER_SET_PASSWORD';
|
|
||||||
case VER_SET_OTP = 'VER_SET_OTP';
|
|
||||||
case VER_SET_DISPLAY_NAME = 'VER_SET_DISPLAY_NAME';
|
|
||||||
case VER_EMAIL = 'VER_EMAIL';
|
|
||||||
case VER_SMS = 'VER_SMS';
|
|
||||||
case VER_PHONE_CALL = 'VER_PHONE_CALL';
|
|
||||||
case VER_SOLVE_IMAGE_CAPTCHA = 'VER_SOLVE_IMAGE_CAPTCHA';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an array of PeerFlags enums to a string representation
|
* Converts an array of PeerFlags enums to a string representation
|
||||||
*
|
*
|
||||||
|
@ -48,20 +39,4 @@ enum PeerFlags : string
|
||||||
|
|
||||||
return array_map(fn(string $value) => PeerFlags::from(trim($value)), explode(',', $flagString));
|
return array_map(fn(string $value) => PeerFlags::from(trim($value)), explode(',', $flagString));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the flag is public. Public flags can be seen by other peers.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isPublic(): bool
|
|
||||||
{
|
|
||||||
return match($this)
|
|
||||||
{
|
|
||||||
self::VER_SET_PASSWORD,
|
|
||||||
self::VER_SET_OTP,
|
|
||||||
self::VER_SOLVE_IMAGE_CAPTCHA => false,
|
|
||||||
default => true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
enum SessionFlags : string
|
enum SessionFlags : string
|
||||||
{
|
{
|
||||||
// Verification, require fields
|
// Verification, require fields
|
||||||
case VER_SET_PASSWORD = 'VER_SET_PASSWORD'; // Peer has to set a password
|
case SET_PASSWORD = 'SET_PASSWORD'; // Peer has to set a password
|
||||||
case VER_SET_OTP = 'VER_SET_OTP'; // Peer has to set an OTP
|
case SET_OTP = 'SET_OTP'; // Peer has to set an OTP
|
||||||
case VER_SET_DISPLAY_NAME = 'VER_SET_DISPLAY_NAME'; // Peer has to set a display name
|
case SET_DISPLAY_NAME = 'SET_DISPLAY_NAME'; // Peer has to set a display name
|
||||||
|
|
||||||
// Verification, verification requirements
|
// Verification, verification requirements
|
||||||
case VER_EMAIL = 'VER_EMAIL'; // Peer has to verify their email
|
case VER_EMAIL = 'VER_EMAIL'; // Peer has to verify their email
|
||||||
|
@ -18,4 +18,31 @@
|
||||||
// Login, require fields
|
// Login, require fields
|
||||||
case VER_PASSWORD = 'VER_PASSWORD'; // Peer has to enter their password
|
case VER_PASSWORD = 'VER_PASSWORD'; // Peer has to enter their password
|
||||||
case VER_OTP = 'VER_OTP'; // Peer has to enter their OTP
|
case VER_OTP = 'VER_OTP'; // Peer has to enter their OTP
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array of SessionFlags to a comma-separated string of their values.
|
||||||
|
*
|
||||||
|
* @param array $flags An array of SessionFlags objects to be converted.
|
||||||
|
* @return string A comma-separated string of the values of the provided SessionFlags.
|
||||||
|
*/
|
||||||
|
public static function toString(array $flags): string
|
||||||
|
{
|
||||||
|
return implode(',', array_map(fn(SessionFlags $flag) => $flag->value, $flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a comma-separated string of flag values into an array of SessionFlags objects.
|
||||||
|
*
|
||||||
|
* @param string $flagString A comma-separated string representing flag values.
|
||||||
|
* @return array An array of SessionFlags objects created from the provided string.
|
||||||
|
*/
|
||||||
|
public static function fromString(string $flagString): array
|
||||||
|
{
|
||||||
|
if (empty($flagString))
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_map(fn(string $value) => SessionFlags::from(trim($value)), explode(',', $flagString));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,11 @@ namespace Socialbox\Enums;
|
||||||
|
|
||||||
enum SessionState : string
|
enum SessionState : string
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* The session is awaiting a Diffie-Hellman exchange to be completed
|
||||||
|
*/
|
||||||
|
case AWAITING_DHE = 'AWAITING_DHE';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The session is currently active and usable
|
* The session is currently active and usable
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,15 +17,19 @@ enum StandardError : int
|
||||||
|
|
||||||
// Authentication/Cryptography Errors
|
// Authentication/Cryptography Errors
|
||||||
case INVALID_PUBLIC_KEY = -3000;
|
case INVALID_PUBLIC_KEY = -3000;
|
||||||
case UNSUPPORTED_AUTHENTICATION_TYPE = -3001;
|
|
||||||
case ALREADY_AUTHENTICATED = -3002;
|
case SESSION_REQUIRED = -3001;
|
||||||
case AUTHENTICATION_REQUIRED = -3003;
|
case SESSION_NOT_FOUND = -3002;
|
||||||
case SESSION_NOT_FOUND = -3004;
|
case SESSION_EXPIRED = -3003;
|
||||||
case SESSION_REQUIRED = -3005;
|
case SESSION_DHE_REQUIRED = -3004;
|
||||||
case REGISTRATION_DISABLED = -3006;
|
|
||||||
case CAPTCHA_NOT_AVAILABLE = -3007;
|
case ALREADY_AUTHENTICATED = -3005;
|
||||||
case INCORRECT_CAPTCHA_ANSWER = -3008;
|
case UNSUPPORTED_AUTHENTICATION_TYPE = -3006;
|
||||||
case CAPTCHA_EXPIRED = -3009;
|
case AUTHENTICATION_REQUIRED = -3007;
|
||||||
|
case REGISTRATION_DISABLED = -3008;
|
||||||
|
case CAPTCHA_NOT_AVAILABLE = -3009;
|
||||||
|
case INCORRECT_CAPTCHA_ANSWER = -3010;
|
||||||
|
case CAPTCHA_EXPIRED = -3011;
|
||||||
|
|
||||||
// General Error Messages
|
// General Error Messages
|
||||||
case PEER_NOT_FOUND = -4000;
|
case PEER_NOT_FOUND = -4000;
|
||||||
|
|
|
@ -1,56 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Socialbox\Enums;
|
namespace Socialbox\Enums;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration of standard header names used in HTTP communication.
|
* Enumeration of standard header names used in HTTP communication.
|
||||||
*/
|
*/
|
||||||
enum StandardHeaders : string
|
enum StandardHeaders : string
|
||||||
{
|
{
|
||||||
case CONTENT_TYPE = 'Content-Type';
|
case REQUEST_TYPE = 'Request-Type';
|
||||||
|
case IDENTIFY_AS = 'Identify-As';
|
||||||
case CLIENT_NAME = 'Client-Name';
|
case CLIENT_NAME = 'Client-Name';
|
||||||
case CLIENT_VERSION = 'Client-Version';
|
case CLIENT_VERSION = 'Client-Version';
|
||||||
case SESSION_UUID = 'Session-UUID';
|
|
||||||
case FROM_PEER = 'From-Peer';
|
|
||||||
case SIGNATURE = 'Signature';
|
|
||||||
case PUBLIC_KEY = 'Public-Key';
|
case PUBLIC_KEY = 'Public-Key';
|
||||||
|
|
||||||
/**
|
case SESSION_UUID = 'Session-UUID';
|
||||||
* Determines if the current instance is required based on its type.
|
case SIGNATURE = 'Signature';
|
||||||
*
|
|
||||||
* @return bool Returns true if the instance is of type CONTENT_TYPE, CLIENT_VERSION, or CLIENT_NAME; otherwise, false.
|
|
||||||
*/
|
|
||||||
public function isRequired(): bool
|
|
||||||
{
|
|
||||||
return match($this)
|
|
||||||
{
|
|
||||||
self::CONTENT_TYPE,
|
|
||||||
self::CLIENT_VERSION,
|
|
||||||
self::CLIENT_NAME
|
|
||||||
=> true,
|
|
||||||
|
|
||||||
default => false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves an array of required headers.
|
|
||||||
*
|
|
||||||
* @return array An array containing only the headers that are marked as required.
|
|
||||||
*/
|
|
||||||
public static function getRequiredHeaders(): array
|
|
||||||
{
|
|
||||||
$results = [];
|
|
||||||
foreach(StandardHeaders::cases() as $header)
|
|
||||||
{
|
|
||||||
if($header->isRequired())
|
|
||||||
{
|
|
||||||
$results[] = $header->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
|
@ -65,4 +29,4 @@ enum StandardHeaders : string
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,17 +12,12 @@ use Socialbox\Classes\StandardMethods\Register;
|
||||||
use Socialbox\Exceptions\StandardException;
|
use Socialbox\Exceptions\StandardException;
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
use Socialbox\Objects\ClientRequest;
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\ClientRequestOld;
|
||||||
use Socialbox\Objects\RpcRequest;
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
enum StandardMethods : string
|
enum StandardMethods : string
|
||||||
{
|
{
|
||||||
case PING = 'ping';
|
case PING = 'ping';
|
||||||
case CREATE_SESSION = 'createSession';
|
|
||||||
case REGISTER = 'register';
|
|
||||||
case IDENTIFY = 'identify';
|
|
||||||
case GET_ME = 'getMe';
|
|
||||||
case VERIFICATION_GET_IMAGE_CAPTCHA = 'verificationGetImageCaptcha';
|
|
||||||
case VERIFICATION_ANSWER_IMAGE_CAPTCHA = 'verificationAnswerImageCaptcha';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ClientRequest $request
|
* @param ClientRequest $request
|
||||||
|
@ -35,12 +30,6 @@ enum StandardMethods : string
|
||||||
return match ($this)
|
return match ($this)
|
||||||
{
|
{
|
||||||
self::PING => Ping::execute($request, $rpcRequest),
|
self::PING => Ping::execute($request, $rpcRequest),
|
||||||
self::CREATE_SESSION => CreateSession::execute($request, $rpcRequest),
|
|
||||||
self::REGISTER => Register::execute($request, $rpcRequest),
|
|
||||||
self::IDENTIFY => Identify::execute($request, $rpcRequest),
|
|
||||||
self::GET_ME => GetMe::execute($request, $rpcRequest),
|
|
||||||
self::VERIFICATION_GET_IMAGE_CAPTCHA => VerificationGetImageCaptcha::execute($request, $rpcRequest),
|
|
||||||
self::VERIFICATION_ANSWER_IMAGE_CAPTCHA => VerificationAnswerImageCaptcha::execute($request, $rpcRequest),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
21
src/Socialbox/Enums/Types/RequestType.php
Normal file
21
src/Socialbox/Enums/Types/RequestType.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Enums\Types;
|
||||||
|
|
||||||
|
enum RequestType : string
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Represents the action of initiating a session.
|
||||||
|
*/
|
||||||
|
case INITIATE_SESSION = 'init';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the action of performing a Diffie-Hellman key exchange.
|
||||||
|
*/
|
||||||
|
case DHE_EXCHANGE = 'dhe';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the action of performing a remote procedure call.
|
||||||
|
*/
|
||||||
|
case RPC = 'rpc';
|
||||||
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Socialbox\Exceptions;
|
namespace Socialbox\Exceptions;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
class DatabaseOperationException extends Exception
|
class DatabaseOperationException extends Exception
|
||||||
{
|
{
|
||||||
public function __construct(string $message, ?Throwable $throwable=null)
|
public function __construct(string $message, ?Throwable $throwable=null)
|
||||||
{
|
{
|
||||||
parent::__construct($message, 500, $throwable);
|
parent::__construct($message, 500, $throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
21
src/Socialbox/Exceptions/RequestException.php
Normal file
21
src/Socialbox/Exceptions/RequestException.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class RequestException extends Exception
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initializes a new instance of the exception class with a custom message, code, and a previous throwable.
|
||||||
|
*
|
||||||
|
* @param string $message The exception message. Defaults to an empty string.
|
||||||
|
* @param int $code The exception code. Defaults to 0.
|
||||||
|
* @param Throwable|null $previous The previous throwable used for exception chaining. Defaults to null.
|
||||||
|
*/
|
||||||
|
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ use Socialbox\Enums\StandardError;
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
use Socialbox\Exceptions\StandardException;
|
use Socialbox\Exceptions\StandardException;
|
||||||
use Socialbox\Objects\Database\RegisteredPeerRecord;
|
use Socialbox\Objects\Database\RegisteredPeerRecord;
|
||||||
|
use Socialbox\Objects\Database\SecurePasswordRecord;
|
||||||
use Symfony\Component\Uid\Uuid;
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
class RegisteredPeerManager
|
class RegisteredPeerManager
|
||||||
|
@ -55,9 +56,6 @@ class RegisteredPeerManager
|
||||||
Logger::getLogger()->verbose(sprintf("Creating a new peer with username %s", $username));
|
Logger::getLogger()->verbose(sprintf("Creating a new peer with username %s", $username));
|
||||||
$uuid = Uuid::v4()->toRfc4122();
|
$uuid = Uuid::v4()->toRfc4122();
|
||||||
|
|
||||||
// If `enabled` is True, we insert the peer into the database as an activated account.
|
|
||||||
if($enabled)
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$statement = Database::getConnection()->prepare('INSERT INTO `registered_peers` (uuid, username, enabled) VALUES (?, ?, ?)');
|
$statement = Database::getConnection()->prepare('INSERT INTO `registered_peers` (uuid, username, enabled) VALUES (?, ?, ?)');
|
||||||
|
@ -74,62 +72,6 @@ class RegisteredPeerManager
|
||||||
return $uuid;
|
return $uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we insert the peer into the database as a disabled account & the required verification flags.
|
|
||||||
$flags = [];
|
|
||||||
|
|
||||||
if(Configuration::getRegistrationConfiguration()->isPasswordRequired())
|
|
||||||
{
|
|
||||||
$flags[] = PeerFlags::VER_SET_PASSWORD;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Configuration::getRegistrationConfiguration()->isOtpRequired())
|
|
||||||
{
|
|
||||||
$flags[] = PeerFlags::VER_SET_OTP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Configuration::getRegistrationConfiguration()->isDisplayNameRequired())
|
|
||||||
{
|
|
||||||
$flags[] = PeerFlags::VER_SET_DISPLAY_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Configuration::getRegistrationConfiguration()->isEmailVerificationRequired())
|
|
||||||
{
|
|
||||||
$flags[] = PeerFlags::VER_EMAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Configuration::getRegistrationConfiguration()->isSmsVerificationRequired())
|
|
||||||
{
|
|
||||||
$flags[] = PeerFlags::VER_SMS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Configuration::getRegistrationConfiguration()->isPhoneCallVerificationRequired())
|
|
||||||
{
|
|
||||||
$flags[] = PeerFlags::VER_PHONE_CALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Configuration::getRegistrationConfiguration()->isImageCaptchaVerificationRequired())
|
|
||||||
{
|
|
||||||
$flags[] = PeerFlags::VER_SOLVE_IMAGE_CAPTCHA;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$implodedFlags = implode(',', array_map(fn($flag) => $flag->name, $flags));
|
|
||||||
$statement = Database::getConnection()->prepare('INSERT INTO `registered_peers` (uuid, username, enabled, flags) VALUES (?, ?, ?, ?)');
|
|
||||||
$statement->bindParam(1, $uuid);
|
|
||||||
$statement->bindParam(2, $username);
|
|
||||||
$statement->bindParam(3, $enabled, PDO::PARAM_BOOL);
|
|
||||||
$statement->bindParam(4, $implodedFlags);
|
|
||||||
$statement->execute();
|
|
||||||
}
|
|
||||||
catch(PDOException $e)
|
|
||||||
{
|
|
||||||
throw new DatabaseOperationException('Failed to create the peer in the database', $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a peer from the database based on the given UUID or RegisteredPeerRecord.
|
* Deletes a peer from the database based on the given UUID or RegisteredPeerRecord.
|
||||||
* WARNING: This operation is cascading and will delete all associated data.
|
* WARNING: This operation is cascading and will delete all associated data.
|
||||||
|
@ -200,11 +142,10 @@ class RegisteredPeerManager
|
||||||
* Retrieves a peer record by the given username.
|
* Retrieves a peer record by the given username.
|
||||||
*
|
*
|
||||||
* @param string $username The username of the peer to be retrieved.
|
* @param string $username The username of the peer to be retrieved.
|
||||||
* @return RegisteredPeerRecord The record of the peer associated with the given username.
|
* @return RegisteredPeerRecord|null The record of the peer associated with the given username.
|
||||||
* @throws DatabaseOperationException If there is an error while querying the database.
|
* @throws DatabaseOperationException If there is an error while querying the database.
|
||||||
* @throws StandardException If the peer does not exist.
|
|
||||||
*/
|
*/
|
||||||
public static function getPeerByUsername(string $username): RegisteredPeerRecord
|
public static function getPeerByUsername(string $username): ?RegisteredPeerRecord
|
||||||
{
|
{
|
||||||
Logger::getLogger()->verbose(sprintf("Retrieving peer %s from the database", $username));
|
Logger::getLogger()->verbose(sprintf("Retrieving peer %s from the database", $username));
|
||||||
|
|
||||||
|
@ -218,7 +159,7 @@ class RegisteredPeerManager
|
||||||
|
|
||||||
if($result === false)
|
if($result === false)
|
||||||
{
|
{
|
||||||
throw new StandardException(sprintf("The requested peer '%s' does not exist", $username), StandardError::PEER_NOT_FOUND);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RegisteredPeerRecord($result);
|
return new RegisteredPeerRecord($result);
|
||||||
|
@ -365,4 +306,35 @@ class RegisteredPeerManager
|
||||||
throw new DatabaseOperationException('Failed to remove the flag from the peer in the database', $e);
|
throw new DatabaseOperationException('Failed to remove the flag from the peer in the database', $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static function getPasswordAuthentication(string|RegisteredPeerRecord $peerUuid): ?SecurePasswordRecord
|
||||||
|
{
|
||||||
|
if($peerUuid instanceof RegisteredPeerRecord)
|
||||||
|
{
|
||||||
|
$peerUuid = $peerUuid->getUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement = Database::getConnection()->prepare('SELECT * FROM `authentication_passwords` WHERE peer_uuid=?');
|
||||||
|
$statement->bindParam(1, $peerUuid);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if($result === false)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SecurePasswordRecord($result);
|
||||||
|
}
|
||||||
|
catch(PDOException | \DateMalformedStringException $e)
|
||||||
|
{
|
||||||
|
throw new DatabaseOperationException('Failed to get the secure password record from the database', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,10 +7,12 @@
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
|
use Socialbox\Classes\Configuration;
|
||||||
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\Classes\Utilities;
|
||||||
|
use Socialbox\Enums\Flags\SessionFlags;
|
||||||
use Socialbox\Enums\SessionState;
|
use Socialbox\Enums\SessionState;
|
||||||
use Socialbox\Enums\StandardError;
|
use Socialbox\Enums\StandardError;
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
@ -31,25 +33,89 @@
|
||||||
* @throws InvalidArgumentException If the public key is empty or invalid.
|
* @throws InvalidArgumentException If the public key is empty or invalid.
|
||||||
* @throws DatabaseOperationException If there is an error while creating the session in the database.
|
* @throws DatabaseOperationException If there is an error while creating the session in the database.
|
||||||
*/
|
*/
|
||||||
public static function createSession(string $publicKey): string
|
public static function createSession(string $publicKey, RegisteredPeerRecord $peer): string
|
||||||
{
|
{
|
||||||
if($publicKey === '')
|
if($publicKey === '')
|
||||||
{
|
{
|
||||||
throw new InvalidArgumentException('The public key cannot be empty', 400);
|
throw new InvalidArgumentException('The public key cannot be empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Cryptography::validatePublicKey($publicKey))
|
if(!Cryptography::validatePublicKey($publicKey))
|
||||||
{
|
{
|
||||||
throw new InvalidArgumentException('The given public key is invalid', 400);
|
throw new InvalidArgumentException('The given public key is invalid');
|
||||||
}
|
}
|
||||||
|
|
||||||
$uuid = Uuid::v4()->toRfc4122();
|
$uuid = Uuid::v4()->toRfc4122();
|
||||||
|
$flags = [];
|
||||||
|
|
||||||
|
if($peer->isEnabled())
|
||||||
|
{
|
||||||
|
if(RegisteredPeerManager::getPasswordAuthentication($peer))
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::VER_PASSWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getRegistrationConfiguration()->isImageCaptchaVerificationRequired())
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::VER_IMAGE_CAPTCHA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(Configuration::getRegistrationConfiguration()->isDisplayNameRequired())
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::SET_DISPLAY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getRegistrationConfiguration()->isEmailVerificationRequired())
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::VER_EMAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getRegistrationConfiguration()->isSmsVerificationRequired())
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::VER_SMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getRegistrationConfiguration()->isPhoneCallVerificationRequired())
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::VER_PHONE_CALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getRegistrationConfiguration()->isImageCaptchaVerificationRequired())
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::VER_IMAGE_CAPTCHA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getRegistrationConfiguration()->isPasswordRequired())
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::SET_PASSWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getRegistrationConfiguration()->isOtpRequired())
|
||||||
|
{
|
||||||
|
$flags[] = SessionFlags::SET_OTP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($flags) > 0)
|
||||||
|
{
|
||||||
|
$implodedFlags = SessionFlags::toString($flags);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$implodedFlags = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$peerUuid = $peer->getUuid();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$statement = Database::getConnection()->prepare("INSERT INTO sessions (uuid, public_key) VALUES (?, ?)");
|
$statement = Database::getConnection()->prepare("INSERT INTO sessions (uuid, peer_uuid, public_key, flags) VALUES (?, ?, ?, ?)");
|
||||||
$statement->bindParam(1, $uuid);
|
$statement->bindParam(1, $uuid);
|
||||||
$statement->bindParam(2, $publicKey);
|
$statement->bindParam(2, $peerUuid);
|
||||||
|
$statement->bindParam(3, $publicKey);
|
||||||
|
$statement->bindParam(4, $implodedFlags);
|
||||||
$statement->execute();
|
$statement->execute();
|
||||||
}
|
}
|
||||||
catch(PDOException $e)
|
catch(PDOException $e)
|
||||||
|
@ -219,6 +285,8 @@
|
||||||
$statement = Database::getConnection()->prepare('UPDATE sessions SET state=? WHERE uuid=?');
|
$statement = Database::getConnection()->prepare('UPDATE sessions SET state=? WHERE uuid=?');
|
||||||
$statement->bindParam(1, $state_value);
|
$statement->bindParam(1, $state_value);
|
||||||
$statement->bindParam(2, $uuid);
|
$statement->bindParam(2, $uuid);
|
||||||
|
|
||||||
|
$statement->execute();
|
||||||
}
|
}
|
||||||
catch(PDOException $e)
|
catch(PDOException $e)
|
||||||
{
|
{
|
||||||
|
@ -226,6 +294,34 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the encryption key for the specified session.
|
||||||
|
*
|
||||||
|
* @param string $uuid The unique identifier of the session for which the encryption key is to be set.
|
||||||
|
* @param string $encryptionKey The new encryption key to be assigned.
|
||||||
|
* @return void
|
||||||
|
* @throws DatabaseOperationException If the database operation fails.
|
||||||
|
*/
|
||||||
|
public static function setEncryptionKey(string $uuid, string $encryptionKey): void
|
||||||
|
{
|
||||||
|
Logger::getLogger()->verbose(sprintf('Setting the encryption key for %s', $uuid));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$state_value = SessionState::ACTIVE->value;
|
||||||
|
$statement = Database::getConnection()->prepare('UPDATE sessions SET state=?, encryption_key=? WHERE uuid=?');
|
||||||
|
$statement->bindParam(1, $state_value);
|
||||||
|
$statement->bindParam(2, $encryptionKey);
|
||||||
|
$statement->bindParam(3, $uuid);
|
||||||
|
|
||||||
|
$statement->execute();
|
||||||
|
}
|
||||||
|
catch(PDOException $e)
|
||||||
|
{
|
||||||
|
throw new DatabaseOperationException('Failed to set the encryption key', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the flags associated with a specific session.
|
* Retrieves the flags associated with a specific session.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,142 +1,245 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Socialbox\Objects;
|
namespace Socialbox\Objects;
|
||||||
|
|
||||||
use RuntimeException;
|
use InvalidArgumentException;
|
||||||
use Socialbox\Classes\Cryptography;
|
use Socialbox\Classes\Cryptography;
|
||||||
use Socialbox\Enums\StandardError;
|
use Socialbox\Classes\Utilities;
|
||||||
use Socialbox\Enums\StandardHeaders;
|
use Socialbox\Enums\SessionState;
|
||||||
use Socialbox\Exceptions\CryptographyException;
|
use Socialbox\Enums\StandardHeaders;
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
use Socialbox\Enums\Types\RequestType;
|
||||||
use Socialbox\Exceptions\StandardException;
|
use Socialbox\Exceptions\CryptographyException;
|
||||||
use Socialbox\Managers\SessionManager;
|
use Socialbox\Exceptions\RequestException;
|
||||||
|
use Socialbox\Managers\SessionManager;
|
||||||
|
use Socialbox\Objects\Database\SessionRecord;
|
||||||
|
|
||||||
class ClientRequest
|
class ClientRequest
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private array $headers;
|
private array $headers;
|
||||||
|
private RequestType $requestType;
|
||||||
|
private ?string $requestBody;
|
||||||
|
|
||||||
/**
|
private string $clientName;
|
||||||
* @var RpcRequest[]
|
private string $clientVersion;
|
||||||
*/
|
private ?string $identifyAs;
|
||||||
private array $requests;
|
private ?string $sessionUuid;
|
||||||
|
private ?string $signature;
|
||||||
|
|
||||||
/**
|
public function __construct(array $headers, ?string $requestBody)
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private string $requestHash;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ClientRequest constructor.
|
|
||||||
*
|
|
||||||
* @param array $headers The headers of the request
|
|
||||||
* @param RpcRequest[] $requests The RPC requests of the client
|
|
||||||
*/
|
|
||||||
public function __construct(array $headers, array $requests, string $requestHash)
|
|
||||||
{
|
{
|
||||||
$this->headers = $headers;
|
$this->headers = $headers;
|
||||||
$this->requests = $requests;
|
$this->requestBody = $requestBody;
|
||||||
$this->requestHash = $requestHash;
|
|
||||||
|
$this->clientName = $headers[StandardHeaders::CLIENT_NAME->value];
|
||||||
|
$this->clientVersion = $headers[StandardHeaders::CLIENT_VERSION->value];
|
||||||
|
$this->requestType = RequestType::from($headers[StandardHeaders::REQUEST_TYPE->value]);
|
||||||
|
$this->identifyAs = $headers[StandardHeaders::IDENTIFY_AS->value] ?? null;
|
||||||
|
$this->sessionUuid = $headers[StandardHeaders::SESSION_UUID->value] ?? null;
|
||||||
|
$this->signature = $headers[StandardHeaders::SIGNATURE->value] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getHeaders(): array
|
public function getHeaders(): array
|
||||||
{
|
{
|
||||||
return $this->headers;
|
return $this->headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function headerExists(StandardHeaders|string $header): bool
|
||||||
* @return RpcRequest[]
|
|
||||||
*/
|
|
||||||
public function getRequests(): array
|
|
||||||
{
|
{
|
||||||
return $this->requests;
|
if(is_string($header))
|
||||||
|
{
|
||||||
|
return isset($this->headers[$header]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHash(): string
|
return isset($this->headers[$header->value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeader(StandardHeaders|string $header): ?string
|
||||||
{
|
{
|
||||||
return $this->requestHash;
|
if(!$this->headerExists($header))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_string($header))
|
||||||
|
{
|
||||||
|
return $this->headers[$header];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->headers[$header->value];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestBody(): ?string
|
||||||
|
{
|
||||||
|
return $this->requestBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClientName(): string
|
public function getClientName(): string
|
||||||
{
|
{
|
||||||
return $this->headers[StandardHeaders::CLIENT_NAME->value];
|
return $this->clientName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClientVersion(): string
|
public function getClientVersion(): string
|
||||||
{
|
{
|
||||||
return $this->headers[StandardHeaders::CLIENT_VERSION->value];
|
return $this->clientVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestType(): RequestType
|
||||||
|
{
|
||||||
|
return $this->requestType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIdentifyAs(): ?PeerAddress
|
||||||
|
{
|
||||||
|
if($this->identifyAs === null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PeerAddress::fromAddress($this->identifyAs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSessionUuid(): ?string
|
public function getSessionUuid(): ?string
|
||||||
{
|
{
|
||||||
if(!isset($this->headers[StandardHeaders::SESSION_UUID->value]))
|
return $this->sessionUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSession(): ?SessionRecord
|
||||||
|
{
|
||||||
|
if($this->sessionUuid === null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->headers[StandardHeaders::SESSION_UUID->value];
|
return SessionManager::getSession($this->sessionUuid);
|
||||||
}
|
|
||||||
|
|
||||||
public function getFromPeer(): ?PeerAddress
|
|
||||||
{
|
|
||||||
if(!isset($this->headers[StandardHeaders::FROM_PEER->value]))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PeerAddress::fromAddress($this->headers[StandardHeaders::FROM_PEER->value]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSignature(): ?string
|
public function getSignature(): ?string
|
||||||
{
|
{
|
||||||
if(!isset($this->headers[StandardHeaders::SIGNATURE->value]))
|
return $this->signature;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->headers[StandardHeaders::SIGNATURE->value];
|
private function verifySignature(string $decryptedContent): bool
|
||||||
|
{
|
||||||
|
if($this->getSignature() == null || $this->getSessionUuid() == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Cryptography::verifyContent(hash('sha1', $decryptedContent), $this->getSignature(), $this->getSession()->getPublicKey());
|
||||||
|
}
|
||||||
|
catch(CryptographyException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* Handles a POST request, returning an array of RpcRequest objects
|
||||||
* @throws DatabaseOperationException
|
* expects a JSON encoded body with either a single RpcRequest object or an array of RpcRequest objects
|
||||||
|
*
|
||||||
|
* @return RpcRequest[] The parsed RpcRequest objects
|
||||||
|
* @throws RequestException Thrown if the request is invalid
|
||||||
*/
|
*/
|
||||||
public function verifySignature(): bool
|
public function getRpcRequests(): array
|
||||||
{
|
{
|
||||||
$signature = $this->getSignature();
|
if($this->getSessionUuid() === null)
|
||||||
$sessionUuid = $this->getSessionUuid();
|
|
||||||
|
|
||||||
if($signature == null || $sessionUuid == null)
|
|
||||||
{
|
{
|
||||||
return false;
|
throw new RequestException("Session UUID required", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the existing session
|
||||||
|
$session = $this->getSession();
|
||||||
|
|
||||||
|
// If we're awaiting a DHE, encryption is not possible at this point
|
||||||
|
if($session->getState() === SessionState::AWAITING_DHE)
|
||||||
|
{
|
||||||
|
throw new RequestException("DHE exchange required", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the session is not active, we can't serve these requests
|
||||||
|
if($session->getState() !== SessionState::ACTIVE)
|
||||||
|
{
|
||||||
|
throw new RequestException("Session is not active", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to decrypt the content and verify the signature of the request
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$session = SessionManager::getSession($sessionUuid);
|
$decrypted = Cryptography::decryptTransport($this->requestBody, $session->getEncryptionKey());
|
||||||
|
|
||||||
|
if(!$this->verifySignature($decrypted))
|
||||||
|
{
|
||||||
|
throw new RequestException("Invalid request signature", 401);
|
||||||
}
|
}
|
||||||
catch(StandardException $e)
|
}
|
||||||
|
catch (CryptographyException $e)
|
||||||
{
|
{
|
||||||
if($e->getStandardError() == StandardError::SESSION_NOT_FOUND)
|
throw new RequestException("Failed to decrypt request body", 400, $e);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException($e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// At this stage, all checks has passed; we can try parsing the RPC request
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Cryptography::verifyContent($this->getHash(), $signature, $session->getPublicKey());
|
// Decode the request body
|
||||||
|
$body = Utilities::jsonDecode($decrypted);
|
||||||
}
|
}
|
||||||
catch(CryptographyException $e)
|
catch(InvalidArgumentException $e)
|
||||||
{
|
{
|
||||||
return false;
|
throw new RequestException("Invalid JSON in request body: " . $e->getMessage(), 400, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the body only contains a method, we assume it's a single request
|
||||||
|
if(isset($body['method']))
|
||||||
|
{
|
||||||
|
return [$this->parseRequest($body)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we assume it's an array of requests
|
||||||
|
return array_map(fn($request) => $this->parseRequest($request), $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the raw request data into an RpcRequest object
|
||||||
|
*
|
||||||
|
* @param array $data The raw request data
|
||||||
|
* @return RpcRequest The parsed RpcRequest object
|
||||||
|
* @throws RequestException If the request is invalid
|
||||||
|
*/
|
||||||
|
private function parseRequest(array $data): RpcRequest
|
||||||
|
{
|
||||||
|
if(!isset($data['method']))
|
||||||
|
{
|
||||||
|
throw new RequestException("Missing 'method' key in request", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($data['id']))
|
||||||
|
{
|
||||||
|
if(!is_string($data['id']))
|
||||||
|
{
|
||||||
|
throw new RequestException("Invalid 'id' key in request: Expected string", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen($data['id']) === 0)
|
||||||
|
{
|
||||||
|
throw new RequestException("Invalid 'id' key in request: Expected non-empty string", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen($data['id']) > 8)
|
||||||
|
{
|
||||||
|
throw new RequestException("Invalid 'id' key in request: Expected string of length <= 8", 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($data['parameters']))
|
||||||
|
{
|
||||||
|
if(!is_array($data['parameters']))
|
||||||
|
{
|
||||||
|
throw new RequestException("Invalid 'parameters' key in request: Expected array", 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RpcRequest($data['method'], $data['id'] ?? null, $data['parameters'] ?? null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
162
src/Socialbox/Objects/ClientRequestOld.php
Normal file
162
src/Socialbox/Objects/ClientRequestOld.php
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Objects;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
use Socialbox\Classes\Cryptography;
|
||||||
|
use Socialbox\Enums\SessionState;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\StandardHeaders;
|
||||||
|
use Socialbox\Exceptions\CryptographyException;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\StandardException;
|
||||||
|
use Socialbox\Managers\SessionManager;
|
||||||
|
|
||||||
|
class ClientRequestOld
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private array $headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RpcRequest[]
|
||||||
|
*/
|
||||||
|
private array $requests;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private string $requestHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClientRequest constructor.
|
||||||
|
*
|
||||||
|
* @param array $headers The headers of the request
|
||||||
|
* @param RpcRequest[] $requests The RPC requests of the client
|
||||||
|
*/
|
||||||
|
public function __construct(array $headers, array $requests, string $requestHash)
|
||||||
|
{
|
||||||
|
$this->headers = $headers;
|
||||||
|
$this->requests = $requests;
|
||||||
|
$this->requestHash = $requestHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getHeaders(): array
|
||||||
|
{
|
||||||
|
return $this->headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return RpcRequest[]
|
||||||
|
*/
|
||||||
|
public function getRequests(): array
|
||||||
|
{
|
||||||
|
return $this->requests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHash(): string
|
||||||
|
{
|
||||||
|
return $this->requestHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClientName(): string
|
||||||
|
{
|
||||||
|
return $this->headers[StandardHeaders::CLIENT_NAME->value];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClientVersion(): string
|
||||||
|
{
|
||||||
|
return $this->headers[StandardHeaders::CLIENT_VERSION->value];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSessionUuid(): ?string
|
||||||
|
{
|
||||||
|
if(!isset($this->headers[StandardHeaders::SESSION_UUID->value]))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->headers[StandardHeaders::SESSION_UUID->value];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFromPeer(): ?PeerAddress
|
||||||
|
{
|
||||||
|
if(!isset($this->headers[StandardHeaders::FROM_PEER->value]))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PeerAddress::fromAddress($this->headers[StandardHeaders::FROM_PEER->value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSignature(): ?string
|
||||||
|
{
|
||||||
|
if(!isset($this->headers[StandardHeaders::SIGNATURE->value]))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->headers[StandardHeaders::SIGNATURE->value];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateSession(): void
|
||||||
|
{
|
||||||
|
if($this->getSessionUuid() == null)
|
||||||
|
{
|
||||||
|
throw new StandardException(StandardError::SESSION_REQUIRED->getMessage(), StandardError::SESSION_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
|
$session = SessionManager::getSession($this->getSessionUuid());
|
||||||
|
|
||||||
|
switch($session->getState())
|
||||||
|
{
|
||||||
|
case SessionState::AWAITING_DHE:
|
||||||
|
throw new StandardException(StandardError::SESSION_DHE_REQUIRED->getMessage(), StandardError::SESSION_DHE_REQUIRED);
|
||||||
|
|
||||||
|
case SessionState::EXPIRED:
|
||||||
|
throw new StandardException(StandardError::SESSION_EXPIRED->getMessage(), StandardError::SESSION_EXPIRED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
* @throws DatabaseOperationException
|
||||||
|
*/
|
||||||
|
public function verifySignature(): bool
|
||||||
|
{
|
||||||
|
$signature = $this->getSignature();
|
||||||
|
$sessionUuid = $this->getSessionUuid();
|
||||||
|
|
||||||
|
if($signature == null || $sessionUuid == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$session = SessionManager::getSession($sessionUuid);
|
||||||
|
}
|
||||||
|
catch(StandardException $e)
|
||||||
|
{
|
||||||
|
if($e->getStandardError() == StandardError::SESSION_NOT_FOUND)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Cryptography::verifyContent($this->getHash(), $signature, $session->getPublicKey());
|
||||||
|
}
|
||||||
|
catch(CryptographyException $e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@
|
||||||
namespace Socialbox\Objects\Database;
|
namespace Socialbox\Objects\Database;
|
||||||
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Socialbox\Classes\Utilities;
|
|
||||||
use Socialbox\Enums\Flags\SessionFlags;
|
use Socialbox\Enums\Flags\SessionFlags;
|
||||||
use Socialbox\Enums\SessionState;
|
use Socialbox\Enums\SessionState;
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
@ -15,6 +14,7 @@
|
||||||
private bool $authenticated;
|
private bool $authenticated;
|
||||||
private string $publicKey;
|
private string $publicKey;
|
||||||
private SessionState $state;
|
private SessionState $state;
|
||||||
|
private ?string $encryptionKey;
|
||||||
/**
|
/**
|
||||||
* @var SessionFlags[]
|
* @var SessionFlags[]
|
||||||
*/
|
*/
|
||||||
|
@ -40,7 +40,8 @@
|
||||||
$this->publicKey = $data['public_key'];
|
$this->publicKey = $data['public_key'];
|
||||||
$this->created = $data['created'];
|
$this->created = $data['created'];
|
||||||
$this->lastRequest = $data['last_request'];
|
$this->lastRequest = $data['last_request'];
|
||||||
$this->flags = Utilities::unserializeList($data['flags']);
|
$this->encryptionKey = $data['encryption_key'] ?? null;
|
||||||
|
$this->flags = SessionFlags::fromString($data['flags']);
|
||||||
|
|
||||||
if(SessionState::tryFrom($data['state']) == null)
|
if(SessionState::tryFrom($data['state']) == null)
|
||||||
{
|
{
|
||||||
|
@ -108,6 +109,16 @@
|
||||||
return $this->state;
|
return $this->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the encryption key associated with the instance.
|
||||||
|
*
|
||||||
|
* @return string|null Returns the encryption key as a string.
|
||||||
|
*/
|
||||||
|
public function getEncryptionKey(): ?string
|
||||||
|
{
|
||||||
|
return $this->encryptionKey;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the creation date and time of the object.
|
* Retrieves the creation date and time of the object.
|
||||||
*
|
*
|
||||||
|
@ -163,7 +174,7 @@
|
||||||
'authenticated' => $this->authenticated,
|
'authenticated' => $this->authenticated,
|
||||||
'public_key' => $this->publicKey,
|
'public_key' => $this->publicKey,
|
||||||
'state' => $this->state->value,
|
'state' => $this->state->value,
|
||||||
'flags' => Utilities::serializeList($this->flags),
|
'flags' => SessionFlags::toString($this->flags),
|
||||||
'created' => $this->created,
|
'created' => $this->created,
|
||||||
'last_request' => $this->lastRequest,
|
'last_request' => $this->lastRequest,
|
||||||
];
|
];
|
||||||
|
|
|
@ -3,40 +3,283 @@
|
||||||
namespace Socialbox;
|
namespace Socialbox;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
use Socialbox\Classes\Configuration;
|
use Socialbox\Classes\Configuration;
|
||||||
|
use Socialbox\Classes\Cryptography;
|
||||||
use Socialbox\Classes\Logger;
|
use Socialbox\Classes\Logger;
|
||||||
use Socialbox\Classes\RpcHandler;
|
|
||||||
use Socialbox\Classes\Utilities;
|
use Socialbox\Classes\Utilities;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\SessionState;
|
||||||
use Socialbox\Enums\StandardError;
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\StandardHeaders;
|
||||||
use Socialbox\Enums\StandardMethods;
|
use Socialbox\Enums\StandardMethods;
|
||||||
use Socialbox\Exceptions\RpcException;
|
use Socialbox\Enums\Types\RequestType;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RequestException;
|
||||||
use Socialbox\Exceptions\StandardException;
|
use Socialbox\Exceptions\StandardException;
|
||||||
|
use Socialbox\Managers\RegisteredPeerManager;
|
||||||
|
use Socialbox\Managers\SessionManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
|
||||||
class Socialbox
|
class Socialbox
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handles the RPC (Remote Procedure Call) requests by parsing the client request,
|
* Handles incoming client requests by validating required headers and processing
|
||||||
* executing the appropriate methods, and returning the responses.
|
* the request based on its type. The method ensures proper handling of
|
||||||
|
* specific request types like RPC, session initiation, and DHE exchange,
|
||||||
|
* while returning an appropriate HTTP response for invalid or missing data.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function handleRpc(): void
|
public static function handleRequest(): void
|
||||||
{
|
{
|
||||||
try
|
$requestHeaders = Utilities::getRequestHeaders();
|
||||||
|
|
||||||
|
if(!isset($requestHeaders[StandardHeaders::REQUEST_TYPE->value]))
|
||||||
{
|
{
|
||||||
$clientRequest = RpcHandler::getClientRequest();
|
http_response_code(400);
|
||||||
}
|
print('Missing required header: ' . StandardHeaders::REQUEST_TYPE->value);
|
||||||
catch(RpcException $e)
|
|
||||||
{
|
|
||||||
Logger::getLogger()->error('Failed to parse the client request', $e);
|
|
||||||
http_response_code($e->getCode());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::getLogger()->verbose(sprintf('Received %d RPC request(s) from %s', count($clientRequest->getRequests()), $_SERVER['REMOTE_ADDR']));
|
if(!isset($requestHeaders[StandardHeaders::CLIENT_NAME->value]))
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
print('Missing required header: ' . StandardHeaders::CLIENT_NAME->value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($requestHeaders[StandardHeaders::CLIENT_VERSION->value]))
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
print('Missing required header: ' . StandardHeaders::CLIENT_VERSION->value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$clientRequest = new ClientRequest($requestHeaders, file_get_contents('php://input') ?? null);
|
||||||
|
|
||||||
|
// Handle the request type, only `init` and `dhe` are not encrypted using the session's encrypted key
|
||||||
|
// RPC Requests must be encrypted and signed by the client, vice versa for server responses.
|
||||||
|
switch(RequestType::tryFrom($clientRequest->getHeader(StandardHeaders::REQUEST_TYPE)))
|
||||||
|
{
|
||||||
|
case RequestType::INITIATE_SESSION:
|
||||||
|
self::handleInitiateSession($clientRequest);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RequestType::DHE_EXCHANGE:
|
||||||
|
self::handleDheExchange($clientRequest);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RequestType::RPC:
|
||||||
|
self::handleRpc($clientRequest);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
http_response_code(400);
|
||||||
|
print('Invalid Request-Type header');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a client request to initiate a session. Validates required headers,
|
||||||
|
* ensures the peer is authorized and enabled, and creates a new session UUID
|
||||||
|
* if all checks pass. Handles edge cases like missing headers, invalid inputs,
|
||||||
|
* or unauthorized peers.
|
||||||
|
*
|
||||||
|
* @param ClientRequest $clientRequest The request from the client containing
|
||||||
|
* the required headers and information.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function handleInitiateSession(ClientRequest $clientRequest): void
|
||||||
|
{
|
||||||
|
if(!$clientRequest->headerExists(StandardHeaders::PUBLIC_KEY))
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
print('Missing required header: ' . StandardHeaders::PUBLIC_KEY->value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$clientRequest->headerExists(StandardHeaders::IDENTIFY_AS))
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
print('Missing required header: ' . StandardHeaders::IDENTIFY_AS->value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Validator::validatePeerAddress($clientRequest->getHeader(StandardHeaders::IDENTIFY_AS)))
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
print('Invalid Identify-As header: ' . $clientRequest->getHeader(StandardHeaders::IDENTIFY_AS));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if the peer address points to the domain of this server, if not we can't accept the request
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$registeredPeer = RegisteredPeerManager::getPeerByUsername($clientRequest->getIdentifyAs()->getUsername());
|
||||||
|
|
||||||
|
// If the peer is registered, check if it is enabled
|
||||||
|
if($registeredPeer !== null && !$registeredPeer->isEnabled())
|
||||||
|
{
|
||||||
|
// Refuse to create a session if the peer is disabled/banned
|
||||||
|
// This also prevents multiple sessions from being created for the same peer
|
||||||
|
// A cron job should be used to clean up disabled peers
|
||||||
|
http_response_code(403);
|
||||||
|
print('Unauthorized: The requested peer is disabled/banned');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check if registration is enabled
|
||||||
|
if(!Configuration::getRegistrationConfiguration()->isRegistrationEnabled())
|
||||||
|
{
|
||||||
|
http_response_code(403);
|
||||||
|
print('Unauthorized: Registration is disabled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the peer if it is not already registered
|
||||||
|
$peerUuid = RegisteredPeerManager::createPeer(PeerAddress::fromAddress($clientRequest->getHeader(StandardHeaders::IDENTIFY_AS))->getUsername());
|
||||||
|
// Retrieve the peer object
|
||||||
|
$registeredPeer = RegisteredPeerManager::getPeer($peerUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the session UUID
|
||||||
|
$sessionUuid = SessionManager::createSession($clientRequest->getHeader(StandardHeaders::PUBLIC_KEY), $registeredPeer);
|
||||||
|
http_response_code(201); // Created
|
||||||
|
print($sessionUuid); // Return the session UUID
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
http_response_code(412); // Precondition failed
|
||||||
|
print($e->getMessage()); // Why the request failed
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('An internal error occurred while initiating the session', $e);
|
||||||
|
http_response_code(500); // Internal server error
|
||||||
|
if(Configuration::getSecurityConfiguration()->isDisplayInternalExceptions())
|
||||||
|
{
|
||||||
|
print(Utilities::throwableToString($e));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print('An internal error occurred');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the Diffie-Hellman key exchange by decrypting the encrypted key passed on from the client using
|
||||||
|
* the server's private key and setting the encryption key to the session.
|
||||||
|
*
|
||||||
|
* 412: Headers malformed
|
||||||
|
* 400: Bad request
|
||||||
|
* 500: Internal server error
|
||||||
|
* 204: Success, no content.
|
||||||
|
*
|
||||||
|
* @param ClientRequest $clientRequest
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function handleDheExchange(ClientRequest $clientRequest): void
|
||||||
|
{
|
||||||
|
// Check if the session UUID is set in the headers
|
||||||
|
if(!$clientRequest->headerExists(StandardHeaders::SESSION_UUID))
|
||||||
|
{
|
||||||
|
Logger::getLogger()->verbose('Missing required header: ' . StandardHeaders::SESSION_UUID->value);
|
||||||
|
|
||||||
|
http_response_code(412);
|
||||||
|
print('Missing required header: ' . StandardHeaders::SESSION_UUID->value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the request body is empty
|
||||||
|
if(empty($clientRequest->getRequestBody()))
|
||||||
|
{
|
||||||
|
Logger::getLogger()->verbose('Bad request: The key exchange request body is empty');
|
||||||
|
|
||||||
|
http_response_code(400);
|
||||||
|
print('Bad request: The key exchange request body is empty');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the session is awaiting a DHE exchange
|
||||||
|
if($clientRequest->getSession()->getState() !== SessionState::AWAITING_DHE)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->verbose('Bad request: The session is not awaiting a DHE exchange');
|
||||||
|
|
||||||
|
http_response_code(400);
|
||||||
|
print('Bad request: The session is not awaiting a DHE exchange');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Attempt to decrypt the encrypted key passed on from the client
|
||||||
|
$encryptionKey = Cryptography::decryptContent($clientRequest->getRequestBody(), Configuration::getInstanceConfiguration()->getPrivateKey());
|
||||||
|
}
|
||||||
|
catch (Exceptions\CryptographyException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error(sprintf('Bad Request: Failed to decrypt the key for session %s', $clientRequest->getSessionUuid()), $e);
|
||||||
|
|
||||||
|
http_response_code(400);
|
||||||
|
print('Bad Request: Cryptography error, make sure you have encrypted the key using the server\'s public key; ' . $e->getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Finally set the encryption key to the session
|
||||||
|
SessionManager::setEncryptionKey($clientRequest->getSessionUuid(), $encryptionKey);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Failed to set the encryption key for the session', $e);
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
if(Configuration::getSecurityConfiguration()->isDisplayInternalExceptions())
|
||||||
|
{
|
||||||
|
print(Utilities::throwableToString($e));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print('Internal Server Error: Failed to set the encryption key for the session');
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::getLogger()->info(sprintf('DHE exchange completed for session %s', $clientRequest->getSessionUuid()));
|
||||||
|
http_response_code(204); // Success, no content
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles incoming RPC requests from a client, processes each request,
|
||||||
|
* and returns the appropriate response(s) or error(s).
|
||||||
|
*
|
||||||
|
* @param ClientRequest $clientRequest The client's request containing one or multiple RPC calls.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function handleRpc(ClientRequest $clientRequest): void
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$clientRequests = $clientRequest->getRpcRequests();
|
||||||
|
}
|
||||||
|
catch (RequestException $e)
|
||||||
|
{
|
||||||
|
http_response_code($e->getCode());
|
||||||
|
print($e->getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::getLogger()->verbose(sprintf('Received %d RPC request(s) from %s', count($clientRequests), $_SERVER['REMOTE_ADDR']));
|
||||||
|
|
||||||
$results = [];
|
$results = [];
|
||||||
foreach($clientRequest->getRequests() as $rpcRequest)
|
foreach($clientRequests as $rpcRequest)
|
||||||
{
|
{
|
||||||
$method = StandardMethods::tryFrom($rpcRequest->getMethod());
|
$method = StandardMethods::tryFrom($rpcRequest->getMethod());
|
||||||
|
|
||||||
|
@ -61,7 +304,7 @@
|
||||||
catch(Exception $e)
|
catch(Exception $e)
|
||||||
{
|
{
|
||||||
Logger::getLogger()->error('An internal error occurred while processing the RPC request', $e);
|
Logger::getLogger()->error('An internal error occurred while processing the RPC request', $e);
|
||||||
if(Configuration::getConfiguration()['security']['display_internal_exceptions'])
|
if(Configuration::getSecurityConfiguration()->isDisplayInternalExceptions())
|
||||||
{
|
{
|
||||||
$response = $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR, Utilities::throwableToString($e));
|
$response = $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR, Utilities::throwableToString($e));
|
||||||
}
|
}
|
||||||
|
@ -79,21 +322,43 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$response = null;
|
||||||
|
|
||||||
if(count($results) == 0)
|
if(count($results) == 0)
|
||||||
{
|
{
|
||||||
Logger::getLogger()->verbose('No results to return');
|
$response = null;
|
||||||
|
}
|
||||||
|
elseif(count($results) == 1)
|
||||||
|
{
|
||||||
|
$response = json_encode($results[0], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$response = json_encode($results, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($response === null)
|
||||||
|
{
|
||||||
http_response_code(204);
|
http_response_code(204);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count($results) == 1)
|
try
|
||||||
{
|
{
|
||||||
Logger::getLogger()->verbose('Returning single result');
|
$encryptedResponse = Cryptography::encryptTransport($response, $clientRequest->getSession()->getEncryptionKey());
|
||||||
print(json_encode($results[0]));
|
$signature = Cryptography::signContent($response, Configuration::getInstanceConfiguration()->getPrivateKey(), true);
|
||||||
|
}
|
||||||
|
catch (Exceptions\CryptographyException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Failed to encrypt the response', $e);
|
||||||
|
http_response_code(500);
|
||||||
|
print('Internal Server Error: Failed to encrypt the response');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::getLogger()->verbose('Returning multiple results');
|
http_response_code(200);
|
||||||
print(json_encode($results));
|
header('Content-Type: application/octet-stream');
|
||||||
|
header(StandardHeaders::SIGNATURE->value . ': ' . $signature);
|
||||||
|
print($encryptedResponse);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue