Implemented Tamer & Cache Drivers (WIP)
This commit is contained in:
parent
26f0f31cc6
commit
d346c4d23d
39 changed files with 2211 additions and 913 deletions
3
.idea/inspectionProfiles/Project_Default.xml
generated
3
.idea/inspectionProfiles/Project_Default.xml
generated
|
@ -19,6 +19,9 @@
|
|||
<option value="X-Args-3" />
|
||||
<option value="X-Args-4" />
|
||||
<option value="X-Args-5" />
|
||||
<option value="X-Temperature" />
|
||||
<option value="X-Model" />
|
||||
<option value="X-OPENAI-API-KEY" />
|
||||
</set>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
|
|
3
.idea/php.xml
generated
3
.idea/php.xml
generated
|
@ -24,6 +24,7 @@
|
|||
<path value="/var/ncc/packages/com.php_school.cli_menu=4.3.0" />
|
||||
<path value="/var/ncc/packages/com.php_school.terminal=0.2.1" />
|
||||
<path value="/var/ncc/packages/com.malios.php_to_ascii_table=3.0.0" />
|
||||
<path value="/var/ncc/packages/net.nosial.tamerlib=2.0.0" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="8.1" />
|
||||
|
@ -73,12 +74,10 @@
|
|||
<extension name="mbstring" enabled="false" />
|
||||
<extension name="mcrypt" enabled="false" />
|
||||
<extension name="memcache" enabled="false" />
|
||||
<extension name="memcached" enabled="false" />
|
||||
<extension name="ming" enabled="false" />
|
||||
<extension name="mongo" enabled="false" />
|
||||
<extension name="mosquitto-php" enabled="false" />
|
||||
<extension name="mqseries" enabled="false" />
|
||||
<extension name="msgpack" enabled="true" />
|
||||
<extension name="mssql" enabled="false" />
|
||||
<extension name="mysql" enabled="false" />
|
||||
<extension name="mysql_xdevapi" enabled="false" />
|
||||
|
|
125
STANDARD.md
125
STANDARD.md
|
@ -18,6 +18,12 @@ spam and malicious content on various communication channels such as email, soci
|
|||
* [Client TOTP Signature](#client-totp-signature)
|
||||
* [Client Object](#client-object)
|
||||
* [Federation Standard](#federation-standard)
|
||||
* [Available Types](#available-types)
|
||||
* [ClientIdentity Object](#clientidentity-object)
|
||||
* [Invokable Methods](#invokable-methods)
|
||||
* [ping Method](#ping-method)
|
||||
* [whoami Method](#whoami-method)
|
||||
* [create_client Method](#createclient-method)
|
||||
* [Standard Federated Addresses](#standard-federated-addresses)
|
||||
* [Query Document](#query-document)
|
||||
* [QueryDocument Versioning](#querydocument-versioning)
|
||||
|
@ -73,6 +79,125 @@ TODO: Write this section
|
|||
|
||||
The federation standard is a standard which defines how clients should communicate with servers, the standard
|
||||
|
||||
## Available Types
|
||||
|
||||
|
||||
### ClientIdentity Object
|
||||
|
||||
The ClientIdentity object is used to identify the client, this standard object contains information about what client
|
||||
is invoking the method, this object is required to be provided by all clients when invoking a method, this object
|
||||
is used by the server to determine if the client is authorized to invoke the method and to determine what permissions
|
||||
the client has.
|
||||
|
||||
In such cases where a peer is attempting to invoke a method through a client such as, the server will identify the
|
||||
invoker as the peer on behalf of the client, this is done by providing the peer's standard federated address in the
|
||||
`peer` field of the ClientIdentity object
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-------------------------|----------|----------|--------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `client_uuid` | `string` | Yes | The UUID of the client that is invoking the request |
|
||||
| `client_totp_signature` | `string` | No | Optional. The client's TOTP signature that proves the request came from the client, only applicable if the client uses authorization |
|
||||
| `peer` | `string` | No | Optional. The peer that's invoking the command on behalf of the client |
|
||||
|
||||
> Note: If a server has strict permissions enabled, in such cases where the peer has higher permissions than the
|
||||
client, the server will default to maximum permissions as the client rather than the peer, this is to
|
||||
prevent clients without high permissions from abusing the system by using a peer with higher permissions.
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
## Invokable Methods
|
||||
|
||||
Invokable Methods are used by clients, peers or the server to invoke a method on the server, the server will use the
|
||||
provided information to determine who's invoking the method and if the method is allowed to be invoked by the peer.
|
||||
|
||||
All these methods are standard and required to be implemented by all servers, however, approach would allow for servers
|
||||
to implement additional methods to allow for additional functionality that may not be available in the standard,
|
||||
however, this would require the client to be aware of the additional methods and the server to be aware of the
|
||||
additional methods.
|
||||
|
||||
These methods are supposed to invokable by a virtual shell or API endpoint, however, this means while the client does
|
||||
not need to implement or use the shell to invoke these methods but can use the API to achieve the same result as the
|
||||
shell.
|
||||
|
||||
|
||||
### ping Method
|
||||
|
||||
The ping method is used to check if the server is online and to check the server's version, the ping method is
|
||||
available to all peers. Returns True if the execution was successful, otherwise the client should assume the server
|
||||
is offline or the server is temporarily unreachable.
|
||||
|
||||
JSON-RPC Example:
|
||||
|
||||
```jsonl
|
||||
> {"jsonrpc": "2.0", "method": "ping", "id": 1}
|
||||
< {"jsonrpc": "2.0", "result": true, "id": 1}
|
||||
```
|
||||
|
||||
Shell Example:
|
||||
|
||||
```shell
|
||||
$ ping
|
||||
true
|
||||
```
|
||||
|
||||
Default Permission Table:
|
||||
|
||||
| root | admin | operator | agent | client | guest |
|
||||
|:----:|:-----:|:--------:|:-----:|:------:|:-----:|
|
||||
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
### whoami Method
|
||||
|
||||
The whoami method is used to check the client's permissions and other information, the whoami method is available
|
||||
to all peers. Returns the identified "*uid*" of whoever is invoking the method, this can either be `root` if the
|
||||
function is being invoked by the server or from the root shell, or the client's UID if the function is being invoked
|
||||
by a client or finally the peer's Standard Federated Address if the function is being invoked by a peer.
|
||||
|
||||
JSON-RPC Example:
|
||||
|
||||
```jsonl
|
||||
> {"jsonrpc": "2.0", "method": "whoami", "id": 1}
|
||||
< {"jsonrpc": "2.0", "result": "root", "id": 1}
|
||||
```
|
||||
|
||||
Shell Example:
|
||||
|
||||
```shell
|
||||
$ whoami
|
||||
root
|
||||
```
|
||||
|
||||
Default Permission Table:
|
||||
|
||||
| root | admin | operator | agent | client | guest |
|
||||
|:----:|:-----:|:--------:|:-----:|:------:|:-----:|
|
||||
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
### create_client Method
|
||||
|
||||
The create_client method is used to create a new client, the create_client method is only available to the root & admin
|
||||
user by default, however, this can be changed by the server's configuration. Returns the UID of the newly created client
|
||||
if the execution was successful
|
||||
|
||||
Parameters:
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|------------------|----------|----------|:-------------------------------------------|
|
||||
| `uid` | `string` | Yes | The user ID of that is invoking the method |
|
||||
| `totp_signature` | `string` | Yes | The TOTP signature for authentication |
|
||||
| `name` | `string` | Yes | The username of the client |
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
## Standard Federated Addresses
|
||||
|
||||
A standard federated address is a universally unique identifier which can represent multiple types of peers from
|
||||
|
|
12
project.json
12
project.json
|
@ -43,6 +43,18 @@
|
|||
"source_type": "remote",
|
||||
"source": "nosial/libs.config=latest@n64"
|
||||
},
|
||||
{
|
||||
"name": "net.nosial.optslib",
|
||||
"version": "latest",
|
||||
"source_type": "remote",
|
||||
"source": "nosial/libs.opts=latest@n64"
|
||||
},
|
||||
{
|
||||
"name": "net.nosial.tamerlib",
|
||||
"version": "latest",
|
||||
"source_type": "remote",
|
||||
"source": "nosial/libs.tamer=latest@n64"
|
||||
},
|
||||
{
|
||||
"name": "net.nosial.loglib",
|
||||
"version": "latest",
|
||||
|
|
|
@ -4,146 +4,220 @@
|
|||
|
||||
namespace FederationCLI;
|
||||
|
||||
use Exception;
|
||||
use FederationLib\Classes\Configuration;
|
||||
use FederationLib\Enums\Standard\Methods;
|
||||
use FederationLib\Exceptions\DatabaseException;
|
||||
use FederationLib\Exceptions\Standard\AccessDeniedException;
|
||||
use FederationLib\Exceptions\Standard\ClientNotFoundException;
|
||||
use FederationLib\Exceptions\Standard\InternalServerException;
|
||||
use FederationLib\Exceptions\Standard\InvalidClientDescriptionException;
|
||||
use FederationLib\Exceptions\Standard\InvalidClientNameException;
|
||||
use FederationLib\FederationLib;
|
||||
use JsonException;
|
||||
use OptsLib\Parse;
|
||||
use TamerLib\Enums\TamerMode;
|
||||
use TamerLib\Exceptions\ServerException;
|
||||
use TamerLib\Exceptions\WorkerFailedException;
|
||||
use TamerLib\tm;
|
||||
|
||||
class InteractiveMode
|
||||
{
|
||||
/**
|
||||
* The current menu the user is in
|
||||
*
|
||||
* @var string
|
||||
* @var FederationLib
|
||||
*/
|
||||
private static $current_menu = 'Main';
|
||||
|
||||
/**
|
||||
* An array of menu pointers to functions
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private static $menu_pointers = [
|
||||
'ClientManager' => 'FederationCLI\InteractiveMode\ClientManager::processCommand',
|
||||
'ConfigManager' => 'FederationCLI\InteractiveMode\ConfigurationManager::processCommand'
|
||||
];
|
||||
|
||||
private static $help_pointers =[
|
||||
'ClientManager' => 'FederationCLI\InteractiveMode\ClientManager::help',
|
||||
'ConfigManager' => 'FederationCLI\InteractiveMode\ConfigurationManager::help'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var FederationLib|null
|
||||
*/
|
||||
private static $federation_lib = null;
|
||||
private static $federation_lib;
|
||||
|
||||
/**
|
||||
* Main entry point for the interactive mode
|
||||
*
|
||||
* @param array $args
|
||||
* @return void
|
||||
* @throws ServerException
|
||||
* @throws WorkerFailedException
|
||||
*/
|
||||
public static function main(array $args=[]): void
|
||||
{
|
||||
tm::initialize(TamerMode::CLIENT, Configuration::getTamerLibConfiguration()->getServerConfiguration());
|
||||
tm::createWorker(Configuration::getTamerLibConfiguration()->getCliWorkers(), FederationLib::getSubprocessorPath());
|
||||
|
||||
self::$federation_lib = new FederationLib();
|
||||
|
||||
while(true)
|
||||
{
|
||||
print(sprintf('federation@%s:~$ ', self::$current_menu));
|
||||
print(sprintf('%s@%s:~$ ', 'root', Configuration::getHostName()));
|
||||
$input = trim(fgets(STDIN));
|
||||
|
||||
self::processCommand($input);
|
||||
try
|
||||
{
|
||||
switch(strtolower(explode(' ', $input)[0]))
|
||||
{
|
||||
case Methods::PING:
|
||||
self::ping();
|
||||
break;
|
||||
case Methods::WHOAMI:
|
||||
self::whoami();
|
||||
break;
|
||||
|
||||
case Methods::CREATE_CLIENT:
|
||||
self::createClient($input);
|
||||
break;
|
||||
case Methods::GET_CLIENT:
|
||||
self::getClient($input);
|
||||
break;
|
||||
case Methods::CHANGE_CLIENT_NAME:
|
||||
self::changeClientName($input);
|
||||
break;
|
||||
|
||||
default:
|
||||
print(sprintf('Command \'%s\' not found' . PHP_EOL, $input));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
print(sprintf('Error: %s' . PHP_EOL, $e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a command from the user
|
||||
* Invokes the ping method
|
||||
*
|
||||
* @return void
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws InternalServerException
|
||||
*/
|
||||
private static function ping(): void
|
||||
{
|
||||
if(self::$federation_lib->ping(null))
|
||||
{
|
||||
print('OK' . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
print('ERROR' . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the whoami method and prints the result
|
||||
*
|
||||
* @return void
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws InternalServerException
|
||||
*/
|
||||
private static function whoami(): void
|
||||
{
|
||||
print(self::$federation_lib->whoami(null) . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input
|
||||
* @return void
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws DatabaseException
|
||||
* @throws InternalServerException
|
||||
* @throws InvalidClientDescriptionException
|
||||
* @throws InvalidClientNameException
|
||||
*/
|
||||
private static function createClient(string $input): void
|
||||
{
|
||||
$parsed_arguments = Parse::parseArgument($input);
|
||||
|
||||
$name = $parsed_arguments['name'] ?? $parsed_arguments['n'] ?? null;
|
||||
$description = $parsed_arguments['description'] ?? $parsed_arguments['d'] ?? null;
|
||||
|
||||
print(self::$federation_lib->createClient(null, $name, $description) . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input
|
||||
* @return void
|
||||
* @throws AccessDeniedException
|
||||
* @throws DatabaseException
|
||||
* @throws InternalServerException
|
||||
* @throws JsonException
|
||||
* @noinspection PhpMultipleClassDeclarationsInspection
|
||||
*/
|
||||
private static function getClient(string $input): void
|
||||
{
|
||||
$parsed_arguments = Parse::parseArgument($input);
|
||||
|
||||
$uuid = $parsed_arguments['uuid'] ?? $parsed_arguments['u'] ?? null;
|
||||
$raw = $parsed_arguments['raw'] ?? $parsed_arguments['r'] ?? false;
|
||||
|
||||
if($uuid === null | $uuid === '')
|
||||
{
|
||||
print('Missing required argument \'uuid\'' . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$client = self::$federation_lib->getClient(null, $uuid);
|
||||
|
||||
if($raw)
|
||||
{
|
||||
print(json_encode($client->toArray(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT) . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
Utilities::printData('CLIENT LOOKUP RESULTS', [
|
||||
'UUID' => $client->getUuid(),
|
||||
'NAME' => $client->getName(),
|
||||
'DESCRIPTION' => $client->getDescription() ?? 'N/A',
|
||||
'PERMISSION_ROLE' => $client->getPermissionRole(),
|
||||
'CREATED_AT' => date('Y-m-d H:i:s', $client->getCreatedTimestamp()),
|
||||
'UPDATED_AT' => date('Y-m-d H:i:s', $client->getUpdatedTimestamp()),
|
||||
'SEEN_AT' => date('Y-m-d H:i:s', $client->getSeenTimestamp())
|
||||
]);
|
||||
}
|
||||
catch(ClientNotFoundException)
|
||||
{
|
||||
print(sprintf('Client with UUID \'%s\' not found' . PHP_EOL, $uuid));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the name of a client with the given UUID
|
||||
*
|
||||
* @param string $input
|
||||
* @return void
|
||||
* @throws AccessDeniedException
|
||||
* @throws DatabaseException
|
||||
* @throws InternalServerException
|
||||
* @throws InvalidClientNameException
|
||||
*/
|
||||
private static function processCommand(string $input): void
|
||||
private static function changeClientName(string $input): void
|
||||
{
|
||||
$parsed_input = Utilities::parseShellInput($input);
|
||||
$parsed_arguments = Parse::parseArgument($input);
|
||||
|
||||
switch(strtolower($parsed_input['command']))
|
||||
$uuid = $parsed_arguments['uuid'] ?? $parsed_arguments['u'] ?? null;
|
||||
$name = $parsed_arguments['name'] ?? $parsed_arguments['n'] ?? null;
|
||||
|
||||
if($uuid === null | $uuid === '')
|
||||
{
|
||||
case 'help':
|
||||
print('Available commands:' . PHP_EOL);
|
||||
print(' help - displays the help menu for the current menu and global commands' . PHP_EOL);
|
||||
print(' client_manager - enter client manager mode' . PHP_EOL);
|
||||
print(' config_manager - enter config manager mode' . PHP_EOL);
|
||||
print(' clear - clears the screen' . PHP_EOL);
|
||||
print(' exit - exits the current menu, if on the main menu, exits the program' . PHP_EOL);
|
||||
|
||||
if(isset(self::$help_pointers[self::$current_menu]))
|
||||
{
|
||||
call_user_func(self::$help_pointers[self::$current_menu]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'client_manager':
|
||||
self::$current_menu = 'ClientManager';
|
||||
break;
|
||||
|
||||
case 'config_manager':
|
||||
self::$current_menu = 'ConfigManager';
|
||||
break;
|
||||
|
||||
case 'clear':
|
||||
print(chr(27) . "[2J" . chr(27) . "[;H");
|
||||
break;
|
||||
|
||||
case 'exit':
|
||||
if(self::$current_menu != 'Main')
|
||||
{
|
||||
self::$current_menu = 'Main';
|
||||
break;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
default:
|
||||
if(!isset(self::$menu_pointers[self::$current_menu]))
|
||||
{
|
||||
print(sprintf('Unknown command: %s', $parsed_input['command']) . PHP_EOL);
|
||||
break;
|
||||
}
|
||||
|
||||
call_user_func(self::$menu_pointers[self::$current_menu], $input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current menu
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentMenu(): string
|
||||
{
|
||||
return self::$current_menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current menu to the specified value
|
||||
*
|
||||
* @param string $current_menu
|
||||
*/
|
||||
public static function setCurrentMenu(string $current_menu): void
|
||||
{
|
||||
self::$current_menu = $current_menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FederationLib instance
|
||||
*
|
||||
* @return FederationLib
|
||||
*/
|
||||
public static function getFederationLib(): FederationLib
|
||||
{
|
||||
if(self::$federation_lib == null)
|
||||
{
|
||||
self::$federation_lib = new FederationLib();
|
||||
print('Missing required argument \'uuid\'' . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
return self::$federation_lib;
|
||||
if($name === null | $name === '')
|
||||
{
|
||||
print('Missing required argument \'name\'' . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
self::$federation_lib->changeClientName(null, $uuid, $name);
|
||||
print('OK' . PHP_EOL);
|
||||
}
|
||||
catch(ClientNotFoundException)
|
||||
{
|
||||
print(sprintf('Client with UUID \'%s\' not found' . PHP_EOL, $uuid));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FederationCLI\InteractiveMode;
|
||||
|
||||
use AsciiTable\Builder;
|
||||
use Exception;
|
||||
use FederationCLI\InteractiveMode;
|
||||
use FederationCLI\Utilities;
|
||||
use FederationLib\Enums\FilterOrder;
|
||||
use FederationLib\Enums\Filters\ListClientsFilter;
|
||||
use FederationLib\Exceptions\DatabaseException;
|
||||
use FederationLib\Objects\Client;
|
||||
|
||||
class ClientManager
|
||||
{
|
||||
/**
|
||||
* @param string $input
|
||||
* @return void
|
||||
*/
|
||||
public static function processCommand(string $input): void
|
||||
{
|
||||
$parsed_input = Utilities::parseShellInput($input);
|
||||
|
||||
try
|
||||
{
|
||||
switch(strtolower($parsed_input['command']))
|
||||
{
|
||||
case 'register':
|
||||
self::registerClient();
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
// list [optional: page] [optional: filter] [optional: order] [optional: max_items]
|
||||
self::listClients($parsed_input['args']);
|
||||
break;
|
||||
|
||||
case 'total':
|
||||
self::totalClients();
|
||||
break;
|
||||
|
||||
case 'total_pages':
|
||||
self::totalPages($parsed_input['args']);
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
self::getClient($parsed_input['args']);
|
||||
break;
|
||||
|
||||
default:
|
||||
print(sprintf('Unknown command: %s', $parsed_input['command']) . PHP_EOL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
Utilities::printExceptionStack($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the help message for the client manager
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function help(): void
|
||||
{
|
||||
print('Client manager commands:' . PHP_EOL);
|
||||
print(' register - registers a new client with the federation' . PHP_EOL);
|
||||
print(' list [optional: page (default 1)] [optional: filter (default created_timestamp)] [optional: order (default asc)] [optional: max_items (default 100)] - lists clients' . PHP_EOL);
|
||||
print(' total - gets the total number of clients' . PHP_EOL);
|
||||
print(' total_pages [optional: max_items (default 100)] - gets the total number of pages of clients' . PHP_EOL);
|
||||
print(' get [client uuid] - gets a client by UUID' . PHP_EOL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new client with the federation. prints the UUID of the client if successful.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function registerClient(): void
|
||||
{
|
||||
$client = new Client();
|
||||
|
||||
$client->setName(Utilities::promptInput('Client name (default: Random): '));
|
||||
$client->setDescription(Utilities::promptInput('Client description (default: N/A): '));
|
||||
$client->setQueryPermission((int)Utilities::parseBoolean(Utilities::promptInput('Query permission (default: 1): ')));
|
||||
$client->setUpdatePermission((int)Utilities::parseBoolean(Utilities::promptInput('Update permission (default: 0): ')));
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
$client_uuid = InteractiveMode::getFederationLib()->getClientManager()->registerClient($client);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
print('Failed to register client: ' . $e->getMessage() . PHP_EOL);
|
||||
Utilities::printExceptionStack($e);
|
||||
return;
|
||||
}
|
||||
|
||||
print(sprintf('Client registered successfully, UUID: %s', $client_uuid) . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
* @return void
|
||||
* @throws DatabaseException
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
* @throws \RedisException
|
||||
*/
|
||||
private static function listClients(array $args): void
|
||||
{
|
||||
$page = $args[0] ?? 1;
|
||||
$filter = $args[1] ?? ListClientsFilter::CREATED_TIMESTAMP;
|
||||
$order = $args[2] ?? FilterOrder::ASCENDING;
|
||||
$max_items = $args[3] ?? 100;
|
||||
|
||||
$clients = InteractiveMode::getFederationLib()->getClientManager()->listClients($page, $filter, $order, $max_items);
|
||||
|
||||
if(count($clients) === 0)
|
||||
{
|
||||
print('No clients found' . PHP_EOL);
|
||||
}
|
||||
else
|
||||
{
|
||||
$builder = new Builder();
|
||||
foreach($clients as $client)
|
||||
{
|
||||
$builder->addRow($client->toArray());
|
||||
}
|
||||
$builder->setTitle(sprintf('Clients (page %d, filter %s, order %s, max items %d)', $page, $filter, $order, $max_items));
|
||||
print($builder->renderTable() . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
private static function getClient(mixed $args)
|
||||
{
|
||||
$client_uuid = $args[0] ?? null;
|
||||
|
||||
if(is_null($client_uuid))
|
||||
{
|
||||
print('Client UUID required' . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$client = InteractiveMode::getFederationLib()->getClientManager()->getClient($client_uuid);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
print('Failed to get client: ' . $e->getMessage() . PHP_EOL);
|
||||
Utilities::printExceptionStack($e);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($client->toArray() as $key => $value)
|
||||
{
|
||||
print match ($key)
|
||||
{
|
||||
'id' => (sprintf(' UUID: %s', $value) . PHP_EOL),
|
||||
'enabled' => (sprintf(' Enabled: %s', (Utilities::parseBoolean($value) ? 'Yes' : 'No')) . PHP_EOL),
|
||||
'name' => (sprintf(' Name: %s', $value ?? 'N/A') . PHP_EOL),
|
||||
'description' => (sprintf(' Description: %s', $value ?? 'N/A') . PHP_EOL),
|
||||
'secret_totp' => (sprintf(' Secret TOTP: %s', $value ?? 'N/A') . PHP_EOL),
|
||||
'query_permission' => (sprintf(' Query permission Level: %s', $value) . PHP_EOL),
|
||||
'update_permission' => (sprintf(' Update permission Level: %s', $value) . PHP_EOL),
|
||||
'flags' => (sprintf(' Flags: %s', $value) . PHP_EOL),
|
||||
'created_timestamp' => (sprintf(' Created: %s', date('Y-m-d H:i:s', $value)) . PHP_EOL),
|
||||
'updated_timestamp' => (sprintf(' Updated: %s', date('Y-m-d H:i:s', $value)) . PHP_EOL),
|
||||
'seen_timestamp' => (sprintf(' Last seen: %s', date('Y-m-d H:i:s', $value)) . PHP_EOL),
|
||||
default => (sprintf(' %s: %s', $key, $value) . PHP_EOL),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static function totalClients()
|
||||
{
|
||||
print(sprintf('Total clients: %d', InteractiveMode::getFederationLib()->getClientManager()->getTotalClients()) . PHP_EOL);
|
||||
}
|
||||
|
||||
private static function totalPages(mixed $args)
|
||||
{
|
||||
$max_items = $args[0] ?? 100;
|
||||
|
||||
print(sprintf('Total pages: %d', InteractiveMode::getFederationLib()->getClientManager()->getTotalPages($max_items)) . PHP_EOL);
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FederationCLI\InteractiveMode;
|
||||
|
||||
use Exception;
|
||||
use FederationCLI\Utilities;
|
||||
use FederationLib\Classes\Configuration;
|
||||
|
||||
class ConfigurationManager
|
||||
{
|
||||
/**
|
||||
* @param string $input
|
||||
* @return void
|
||||
*/
|
||||
public static function processCommand(string $input): void
|
||||
{
|
||||
$parsed_input = Utilities::parseShellInput($input);
|
||||
|
||||
switch(strtolower($parsed_input['command']))
|
||||
{
|
||||
case 'read':
|
||||
self::read($parsed_input['args'][0] ?? null);
|
||||
break;
|
||||
|
||||
case 'write':
|
||||
self::write($parsed_input['args'][0] ?? null, $parsed_input['args'][1] ?? null);
|
||||
break;
|
||||
|
||||
case 'save':
|
||||
self::save();
|
||||
break;
|
||||
|
||||
default:
|
||||
print(sprintf('Unknown command: %s', $parsed_input['command']) . PHP_EOL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the help message for the client manager
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function help(): void
|
||||
{
|
||||
print('Configuration manager commands:' . PHP_EOL);
|
||||
print(' read - reads the current configuration' . PHP_EOL);
|
||||
print(' read <key> - reads the value of the specified configuration key' . PHP_EOL);
|
||||
print(' write <key> <value> - writes the value of the specified configuration key' . PHP_EOL);
|
||||
print(' save - saves the current configuration to disk' . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the current configuration or the value of a specific key
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return void
|
||||
*/
|
||||
private static function read(?string $key=null): void
|
||||
{
|
||||
if($key === null)
|
||||
{
|
||||
$value = Configuration::getConfiguration();
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = Configuration::getConfigurationObject()->get($key);
|
||||
}
|
||||
|
||||
if(is_null($value))
|
||||
{
|
||||
print('No value found for key: ' . $key . PHP_EOL);
|
||||
}
|
||||
elseif(is_array($value))
|
||||
{
|
||||
print(json_encode($value, JSON_PRETTY_PRINT) . PHP_EOL);
|
||||
}
|
||||
else
|
||||
{
|
||||
print($value . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the value of a specific configuration key
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
private static function write(string $key, string $value): void
|
||||
{
|
||||
Configuration::getConfigurationObject()->set($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the current configuration to disk
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function save(): void
|
||||
{
|
||||
try
|
||||
{
|
||||
Configuration::getConfigurationObject()->save();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
print('Failed to save configuration: ' . $e->getMessage() . PHP_EOL);
|
||||
Utilities::printExceptionStack($e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -98,4 +98,45 @@
|
|||
|
||||
return self::parseBoolean($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints data in a formatted manner
|
||||
*
|
||||
* @param $banner_text
|
||||
* @param $data
|
||||
* @param int $body_width
|
||||
* @return void
|
||||
*/
|
||||
public static function printData($banner_text, $data, int $body_width = 70)
|
||||
{
|
||||
// Padding and wrap for the longest key
|
||||
$max_key_len = max(array_map('strlen', array_keys($data)));
|
||||
|
||||
// Adjust padding for body_width
|
||||
$padding = $body_width - ($max_key_len + 4); // Additional 2 spaces for initial padding
|
||||
|
||||
// Banner
|
||||
$banner_width = $body_width + 2;
|
||||
echo str_repeat("*", $banner_width) . PHP_EOL;
|
||||
echo "* " . str_pad($banner_text, $banner_width - 4, ' ', STR_PAD_RIGHT) . " *" . PHP_EOL;
|
||||
echo str_repeat("*", $banner_width) . PHP_EOL;
|
||||
|
||||
// Print data
|
||||
foreach ($data as $key => $value) {
|
||||
// Split value into lines if it's too long
|
||||
$lines = str_split($value, $padding);
|
||||
|
||||
// Print lines
|
||||
foreach ($lines as $i => $line) {
|
||||
if ($i == 0) {
|
||||
// First line - print key and value
|
||||
echo ' ' . str_pad(strtoupper($key), $max_key_len, ' ', STR_PAD_RIGHT) . ' ' . $line . PHP_EOL;
|
||||
} else {
|
||||
// Additional lines - only value
|
||||
echo str_repeat(' ', $max_key_len + 4) . $line . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
use Exception;
|
||||
use FederationLib\Classes\Configuration\CacheServerConfiguration;
|
||||
use FederationLib\Classes\Configuration\TamerLibConfiguration;
|
||||
use FederationLib\Enums\Standard\Methods;
|
||||
use FederationLib\Enums\Standard\PermissionRole;
|
||||
use RuntimeException;
|
||||
|
||||
class Configuration
|
||||
|
@ -15,6 +18,11 @@
|
|||
*/
|
||||
private static $configuration;
|
||||
|
||||
/**
|
||||
* @var TamerLibConfiguration|null
|
||||
*/
|
||||
private static $tamerlib_configuration;
|
||||
|
||||
/**
|
||||
* Returns the full raw configuration array.
|
||||
*
|
||||
|
@ -37,46 +45,55 @@
|
|||
self::$configuration->setDefault('database.reconnect_interval', 1800);
|
||||
|
||||
/** Multi-Cache Configuration */
|
||||
self::$configuration->setDefault('cache_system.enabled', true);
|
||||
// Cache System Configuration
|
||||
self::$configuration->setDefault('cache_system.enabled', true);
|
||||
self::$configuration->setDefault('cache_system.opened_connection_priority', 20); // Higher is better
|
||||
self::$configuration->setDefault('cache_system.error_connection_priority', -30); // Lower is better
|
||||
// Client Objects
|
||||
self::$configuration->setDefault('cache_system.cache.client_objects_enabled', true);
|
||||
self::$configuration->setDefault('cache_system.cache.client_objects_ttl', 200);
|
||||
self::$configuration->setDefault('cache_system.cache.client_objects_server_preference', 'redis_master');
|
||||
self::$configuration->setDefault('cache_system.cache.client_objects_server_fallback', 'any');
|
||||
self::$configuration->setDefault('cache_system.cache.client_objects_server_fallback', 'redis_slave');
|
||||
// Peer Objects
|
||||
self::$configuration->setDefault('cache_system.cache.peer_objects_enabled', true);
|
||||
self::$configuration->setDefault('cache_system.cache.peer_objects_ttl', 200);
|
||||
self::$configuration->setDefault('cache_system.cache.peer_objects_server_preference', 'redis_master');
|
||||
self::$configuration->setDefault('cache_system.cache.peer_objects_server_fallback', 'any');
|
||||
// Redis Configuration
|
||||
self::$configuration->setDefault('cache_system.cache.peer_objects_server_fallback', 'redis_slave');
|
||||
/** Multi-Cache Server Configuration */
|
||||
// Redis Master Configuration
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.enabled', true);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.host', 'localhost');
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.port', 6379);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.driver', 'redis');
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.priority', 1);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.username', null);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.priority', 100);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.password', null);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.database', null);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_master.reconnect_interval', 1800);
|
||||
// Memcached Configuration
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.enabled', false);
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.host', 'localhost');
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.port', 11211);
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.driver', 'memcached');
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.priority', 1);
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.username', null);
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.password', null);
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.database', null);
|
||||
self::$configuration->setDefault('cache_system.servers.memcached_master.reconnect_interval', 1800);
|
||||
|
||||
|
||||
// Redis Slave Configuration
|
||||
self::$configuration->setDefault('cache_system.servers.redis_slave.enabled', false);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_slave.host', 'localhost');
|
||||
self::$configuration->setDefault('cache_system.servers.redis_slave.port', 11211);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_slave.priority', 50);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_slave.password', null);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_slave.database', null);
|
||||
self::$configuration->setDefault('cache_system.servers.redis_slave.reconnect_interval', 1800);
|
||||
|
||||
/** Federation Configuration */
|
||||
self::$configuration->setDefault('federation.hostname', 'FederationLib'); // Server Hostname
|
||||
self::$configuration->setDefault('federation.events_retention', 1209600); // Two Weeks
|
||||
self::$configuration->setDefault('federation.events_enabled.generic', true);
|
||||
self::$configuration->setDefault('federation.events_enabled.client_Created', true);
|
||||
self::$configuration->setDefault('federation.security.strict_permissions', true); // Security Feature, prevents clients & peers from elevating their permissions
|
||||
self::$configuration->setDefault('federation.security.method_permissions.ping', 5); // Guest or above
|
||||
self::$configuration->setDefault('federation.security.method_permissions.whoami', 5); // Guest or above
|
||||
self::$configuration->setDefault('federation.security.method_permissions.create_client', 2); // Admin or above
|
||||
self::$configuration->setDefault('federation.security.method_permissions.get_client', 2); // Admin or above
|
||||
self::$configuration->setDefault('federation.security.method_permissions.update_client_name', 2); // Admin or above
|
||||
|
||||
/** TamerLib Configuration */
|
||||
self::$configuration->setDefault('federation.tamer_lib.cli_workers', 8);
|
||||
self::$configuration->setDefault('federation.tamer_lib.node_workers', 20);
|
||||
self::$configuration->setDefault('federation.tamer_lib.server.host', '127.0.0.1');
|
||||
self::$configuration->setDefault('federation.tamer_lib.server.port', 6379);
|
||||
self::$configuration->setDefault('federation.tamer_lib.server.password', null);
|
||||
self::$configuration->setDefault('federation.tamer_lib.server.database', null);
|
||||
|
||||
/** Save the configuration's default values if they don't exist */
|
||||
try
|
||||
|
@ -107,6 +124,19 @@
|
|||
return self::$configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TamerLibConfiguration
|
||||
*/
|
||||
public static function getTamerLibConfiguration(): TamerLibConfiguration
|
||||
{
|
||||
if(self::$tamerlib_configuration === null)
|
||||
{
|
||||
self::$tamerlib_configuration = new TamerLibConfiguration(self::getConfiguration());
|
||||
}
|
||||
|
||||
return self::$tamerlib_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns driver of the database.
|
||||
*
|
||||
|
@ -194,6 +224,46 @@
|
|||
return (int)self::getConfiguration()['database']['reconnect_interval'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hostname of the server.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getHostName(): string
|
||||
{
|
||||
return self::getConfiguration()['federation']['hostname'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns True if the strict permission system is enabled and False if not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function strictPermissionEnabled(): bool
|
||||
{
|
||||
return (bool)self::getConfiguration()['federation']['security']['strict_permissions'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permission level required to execute a command.
|
||||
*
|
||||
* @param string $command
|
||||
* @return int
|
||||
*/
|
||||
public static function getMethodPermission(string $command): int
|
||||
{
|
||||
if(isset(self::getConfiguration()['federation']['security']['method_permissions'][$command]))
|
||||
{
|
||||
return (int)self::getConfiguration()['federation']['security']['method_permissions'][$command];
|
||||
}
|
||||
|
||||
return match ($command)
|
||||
{
|
||||
Methods::CREATE_CLIENT => PermissionRole::ADMIN,
|
||||
default => PermissionRole::GUEST,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns True if the cache system is enabled for FederationLib
|
||||
* and False if not, based on the configuration.
|
||||
|
@ -223,4 +293,24 @@
|
|||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the additional priority to add/remove if the connection is opened.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getCacheOpenedConnectionPriority(): int
|
||||
{
|
||||
return (int)self::getConfiguration()['cache_system']['opened_connection_priority'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns additional priority to add/remove if the connection is closed.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getCacheErrorConnectionPriority(): int
|
||||
{
|
||||
return (int)self::getConfiguration()['cache_system']['error_connection_priority'];
|
||||
}
|
||||
}
|
|
@ -24,21 +24,11 @@
|
|||
*/
|
||||
private ?int $port;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $driver;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private ?int $priority;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $username;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
|
@ -57,6 +47,7 @@
|
|||
/**
|
||||
* CacheServerConfiguration constructor.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct(string $name, array $configuration)
|
||||
|
@ -65,9 +56,7 @@
|
|||
$this->enabled = $configuration['enabled'] ?? false;
|
||||
$this->host = $configuration['host'] ?? null;
|
||||
$this->port = $configuration['port'] ?? null;
|
||||
$this->driver = $configuration['driver'] ?? null;
|
||||
$this->priority = $configuration['priority'] ?? null;
|
||||
$this->username = $configuration['username'] ?? null;
|
||||
$this->password = $configuration['password'] ?? null;
|
||||
$this->database = $configuration['database'] ?? null;
|
||||
$this->reconnect_interval = $configuration['reconnect_interval'] ?? null;
|
||||
|
@ -76,7 +65,7 @@
|
|||
/**
|
||||
* Returns the name of the cache server
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName(): ?string
|
||||
{
|
||||
|
@ -88,7 +77,7 @@
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getEnabled(): bool
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->enabled ?? false;
|
||||
}
|
||||
|
@ -111,14 +100,6 @@
|
|||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDriver(): ?string
|
||||
{
|
||||
return $this->driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
|
@ -127,14 +108,6 @@
|
|||
return $this->priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Classes\Configuration;
|
||||
|
||||
use TamerLib\Objects\ServerConfiguration;
|
||||
|
||||
class TamerLibConfiguration
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $cli_workers;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $node_workers;
|
||||
|
||||
/**
|
||||
* @var ServerConfiguration
|
||||
*/
|
||||
private $server_configuration;
|
||||
|
||||
/**
|
||||
* TamerLibConfiguration constructor.
|
||||
*
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct(array $configuration)
|
||||
{
|
||||
$this->cli_workers = $configuration['federation.tamer_lib.cli_workers'] ?? 8;
|
||||
$this->node_workers = $configuration['federation.tamer_lib.node_workers'] ?? 20;
|
||||
|
||||
$this->server_configuration = new ServerConfiguration(
|
||||
$configuration['federation.tamer_lib.server.host'] ?? '127.0.0.1',
|
||||
$configuration['federation.tamer_lib.server.port'] ?? 6379,
|
||||
$configuration['federation.tamer_lib.server.password'] ?? null,
|
||||
$configuration['federation.tamer_lib.server.database'] ?? 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of CLI workers to use.
|
||||
*
|
||||
* @return int|mixed
|
||||
*/
|
||||
public function getCliWorkers(): mixed
|
||||
{
|
||||
return $this->cli_workers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of Node workers to use.
|
||||
*
|
||||
* @return int|mixed
|
||||
*/
|
||||
public function getNodeWorkers(): mixed
|
||||
{
|
||||
return $this->node_workers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ServerConfiguration object for TamerLib.
|
||||
*
|
||||
* @return ServerConfiguration
|
||||
*/
|
||||
public function getServerConfiguration(): ServerConfiguration
|
||||
{
|
||||
return $this->server_configuration;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@
|
|||
{
|
||||
try
|
||||
{
|
||||
Log::info('net.nosial.federationlib', sprintf('Connecting to the database: %s://%s@%s:%s/%s', Configuration::getDatabaseDriver(), Configuration::getDatabaseUsername(), Configuration::getDatabaseHost(), Configuration::getDatabasePort(), Configuration::getDatabaseName()));
|
||||
Log::debug('net.nosial.federationlib', sprintf('Connecting to the database: %s://%s@%s:%s/%s', Configuration::getDatabaseDriver(), Configuration::getDatabaseUsername(), Configuration::getDatabaseHost(), Configuration::getDatabasePort(), Configuration::getDatabaseName()));
|
||||
$connection = DriverManager::getConnection([
|
||||
'driver' => Configuration::getDatabaseDriver(),
|
||||
'host' => Configuration::getDatabaseHost(),
|
||||
|
@ -63,7 +63,7 @@
|
|||
/** @noinspection NestedPositiveIfStatementsInspection */
|
||||
if(time() - self::$sql_last_connection_time > Configuration::getDatabaseReconnectInterval())
|
||||
{
|
||||
Log::info('net.nosial.federationlib', sprintf('Interval to reconnect to the %s server has been reached, reconnecting...', Configuration::getDatabaseDriver()));
|
||||
Log::debug('net.nosial.federationlib', sprintf('Interval to reconnect to the %s server has been reached, reconnecting...', Configuration::getDatabaseDriver()));
|
||||
|
||||
// Reconnect to the database.
|
||||
self::$sql_connection->close();
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Classes;
|
||||
|
||||
class Memcached
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private static $memcached_last_connection_time;
|
||||
|
||||
/**
|
||||
* @var \Memcached|null
|
||||
*/
|
||||
private static $memcached_connection;
|
||||
|
||||
|
||||
}
|
|
@ -5,6 +5,10 @@
|
|||
namespace FederationLib\Classes;
|
||||
|
||||
use Exception;
|
||||
use FederationLib\Classes\Configuration\CacheServerConfiguration;
|
||||
use FederationLib\Enums\Misc;
|
||||
use FederationLib\Exceptions\CacheConnectionException;
|
||||
use FederationLib\Exceptions\CacheDriverException;
|
||||
use LogLib\Log;
|
||||
use RedisException;
|
||||
|
||||
|
@ -13,70 +17,195 @@
|
|||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private static $redis_last_connection_time;
|
||||
private $redis_last_connection_time;
|
||||
|
||||
/**
|
||||
* @var \Redis|null
|
||||
*/
|
||||
private static $redis_connection;
|
||||
private $redis_connection;
|
||||
|
||||
/**
|
||||
* @var CacheServerConfiguration
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $connection_error;
|
||||
|
||||
/**
|
||||
* Redis constructor.
|
||||
*
|
||||
* @param CacheServerConfiguration $configuration
|
||||
*/
|
||||
public function __construct(CacheServerConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->connection_error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the redis server is available to connect to
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAvailable(): bool
|
||||
{
|
||||
if(!$this->configuration->isEnabled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the redis server is connected.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnected(): bool
|
||||
{
|
||||
if(!$this->configuration->isEnabled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->redis_connection !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnectionError(): bool
|
||||
{
|
||||
return $this->connection_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects from the redis server if it's connected.
|
||||
*
|
||||
* @param bool $reset_error
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect(bool $reset_error=true): void
|
||||
{
|
||||
if(!$this->isConnected())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->redis_connection->close();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
Log::warning(Misc::FEDERATIONLIB, sprintf('Failed to disconnect from redis server: %s', $e->getMessage()));
|
||||
}
|
||||
|
||||
if($reset_error)
|
||||
{
|
||||
$this->connection_error = false;
|
||||
}
|
||||
|
||||
$this->redis_connection = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a connection to the redis server.
|
||||
*
|
||||
* @param bool $throw_exception
|
||||
* @return void
|
||||
* @throws CacheConnectionException
|
||||
* @throws CacheDriverException
|
||||
*/
|
||||
public function connect(bool $throw_exception=true): void
|
||||
{
|
||||
if(!$this->configuration->isEnabled())
|
||||
{
|
||||
if($throw_exception)
|
||||
{
|
||||
throw new CacheDriverException(sprintf('Failed to connect to the redis server \'%s\' because it\'s disabled.', $this->configuration->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
if($this->redis_connection !== null)
|
||||
{
|
||||
if($this->redis_last_connection_time === null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->redis_last_connection_time < (time() - $this->configuration->getReconnectInterval()))
|
||||
{
|
||||
Log::verbose(Misc::FEDERATIONLIB, sprintf('Interval limit of %s seconds to reconnect to the redis server \'%s\' has been reached, reconnecting...', $this->configuration->getReconnectInterval(), $this->configuration->getName()));
|
||||
$this->disconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Log::info(Misc::FEDERATIONLIB, sprintf('Connecting to the redis server \'%s\': %s:%s', $this->configuration->getName(), $this->configuration->getHost(), $this->configuration->getPort()));
|
||||
$redis = new \Redis();
|
||||
$redis->connect($this->configuration->getHost(), $this->configuration->getPort());
|
||||
if($this->configuration->getPassword() !== null)
|
||||
{
|
||||
$redis->auth($this->configuration->getPassword());
|
||||
}
|
||||
$redis->select($this->configuration->getDatabase());
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$this->connection_error = true;
|
||||
|
||||
if($throw_exception)
|
||||
{
|
||||
throw new CacheConnectionException(sprintf('Failed to connect to the redis server \'%s\': %s', $this->configuration->getName(), $e->getMessage()), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
Log::warning(Misc::FEDERATIONLIB, sprintf('Failed to connect to the redis server \'%s\': %s', $this->configuration->getName(), $e->getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->redis_connection = $redis;
|
||||
$this->redis_last_connection_time = time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns/Establishes a connection to the redis server.
|
||||
* Returns null if redis is disabled.
|
||||
*
|
||||
* @param bool $throw_exception
|
||||
* @return \Redis|null
|
||||
* @throws RedisException
|
||||
* @throws CacheConnectionException
|
||||
* @throws CacheDriverException
|
||||
*/
|
||||
public static function getConnection(): ?\Redis
|
||||
public function getConnection(bool $throw_exception=true): ?\Redis
|
||||
{
|
||||
if(!Configuration::isRedisEnabled())
|
||||
if(!$this->configuration->isEnabled())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if(self::$redis_connection === null)
|
||||
if(!$this->isConnected())
|
||||
{
|
||||
try
|
||||
{
|
||||
Log::info('net.nosial.federationlib', sprintf('Connecting to the redis server: %s:%s', Configuration::getRedisHost(), Configuration::getRedisPort()));
|
||||
$redis = new \Redis();
|
||||
$redis->connect(Configuration::getRedisHost(), Configuration::getRedisPort());
|
||||
if(Configuration::getRedisPassword() !== null)
|
||||
{
|
||||
$redis->auth(Configuration::getRedisPassword());
|
||||
}
|
||||
$redis->select(Configuration::getRedisDatabase());
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new RedisException('Failed to connect to the redis server: ' . $e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
self::$redis_connection = $redis;
|
||||
self::$redis_last_connection_time = time();
|
||||
$this->connect($throw_exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(self::$redis_last_connection_time === null || self::$redis_last_connection_time < (time() - Configuration::getRedisReconnectInterval()))
|
||||
if($this->redis_last_connection_time < (time() - $this->configuration->getReconnectInterval()))
|
||||
{
|
||||
Log::info('net.nosial.federationlib', 'Interval to reconnect to the redis server has been reached, reconnecting...');
|
||||
|
||||
try
|
||||
{
|
||||
self::$redis_connection->close();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
// Do nothing
|
||||
unset($e);
|
||||
}
|
||||
|
||||
self::$redis_connection = null;
|
||||
return self::getConnection();
|
||||
$this->connect($throw_exception);
|
||||
}
|
||||
}
|
||||
|
||||
return self::$redis_connection;
|
||||
return $this->redis_connection;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,8 +213,17 @@
|
|||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public static function getRedisLastConnectionTime(): ?int
|
||||
public function getRedisLastConnectionTime(): ?int
|
||||
{
|
||||
return self::$redis_last_connection_time;
|
||||
return $this->redis_last_connection_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CacheServerConfiguration
|
||||
*/
|
||||
public function getConfiguration(): CacheServerConfiguration
|
||||
{
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
namespace FederationLib\Classes;
|
||||
|
||||
use Exception;
|
||||
use FederationLib\Enums\SerializationMethod;
|
||||
use FederationLib\Interfaces\SerializableObjectInterface;
|
||||
use InvalidArgumentException;
|
||||
|
@ -180,4 +181,34 @@
|
|||
}
|
||||
return $outliers;
|
||||
}
|
||||
|
||||
public static function weightedRandomPick( array $data): string
|
||||
{
|
||||
$totalWeight = array_sum($data);
|
||||
if($totalWeight == 0)
|
||||
{
|
||||
throw new InvalidArgumentException('Total weight cannot be 0');
|
||||
}
|
||||
|
||||
// Normalize weights to 0-1
|
||||
foreach ($data as $item => $weight)
|
||||
{
|
||||
$data[$item] = $weight / $totalWeight;
|
||||
}
|
||||
|
||||
// Generate a random number between 0 and 1
|
||||
$rand = mt_rand() / getrandmax();
|
||||
|
||||
// Select an item
|
||||
$cumulativeWeight = 0.0;
|
||||
foreach ($data as $item => $weight)
|
||||
{
|
||||
$cumulativeWeight += $weight;
|
||||
|
||||
if ($rand < $cumulativeWeight)
|
||||
{
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
use FederationLib\Enums\Standard\PeerType;
|
||||
use FederationLib\Enums\Standard\InternetPeerType;
|
||||
use FederationLib\Enums\Standard\PeerAssociationType;
|
||||
use FederationLib\Enums\Standard\PermissionRole;
|
||||
use FederationLib\Enums\Standard\UserPeerType;
|
||||
|
||||
class Validate
|
||||
|
@ -57,4 +58,48 @@
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a client name based on certain criteria.
|
||||
*
|
||||
* The client name must be alphanumeric, allowing spaces, periods, dashes, and underscores,
|
||||
* with a minimum length of 3 characters and a maximum length of 42 characters.
|
||||
*
|
||||
* @param string $name The client name to validate
|
||||
* @return bool Returns true if the client name is valid, false otherwise
|
||||
*/
|
||||
public static function clientName(string $name): bool
|
||||
{
|
||||
if (!preg_match('/^[a-zA-Z0-9\s\.\-_]+$/', $name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$length = strlen($name);
|
||||
return !($length < 3 || $length > 42);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a client description based on certain criteria.
|
||||
*
|
||||
* @param string $description The client description to validate
|
||||
* @return bool Returns true if the client description is valid, false otherwise
|
||||
*/
|
||||
public static function clientDescription(string $description): bool
|
||||
{
|
||||
$length = strlen($description);
|
||||
return !($length < 3 || $length > 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the given permission role is valid.
|
||||
*
|
||||
* @param string|int $role
|
||||
* @return bool
|
||||
*/
|
||||
public static function permissionRole(string|int $role): bool
|
||||
{
|
||||
return (int)$role >= 0 && (int)$role <= 5;
|
||||
}
|
||||
|
||||
}
|
22
src/FederationLib/Enums/CommandApplets.php
Normal file
22
src/FederationLib/Enums/CommandApplets.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Enums;
|
||||
|
||||
use FederationLib\Classes\CommandApplets\DateCommand;
|
||||
use FederationLib\Classes\CommandApplets\HostnameCommand;
|
||||
use FederationLib\Classes\CommandApplets\WhoamiCommand;
|
||||
|
||||
final class CommandApplets
|
||||
{
|
||||
const WHOAMI = [WhoamiCommand::class, 'whoami'];
|
||||
|
||||
const HOSTNAME = [HostnameCommand::class, 'hostname'];
|
||||
|
||||
const DATE = [DateCommand::class, 'date'];
|
||||
|
||||
const ALL = [
|
||||
self::WHOAMI,
|
||||
self::HOSTNAME,
|
||||
self::DATE
|
||||
];
|
||||
}
|
|
@ -7,7 +7,17 @@
|
|||
/**
|
||||
* An internal server error occurred.
|
||||
*/
|
||||
public const INTERNAL_SERVER_ERROR = 0;
|
||||
public const INTERNAL_SERVER_ERROR = -1000;
|
||||
|
||||
/**
|
||||
* The invoker does not have permission to perform the requested action.
|
||||
*/
|
||||
public const ACCESS_DENIED = -1001;
|
||||
|
||||
/**
|
||||
* The requested method is disabled.
|
||||
*/
|
||||
public const METHOD_DISABLED = -1002;
|
||||
|
||||
|
||||
|
||||
|
@ -37,19 +47,13 @@
|
|||
public const CLIENT_DISABLED = 1004;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The requested user entity was not found.
|
||||
*/
|
||||
public const PEER_NOT_FOUND = 2000;
|
||||
|
||||
/**
|
||||
* The requested peer association was not found.
|
||||
*/
|
||||
public const PEER_ASSOCIATION_NOT_FOUND = 3000;
|
||||
|
||||
/**
|
||||
* The requested peer association type is invalid.
|
||||
*/
|
||||
public const INVALID_PEER_ASSOCIATION_TYPE = 3001;
|
||||
public const ALL = [
|
||||
self::INTERNAL_SERVER_ERROR,
|
||||
self::ACCESS_DENIED,
|
||||
self::CLIENT_NOT_FOUND,
|
||||
self::INVALID_CLIENT_NAME,
|
||||
self::INVALID_CLIENT_DESCRIPTION,
|
||||
self::SIGNATURE_VERIFICATION_FAILED,
|
||||
self::CLIENT_DISABLED
|
||||
];
|
||||
}
|
24
src/FederationLib/Enums/Standard/Methods.php
Normal file
24
src/FederationLib/Enums/Standard/Methods.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Enums\Standard;
|
||||
|
||||
final class Methods
|
||||
{
|
||||
public const PING = 'ping';
|
||||
public const WHOAMI = 'whoami';
|
||||
|
||||
public const CREATE_CLIENT = 'create_client';
|
||||
public const GET_CLIENT = 'get_client';
|
||||
public const CHANGE_CLIENT_NAME = 'change_client_name';
|
||||
public const CHANGE_CLIENT_DESCRIPTION = 'change_client_description';
|
||||
|
||||
public const ALL = [
|
||||
self::PING,
|
||||
self::WHOAMI,
|
||||
|
||||
self::CREATE_CLIENT,
|
||||
self::GET_CLIENT,
|
||||
self::CHANGE_CLIENT_NAME,
|
||||
self::CHANGE_CLIENT_DESCRIPTION
|
||||
];
|
||||
}
|
22
src/FederationLib/Enums/Standard/PermissionRole.php
Normal file
22
src/FederationLib/Enums/Standard/PermissionRole.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Enums\Standard;
|
||||
|
||||
final class PermissionRole
|
||||
{
|
||||
public const ROOT = 0;
|
||||
public const ADMIN = 1;
|
||||
public const OPERATOR = 2;
|
||||
public const AGENT = 3;
|
||||
public const CLIENT = 4;
|
||||
public const GUEST = 5;
|
||||
|
||||
public const ALL = [
|
||||
self::ROOT => 'root',
|
||||
self::ADMIN => 'admin',
|
||||
self::OPERATOR => 'operator',
|
||||
self::AGENT => 'agent',
|
||||
self::CLIENT => 'client',
|
||||
self::GUEST => 'guest'
|
||||
];
|
||||
}
|
19
src/FederationLib/Exceptions/CacheConnectionException.php
Normal file
19
src/FederationLib/Exceptions/CacheConnectionException.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class CacheConnectionException extends Exception
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
20
src/FederationLib/Exceptions/CacheDriverException.php
Normal file
20
src/FederationLib/Exceptions/CacheDriverException.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
<?php
|
||||
|
||||
namespace FederationLib\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class CacheDriverException extends Exception
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(string $message = "", ?Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $previous);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Exceptions\Standard;
|
||||
|
||||
use Exception;
|
||||
use FederationLib\Enums\Standard\ErrorCodes;
|
||||
use Throwable;
|
||||
|
||||
class AccessDeniedException extends Exception
|
||||
{
|
||||
public function __construct(string $message = "", ?Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, ErrorCodes::ACCESS_DENIED, $previous);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Exceptions\Standard;
|
||||
|
||||
use Exception;
|
||||
use FederationLib\Enums\Standard\ErrorCodes;
|
||||
use Throwable;
|
||||
|
||||
class InternalServerException extends Exception
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(string $message = "", ?Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, ErrorCodes::INTERNAL_SERVER_ERROR, $previous);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Exceptions\Standard;
|
||||
|
||||
use Exception;
|
||||
use FederationLib\Enums\Standard\ErrorCodes;
|
||||
use Throwable;
|
||||
|
||||
class InvalidClientDescriptionException extends Exception
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(string $message = "", ?Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, ErrorCodes::INVALID_CLIENT_DESCRIPTION, $previous);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Exceptions\Standard;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class InvalidPermissionRoleException extends Exception
|
||||
{
|
||||
|
||||
public function __construct(string $message = "", ?Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 0, $previous);
|
||||
}
|
||||
}
|
|
@ -2,8 +2,25 @@
|
|||
|
||||
namespace FederationLib;
|
||||
|
||||
use Exception;
|
||||
use FederationLib\Classes\Configuration;
|
||||
use FederationLib\Enums\Misc;
|
||||
use FederationLib\Enums\Standard\ErrorCodes;
|
||||
use FederationLib\Enums\Standard\Methods;
|
||||
use FederationLib\Exceptions\DatabaseException;
|
||||
use FederationLib\Exceptions\Standard\AccessDeniedException;
|
||||
use FederationLib\Exceptions\Standard\ClientNotFoundException;
|
||||
use FederationLib\Exceptions\Standard\InternalServerException;
|
||||
use FederationLib\Exceptions\Standard\InvalidClientDescriptionException;
|
||||
use FederationLib\Exceptions\Standard\InvalidClientNameException;
|
||||
use FederationLib\Managers\ClientManager;
|
||||
use FederationLib\Managers\EventLogManager;
|
||||
use FederationLib\Objects\Client;
|
||||
use FederationLib\Objects\ResolvedIdentity;
|
||||
use FederationLib\Objects\Standard\ClientIdentity;
|
||||
use LogLib\Log;
|
||||
use TamerLib\Enums\TamerMode;
|
||||
use TamerLib\tm;
|
||||
use Throwable;
|
||||
|
||||
class FederationLib
|
||||
{
|
||||
|
@ -12,35 +29,241 @@
|
|||
*/
|
||||
private ClientManager $client_manager;
|
||||
|
||||
/**
|
||||
* @var EventLogManager
|
||||
*/
|
||||
private EventLogManager $event_log_manager;
|
||||
|
||||
/**
|
||||
* FederationLib constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->client_manager = new ClientManager($this);
|
||||
$this->event_log_manager = new EventLogManager($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Client manager instance
|
||||
* Registers functions to the TamerLib instance, if applicable
|
||||
*
|
||||
* @return ClientManager
|
||||
* @return void
|
||||
*/
|
||||
public function getClientManager(): ClientManager
|
||||
public function registerFunctions(): void
|
||||
{
|
||||
return $this->client_manager;
|
||||
if(tm::getMode() !== TamerMode::WORKER)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->client_manager->registerFunctions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EventLogManager
|
||||
* Resolves the permission role from the given identity and attempts to check if the identity has the
|
||||
* required permission to perform the given method
|
||||
*
|
||||
* @param ClientIdentity|null $identity
|
||||
* @return ResolvedIdentity
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws InternalServerException
|
||||
*/
|
||||
public function getEventLogManager(): EventLogManager
|
||||
private function resolveIdentity(?ClientIdentity $identity): ResolvedIdentity
|
||||
{
|
||||
return $this->event_log_manager;
|
||||
if($identity === null)
|
||||
{
|
||||
return new ResolvedIdentity(null, null, true);
|
||||
}
|
||||
|
||||
$get_client = tm::do('client_getClient', [$identity->getClientUuid()]);
|
||||
$peer = null;
|
||||
|
||||
try
|
||||
{
|
||||
$client = tm::waitFor($get_client);
|
||||
}
|
||||
catch(ClientNotFoundException $e)
|
||||
{
|
||||
tm::clear();
|
||||
throw new ClientNotFoundException('The client you are trying to access does not exist', $e);
|
||||
}
|
||||
catch(Exception|Throwable $e)
|
||||
{
|
||||
tm::clear();
|
||||
throw new InternalServerException('There was an error while trying to access the client', $e);
|
||||
}
|
||||
|
||||
tm::dof('client_updateLastSeen');
|
||||
return new ResolvedIdentity($client, $peer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given identity has the required permission to perform the given method
|
||||
*
|
||||
* @param string $method
|
||||
* @param ResolvedIdentity $resolved_identity
|
||||
* @return bool
|
||||
*/
|
||||
private function checkPermission(string $method, ResolvedIdentity $resolved_identity): bool
|
||||
{
|
||||
return $resolved_identity->getPermissionRole() <= Configuration::getMethodPermission($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the client
|
||||
*
|
||||
* @param ClientIdentity|null $identity
|
||||
* @return bool
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws InternalServerException
|
||||
*/
|
||||
public function ping(?ClientIdentity $identity): bool
|
||||
{
|
||||
if(!$this->checkPermission(Methods::PING, $this->resolveIdentity($identity)))
|
||||
{
|
||||
throw new Exceptions\Standard\AccessDeniedException('You do not have permission to perform this action');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClientIdentity|null $identity
|
||||
* @return string
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws InternalServerException
|
||||
*/
|
||||
public function whoami(?ClientIdentity $identity): string
|
||||
{
|
||||
$resolved_identity = $this->resolveIdentity($identity);
|
||||
|
||||
if(!$this->checkPermission(Methods::WHOAMI, $resolved_identity))
|
||||
{
|
||||
throw new Exceptions\Standard\AccessDeniedException('You do not have permission to perform this action');
|
||||
}
|
||||
|
||||
if($resolved_identity->getPeer() !== null)
|
||||
{
|
||||
return $resolved_identity->getPeer()->getFederatedAddress();
|
||||
}
|
||||
|
||||
if($resolved_identity->getClient() !== null)
|
||||
{
|
||||
return $resolved_identity->getClient()->getUuid();
|
||||
}
|
||||
|
||||
return 'root';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new client into the database
|
||||
*
|
||||
* @param ClientIdentity|null $identity
|
||||
* @param string|null $name
|
||||
* @param string|null $description
|
||||
* @return string
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws DatabaseException
|
||||
* @throws InternalServerException
|
||||
* @throws InvalidClientDescriptionException
|
||||
* @throws InvalidClientNameException
|
||||
*/
|
||||
public function createClient(?ClientIdentity $identity, ?string $name=null, ?string $description=null): string
|
||||
{
|
||||
if(!$this->checkPermission(Methods::CREATE_CLIENT, $this->resolveIdentity($identity)))
|
||||
{
|
||||
throw new Exceptions\Standard\AccessDeniedException('You do not have sufficient permission to create a client');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return $this->client_manager->registerClient($name, $description);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
if(in_array($e->getCode(), ErrorCodes::ALL, true))
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
|
||||
throw new Exceptions\Standard\InternalServerException('There was an error while creating the client', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an existing client from the database
|
||||
*
|
||||
* @param ClientIdentity|null $identity
|
||||
* @param string|Client $client_uuid
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws DatabaseException
|
||||
* @throws InternalServerException
|
||||
* @return Objects\Standard\Client
|
||||
*/
|
||||
public function getClient(?ClientIdentity $identity, string|Client $client_uuid): Objects\Standard\Client
|
||||
{
|
||||
if(!$this->checkPermission(Methods::GET_CLIENT, $this->resolveIdentity($identity)))
|
||||
{
|
||||
throw new Exceptions\Standard\AccessDeniedException('You do not have sufficient permission to fetch a client from the database');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Return the standard client object
|
||||
return new Objects\Standard\Client($this->client_manager->getClient($client_uuid));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
if(in_array($e->getCode(), ErrorCodes::ALL, true))
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
|
||||
throw new Exceptions\Standard\InternalServerException('There was an error while getting the client', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the name of an existing client, return True if successful
|
||||
*
|
||||
* @param ClientIdentity|null $identity
|
||||
* @param string $client_uuid
|
||||
* @param string $new_name
|
||||
* @return bool
|
||||
* @throws AccessDeniedException
|
||||
* @throws ClientNotFoundException
|
||||
* @throws DatabaseException
|
||||
* @throws InternalServerException
|
||||
* @throws InvalidClientNameException
|
||||
*/
|
||||
public function changeClientName(?ClientIdentity $identity, string $client_uuid, string $new_name): bool
|
||||
{
|
||||
if(!$this->checkPermission(Methods::CHANGE_CLIENT_NAME, $this->resolveIdentity($identity)))
|
||||
{
|
||||
throw new Exceptions\Standard\AccessDeniedException('You do not have sufficient permission to change the name of a client');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->client_manager->changeClientName($client_uuid, $new_name);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
if(in_array($e->getCode(), ErrorCodes::ALL, true))
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
|
||||
throw new Exceptions\Standard\InternalServerException('There was an error while changing the client name', $e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getSubprocessorPath(): string
|
||||
{
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'subproc';
|
||||
}
|
||||
|
||||
}
|
|
@ -3,8 +3,8 @@
|
|||
namespace FederationLib\Interfaces;
|
||||
|
||||
use FederationLib\Classes\Configuration\CacheServerConfiguration;
|
||||
use FederationLib\Classes\Memcached;
|
||||
use FederationLib\Classes\Redis;
|
||||
use Memcached;
|
||||
use Redis;
|
||||
|
||||
interface CacheDriverInterface
|
||||
{
|
||||
|
@ -29,6 +29,108 @@
|
|||
*/
|
||||
public function getConnection(): Redis|Memcached;
|
||||
|
||||
/**
|
||||
* Returns the values of the specified key
|
||||
*
|
||||
* For every key that does not hold a string value or does not exist, the special value false is returned.
|
||||
* Because of this, the operation never fails.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key): mixed;
|
||||
|
||||
/**
|
||||
* Gets a value from the hash stored at key. If the hash table doesn't exist,
|
||||
* or the key doesn't exist, FALSE is returned
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $field
|
||||
* @return mixed
|
||||
*/
|
||||
public function hGet(string $key, string $hashKey): mixed;
|
||||
|
||||
/**
|
||||
* Returns the whole hash, as an array of strings indexed by string
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function hGetAll(string $key): array;
|
||||
|
||||
/**
|
||||
* Sets a value in the cache server
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set(string $key, mixed $value): void;
|
||||
|
||||
/**
|
||||
* Fills in a whole hash. Non-string values are converted to string, using the standard (string) cast.
|
||||
* NULL values are stored as empty string
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $values
|
||||
* @return void
|
||||
*/
|
||||
public function hMSet(string $key, array $values): void;
|
||||
|
||||
/**
|
||||
* Adds a value to the hash stored at key. If this value is already in the hash, FALSE is returned.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $hash_key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function hSet(string $key, string $hash_key, mixed $value): void;
|
||||
|
||||
/**
|
||||
* Verify if the specified key exists
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($key): bool;
|
||||
|
||||
/**
|
||||
* Remove specified keys
|
||||
*
|
||||
* @param $key1
|
||||
* @param mixed ...$other_keys
|
||||
* @return void
|
||||
*/
|
||||
public function delete($key1, ...$other_keys): void;
|
||||
|
||||
/**
|
||||
* Sets a key to expire in a certain amount of seconds
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $seconds
|
||||
* @return void
|
||||
*/
|
||||
public function expire(string $key, int $seconds): void;
|
||||
|
||||
/**
|
||||
* Determines if the cache server is available (does not necessarily mean that the server is connected)
|
||||
* This is useful for checking if the cache server is available before attempting to use it
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAvailable(): bool;
|
||||
|
||||
/**
|
||||
* Determines if the cache server is currently connected
|
||||
* If the ping parameter is set to true, this will ping the server to ensure that it is connected
|
||||
* otherwise it will assume the connection is stable if a connection has been established
|
||||
*
|
||||
* @param bool $ping This will ping the server to check if it is connected
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnected(bool $ping=false): bool;
|
||||
|
||||
/**
|
||||
* Connects to the cache server
|
||||
*
|
||||
|
@ -43,5 +145,4 @@
|
|||
*/
|
||||
public function disconnect(): void;
|
||||
|
||||
|
||||
}
|
24
src/FederationLib/Interfaces/CommandAppletInterface.php
Normal file
24
src/FederationLib/Interfaces/CommandAppletInterface.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Interfaces;
|
||||
|
||||
use FederationLib\Objects\InvokeResults;
|
||||
|
||||
interface CommandAppletInterface
|
||||
{
|
||||
/**
|
||||
* Returns the command to execute.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCommand(): string;
|
||||
|
||||
/**
|
||||
* Executes the command and returns the results.
|
||||
*
|
||||
* @param string $uid
|
||||
* @param array $args
|
||||
* @return InvokeResults
|
||||
*/
|
||||
public static function execute(string $uid, array $args): InvokeResults;
|
||||
}
|
|
@ -2,22 +2,28 @@
|
|||
|
||||
namespace FederationLib\Managers;
|
||||
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Exception;
|
||||
use FederationLib\Classes\Configuration;
|
||||
use FederationLib\Classes\Database;
|
||||
use FederationLib\Classes\Redis;
|
||||
use FederationLib\Classes\Security;
|
||||
use FederationLib\Classes\Utilities;
|
||||
use FederationLib\Classes\Validate;
|
||||
use FederationLib\Enums\DatabaseTables;
|
||||
use FederationLib\Enums\EventPriority;
|
||||
use FederationLib\Enums\FilterOrder;
|
||||
use FederationLib\Enums\Filters\ListClientsFilter;
|
||||
use FederationLib\Enums\Standard\EventCode;
|
||||
use FederationLib\Exceptions\DatabaseException;
|
||||
use FederationLib\Exceptions\Standard\ClientNotFoundException;
|
||||
use FederationLib\Exceptions\Standard\InvalidClientDescriptionException;
|
||||
use FederationLib\Exceptions\Standard\InvalidClientNameException;
|
||||
use FederationLib\Exceptions\Standard\InvalidPermissionRoleException;
|
||||
use FederationLib\FederationLib;
|
||||
use FederationLib\Objects\Client;
|
||||
use LogLib\Log;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use TamerLib\Enums\TamerMode;
|
||||
use TamerLib\tm;
|
||||
|
||||
class ClientManager
|
||||
{
|
||||
|
@ -36,66 +42,65 @@
|
|||
$this->federationLib = $federationLib;
|
||||
}
|
||||
|
||||
public function registerFunctions(): void
|
||||
{
|
||||
if(tm::getMode() !== TamerMode::WORKER)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tm::addFunction('client_registerClient', [$this, 'registerClient']);
|
||||
tm::addFunction('client_getClient', [$this, 'getClient']);
|
||||
tm::addFunction('client_changeClientName', [$this, 'changeClientName']);
|
||||
tm::addFunction('client_changeClientDescription', [$this, 'changeClientDescription']);
|
||||
tm::addFunction('client_changeClientPermissionRole', [$this, 'changeClientPermissionRole']);
|
||||
tm::addFunction('client_updateClient', [$this, 'updateClient']);
|
||||
tm::addFunction('client_updateLastSeen', [$this, 'updateLastSeen']);
|
||||
tm::addFunction('client_listClients', [$this, 'listClients']);
|
||||
tm::addFunction('client_getTotalClients', [$this, 'getTotalClients']);
|
||||
tm::addFunction('client_getTotalPages', [$this, 'getTotalPages']);
|
||||
tm::addFunction('client_deleteClient', [$this, 'deleteClient']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a client into the database, returns the UUID that was generated for the client.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string|null $name
|
||||
* @param string|null $description
|
||||
* @return string
|
||||
* @throws DatabaseException
|
||||
* @throws InvalidClientDescriptionException
|
||||
* @throws InvalidClientNameException
|
||||
*/
|
||||
public function registerClient(Client $client): string
|
||||
public function registerClient(?string $name=null, ?string $description=null): string
|
||||
{
|
||||
$qb = Database::getConnection()->createQueryBuilder();
|
||||
$qb->insert(DatabaseTables::CLIENTS);
|
||||
$uuid = Uuid::v4()->toRfc4122();
|
||||
|
||||
foreach($client->toArray() as $key => $value)
|
||||
if($name === null)
|
||||
{
|
||||
switch($key)
|
||||
$name = Utilities::generateName(4);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!Validate::clientName($name))
|
||||
{
|
||||
case 'id':
|
||||
$qb->setValue($key, ':' . $key);
|
||||
$qb->setParameter($key, $uuid);
|
||||
break;
|
||||
|
||||
case 'name':
|
||||
if($value === null || strlen($value) === 0 || !preg_match('/^[a-zA-Z0-9_\-]+$/', $value ))
|
||||
{
|
||||
$value = Utilities::generateName(4);
|
||||
Log::debug('net.nosial.federationlib', sprintf('generated name for client: %s', $value));
|
||||
}
|
||||
|
||||
$qb->setValue($key, ':' . $key);
|
||||
$qb->setParameter($key, substr($value, 0, 64));
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
if($value !== null)
|
||||
{
|
||||
$qb->setValue($key, ':' . $key);
|
||||
$qb->setParameter($key, substr($value, 0, 255));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'enabled':
|
||||
$qb->setValue($key, ':' . $key);
|
||||
$qb->setParameter($key, $value ? 1 : 0);
|
||||
break;
|
||||
|
||||
case 'seen_timestamp':
|
||||
case 'updated_timestamp':
|
||||
case 'created_timestamp':
|
||||
$qb->setValue($key, ':' . $key);
|
||||
$qb->setParameter($key, time());
|
||||
break;
|
||||
|
||||
default:
|
||||
$qb->setValue($key, ':' . $key);
|
||||
$qb->setParameter($key, $value);
|
||||
break;
|
||||
throw new InvalidClientNameException(sprintf('Invalid client name: %s', $name));
|
||||
}
|
||||
}
|
||||
|
||||
if($description !== null && strlen($description) > 128)
|
||||
{
|
||||
throw new InvalidClientDescriptionException(sprintf('Invalid client description: %s', $description));
|
||||
}
|
||||
|
||||
$qb->setValue('uuid', $qb->createNamedParameter($uuid));
|
||||
$qb->setValue('name', $qb->createNamedParameter($name));
|
||||
$qb->setValue('description', $qb->createNamedParameter($description, (is_null($description) ? ParameterType::NULL : ParameterType::STRING)));
|
||||
$qb->setValue('secret_totp', $qb->createNamedParameter(Security::generateSecret()));
|
||||
$qb->setValue('flags', $qb->createNamedParameter(null, ParameterType::NULL));
|
||||
|
||||
try
|
||||
{
|
||||
$qb->executeStatement();
|
||||
|
@ -105,11 +110,6 @@
|
|||
throw new DatabaseException('Failed to register client: ' . $e->getMessage(), $e);
|
||||
}
|
||||
|
||||
$this->federationLib->getEventLogManager()->logEvent(
|
||||
EventCode::CLIENT_CREATED, EventPriority::LOW, null,
|
||||
sprintf('Registered client with UUID %s', $uuid)
|
||||
);
|
||||
|
||||
Log::info('net.nosial.federationlib', sprintf('Registered client with UUID %s', $uuid));
|
||||
return $uuid;
|
||||
}
|
||||
|
@ -117,38 +117,24 @@
|
|||
/**
|
||||
* Returns an existing client from the database.
|
||||
*
|
||||
* @param string|Client $uuid
|
||||
* @param string|Client $client_uuid
|
||||
* @return Client
|
||||
* @throws ClientNotFoundException
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function getClient(string|Client $uuid): Client
|
||||
public function getClient(string|Client $client_uuid): Client
|
||||
{
|
||||
if($uuid instanceof Client)
|
||||
if($client_uuid instanceof Client)
|
||||
{
|
||||
$uuid = $uuid->getUuid();
|
||||
}
|
||||
|
||||
if(Configuration::isRedisCacheClientObjectsEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
if(Redis::getConnection()?->exists(sprintf('Client<%s>', $uuid)))
|
||||
{
|
||||
return Client::fromArray(Redis::getConnection()?->hGetAll(sprintf('Client<%s>', $uuid)));
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
Log::warning('net.nosial.federationlib', sprintf('Failed to get Client from redis: %s', $e->getMessage()));
|
||||
}
|
||||
$client_uuid = $client_uuid->getUuid();
|
||||
}
|
||||
|
||||
$qb = Database::getConnection()->createQueryBuilder();
|
||||
$qb->select('*');
|
||||
$qb->from(DatabaseTables::CLIENTS);
|
||||
$qb->where('uuid = :uuid');
|
||||
$qb->setParameter('uuid', $uuid);
|
||||
$qb->setParameter('uuid', $client_uuid);
|
||||
$qb->setMaxResults(1);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -156,7 +142,7 @@
|
|||
|
||||
if($result->rowCount() === 0)
|
||||
{
|
||||
throw new ClientNotFoundException($uuid);
|
||||
throw new ClientNotFoundException($client_uuid);
|
||||
}
|
||||
|
||||
$client = Client::fromArray($result->fetchAssociative());
|
||||
|
@ -170,22 +156,166 @@
|
|||
throw new DatabaseException('Failed to get Client: ' . $e->getMessage(), $e);
|
||||
}
|
||||
|
||||
if(Configuration::isRedisCacheClientObjectsEnabled())
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the name of a client.
|
||||
*
|
||||
* @param string|Client $client_uuid
|
||||
* @param string|null $name
|
||||
* @return void
|
||||
* @throws ClientNotFoundException
|
||||
* @throws DatabaseException
|
||||
* @throws InvalidClientNameException
|
||||
*/
|
||||
public function changeClientName(string|Client $client_uuid, ?string $name=null): void
|
||||
{
|
||||
if($client_uuid instanceof Client)
|
||||
{
|
||||
try
|
||||
$client_uuid = $client_uuid->getUuid();
|
||||
}
|
||||
|
||||
if($name === null)
|
||||
{
|
||||
$name = Utilities::generateName(4);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!Validate::clientName($name))
|
||||
{
|
||||
Redis::getConnection()?->hMSet((string)$client, $client->toArray());
|
||||
Redis::getConnection()?->expire((string)$client, Configuration::getRedisCacheClientObjectsTTL());
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
Log::warning('net.nosial.federationlib', sprintf('Failed to cache Client in redis: %s', $e->getMessage()));
|
||||
throw new InvalidClientNameException(sprintf('Invalid client name: %s', $name));
|
||||
}
|
||||
}
|
||||
|
||||
return $client;
|
||||
$qb = Database::getConnection()->createQueryBuilder();
|
||||
$qb->update(DatabaseTables::CLIENTS);
|
||||
$qb->set('name', ':name');
|
||||
$qb->setParameter('name', $name);
|
||||
$qb->set('updated_timestamp', ':updated_timestamp');
|
||||
$qb->setParameter('updated_timestamp', time(), ParameterType::INTEGER);
|
||||
$qb->where('uuid = :uuid');
|
||||
$qb->setParameter('uuid', $client_uuid);
|
||||
$qb->setMaxResults(1);
|
||||
|
||||
try
|
||||
{
|
||||
$affected_rows = $qb->executeStatement();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new DatabaseException('Failed to change client name: ' . $e->getMessage(), $e);
|
||||
}
|
||||
|
||||
if($affected_rows === 0)
|
||||
{
|
||||
throw new ClientNotFoundException($client_uuid);
|
||||
}
|
||||
|
||||
Log::verbose('net.nosial.federationlib', sprintf('Changed client name for client %s to %s', $client_uuid, $name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the description of a client
|
||||
*
|
||||
* @param string|Client $client_uuid
|
||||
* @param string|null $description
|
||||
* @return void
|
||||
* @throws ClientNotFoundException
|
||||
* @throws DatabaseException
|
||||
* @throws InvalidClientDescriptionException
|
||||
*/
|
||||
public function changeClientDescription(string|Client $client_uuid, ?string $description=null): void
|
||||
{
|
||||
if($client_uuid instanceof Client)
|
||||
{
|
||||
$client_uuid = $client_uuid->getUuid();
|
||||
}
|
||||
|
||||
if($description !== null && strlen($description) > 128)
|
||||
{
|
||||
throw new InvalidClientDescriptionException(sprintf('Invalid client description: %s', $description));
|
||||
}
|
||||
|
||||
$qb = Database::getConnection()->createQueryBuilder();
|
||||
$qb->update(DatabaseTables::CLIENTS);
|
||||
$qb->set('description', ':description');
|
||||
$qb->setParameter('description', $description, (is_null($description) ? ParameterType::NULL : ParameterType::STRING));
|
||||
$qb->set('updated_timestamp', ':updated_timestamp');
|
||||
$qb->setParameter('updated_timestamp', time(), ParameterType::INTEGER);
|
||||
$qb->where('uuid = :uuid');
|
||||
$qb->setParameter('uuid', $client_uuid);
|
||||
$qb->setMaxResults(1);
|
||||
|
||||
try
|
||||
{
|
||||
$affected_rows = $qb->executeStatement();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new DatabaseException('Failed to change client description: ' . $e->getMessage(), $e);
|
||||
}
|
||||
|
||||
if($affected_rows === 0)
|
||||
{
|
||||
throw new ClientNotFoundException($client_uuid);
|
||||
}
|
||||
|
||||
Log::verbose('net.nosial.federationlib', sprintf('Changed client description for client %s to %s', $client_uuid, $description));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the permission role of a client.
|
||||
*
|
||||
* @param string|Client $client_uuid
|
||||
* @param int $permission_role
|
||||
* @return void
|
||||
* @throws ClientNotFoundException
|
||||
* @throws DatabaseException
|
||||
* @throws InvalidPermissionRoleException
|
||||
*/
|
||||
public function changeClientPermissionRole(string|Client $client_uuid, int $permission_role): void
|
||||
{
|
||||
if($client_uuid instanceof Client)
|
||||
{
|
||||
$client_uuid = $client_uuid->getUuid();
|
||||
}
|
||||
|
||||
if(!Validate::permissionRole($permission_role))
|
||||
{
|
||||
throw new InvalidPermissionRoleException(sprintf('Invalid permission role: %s', $permission_role));
|
||||
}
|
||||
|
||||
$time = time();
|
||||
|
||||
$qb = Database::getConnection()->createQueryBuilder();
|
||||
$qb->update(DatabaseTables::CLIENTS);
|
||||
$qb->set('permission_role', ':permission_role');
|
||||
$qb->setParameter('permission_role', $permission_role, ParameterType::INTEGER);
|
||||
$qb->set('updated_timestamp', ':updated_timestamp');
|
||||
$qb->setParameter('updated_timestamp', $time, ParameterType::INTEGER);
|
||||
$qb->where('uuid = :uuid');
|
||||
$qb->setParameter('uuid', $client_uuid);
|
||||
$qb->setMaxResults(1);
|
||||
|
||||
try
|
||||
{
|
||||
$affected_rows = $qb->executeStatement();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new DatabaseException('Failed to change client permission role: ' . $e->getMessage(), $e);
|
||||
}
|
||||
|
||||
if($affected_rows === 0)
|
||||
{
|
||||
throw new ClientNotFoundException($client_uuid);
|
||||
}
|
||||
|
||||
Log::verbose('net.nosial.federationlib', sprintf('Changed client permission role for client %s to %s', $client_uuid, $permission_role));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates a client record in the database, if the client does not exist it will be created.
|
||||
* This function is cache aware, if the client is cached it will only update the changed values.
|
||||
|
|
|
@ -2,171 +2,22 @@
|
|||
|
||||
namespace FederationLib\Managers;
|
||||
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Exception;
|
||||
use FederationLib\Classes\Configuration;
|
||||
use FederationLib\Classes\Database;
|
||||
use FederationLib\Enums\DatabaseTables;
|
||||
use FederationLib\Enums\Misc;
|
||||
use FederationLib\Enums\Standard\InternetPeerType;
|
||||
use FederationLib\Enums\Standard\PeerType;
|
||||
use FederationLib\Enums\Standard\UserPeerType;
|
||||
use FederationLib\Exceptions\DatabaseException;
|
||||
use FederationLib\Exceptions\Standard\PeerNotFoundException;
|
||||
use FederationLib\Exceptions\Standard\UnsupportedPeerType;
|
||||
use FederationLib\FederationLib;
|
||||
use FederationLib\Objects\Client;
|
||||
use FederationLib\Objects\ParsedFederatedAddress;
|
||||
use FederationLib\Objects\Peer;
|
||||
use LogLib\Log;
|
||||
|
||||
class PeerManager
|
||||
{
|
||||
/**
|
||||
* @var FederationLib
|
||||
*/
|
||||
private $federationLib;
|
||||
private FederationLib $federationLib;
|
||||
|
||||
/**
|
||||
* PeerManager constructor.
|
||||
*
|
||||
* @param FederationLib $federationLib
|
||||
*/
|
||||
public function __construct(FederationLib $federationLib)
|
||||
{
|
||||
$this->federationLib = $federationLib;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Peer Type of the federated address, returns "unknown" if the
|
||||
* type is not supported by the server
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
private function getPeerType(string $type): string
|
||||
{
|
||||
if(in_array(strtolower($type), InternetPeerType::ALL))
|
||||
return PeerType::INTERNET;
|
||||
|
||||
if(in_array(strtolower($type), UserPeerType::ALL))
|
||||
return PeerType::USER;
|
||||
|
||||
return PeerType::UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a raw federated address and returns a ParsedFederatedAddress object
|
||||
*
|
||||
* @param string $federated_address
|
||||
* @return ParsedFederatedAddress
|
||||
* @throws UnsupportedPeerType
|
||||
*/
|
||||
private function parseAddress(string $federated_address): ParsedFederatedAddress
|
||||
{
|
||||
$parsed_address = new ParsedFederatedAddress($federated_address);
|
||||
|
||||
if($this->getPeerType($parsed_address->getPeerType()) === PeerType::UNKNOWN)
|
||||
{
|
||||
throw new UnsupportedPeerType($parsed_address->getPeerType());
|
||||
}
|
||||
|
||||
return $parsed_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new peer into the database
|
||||
*
|
||||
* @param string|Client $client_uuid
|
||||
* @param string $federated_address
|
||||
* @return void
|
||||
* @throws DatabaseException
|
||||
* @throws UnsupportedPeerType
|
||||
*/
|
||||
public function registerPeer(string|Client $client_uuid, string $federated_address): void
|
||||
{
|
||||
// If the client_uuid is a Client object, get the UUID from it
|
||||
if ($client_uuid instanceof Client)
|
||||
{
|
||||
$client_uuid = $client_uuid->getUuid();
|
||||
}
|
||||
|
||||
// Check if the peer type is supported by the server
|
||||
$parsed_address = $this->parseAddress($federated_address);
|
||||
|
||||
try
|
||||
{
|
||||
// Generate a query to insert the peer into the database
|
||||
$query_builder = Database::getConnection()->createQueryBuilder();
|
||||
$query_builder->insert(DatabaseTables::PEERS);
|
||||
$timestamp = time();
|
||||
|
||||
$query_builder->values([
|
||||
'federated_address' => $query_builder->createNamedParameter($parsed_address->getAddress()),
|
||||
'client_first_seen' => $query_builder->createNamedParameter($client_uuid),
|
||||
'client_last_seen' => $query_builder->createNamedParameter($client_uuid),
|
||||
'active_restriction' => $query_builder->createNamedParameter(null, ParameterType::NULL),
|
||||
'discovered_timestamp' => $query_builder->createNamedParameter($timestamp, ParameterType::INTEGER),
|
||||
'seen_timestamp' => $query_builder->createNamedParameter($timestamp, ParameterType::INTEGER),
|
||||
]);
|
||||
|
||||
$query_builder->executeStatement();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new DatabaseException(sprintf('Failed to register peer %s: %s', $parsed_address->getAddress(), $e->getMessage()), $e);
|
||||
}
|
||||
|
||||
Log::info(Misc::FEDERATIONLIB, sprintf('Registered new peer: %s', $parsed_address->getAddress()));
|
||||
}
|
||||
|
||||
public function cachePeerObject(Peer $peer): void
|
||||
{
|
||||
if(!Configuration::isRedisEnabled() && !Configuration::isPeerObjectsCached())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a peer from the database by its federated address
|
||||
*
|
||||
* @param string $federated_address
|
||||
* @return Peer
|
||||
* @throws DatabaseException
|
||||
* @throws PeerNotFoundException
|
||||
* @throws UnsupportedPeerType
|
||||
*/
|
||||
public function getPeer(string $federated_address): Peer
|
||||
{
|
||||
// Check if the peer type is supported by the server
|
||||
$parsed_address = $this->parseAddress($federated_address);
|
||||
|
||||
try
|
||||
{
|
||||
$query_builder = Database::getConnection()->createQueryBuilder();
|
||||
$query_builder->select('*');
|
||||
$query_builder->from(DatabaseTables::PEERS);
|
||||
$query_builder->where('federated_address = :federated_address');
|
||||
$query_builder->setParameter('federated_address', $parsed_address->getAddress());
|
||||
$query_builder->setMaxResults(1);
|
||||
|
||||
$result = $query_builder->executeQuery();
|
||||
|
||||
if($result->rowCount() === 0)
|
||||
{
|
||||
throw new PeerNotFoundException($parsed_address->getAddress());
|
||||
}
|
||||
|
||||
return Peer::fromArray($result->fetchAssociative());
|
||||
}
|
||||
catch(PeerNotFoundException $e)
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new DatabaseException(sprintf('Failed to get peer %s: %s', $parsed_address->getAddress(), $e->getMessage()), $e);
|
||||
}
|
||||
}
|
||||
}
|
177
src/FederationLib/Managers/RedisConnectionManager.php
Normal file
177
src/FederationLib/Managers/RedisConnectionManager.php
Normal file
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Managers;
|
||||
|
||||
use Exception;
|
||||
use FederationLib\Classes\Configuration;
|
||||
use FederationLib\Classes\Redis;
|
||||
use FederationLib\Classes\Utilities;
|
||||
use FederationLib\Exceptions\CacheConnectionException;
|
||||
use FederationLib\Exceptions\CacheDriverException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class RedisConnectionManager
|
||||
{
|
||||
/**
|
||||
* @var Redis[]|null
|
||||
*/
|
||||
private static $connections;
|
||||
|
||||
/**
|
||||
* Returns the requested redis connection to use
|
||||
*
|
||||
* WARNING: Very advanced, terrifying code ahead - proceed with caution
|
||||
* This code is responsible for determining which redis connection to use
|
||||
* based on the configuration and the state of the connections
|
||||
* It's a bit of a mess, but it works
|
||||
*
|
||||
* @param string|null $name
|
||||
* @param string|null $fallback
|
||||
* @return \Redis
|
||||
* @throws CacheConnectionException
|
||||
* @throws CacheDriverException
|
||||
*/
|
||||
public static function getConnection(?string $name=null, ?string $fallback=null): \Redis
|
||||
{
|
||||
if(self::$connections === null)
|
||||
{
|
||||
self::$connections = [];
|
||||
foreach(Configuration::getCacheServers() as $configuration)
|
||||
{
|
||||
self::$connections[$configuration->getName()] = new Redis($configuration);
|
||||
}
|
||||
}
|
||||
|
||||
// If the name isn't null or "any", return the connection with that name
|
||||
if($name !== null || strtolower($name) !== 'any')
|
||||
{
|
||||
if(!isset(self::$connections[$name]))
|
||||
{
|
||||
if($fallback !== null)
|
||||
{
|
||||
return self::getConnection($fallback);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Redis connection with name '$name' not found");
|
||||
}
|
||||
|
||||
if(!self::$connections[$name]->isAvailable())
|
||||
{
|
||||
if($fallback !== null)
|
||||
{
|
||||
return self::getConnection($fallback);
|
||||
}
|
||||
|
||||
throw new CacheConnectionException("Redis connection with name '$name' is not available");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return self::$connections[$name]->getConnection();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
if($fallback !== null)
|
||||
{
|
||||
return self::getConnection($fallback);
|
||||
}
|
||||
|
||||
throw new CacheConnectionException(sprintf("Failed to retrieve the connection for \'%s\'", $name), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
// Assuming we're here, we're looking for any connection
|
||||
|
||||
// Build the weights array
|
||||
$weights = [];
|
||||
/** @var Redis $connection */
|
||||
foreach(self::$connections as $connection)
|
||||
{
|
||||
if($connection->isAvailable())
|
||||
{
|
||||
$priority = $connection->getConfiguration()->getPriority();
|
||||
|
||||
// Calculate the priority based on the connection state and the configuration
|
||||
if(Configuration::getCacheErrorConnectionPriority() !== 0 && $connection->isConnectionError())
|
||||
{
|
||||
$priority = ((Configuration::getCacheErrorConnectionPriority()) + ($priority));
|
||||
}
|
||||
elseif(Configuration::getCacheOpenedConnectionPriority() !== 0 && $connection->isConnected())
|
||||
{
|
||||
$priority = ((Configuration::getCacheOpenedConnectionPriority()) + ($priority));
|
||||
}
|
||||
|
||||
if((int)$priority > 100)
|
||||
{
|
||||
$priority = 100;
|
||||
}
|
||||
elseif((int)$priority < 0)
|
||||
{
|
||||
$priority = 0;
|
||||
}
|
||||
|
||||
$weights[$connection->getConfiguration()->getName()] = (int)$priority;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($weights) === 0)
|
||||
{
|
||||
// If there are no available connections, this may be based off of the configuration
|
||||
// In this case, resort to the default.
|
||||
/** @var Redis $connection */
|
||||
foreach(self::$connections as $connection)
|
||||
{
|
||||
if($connection->isAvailable())
|
||||
{
|
||||
$weights[$connection->getConfiguration()->getName()] = $connection->getConfiguration()->getPriority();
|
||||
}
|
||||
}
|
||||
|
||||
// If there are still no available connections, throw an exception
|
||||
// It's clearly the user's fault at this point lol
|
||||
if (count($weights) === 0)
|
||||
{
|
||||
if($fallback !== null)
|
||||
{
|
||||
return self::getConnection($fallback);
|
||||
}
|
||||
|
||||
throw new CacheConnectionException("No available Redis connections");
|
||||
}
|
||||
|
||||
if(count($weights) === 1)
|
||||
{
|
||||
// If there's only one available connection, just use that lmao ez
|
||||
return self::$connections[array_key_first($weights)]->getConnection();
|
||||
}
|
||||
}
|
||||
elseif(count($weights) === 1)
|
||||
{
|
||||
// Same as above
|
||||
return self::$connections[array_key_first($weights)]->getConnection();
|
||||
}
|
||||
|
||||
$selected_connection = Utilities::weightedRandomPick($weights);
|
||||
|
||||
try
|
||||
{
|
||||
return self::$connections[$selected_connection]->getConnection();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
if($fallback !== null)
|
||||
{
|
||||
// After all that, at least we have a fallback
|
||||
return self::getConnection($fallback);
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Or not :(
|
||||
throw new CacheConnectionException(sprintf("Failed to retrieve the connection for \'%s\'", $selected_connection), 0, $e);
|
||||
}
|
||||
|
||||
// Voila! je suis fier de ça
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
use FederationCLI\Utilities;
|
||||
use FederationLib\Classes\Security;
|
||||
use FederationLib\Enums\Standard\PermissionRole;
|
||||
use FederationLib\Interfaces\SerializableObjectInterface;
|
||||
|
||||
class Client implements SerializableObjectInterface
|
||||
|
@ -37,13 +38,9 @@
|
|||
|
||||
/**
|
||||
* @var int
|
||||
* @see PermissionRole
|
||||
*/
|
||||
private $query_permission;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $update_permission;
|
||||
private $permission_role;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
|
@ -72,8 +69,7 @@
|
|||
{
|
||||
$this->enabled = true;
|
||||
$this->flags = [];
|
||||
$this->query_permission = 1;
|
||||
$this->update_permission = 0;
|
||||
$this->permission_role = PermissionRole::CLIENT;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,15 +91,9 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function setEnabled(bool $enabled): void
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
* Returns the client's name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
|
@ -111,14 +101,8 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string|null $name
|
||||
*/
|
||||
public function setName(?string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional. Returns the client's description.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDescription(): ?string
|
||||
|
@ -127,32 +111,13 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string|null $description
|
||||
*/
|
||||
public function setDescription(?string $description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables authentication for the client, returns the secret.
|
||||
* Indicates whether or not the client requires authentication.
|
||||
*
|
||||
* @return string
|
||||
* @return bool
|
||||
*/
|
||||
public function enableAuthentication(): string
|
||||
public function requiresAuthentication(): bool
|
||||
{
|
||||
$this->secret_totp = Security::generateSecret();
|
||||
return $this->secret_totp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables authentication for the client, wipes the secret.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disableAuthentication(): void
|
||||
{
|
||||
$this->secret_totp = null;
|
||||
return $this->secret_totp !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,39 +150,9 @@
|
|||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getQueryPermission(): int
|
||||
public function getPermissionRole(): int
|
||||
{
|
||||
return $this->query_permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client's query permission level.
|
||||
*
|
||||
* @param int $query_permission
|
||||
*/
|
||||
public function setQueryPermission(int $query_permission): void
|
||||
{
|
||||
$this->query_permission = $query_permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client's update permission level.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUpdatePermission(): int
|
||||
{
|
||||
return $this->update_permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client's update permission.
|
||||
*
|
||||
* @param int $update_permission
|
||||
*/
|
||||
public function setUpdatePermission(int $update_permission): void
|
||||
{
|
||||
$this->update_permission = $update_permission;
|
||||
return $this->permission_role;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,42 +165,6 @@
|
|||
return $this->flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an array of flags for the client.
|
||||
* This function overrides any existing flags.
|
||||
*
|
||||
* @param string[] $flags
|
||||
*/
|
||||
public function setFlags(array $flags): void
|
||||
{
|
||||
$this->flags = $flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a flag to the client's flags.
|
||||
*
|
||||
* @param string $flag
|
||||
* @return void
|
||||
*/
|
||||
public function appendFlag(string $flag): void
|
||||
{
|
||||
if(!in_array($flag, $this->flags))
|
||||
{
|
||||
$this->flags[] = $flag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a flag from the client's flags.
|
||||
*
|
||||
* @param string $flag
|
||||
* @return void
|
||||
*/
|
||||
public function removeFlag(string $flag): void
|
||||
{
|
||||
$this->flags = array_diff($this->flags, [$flag]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns True if the client has the given flag.
|
||||
*
|
||||
|
@ -327,8 +226,7 @@
|
|||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
'secret_totp' => $this->secret_totp,
|
||||
'query_permission' => $this->query_permission,
|
||||
'update_permission' => $this->update_permission,
|
||||
'permission_role' => $this->permission_role,
|
||||
'flags' => $flags,
|
||||
'created_timestamp' => $this->created_timestamp,
|
||||
'updated_timestamp' => $this->updated_timestamp,
|
||||
|
@ -351,8 +249,7 @@
|
|||
$client->name = $array['name'] ?? null;
|
||||
$client->description = $array['description'] ?? null;
|
||||
$client->secret_totp = $array['secret_totp'] ?? null;
|
||||
$client->query_permission = $array['query_permission'] ?? 0;
|
||||
$client->update_permission = $array['update_permission'] ?? 0;
|
||||
$client->permission_role = $array['permission_role'] ?? PermissionRole::CLIENT;
|
||||
if(isset($array['flags']))
|
||||
{
|
||||
$client->flags = explode(',', $array['flags']);
|
||||
|
|
50
src/FederationLib/Objects/InvokeResults.php
Normal file
50
src/FederationLib/Objects/InvokeResults.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace FederationLib\Objects;
|
||||
|
||||
class InvokeResults
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $exit_code;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* InvokeResults constructor.
|
||||
*
|
||||
* @param int $exit_code
|
||||
* @param string $output
|
||||
*/
|
||||
public function __construct(int $exit_code, string $output)
|
||||
{
|
||||
$this->exit_code = $exit_code;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exit code of the command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getExitCode(): int
|
||||
{
|
||||
return $this->exit_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output of the command.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOutput(): string
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
namespace FederationLib\Objects;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class ParsedFederatedAddress
|
||||
{
|
||||
/**
|
||||
|
@ -30,6 +32,12 @@
|
|||
{
|
||||
preg_match("/(?<source>[a-z0-9]+)\.(?<type>[a-z0-9]+):(?<id>.+)/", $federated_address, $matches);
|
||||
|
||||
// Validate the federated address
|
||||
if (empty($matches))
|
||||
{
|
||||
throw new InvalidArgumentException('Invalid Federated Address');
|
||||
}
|
||||
|
||||
$this->source = $matches['source'];
|
||||
$this->peer_type = $matches['type'];
|
||||
$this->unique_identifier = $matches['id'];
|
||||
|
|
98
src/FederationLib/Objects/ResolvedIdentity.php
Normal file
98
src/FederationLib/Objects/ResolvedIdentity.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Objects;
|
||||
|
||||
use FederationLib\Classes\Configuration;
|
||||
use FederationLib\Enums\Standard\PermissionRole;
|
||||
use FederationLib\Exceptions\Standard\AccessDeniedException;
|
||||
|
||||
class ResolvedIdentity
|
||||
{
|
||||
/**
|
||||
* @var Client|null
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var Peer|null
|
||||
*/
|
||||
private $peer;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $permission_role;
|
||||
|
||||
/**
|
||||
* ResolvedIdentity constructor.
|
||||
*
|
||||
* @param Client|null $client
|
||||
* @param Peer|null $peer
|
||||
*/
|
||||
public function __construct(?Client $client, ?Peer $peer=null, bool $allow_root=false)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->peer = $peer;
|
||||
|
||||
if($this->client === null)
|
||||
{
|
||||
if(!$allow_root)
|
||||
{
|
||||
throw new AccessDeniedException('Missing Client Identity');
|
||||
}
|
||||
|
||||
$this->permission_role = PermissionRole::ROOT;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Configuration::strictPermissionEnabled())
|
||||
{
|
||||
if($this->peer === null)
|
||||
{
|
||||
$this->permission_role = $this->client->getPermissionRole();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->permission_role = $this->peer->getPermissionRole();
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->peer === null)
|
||||
{
|
||||
$this->permission_role = $this->client->getPermissionRole();
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->client->getPermissionRole() > $this->peer->getPermissionRole())
|
||||
{
|
||||
$this->permission_role = $this->client->getPermissionRole();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->permission_role = $this->peer->getPermissionRole();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Client|null
|
||||
*/
|
||||
public function getClient(): ?Client
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Peer|null
|
||||
*/
|
||||
public function getPeer(): ?Peer
|
||||
{
|
||||
return $this->peer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPermissionRole(): int
|
||||
{
|
||||
return $this->permission_role;
|
||||
}
|
||||
}
|
173
src/FederationLib/Objects/Standard/Client.php
Normal file
173
src/FederationLib/Objects/Standard/Client.php
Normal file
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace FederationLib\Objects\Standard;
|
||||
|
||||
use FederationLib\Interfaces\SerializableObjectInterface;
|
||||
|
||||
class Client implements SerializableObjectInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $uuid;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $enabled;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $permission_role;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $created_timestamp;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $updated_timestamp;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $seen_timestamp;
|
||||
|
||||
/**
|
||||
* Client constructor.
|
||||
*
|
||||
* @param \FederationLib\Objects\Client|null $client
|
||||
*/
|
||||
public function __construct(?\FederationLib\Objects\Client $client=null)
|
||||
{
|
||||
if($client === null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->uuid = $client->getUuid();
|
||||
$this->enabled = $client->isEnabled();
|
||||
$this->name = $client->getName();
|
||||
$this->description = $client->getDescription();
|
||||
$this->permission_role = $client->getPermissionRole();
|
||||
$this->created_timestamp = $client->getCreatedTimestamp();
|
||||
$this->updated_timestamp = $client->getUpdatedTimestamp();
|
||||
$this->seen_timestamp = $client->getSeenTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUuid(): string
|
||||
{
|
||||
return $this->uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPermissionRole(): int
|
||||
{
|
||||
return $this->permission_role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCreatedTimestamp(): int
|
||||
{
|
||||
return $this->created_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUpdatedTimestamp(): int
|
||||
{
|
||||
return $this->updated_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSeenTimestamp(): int
|
||||
{
|
||||
return $this->seen_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array representation of the object
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'uuid' => $this->uuid,
|
||||
'enabled' => $this->enabled,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
'created_timestamp' => $this->created_timestamp,
|
||||
'updated_timestamp' => $this->updated_timestamp,
|
||||
'seen_timestamp' => $this->seen_timestamp
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an object from an array representation
|
||||
*
|
||||
* @param array $array
|
||||
* @return SerializableObjectInterface
|
||||
*/
|
||||
public static function fromArray(array $array): SerializableObjectInterface
|
||||
{
|
||||
$object = new self();
|
||||
|
||||
$object->uuid = $array['uuid'];
|
||||
$object->enabled = $array['enabled'];
|
||||
$object->name = $array['name'];
|
||||
$object->description = $array['description'];
|
||||
$object->created_timestamp = $array['created_timestamp'];
|
||||
$object->updated_timestamp = $array['updated_timestamp'];
|
||||
$object->seen_timestamp = $array['seen_timestamp'];
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
84
src/FederationLib/Objects/Standard/ClientIdentity.php
Normal file
84
src/FederationLib/Objects/Standard/ClientIdentity.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace FederationLib\Objects\Standard;
|
||||
|
||||
use FederationLib\Interfaces\SerializableObjectInterface;
|
||||
|
||||
class ClientIdentity implements SerializableObjectInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $client_uuid;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $client_totp_signature;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $peer;
|
||||
|
||||
/**
|
||||
* Returns the client UUID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClientUuid(): string
|
||||
{
|
||||
return $this->client_uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional. Returns the client TOTP signature
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getClientTotpSignature(): ?string
|
||||
{
|
||||
return $this->client_totp_signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional. Returns the peer
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPeer(): ?string
|
||||
{
|
||||
return $this->peer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array representation of the object
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'client_uuid' => $this->client_uuid,
|
||||
'client_totp_signature' => $this->client_totp_signature,
|
||||
'peer' => $this->peer
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the object from an array representation
|
||||
*
|
||||
* @param array $array
|
||||
* @return ClientIdentity
|
||||
*/
|
||||
public static function fromArray(array $array): ClientIdentity
|
||||
{
|
||||
$object = new self();
|
||||
$object->client_uuid = $array['client_uuid'] ?? null;
|
||||
$object->client_totp_signature = $array['client_totp_signature'] ?? null;
|
||||
$object->peer = $array['peer'] ?? null;
|
||||
return $object;
|
||||
}
|
||||
}
|
22
src/FederationLib/subproc
Normal file
22
src/FederationLib/subproc
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
require 'ncc';
|
||||
import('net.nosial.federationlib');
|
||||
|
||||
\TamerLib\tm::initialize(\TamerLib\Enums\TamerMode::WORKER);
|
||||
|
||||
$federation_lib = new \FederationLib\FederationLib();
|
||||
$federation_lib->registerFunctions();
|
||||
|
||||
try
|
||||
{
|
||||
\TamerLib\tm::run();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
\LogLib\Log::error(\FederationLib\Enums\Misc::FEDERATIONLIB, $e->getMessage(), $e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
exit(0);
|
||||
}
|
Loading…
Add table
Reference in a new issue