Add Operator management classes and exception handling
Some checks are pending
CI / release (push) Waiting to run
CI / debug (push) Waiting to run
CI / check-phpunit (push) Waiting to run
CI / check-phpdoc (push) Waiting to run
CI / generate-phpdoc (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / release-documentation (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
Some checks are pending
CI / release (push) Waiting to run
CI / debug (push) Waiting to run
CI / check-phpunit (push) Waiting to run
CI / check-phpdoc (push) Waiting to run
CI / generate-phpdoc (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / release-documentation (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
This commit is contained in:
parent
7c7947be9c
commit
0071c4ebea
6 changed files with 572 additions and 0 deletions
7
.idea/sqldialects.xml
generated
Normal file
7
.idea/sqldialects.xml
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/src/FederationServer/Classes/Managers/OperatorManager.php" dialect="MariaDB" />
|
||||
<file url="file://$PROJECT_DIR$/src/FederationServer/Classes/Resources/operators.sql" dialect="MariaDB" />
|
||||
</component>
|
||||
</project>
|
319
src/FederationServer/Classes/Managers/OperatorManager.php
Normal file
319
src/FederationServer/Classes/Managers/OperatorManager.php
Normal file
|
@ -0,0 +1,319 @@
|
|||
<?php
|
||||
|
||||
namespace FederationServer\Classes\Managers;
|
||||
|
||||
use FederationServer\Classes\DatabaseConnection;
|
||||
use FederationServer\Classes\Utilities;
|
||||
use FederationServer\Exceptions\DatabaseOperationException;
|
||||
use FederationServer\Objects\OperatorRecord;
|
||||
use InvalidArgumentException;
|
||||
use PDOException;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
class OperatorManager
|
||||
{
|
||||
/**
|
||||
* Create a new operator with a unique API key.
|
||||
*
|
||||
* @param string $name The name of the operator.
|
||||
* @return string The generated API key for the operator.
|
||||
* @throws InvalidArgumentException If the name is empty or exceeds 255 characters.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function createOperator(string $name): string
|
||||
{
|
||||
if(empty($name))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator name cannot be empty.');
|
||||
}
|
||||
|
||||
if(strlen($name) > 255)
|
||||
{
|
||||
throw new InvalidArgumentException('Operator name cannot exceed 255 characters.');
|
||||
}
|
||||
|
||||
$uuid = Uuid::v7()->toRfc4122();
|
||||
$apiKey = Utilities::generateString();
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("INSERT INTO operators (uuid, api_key, name) VALUES (:uuid, :api_key, :name)");
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->bindParam(':api_key', $apiKey);
|
||||
$stmt->bindParam(':name', $name);
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to create operator '%s'", $name), 0, $e);
|
||||
}
|
||||
|
||||
return $uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an operator by their UUID.
|
||||
*
|
||||
* @param string $uuid The UUID of the operator.
|
||||
* @return OperatorRecord|null The operator record if found, null otherwise.
|
||||
* @throws InvalidArgumentException If the UUID is empty.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function getOperator(string $uuid): ?OperatorRecord
|
||||
{
|
||||
if(empty($uuid))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator UUID cannot be empty.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("SELECT * FROM operators WHERE uuid=:uuid");
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->execute();
|
||||
|
||||
$data = $stmt->fetch();
|
||||
|
||||
if($data === false)
|
||||
{
|
||||
return null; // No operator found with the given UUID
|
||||
}
|
||||
|
||||
return new OperatorRecord($data);
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to retrieve operator with UUID '%s'", $uuid), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an operator by their API key.
|
||||
*
|
||||
* @param string $apiKey The API key of the operator.
|
||||
* @return OperatorRecord|null The operator record if found, null otherwise.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function getOperatorByApiKey(string $apiKey): ?OperatorRecord
|
||||
{
|
||||
if(empty($apiKey))
|
||||
{
|
||||
throw new InvalidArgumentException('API key cannot be empty.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("SELECT * FROM operators WHERE api_key=:api_key");
|
||||
$stmt->bindParam(':api_key', $apiKey);
|
||||
$stmt->execute();
|
||||
|
||||
$data = $stmt->fetch();
|
||||
|
||||
if($data === false)
|
||||
{
|
||||
return null; // No operator found with the given API key
|
||||
}
|
||||
|
||||
return new OperatorRecord($data);
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to retrieve operator with API key '%s'", $apiKey), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable an operator by their UUID.
|
||||
*
|
||||
* @param string $uuid The UUID of the operator.
|
||||
* @throws InvalidArgumentException If the UUID is empty.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function disableOperator(string $uuid): void
|
||||
{
|
||||
if(empty($uuid))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator UUID cannot be empty.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("UPDATE operators SET disabled=1 WHERE uuid=:uuid");
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to disable operator with UUID '%s'", $uuid), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable an operator by their UUID.
|
||||
*
|
||||
* @param string $uuid The UUID of the operator.
|
||||
* @throws InvalidArgumentException If the UUID is empty.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function enableOperator(string $uuid): void
|
||||
{
|
||||
if(empty($uuid))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator UUID cannot be empty.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("UPDATE operators SET disabled=0 WHERE uuid=:uuid");
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to enable operator with UUID '%s'", $uuid), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an operator by their UUID.
|
||||
*
|
||||
* @param string $uuid The UUID of the operator.
|
||||
* @throws InvalidArgumentException If the UUID is empty.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function deleteOperator(string $uuid): void
|
||||
{
|
||||
if(empty($uuid))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator UUID cannot be empty.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("DELETE FROM operators WHERE uuid=:uuid");
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to delete operator with UUID '%s'", $uuid), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the API key for an operator.
|
||||
*
|
||||
* @param string $uuid The UUID of the operator.
|
||||
* @return string The new API key for the operator.
|
||||
* @throws InvalidArgumentException If the UUID is empty.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function refreshApiKey(string $uuid): string
|
||||
{
|
||||
if(empty($uuid))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator UUID cannot be empty.');
|
||||
}
|
||||
|
||||
$newApiKey = Utilities::generateString();
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("UPDATE operators SET api_key=:api_key WHERE uuid=:uuid");
|
||||
$stmt->bindParam(':api_key', $newApiKey);
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to refresh API key for operator with UUID '%s'", $uuid), 0, $e);
|
||||
}
|
||||
|
||||
return $newApiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the management permissions for an operator.
|
||||
*
|
||||
* @param string $uuid The UUID of the operator.
|
||||
* @param bool $canManageOperators True if the operator can manage other operators, false otherwise.
|
||||
* @throws InvalidArgumentException If the UUID is empty.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function setManageOperators(string $uuid, bool $canManageOperators): void
|
||||
{
|
||||
if(empty($uuid))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator UUID cannot be empty.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("UPDATE operators SET manage_operators=:manage_operators WHERE uuid=:uuid");
|
||||
$stmt->bindParam(':manage_operators', $canManageOperators, \PDO::PARAM_BOOL);
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to set operator management permissions for operator with UUID '%s'", $uuid), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the blacklist management permissions for an operator.
|
||||
*
|
||||
* @param string $uuid The UUID of the operator.
|
||||
* @param bool $canManageBlacklist True if the operator can manage the blacklist, false otherwise.
|
||||
* @throws InvalidArgumentException If the UUID is empty.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function setManageBlacklist(string $uuid, bool $canManageBlacklist): void
|
||||
{
|
||||
if(empty($uuid))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator UUID cannot be empty.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("UPDATE operators SET manage_blacklist=:manage_blacklist WHERE uuid=:uuid");
|
||||
$stmt->bindParam(':manage_blacklist', $canManageBlacklist, \PDO::PARAM_BOOL);
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to set blacklist management permissions for operator with UUID '%s'", $uuid), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the client status for an operator.
|
||||
*
|
||||
* @param string $uuid The UUID of the operator.
|
||||
* @param bool $isClient True if the operator is a client, false otherwise.
|
||||
* @throws InvalidArgumentException If the UUID is empty.
|
||||
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||
*/
|
||||
public static function setClient(string $uuid, bool $isClient): void
|
||||
{
|
||||
if(empty($uuid))
|
||||
{
|
||||
throw new InvalidArgumentException('Operator UUID cannot be empty.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = DatabaseConnection::getConnection()->prepare("UPDATE operators SET is_client=:is_client WHERE uuid=:uuid");
|
||||
$stmt->bindParam(':is_client', $isClient, \PDO::PARAM_BOOL);
|
||||
$stmt->bindParam(':uuid', $uuid);
|
||||
$stmt->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf("Failed to set client status for operator with UUID '%s'", $uuid), 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
26
src/FederationServer/Classes/Utilities.php
Normal file
26
src/FederationServer/Classes/Utilities.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace FederationServer\Classes;
|
||||
|
||||
class Utilities
|
||||
{
|
||||
/*
|
||||
* Generate a random string of specified length.
|
||||
*
|
||||
* @param int $length Length of the random string to generate.
|
||||
* @return string Randomly generated string.
|
||||
*/
|
||||
public static function generateString(int $length=32): string
|
||||
{
|
||||
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$charactersLength = strlen($characters);
|
||||
$randomString = '';
|
||||
|
||||
for ($i = 0; $i < $length; $i++)
|
||||
{
|
||||
$randomString .= $characters[rand(0, $charactersLength - 1)];
|
||||
|
||||
}
|
||||
return $randomString;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace FederationServer\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class DatabaseOperationException extends \Exception
|
||||
{
|
||||
/**
|
||||
* DatabaseOperationException constructor.
|
||||
*
|
||||
* @param string $message The exception message.
|
||||
* @param int $code The exception code.
|
||||
* @param Throwable|null $previous The previous throwable used for the exception chaining.
|
||||
*/
|
||||
public function __construct(string $message="", int $code=0, ?Throwable $previous=null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
21
src/FederationServer/Interfaces/SerializableInterface.php
Normal file
21
src/FederationServer/Interfaces/SerializableInterface.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace FederationServer\Interfaces;
|
||||
|
||||
interface SerializableInterface
|
||||
{
|
||||
/**
|
||||
* Convert the object to an associative array representation.
|
||||
*
|
||||
* @return array An associative array representing the object's state.
|
||||
*/
|
||||
public function toArray(): array;
|
||||
|
||||
/**
|
||||
* Create an instance of the object from an associative array.
|
||||
*
|
||||
* @param array $array An associative array containing the object's state.
|
||||
* @return SerializableInterface An instance of the object.
|
||||
*/
|
||||
public static function fromArray(array $array): SerializableInterface;
|
||||
}
|
179
src/FederationServer/Objects/OperatorRecord.php
Normal file
179
src/FederationServer/Objects/OperatorRecord.php
Normal file
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
namespace FederationServer\Objects;
|
||||
|
||||
use DateTime;
|
||||
use FederationServer\Interfaces\SerializableInterface;
|
||||
|
||||
class OperatorRecord implements SerializableInterface
|
||||
{
|
||||
private string $uuid;
|
||||
private string $apiKey;
|
||||
private string $name;
|
||||
private bool $disabled;
|
||||
private bool $manageOperators;
|
||||
private bool $manageBlacklist;
|
||||
private bool $isClient;
|
||||
private int $created;
|
||||
private int $updated;
|
||||
|
||||
/**
|
||||
* OperatorRecord constructor.
|
||||
*
|
||||
* @param array $data Associative array of operator data.
|
||||
*/
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->uuid = $data['uuid'] ?? '';
|
||||
$this->apiKey = $data['api_key'] ?? '';
|
||||
$this->name = $data['name'] ?? '';
|
||||
$this->disabled = (bool)$data['disabled'] ?? false;
|
||||
$this->manageOperators = (bool)$data['manage_operators'] ?? false;
|
||||
$this->manageBlacklist = (bool)$data['manage_blacklist'] ?? false;
|
||||
$this->isClient = (bool)$data['is_client'] ?? false;
|
||||
$this->created = (int)$data['created'] ?? time();
|
||||
$this->updated = (int)$data['updated'] ?? time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UUID of the operator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUuid(): string
|
||||
{
|
||||
return $this->uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API key of the operator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getApiKey(): string
|
||||
{
|
||||
return $this->apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the operator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the operator is disabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDisabled(): bool
|
||||
{
|
||||
return $this->disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the operator can manage other operators.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canManageOperators(): bool
|
||||
{
|
||||
return $this->manageOperators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the operator can manage the blacklist.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canManageBlacklist(): bool
|
||||
{
|
||||
return $this->manageBlacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the operator is a client.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClient(): bool
|
||||
{
|
||||
return $this->isClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the creation timestamp.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCreated(): int
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last updated timestamp.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUpdated(): int
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'uuid' => $this->uuid,
|
||||
'api_key' => $this->apiKey,
|
||||
'name' => $this->name,
|
||||
'disabled' => $this->disabled,
|
||||
'manage_operators' => $this->manageOperators,
|
||||
'manage_blacklist' => $this->manageBlacklist,
|
||||
'is_client' => $this->isClient,
|
||||
'created' => $this->created,
|
||||
'updated' => $this->updated
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function fromArray(array $array): SerializableInterface
|
||||
{
|
||||
if(isset($array['created']) )
|
||||
{
|
||||
if(is_string($array['created']))
|
||||
{
|
||||
$array['created'] = strtotime($array['created']);
|
||||
}
|
||||
if($array['created'] instanceof DateTime)
|
||||
{
|
||||
$array['created'] = $array['created']->getTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(isset($array['updated']) )
|
||||
{
|
||||
if(is_string($array['updated']))
|
||||
{
|
||||
$array['updated'] = strtotime($array['updated']);
|
||||
}
|
||||
if($array['updated'] instanceof DateTime)
|
||||
{
|
||||
$array['updated'] = $array['updated']->getTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
$object = new self($array);
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue