Made message signing in Cryptography use SHA512 as the message content for... #1
10 changed files with 189 additions and 6 deletions
2
.idea/socialbox-php.iml
generated
2
.idea/socialbox-php.iml
generated
|
@ -2,6 +2,8 @@
|
|||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea/dataSources" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Socialbox\Classes\Configuration;
|
|||
|
||||
class SecurityConfiguration
|
||||
{
|
||||
private bool $displayInternalErrors;
|
||||
private bool $displayInternalExceptions;
|
||||
private int $resolvedServersTtl;
|
||||
private int $captchaTtl;
|
||||
|
||||
|
@ -17,7 +17,7 @@ class SecurityConfiguration
|
|||
*/
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->displayInternalErrors = $data['display_internal_errors'];
|
||||
$this->displayInternalExceptions = $data['display_internal_exceptions'];
|
||||
$this->resolvedServersTtl = $data['resolved_servers_ttl'];
|
||||
$this->captchaTtl = $data['captcha_ttl'];
|
||||
}
|
||||
|
@ -27,9 +27,9 @@ class SecurityConfiguration
|
|||
*
|
||||
* @return bool True if the display of internal errors is enabled, false otherwise.
|
||||
*/
|
||||
public function isDisplayInternalErrors(): bool
|
||||
public function isDisplayInternalExceptions(): bool
|
||||
{
|
||||
return $this->displayInternalErrors;
|
||||
return $this->displayInternalExceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
83
src/Socialbox/Classes/StandardMethods/GetCaptcha.php
Normal file
83
src/Socialbox/Classes/StandardMethods/GetCaptcha.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Socialbox\Classes\StandardMethods;
|
||||
|
||||
use Gregwar\Captcha\CaptchaBuilder;
|
||||
use Socialbox\Abstracts\Method;
|
||||
use Socialbox\Classes\Logger;
|
||||
use Socialbox\Enums\Flags\PeerFlags;
|
||||
use Socialbox\Enums\StandardError;
|
||||
use Socialbox\Exceptions\DatabaseOperationException;
|
||||
use Socialbox\Exceptions\StandardException;
|
||||
use Socialbox\Interfaces\SerializableInterface;
|
||||
use Socialbox\Managers\CaptchaManager;
|
||||
use Socialbox\Managers\RegisteredPeerManager;
|
||||
use Socialbox\Managers\SessionManager;
|
||||
use Socialbox\Objects\ClientRequest;
|
||||
use Socialbox\Objects\RpcRequest;
|
||||
use Socialbox\Objects\Standard\ImageCaptcha;
|
||||
|
||||
class GetCaptcha 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);
|
||||
}
|
||||
|
||||
// Get the session and check if it's already authenticated
|
||||
try
|
||||
{
|
||||
$session = SessionManager::getSession($request->getSessionUuid());
|
||||
}
|
||||
catch(DatabaseOperationException $e)
|
||||
{
|
||||
throw new StandardException("There was an unexpected error while trying to get the session", StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||
}
|
||||
|
||||
// Check for session conditions
|
||||
if($session->getPeerUuid() === null)
|
||||
{
|
||||
return $rpcRequest->produceError(StandardError::AUTHENTICATION_REQUIRED);
|
||||
}
|
||||
|
||||
// Get the peer
|
||||
try
|
||||
{
|
||||
$peer = RegisteredPeerManager::getPeer($session->getPeerUuid());
|
||||
}
|
||||
catch(DatabaseOperationException $e)
|
||||
{
|
||||
throw new StandardException("There was unexpected error while trying to get the peer", StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||
}
|
||||
|
||||
// Check if the VER_SOLVE_IMAGE_CAPTCHA flag exists.
|
||||
if(!$peer->flagExists(PeerFlags::VER_SOLVE_IMAGE_CAPTCHA))
|
||||
{
|
||||
return $rpcRequest->produceError(StandardError::CAPTCHA_NOT_AVAILABLE, 'You are not required to complete a captcha at this time');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Logger::getLogger()->debug('Creating a new captcha for peer ' . $peer->getUuid());
|
||||
$answer = CaptchaManager::createCaptcha($peer);
|
||||
$captchaRecord = CaptchaManager::getCaptcha($peer);
|
||||
}
|
||||
catch (DatabaseOperationException $e)
|
||||
{
|
||||
throw new StandardException("There was an unexpected error while trying create the captcha", StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||
}
|
||||
|
||||
// Build the captcha
|
||||
Logger::getLogger()->debug('Building captcha for peer ' . $peer->getUuid());
|
||||
return $rpcRequest->produceResponse(new ImageCaptcha([
|
||||
'expires' => $captchaRecord->getExpires()->getTimestamp(),
|
||||
'image' => (new CaptchaBuilder($answer))->build()->inline()] // Returns HTML base64 encoded image of the captcha
|
||||
));
|
||||
}
|
||||
}
|
|
@ -23,6 +23,9 @@ enum StandardError : int
|
|||
case SESSION_NOT_FOUND = -3004;
|
||||
case SESSION_REQUIRED = -3005;
|
||||
case REGISTRATION_DISABLED = -3006;
|
||||
case CAPTCHA_NOT_AVAILABLE = -3007;
|
||||
case INCORRECT_CAPTCHA_ANSWER = -3008;
|
||||
case CAPTCHA_EXPIRED = -3009;
|
||||
|
||||
// General Error Messages
|
||||
case PEER_NOT_FOUND = -4000;
|
||||
|
@ -52,6 +55,10 @@ enum StandardError : int
|
|||
self::AUTHENTICATION_REQUIRED => 'Authentication is required to preform this action',
|
||||
self::SESSION_NOT_FOUND => 'The requested session UUID was not found',
|
||||
self::SESSION_REQUIRED => 'A session is required to preform this action',
|
||||
self::REGISTRATION_DISABLED => 'Registration is disabled on the server',
|
||||
self::CAPTCHA_NOT_AVAILABLE => 'Captcha is not available',
|
||||
self::INCORRECT_CAPTCHA_ANSWER => 'The Captcha answer is incorrect',
|
||||
self::CAPTCHA_EXPIRED => 'The captcha has expired and a new captcha needs to be requested',
|
||||
|
||||
self::PEER_NOT_FOUND => 'The requested peer was not found',
|
||||
self::INVALID_USERNAME => 'The given username is invalid, it must be Alphanumeric with a minimum of 3 character but no greater than 255 characters',
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Socialbox\Enums;
|
||||
|
||||
use Socialbox\Classes\StandardMethods\CreateSession;
|
||||
use Socialbox\Classes\StandardMethods\GetCaptcha;
|
||||
use Socialbox\Classes\StandardMethods\GetMe;
|
||||
use Socialbox\Classes\StandardMethods\Ping;
|
||||
use Socialbox\Classes\StandardMethods\Register;
|
||||
|
@ -17,6 +18,7 @@ enum StandardMethods : string
|
|||
case CREATE_SESSION = 'createSession';
|
||||
case REGISTER = 'register';
|
||||
case GET_ME = 'getMe';
|
||||
case GET_CAPTCHA = 'getCaptcha';
|
||||
|
||||
/**
|
||||
* @param ClientRequest $request
|
||||
|
@ -32,6 +34,7 @@ enum StandardMethods : string
|
|||
self::CREATE_SESSION => CreateSession::execute($request, $rpcRequest),
|
||||
self::REGISTER => Register::execute($request, $rpcRequest),
|
||||
self::GET_ME => GetMe::execute($request, $rpcRequest),
|
||||
self::GET_CAPTCHA => GetCaptcha::execute($request, $rpcRequest),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use DateTime;
|
|||
use DateTimeInterface;
|
||||
use PDOException;
|
||||
use Socialbox\Classes\Database;
|
||||
use Socialbox\Classes\Logger;
|
||||
use Socialbox\Classes\Utilities;
|
||||
use Socialbox\Enums\Status\CaptchaStatus;
|
||||
use Socialbox\Exceptions\DatabaseOperationException;
|
||||
|
@ -21,7 +22,7 @@ class CaptchaManager
|
|||
* @return string The answer to the captcha.
|
||||
* @throws DatabaseOperationException If the operation fails.
|
||||
*/
|
||||
private static function createCaptcha(string|RegisteredPeerRecord $peer_uuid): string
|
||||
public static function createCaptcha(string|RegisteredPeerRecord $peer_uuid): string
|
||||
{
|
||||
// If the peer_uuid is a RegisteredPeerRecord, get the UUID
|
||||
if($peer_uuid instanceof RegisteredPeerRecord)
|
||||
|
@ -33,6 +34,7 @@ class CaptchaManager
|
|||
|
||||
if(!self::captchaExists($peer_uuid))
|
||||
{
|
||||
Logger::getLogger()->debug('Creating a new captcha record for peer ' . $peer_uuid);
|
||||
$statement = Database::getConnection()->prepare("INSERT INTO captcha_images (peer_uuid, answer) VALUES (?, ?)");
|
||||
$statement->bindParam(1, $peer_uuid);
|
||||
$statement->bindParam(2, $answer);
|
||||
|
@ -49,6 +51,7 @@ class CaptchaManager
|
|||
return $answer;
|
||||
}
|
||||
|
||||
Logger::getLogger()->debug('Updating an existing captcha record for peer ' . $peer_uuid);
|
||||
$statement = Database::getConnection()->prepare("UPDATE captcha_images SET answer=?, status='UNSOLVED', created=NOW() WHERE peer_uuid=?");
|
||||
$statement->bindParam(1, $answer);
|
||||
$statement->bindParam(2, $peer_uuid);
|
||||
|
@ -83,6 +86,7 @@ class CaptchaManager
|
|||
// Return false if the captcha does not exist
|
||||
if(!self::captchaExists($peer_uuid))
|
||||
{
|
||||
Logger::getLogger()->warning(sprintf("The requested captcha does not exist for the peer %s", $peer_uuid));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -91,21 +95,25 @@ class CaptchaManager
|
|||
// Return false if the captcha has already been solved
|
||||
if($captcha->getStatus() === CaptchaStatus::SOLVED)
|
||||
{
|
||||
Logger::getLogger()->warning(sprintf("The requested captcha has already been solved for the peer %s", $peer_uuid));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return false if the captcha is older than 5 minutes
|
||||
if ($captcha->getCreated() instanceof DateTimeInterface && $captcha->getCreated()->diff(new DateTime())->i > 5)
|
||||
if ($captcha->isExpired())
|
||||
{
|
||||
Logger::getLogger()->warning(sprintf("The requested captcha is older than 5 minutes for the peer %s", $peer_uuid));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the answer
|
||||
if($captcha->getAnswer() !== $answer)
|
||||
{
|
||||
Logger::getLogger()->warning(sprintf("The answer to the requested captcha is incorrect for the peer %s", $peer_uuid));
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger::getLogger()->debug(sprintf("The answer to the requested captcha is correct for the peer %s", $peer_uuid));
|
||||
$statement = Database::getConnection()->prepare("UPDATE captcha_images SET status='SOLVED', answered=NOW() WHERE peer_uuid=?");
|
||||
$statement->bindParam(1, $peer_uuid);
|
||||
|
||||
|
@ -136,6 +144,8 @@ class CaptchaManager
|
|||
$peer_uuid = $peer_uuid->getUuid();
|
||||
}
|
||||
|
||||
Logger::getLogger()->debug('Getting the captcha record for peer ' . $peer_uuid);
|
||||
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("SELECT * FROM captcha_images WHERE peer_uuid=? LIMIT 1");
|
||||
|
@ -171,6 +181,8 @@ class CaptchaManager
|
|||
$peer_uuid = $peer_uuid->getUuid();
|
||||
}
|
||||
|
||||
Logger::getLogger()->debug('Checking if a captcha exists for peer ' . $peer_uuid);
|
||||
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("SELECT COUNT(*) FROM captcha_images WHERE peer_uuid=?");
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Socialbox\Objects\Database;
|
||||
|
||||
use DateTime;
|
||||
use Socialbox\Classes\Configuration;
|
||||
use Socialbox\Enums\Status\CaptchaStatus;
|
||||
use Socialbox\Interfaces\SerializableInterface;
|
||||
|
||||
|
@ -55,6 +56,16 @@ class CaptchaRecord implements SerializableInterface
|
|||
return $this->created;
|
||||
}
|
||||
|
||||
public function getExpires(): DateTime
|
||||
{
|
||||
return $this->created->modify(sprintf("+%s seconds", Configuration::getSecurityConfiguration()->getCaptchaTtl()));
|
||||
}
|
||||
|
||||
public function isExpired(): bool
|
||||
{
|
||||
return $this->getExpires() < new DateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
|
53
src/Socialbox/Objects/Standard/ImageCaptcha.php
Normal file
53
src/Socialbox/Objects/Standard/ImageCaptcha.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Socialbox\Objects\Standard;
|
||||
|
||||
use Socialbox\Interfaces\SerializableInterface;
|
||||
use Socialbox\Objects\Database\CaptchaRecord;
|
||||
|
||||
class ImageCaptcha implements SerializableInterface
|
||||
{
|
||||
private int $expires;
|
||||
private string $image;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->expires = $data['expires'];
|
||||
$this->image = $data['image'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getExpires(): int
|
||||
{
|
||||
return $this->expires;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getImage(): string
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function fromArray(array $data): object
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'expires' => $this->expires,
|
||||
'image' => $this->image
|
||||
];
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@
|
|||
{
|
||||
Logger::getLogger()->debug(sprintf('Processing RPC request for method %s', $rpcRequest->getMethod()));
|
||||
$response = $method->execute($clientRequest, $rpcRequest);
|
||||
Logger::getLogger()->debug(sprintf('%s method executed successfully', $rpcRequest->getMethod()));
|
||||
}
|
||||
catch(StandardException $e)
|
||||
{
|
||||
|
|
11
src/Socialbox/Socialclient.php
Normal file
11
src/Socialbox/Socialclient.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace Socialbox;
|
||||
|
||||
class Socialclient
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
echo "Socialclient class has been initialized";
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue