Made message signing in Cryptography use SHA512 as the message content for... #1

Closed
netkas wants to merge 421 commits from master into dev
10 changed files with 414 additions and 206 deletions
Showing only changes of commit 756297671f - Show all commits

View file

@ -72,6 +72,10 @@ class Configuration
// Registration configuration // Registration configuration
$config->setDefault('registration.enabled', true); $config->setDefault('registration.enabled', true);
$config->setDefault('registration.privacy_policy_document', null);
$config->setDefault('registration.accept_privacy_policy', true);
$config->setDefault('registration.terms_of_service_document', null);
$config->setDefault('registration.accept_terms_of_service', null);
$config->setDefault('registration.password_required', true); $config->setDefault('registration.password_required', true);
$config->setDefault('registration.otp_required', false); $config->setDefault('registration.otp_required', false);
$config->setDefault('registration.display_name_required', false); $config->setDefault('registration.display_name_required', false);

View file

@ -9,6 +9,10 @@ namespace Socialbox\Classes\Configuration;
class RegistrationConfiguration class RegistrationConfiguration
{ {
private bool $registrationEnabled; private bool $registrationEnabled;
private ?string $privacyPolicyDocument;
private bool $acceptPrivacyPolicy;
private ?string $termsOfServiceDocument;
private bool $acceptTermsOfService;
private bool $passwordRequired; private bool $passwordRequired;
private bool $otpRequired; private bool $otpRequired;
private bool $displayNameRequired; private bool $displayNameRequired;
@ -31,6 +35,10 @@ class RegistrationConfiguration
public function __construct(array $data) public function __construct(array $data)
{ {
$this->registrationEnabled = (bool)$data['enabled']; $this->registrationEnabled = (bool)$data['enabled'];
$this->privacyPolicyDocument = $data['privacy_policy_document'] ?? null;
$this->acceptPrivacyPolicy = $data['accept_privacy_policy'] ?? true;
$this->termsOfServiceDocument = $data['terms_of_service_document'] ?? null;
$this->acceptTermsOfService = $data['accept_terms_of_service'] ?? true;
$this->passwordRequired = (bool)$data['password_required']; $this->passwordRequired = (bool)$data['password_required'];
$this->otpRequired = (bool)$data['otp_required']; $this->otpRequired = (bool)$data['otp_required'];
$this->displayNameRequired = (bool)$data['display_name_required']; $this->displayNameRequired = (bool)$data['display_name_required'];
@ -50,6 +58,47 @@ class RegistrationConfiguration
return $this->registrationEnabled; return $this->registrationEnabled;
} }
/**
* Retrieves the privacy policy document.
*
* @return ?string Returns the privacy policy document or null if not set.
*/
public function getPrivacyPolicyDocument(): ?string
{
return $this->privacyPolicyDocument;
}
/**
* Checks if accepting the privacy policy is required.
*
* @return bool Returns true if the privacy policy must be accepted, false otherwise.
*/
public function isAcceptPrivacyPolicyRequired(): bool
{
return $this->acceptPrivacyPolicy;
}
/**
* Retrieves the terms of service document.
*
* @return ?string Returns the terms of service document or null if not set.
*/
public function getTermsOfServiceDocument(): ?string
{
return $this->termsOfServiceDocument;
}
/**
* Checks if accepting the terms of service is required.
*
* @return bool Returns true if the terms of service must be accepted, false otherwise.
*/
public function isAcceptTermsOfServiceRequired(): bool
{
return $this->acceptTermsOfService;
}
/** /**
* Determines if a password is required. * Determines if a password is required.
* *

View file

@ -3,6 +3,7 @@
namespace Socialbox\Classes\StandardMethods; namespace Socialbox\Classes\StandardMethods;
use Socialbox\Abstracts\Method; use Socialbox\Abstracts\Method;
use Socialbox\Enums\StandardError;
use Socialbox\Interfaces\SerializableInterface; use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Objects\ClientRequest; use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest; use Socialbox\Objects\RpcRequest;
@ -15,6 +16,11 @@
*/ */
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{ {
// TODO: Implement execute() method. if($request->getSessionUuid() === null)
{
return $rpcRequest->produceError(StandardError::SESSION_REQUIRED);
}
return $rpcRequest->produceResponse($request->getSession()->toStandardSessionState());
} }
} }

View file

@ -4,12 +4,18 @@
enum SessionFlags : string enum SessionFlags : string
{ {
// Session states
case REGISTRATION_REQUIRED = 'REGISTRATION_REQUIRED'; // Peer has to register
case AUTHENTICATION_REQUIRED = 'AUTHENTICATION_REQUIRED'; // Peer has to authenticate
// Verification, require fields // Verification, require fields
case SET_PASSWORD = 'SET_PASSWORD'; // Peer has to set a password case SET_PASSWORD = 'SET_PASSWORD'; // Peer has to set a password
case SET_OTP = 'SET_OTP'; // Peer has to set an OTP case SET_OTP = 'SET_OTP'; // Peer has to set an OTP
case SET_DISPLAY_NAME = '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_PRIVACY_POLICY = 'VER_PRIVACY_POLICY'; // Peer has to accept the privacy policy
case VER_TERMS_OF_SERVICE = 'VER_TERMS_OF_SERVICE'; // Peer has to accept the terms of service
case VER_EMAIL = 'VER_EMAIL'; // Peer has to verify their email case VER_EMAIL = 'VER_EMAIL'; // Peer has to verify their email
case VER_SMS = 'VER_SMS'; // Peer has to verify their phone number case VER_SMS = 'VER_SMS'; // Peer has to verify their phone number
case VER_PHONE_CALL = 'VER_PHONE_CALL'; // Peer has to verify their phone number via a phone call case VER_PHONE_CALL = 'VER_PHONE_CALL'; // Peer has to verify their phone number via a phone call

View file

@ -1,35 +1,31 @@
<?php <?php
namespace Socialbox\Enums; namespace Socialbox\Enums;
use Socialbox\Classes\StandardMethods\CreateSession; use Socialbox\Classes\StandardMethods\GetSessionState;
use Socialbox\Classes\StandardMethods\Identify; use Socialbox\Classes\StandardMethods\Ping;
use Socialbox\Classes\StandardMethods\VerificationAnswerImageCaptcha; use Socialbox\Exceptions\StandardException;
use Socialbox\Classes\StandardMethods\VerificationGetImageCaptcha; use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Classes\StandardMethods\GetMe; use Socialbox\Objects\ClientRequest;
use Socialbox\Classes\StandardMethods\Ping; use Socialbox\Objects\RpcRequest;
use Socialbox\Classes\StandardMethods\Register;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\ClientRequestOld;
use Socialbox\Objects\RpcRequest;
enum StandardMethods : string enum StandardMethods : string
{
case PING = 'ping';
/**
* @param ClientRequest $request
* @param RpcRequest $rpcRequest
* @return SerializableInterface|null
* @throws StandardException
*/
public function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{ {
return match ($this) case PING = 'ping';
case GET_SESSION_STATE = 'getSessionState';
/**
* @param ClientRequest $request
* @param RpcRequest $rpcRequest
* @return SerializableInterface|null
* @throws StandardException
*/
public function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{ {
self::PING => Ping::execute($request, $rpcRequest), return match ($this)
}; {
} self::PING => Ping::execute($request, $rpcRequest),
} self::GET_SESSION_STATE => GetSessionState::execute($request, $rpcRequest),
};
}
}

View file

@ -27,13 +27,12 @@
* Creates a new session with the given public key. * Creates a new session with the given public key.
* *
* @param string $publicKey The public key to associate with the new session. * @param string $publicKey The public key to associate with the new session.
* * @param RegisteredPeerRecord $peer
* @return string The UUID of the newly created session.
* *
* @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, RegisteredPeerRecord $peer): string public static function createSession(string $publicKey, RegisteredPeerRecord $peer, string $clientName, string $clientVersion): string
{ {
if($publicKey === '') if($publicKey === '')
{ {
@ -50,6 +49,8 @@
if($peer->isEnabled()) if($peer->isEnabled())
{ {
$flags[] = SessionFlags::AUTHENTICATION_REQUIRED;
if(RegisteredPeerManager::getPasswordAuthentication($peer)) if(RegisteredPeerManager::getPasswordAuthentication($peer))
{ {
$flags[] = SessionFlags::VER_PASSWORD; $flags[] = SessionFlags::VER_PASSWORD;
@ -62,6 +63,8 @@
} }
else else
{ {
$flags[] = SessionFlags::REGISTRATION_REQUIRED;
if(Configuration::getRegistrationConfiguration()->isDisplayNameRequired()) if(Configuration::getRegistrationConfiguration()->isDisplayNameRequired())
{ {
$flags[] = SessionFlags::SET_DISPLAY_NAME; $flags[] = SessionFlags::SET_DISPLAY_NAME;
@ -96,6 +99,16 @@
{ {
$flags[] = SessionFlags::SET_OTP; $flags[] = SessionFlags::SET_OTP;
} }
if(Configuration::getRegistrationConfiguration()->isAcceptPrivacyPolicyRequired())
{
$flags[] = SessionFlags::VER_PRIVACY_POLICY;
}
if(Configuration::getRegistrationConfiguration()->isAcceptTermsOfServiceRequired())
{
$flags[] = SessionFlags::VER_TERMS_OF_SERVICE;
}
} }
if(count($flags) > 0) if(count($flags) > 0)
@ -111,11 +124,13 @@
try try
{ {
$statement = Database::getConnection()->prepare("INSERT INTO sessions (uuid, peer_uuid, public_key, flags) VALUES (?, ?, ?, ?)"); $statement = Database::getConnection()->prepare("INSERT INTO sessions (uuid, peer_uuid, client_name, client_version, public_key, flags) VALUES (?, ?, ?, ?, ?, ?)");
$statement->bindParam(1, $uuid); $statement->bindParam(1, $uuid);
$statement->bindParam(2, $peerUuid); $statement->bindParam(2, $peerUuid);
$statement->bindParam(3, $publicKey); $statement->bindParam(3, $clientName);
$statement->bindParam(4, $implodedFlags); $statement->bindParam(4, $clientVersion);
$statement->bindParam(5, $publicKey);
$statement->bindParam(6, $implodedFlags);
$statement->execute(); $statement->execute();
} }
catch(PDOException $e) catch(PDOException $e)

View file

@ -6,11 +6,14 @@
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;
use Socialbox\Managers\RegisteredPeerManager;
class SessionRecord implements SerializableInterface class SessionRecord implements SerializableInterface
{ {
private string $uuid; private string $uuid;
private ?string $peerUuid; private ?string $peerUuid;
private string $clientName;
private string $clientVersion;
private bool $authenticated; private bool $authenticated;
private string $publicKey; private string $publicKey;
private SessionState $state; private SessionState $state;
@ -36,6 +39,8 @@
{ {
$this->uuid = $data['uuid']; $this->uuid = $data['uuid'];
$this->peerUuid = $data['peer_uuid'] ?? null; $this->peerUuid = $data['peer_uuid'] ?? null;
$this->clientName = $data['client_name'];
$this->clientVersion = $data['client_version'];
$this->authenticated = $data['authenticated'] ?? false; $this->authenticated = $data['authenticated'] ?? false;
$this->publicKey = $data['public_key']; $this->publicKey = $data['public_key'];
$this->created = $data['created']; $this->created = $data['created'];
@ -149,6 +154,38 @@
return $this->lastRequest; return $this->lastRequest;
} }
/**
* Retrieves the client name.
*
* @return string Returns the client name.
*/
public function getClientName(): string
{
return $this->clientName;
}
/**
* Retrieves the client version.
*
* @return string Returns the client version.
*/
public function getClientVersion(): string
{
return $this->clientVersion;
}
public function toStandardSessionState(): \Socialbox\Objects\Standard\SessionState
{
return new \Socialbox\Objects\Standard\SessionState([
'uuid' => $this->uuid,
'identified_as' => RegisteredPeerManager::getPeer($this->peerUuid)->getAddress(),
'authenticated' => $this->authenticated,
'flags' => $this->flags,
'created' => $this->created
]);
}
/** /**
* Creates a new instance of the class using the provided array data. * Creates a new instance of the class using the provided array data.
* *

View file

@ -1,193 +1,193 @@
<?php <?php
namespace Socialbox\Objects; namespace Socialbox\Objects;
use InvalidArgumentException; use InvalidArgumentException;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp\BooleanOr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp\BooleanOr;
use Socialbox\Classes\Logger; use Socialbox\Classes\Logger;
use Socialbox\Enums\StandardError; use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\RpcException; use Socialbox\Exceptions\RpcException;
use Socialbox\Exceptions\StandardException; use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface; use Socialbox\Interfaces\SerializableInterface;
class RpcRequest implements SerializableInterface class RpcRequest implements SerializableInterface
{
private ?string $id;
private string $method;
private ?array $parameters;
/**
* Constructs the object from an array of data.
*
* @param string $method The method of the request.
* @param string|null $id The ID of the request.
* @param array|null $parameters The parameters of the request.
*/
public function __construct(string $method, ?string $id, ?array $parameters)
{ {
$this->method = $method; private ?string $id;
$this->parameters = $parameters; private string $method;
$this->id = $id; private ?array $parameters;
}
/** /**
* Returns the ID of the request. * Constructs the object from an array of data.
* *
* @return string|null The ID of the request. * @param string $method The method of the request.
*/ * @param string|null $id The ID of the request.
public function getId(): ?string * @param array|null $parameters The parameters of the request.
{ */
return $this->id; public function __construct(string $method, ?string $id, ?array $parameters)
}
/**
* Returns the method of the request.
*
* @return string The method of the request.
*/
public function getMethod(): string
{
return $this->method;
}
/**
* Returns the parameters of the request.
*
* @return array|null The parameters of the request, null if the request is a notification.
*/
public function getParameters(): ?array
{
return $this->parameters;
}
/**
* Checks if the parameter exists within the RPC request
*
* @param string $parameter The parameter to check
* @return bool True if the parameter exists, False otherwise.
*/
public function containsParameter(string $parameter): bool
{
return isset($this->parameters[$parameter]);
}
/**
* Returns the parameter value from the RPC request
*
* @param string $parameter The parameter name to get
* @return mixed The parameter value, null if the parameter value is null or not found.
*/
public function getParameter(string $parameter): mixed
{
if(!$this->containsParameter($parameter))
{ {
return null; $this->method = $method;
$this->parameters = $parameters;
$this->id = $id;
} }
return $this->parameters[$parameter]; /**
} * Returns the ID of the request.
*
/** * @return string|null The ID of the request.
* Produces a response based off the request, null if the request is a notification */
* public function getId(): ?string
* @param mixed|null $result
* @return RpcResponse|null
*/
public function produceResponse(mixed $result=null): ?RpcResponse
{
if($this->id == null)
{ {
return null; return $this->id;
} }
$valid = false; /**
if(is_array($result)) * Returns the method of the request.
*
* @return string The method of the request.
*/
public function getMethod(): string
{ {
$valid = true; return $this->method;
}
elseif($result instanceof SerializableInterface)
{
$valid = true;
}
elseif(is_string($result))
{
$valid = true;
}
elseif(is_bool($result))
{
$valid = true;
}
elseif(is_int($result))
{
$valid = true;
}
elseif(is_null($result))
{
$valid = true;
} }
if(!$valid) /**
* Returns the parameters of the request.
*
* @return array|null The parameters of the request, null if the request is a notification.
*/
public function getParameters(): ?array
{ {
throw new InvalidArgumentException('The \'$result\' property must either be string, boolean, integer, array, null or SerializableInterface'); return $this->parameters;
} }
Logger::getLogger()->verbose(sprintf('Producing response for request %s', $this->id)); /**
return new RpcResponse($this->id, $result); * Checks if the parameter exists within the RPC request
} *
* @param string $parameter The parameter to check
/** * @return bool True if the parameter exists, False otherwise.
* Produces an error response based off the request, null if the request is a notification */
* public function containsParameter(string $parameter): bool
* @param StandardError $error
* @param string|null $message
* @return RpcError|null
*/
public function produceError(StandardError $error, ?string $message=null): ?RpcError
{
if($this->id == null)
{ {
return null; return isset($this->parameters[$parameter]);
} }
if($message == null) /**
* Returns the parameter value from the RPC request
*
* @param string $parameter The parameter name to get
* @return mixed The parameter value, null if the parameter value is null or not found.
*/
public function getParameter(string $parameter): mixed
{ {
$message = $error->getMessage(); if(!$this->containsParameter($parameter))
{
return null;
}
return $this->parameters[$parameter];
} }
return new RpcError($this->id, $error, $message); /**
} * Produces a response based off the request, null if the request is a notification
*
* @param mixed|null $result
* @return RpcResponse|null
*/
public function produceResponse(mixed $result=null): ?RpcResponse
{
if($this->id == null)
{
return null;
}
/** $valid = false;
* @param StandardException $e if(is_array($result))
* @return RpcError|null {
*/ $valid = true;
public function handleStandardException(StandardException $e): ?RpcError }
{ elseif($result instanceof SerializableInterface)
return $this->produceError($e->getStandardError(), $e->getMessage()); {
} $valid = true;
}
elseif(is_string($result))
{
$valid = true;
}
elseif(is_bool($result))
{
$valid = true;
}
elseif(is_int($result))
{
$valid = true;
}
elseif(is_null($result))
{
$valid = true;
}
/** if(!$valid)
* Returns an array representation of the object. {
* throw new InvalidArgumentException('The \'$result\' property must either be string, boolean, integer, array, null or SerializableInterface');
* @return array }
*/
public function toArray(): array
{
return [
'id' => $this->id,
'method' => $this->method,
'parameters' => $this->parameters
];
}
/** Logger::getLogger()->verbose(sprintf('Producing response for request %s', $this->id));
* Returns the request object from an array of data. return new RpcResponse($this->id, $result);
* }
* @param array $data The data to construct the object from.
* @return RpcRequest The request object. /**
*/ * Produces an error response based off the request, null if the request is a notification
public static function fromArray(array $data): RpcRequest *
{ * @param StandardError $error
return new RpcRequest($data['method'], $data['id'] ?? null, $data['parameters'] ?? null); * @param string|null $message
} * @return RpcError|null
} */
public function produceError(StandardError $error, ?string $message=null): ?RpcError
{
if($this->id == null)
{
return null;
}
if($message == null)
{
$message = $error->getMessage();
}
return new RpcError($this->id, $error, $message);
}
/**
* @param StandardException $e
* @return RpcError|null
*/
public function handleStandardException(StandardException $e): ?RpcError
{
return $this->produceError($e->getStandardError(), $e->getMessage());
}
/**
* Returns an array representation of the object.
*
* @return array
*/
public function toArray(): array
{
return [
'id' => $this->id,
'method' => $this->method,
'parameters' => $this->parameters
];
}
/**
* Returns the request object from an array of data.
*
* @param array $data The data to construct the object from.
* @return RpcRequest The request object.
*/
public static function fromArray(array $data): RpcRequest
{
return new RpcRequest($data['method'], $data['id'] ?? null, $data['parameters'] ?? null);
}
}

View file

@ -2,7 +2,103 @@
namespace Socialbox\Objects\Standard; namespace Socialbox\Objects\Standard;
class SessionState use DateTime;
{ use Socialbox\Enums\Flags\SessionFlags;
use Socialbox\Interfaces\SerializableInterface;
class SessionState implements SerializableInterface
{
private string $uuid;
private string $identifiedAs;
private bool $authenticated;
/**
* @var SessionFlags[]|null
*/
private ?array $flags;
private DateTime $created;
/**
* Constructor for initializing the object with the provided data.
*
* @param array $data An associative array containing the values for initializing the object.
* - 'uuid': string, Unique identifier.
* - 'identified_as': mixed, The identity information.
* - 'authenticated': bool, Whether the object is authenticated.
* - 'flags': string|null, Optional flags in
* @throws \DateMalformedStringException
*/
public function __construct(array $data)
{
$this->uuid = $data['uuid'];
$this->identifiedAs = $data['identified_as'];
$this->authenticated = $data['authenticated'];
if(is_string($data['flags']))
{
$this->flags = SessionFlags::fromString($data['flags']);
}
elseif(is_array($data['flags']))
{
$this->flags = $data['flags'];
}
else
{
$this->flags = null;
}
if(is_int($data['created']))
{
$this->created = new DateTime();
$this->created->setTimestamp($data['created']);
}
elseif($data['created'] instanceof DateTime)
{
$this->created = $data['created'];
}
else
{
$this->created = new DateTime($data['created']);
}
}
public function getUuid(): string
{
return $this->uuid;
}
public function getIdentifiedAs(): string
{
return $this->identifiedAs;
}
public function isAuthenticated(): bool
{
return $this->authenticated;
}
public function getFlags(): ?array
{
return $this->flags;
}
public function getCreated(): DateTime
{
return $this->created;
}
public static function fromArray(array $data): SessionState
{
return new self($data);
}
public function toArray(): array
{
return [
'uuid' => $this->uuid,
'identified_as' => $this->identifiedAs,
'authenticated' => $this->authenticated,
'flags' => $this->flags,
'created' => $this->created->getTimestamp()
];
}
} }

View file

@ -149,8 +149,7 @@
} }
// Create the session UUID // Create the session UUID
// TODO: Save client name and version to the database $sessionUuid = SessionManager::createSession($clientRequest->getHeader(StandardHeaders::PUBLIC_KEY), $registeredPeer, $clientRequest->getClientName(), $clientRequest->getClientVersion());
$sessionUuid = SessionManager::createSession($clientRequest->getHeader(StandardHeaders::PUBLIC_KEY), $registeredPeer);
http_response_code(201); // Created http_response_code(201); // Created
print($sessionUuid); // Return the session UUID print($sessionUuid); // Return the session UUID
} }