From 44a1079d9d82538ffb906f964d86ca626935c324 Mon Sep 17 00:00:00 2001 From: Collin Date: Tue, 24 Sep 2024 09:32:01 +0200 Subject: [PATCH] Commenting --- src/LonaDB/Functions/FunctionManager.php | 25 ++++++-- src/LonaDB/Functions/LonaFunction.php | 20 +++++-- src/LonaDB/Plugins/PluginBase.php | 16 +++-- src/LonaDB/Plugins/PluginManager.php | 61 ++++++++++++++++--- src/LonaDB/Tables/Table.php | 76 ++++++++++++------------ src/LonaDB/Tables/TableManager.php | 47 ++++++++++----- 6 files changed, 165 insertions(+), 80 deletions(-) diff --git a/src/LonaDB/Functions/FunctionManager.php b/src/LonaDB/Functions/FunctionManager.php index d749d65..bdf9a92 100644 --- a/src/LonaDB/Functions/FunctionManager.php +++ b/src/LonaDB/Functions/FunctionManager.php @@ -2,52 +2,65 @@ namespace LonaDB\Functions; +//Encryption/decryption define('AES_256_CBC', 'aes-256-cbc'); +//Load autoload from composer require 'vendor/autoload.php'; + +//Load Main file and LonaFunction class use LonaDB\LonaDB; use LonaDB\Functions\LonaFunction; class FunctionManager{ + //Create all variables private LonaDB $LonaDB; private array $Functions; public function __construct(LonaDB $lonaDB){ $this->LonaDB = $lonaDB; + //Initialize functions array $this->Functions = array(); + //Check if directory "data/functions/" exists, create if it doesn't if(!is_dir("data/")) mkdir("data/"); if(!is_dir("data/functions/")) mkdir("data/functions/"); + //Loop through all files and folders in "data/functions/" foreach (new \DirectoryIterator('data/functions') as $fileInfo) { + //Check if file extension is ".lona" if(str_ends_with($fileInfo->getFilename(), ".lona")){ + //Initialize function instance $this->Functions[substr($fileInfo->getFilename(), 0, -5)] = new LonaFunction($this->LonaDB, $fileInfo->getFilename()); } } } public function GetFunction(string $name) : mixed { + //Check if a function with that name exists if(!$this->Functions[$name]) return false; - + //Return function instance return $this->Functions[$name]; } public function Create(string $name, string $content) : bool { + //Check if a function with that name exists if($this->Functions[$name]) return false; - + //Generate IV $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(AES_256_CBC)); - + //Encrypt function script $encrypted = openssl_encrypt(json_encode($content), AES_256_CBC, $this->LonaDB->config["encryptionKey"], 0, $iv); + //Save file_put_contents("./data/functions/".$name.".lona", $encrypted.":".base64_encode($iv)); - + //Initialize function instance $this->Functions[$name] = new LonaFunction($this->LonaDB, $name . ".lona"); - return true; } public function Delete(string $name) : bool { + //Check if function exists if(!$this->Functions[$name]) return false; - + //Delete function file and instance unset($this->Functions[$name]); unlink("./data/functions/".$name.".lona"); return true; diff --git a/src/LonaDB/Functions/LonaFunction.php b/src/LonaDB/Functions/LonaFunction.php index c0a607f..dd997d6 100644 --- a/src/LonaDB/Functions/LonaFunction.php +++ b/src/LonaDB/Functions/LonaFunction.php @@ -2,39 +2,51 @@ namespace LonaDB\Functions; +//Encryption/decryption define('AES_256_CBC', 'aes-256-cbc'); +//Load Main file use LonaDB\LonaDB; class LonaFunction{ + //Create all variables private string $file; public string $Name; - private array $data; + private array $functions; private LonaDB $LonaDB; public function __construct(LonaDB $lonaDB, string $name){ $this->LonaDB = $lonaDB; - $this->LonaDB->Logger->Load("Trying to load function '".$name."'"); + $this->LonaDB->Logger->Load("Loading function '".$name."'"); + //Split encrypted function from IV $parts = explode(':', file_get_contents("./data/functions/".$name)); + //Decrypt function $temp = json_decode(openssl_decrypt($parts[0], AES_256_CBC, $this->LonaDB->config["encryptionKey"], 0, base64_decode($parts[1])), true); + //Get function name $this->file = substr($name, 0, -5); $this->Name = $this->file; - $function = "\$this->data['" . $this->Name . "'] = new class {\n"; + //Create a eval script to add the function to the functions array + // + //We are using an array because overwriting/defining a function inside eval didn't work for us + //Our workaround is creating a class instance with a run function which is our Lona function + $function = "\$this->functions['" . $this->Name . "'] = new class {\n"; $function .= "public function run(\$LonaDB, \$data, \$server, \$fd) {\n"; $function .= $temp; $function .= "\n} \n};"; + //Add the function to the array eval($function); $this->LonaDB->Logger->Load("Function '" . $this->Name . "' has been loaded"); } public function Execute(LonaDB $LonaDB, array $data, $server, $fd) : mixed { - return $this->data[$this->Name]->run($LonaDB, $data, $server, $fd); + //Run function + return $this->functions[$this->Name]->run($LonaDB, $data, $server, $fd); } } \ No newline at end of file diff --git a/src/LonaDB/Plugins/PluginBase.php b/src/LonaDB/Plugins/PluginBase.php index 68067a0..66a8d52 100644 --- a/src/LonaDB/Plugins/PluginBase.php +++ b/src/LonaDB/Plugins/PluginBase.php @@ -2,12 +2,15 @@ namespace LonaDB\Plugins; +//Load autoload from composer +require 'vendor/autoload.php'; + +//Load Main file and Logger class use LonaDB\LonaDB; use LonaDB\Logger; -require 'vendor/autoload.php'; - class PluginBase{ + //Create all variables private LonaDB $LonaDB; private string $Name; private string $Version; @@ -20,19 +23,20 @@ class PluginBase{ $this->GetLogger()->Load("Loading Plugin '" . $this->Name . "'"); } - public function onEnable() : void { - $this->GetLogger()->Info("Plugin '" . $this->Name . "' has been loaded"); - } - + //Get LonaDB instance final public function GetLonaDB() : LonaDB { return $this->LonaDB; } + //Get Logger instance final public function GetLogger() : Logger { return $this->LonaDB->Logger; } + //Get own plugin name final public function GetName() : string { return $this->Name; } + //Get own plugin version final public function GetVersion() : string { return $this->Version; } //Events + public function onEnable() : void { $this->GetLogger()->Info("Plugin '" . $this->Name . "' has been loaded"); } public function onTableCreate(string $executor, string $name) : void {} public function onTableDelete(string $executor, string $name) : void {} public function onValueSet(string $executor, string $name, string $value) : void {} diff --git a/src/LonaDB/Plugins/PluginManager.php b/src/LonaDB/Plugins/PluginManager.php index cd12dd7..2cc6c50 100644 --- a/src/LonaDB/Plugins/PluginManager.php +++ b/src/LonaDB/Plugins/PluginManager.php @@ -2,10 +2,14 @@ namespace LonaDB\Plugins; +//Load autoload from composer require 'vendor/autoload.php'; + +//Load Main file use LonaDB\LonaDB; class PluginManager{ + //Create all variables private LonaDB $LonaDB; private array $Plugins; private array $EnabledPlugins; @@ -14,42 +18,60 @@ class PluginManager{ public function __construct(LonaDB $lonaDB) { $this->LonaDB = $lonaDB; + //Initialize plugins array $this->Plugins = array(); } public function LoadPlugins () : void { + //Check if plugins have already been loaded + if($this->Loaded = true) return; $this->Loaded = true; + + //Check if "plugins/" directory exists, create if it doesn't if(!is_dir("plugins/")) mkdir("plugins/"); + //For all files and folders in "plugins/" $results = scandir("plugins/"); - foreach($results as $r){ + //Check if file ends with ".phar" => Plugin has been compiled if(str_ends_with($r, ".phar")){ + //Load PHAR file $phar = new \Phar("plugins/" . $r, 0); + //Loop through all files in the PHAR archive foreach (new \RecursiveIteratorIterator($phar) as $file) { + //Check for plugin.json if($file->getFileName() === "plugin.json") { + //Get configuration from plugin.json $conf = json_decode(file_get_contents($file->getPathName()), true); + //Generate path variable for the file eval("\$path = substr(\$file->getPathName(), 0, -". strlen($file->getFileName()) .");"); } } + //Check the configuration if($conf['main'] && $conf['main']['path'] && $conf['main']['class'] && $conf['main']['namespace'] && $conf['name']){ + //Check if main file declared in plugin.json exists file_put_contents($path . $conf['main']['path'], file_get_contents($path . $conf['main']['path'])); if(file_get_contents($path. $conf['main']['path']) !== ""){ try{ + //Load PHAR $this->load_classphp($path, $phar); + //Add it to the Plugins array eval("\$this->Plugins[\$conf['name']] = new " . $conf['main']['namespace'] . "\\" . $conf['main']['class'] . "(\$this->LonaDB, \$conf['name'], \$conf['version']);"); + //Create a thread for it $pid = @ pcntl_fork(); if( $pid == -1 ) { throw new Exception( $this->getError( Thread::COULD_NOT_FORK ), Thread::COULD_NOT_FORK ); } if( $pid ) { + //Save thread process ID $this->pids[$conf['name']] = $pid; } else { + //Run plugin's onEnable event $this->Plugins[$conf['name']]->onEnable(); } } @@ -63,39 +85,47 @@ class PluginManager{ $this->LonaDB->Logger->Error("Could not load the plugin in '" . $r . "'"); } } + //Load plugin from folder => Plugin hasn't been compiled else if($r != "." && $r !== ".."){ + //Scan "plugins/$foler" $debugscan = scandir("plugins/" . $r); + //Check if plugin.json is inside the folder if(in_array("plugin.json", $debugscan)) $conf = json_decode(file_get_contents("plugins/" . $r . "/plugin.json"), true); + //Check configuration if($conf['main'] && $conf['main']['path'] && $conf['main']['class'] && $conf['main']['namespace'] && $conf['name']){ + //Check if main file exists file_put_contents("plugins/" . $r . "/" . $conf['main']['path'], file_get_contents("plugins/" . $r . "/" . $conf['main']['path'])); if(file_get_contents("plugins/" . $r . "/" . $conf['main']['path']) !== ""){ try{ + //Load all PHP files in the folder $this->load_classphp("plugins/" . $r . "/"); + //Check if the plugin should be built if($conf['build']){ + //Build the PHAR $phar = new \Phar("plugins/".$conf['name']."-".$conf['version'].".phar", 0, "plugins/".$conf['name']."-".$conf['version'].".phar"); - - $phar->buildFromDirectory("plugins/".$r."/"); - + $phar->buildFromDirectory("plugins/".$r."/"); $phar->setDefaultStub($conf['main']['namespace'].'/'.$conf['main']['class'].'.php', $conf['main']['namespace'].'/'.$conf['main']['class'].'.php'); - $phar->setAlias($conf['name']."-".$conf['version'].".phar"); - $phar->stopBuffering(); } + //Add plugin to the plugins array eval("\$this->Plugins[\$conf['name']] = new " . $conf['main']['namespace'] . "\\" . $conf['main']['class'] . "(\$this->LonaDB, \$conf['name'], \$conf['version']);"); + //Create a thread for the plugin $pid = @ pcntl_fork(); if( $pid == -1 ) { throw new Exception( $this->getError( Thread::COULD_NOT_FORK ), Thread::COULD_NOT_FORK ); } if( $pid ) { + //Save the process ID $this->pids[$conf['name']] = $pid; } else { + //Run the onEnable event $this->Plugins[$conf['name']]->onEnable(); } } @@ -113,40 +143,53 @@ class PluginManager{ } public function KillThreads() : void { + //Loop through all process IDs foreach($pid as $this->pids) { + //Kill the thread posix_kill( $pid, SIGKILL ); } - $this->LonaDB->Logger->Info("Plugin threads have been killed"); } public function GetPlugin(string $name) : mixed { + //Return plugin instance of ot exists if($this->Plugins[$name]) return $this->Plugins[$name]; else return false; } private function load_classphp(string $path, \Phar $phar = null) : void { + //Check if loading from PHAR if(str_starts_with($path, "phar")){ + //Loop through PHAR foreach (new \RecursiveIteratorIterator($phar) as $file) { + //Load file if its a PHP file if(str_ends_with($file->getPathName(), ".php")) require_once $file->getPathName(); } } + + //Remove the last "/" if its the last character in the path name if(str_ends_with($path, "/")) $path = substr($path, 0, -1); + //Scan directory $items = glob( $path . "/*" ); - + //Loop through the directory foreach( $items as $item ) { + //Check if file ends with PHP $isPhp = str_ends_with($item, ".php"); - + if ( $isPhp ) { + //Load file require_once $item; } else{ + //If its a folder, load PHP files inside $this->load_classphp( $item . "/" ); } } } public function RunEvent(string $executor, string $event, Array $arguments) : void { + //Loop through all plugins foreach($this->Plugins as $pluginName => $pluginInstance) { + //Run event identified by name switch($event){ case "tableCreate": $pluginInstance->onTableCreate($executor, $arguments['name']); diff --git a/src/LonaDB/Tables/Table.php b/src/LonaDB/Tables/Table.php index a647408..d325913 100644 --- a/src/LonaDB/Tables/Table.php +++ b/src/LonaDB/Tables/Table.php @@ -21,131 +21,127 @@ class Table{ public function __construct(LonaDB $lonaDB, bool $create, string $name, string $owner = ""){ $this->LonaDB = $lonaDB; + //Check if this instance is used to create the table if(!$create){ $this->LonaDB->Logger->Load("Trying to load table '".$name."'"); + //Split encrypted file content and IV $parts = explode(':', file_get_contents("./data/tables/".$name)); + //Decrypt table data and load it as a JSON object $temp = json_decode(openssl_decrypt($parts[0], AES_256_CBC, $this->LonaDB->config["encryptionKey"], 0, base64_decode($parts[1])), true); + //Load table informations $this->file = substr($name, 0, -5); $this->data = $temp["data"]; $this->permissions = $temp["permissions"]; $this->Owner = $temp["owner"]; } - else{ + //Instance is used to create the table + else { $this->LonaDB->Logger->Table("Trying to generate table '".$name."'"); + //Load table informations and create empty data and permissions array $this->file = $name; $this->data = array(); $this->permissions = array(); $this->Owner = $owner; + //Save the empty table $this->Save(); } $this->Name = $this->file; } - public function GetData() : array { - return $this->data; - } + //Return an array of all variables in the table + public function GetData() : array { return $this->data; } - public function GetOwner(string $user = "") : string { - if($user === "") return $this->Owner; - - $this->LonaDB->Logger->Table("(".$this->file.") User '".$user."' is trying to get the owner name."); - - return $this->Owner; - } + //Return the table owner's name + public function GetOwner(string $user = "") : string { return $this->Owner; } public function SetOwner(string $name, string $user) : bool { $this->LonaDB->Logger->Table("(".$this->file.") User '".$user."' is trying to change the owner to '".$name."'"); + //Check if the executing user is either root or the owner of the table if($user !== "root" && $user !== $this->Owner) return false; + //Change the owner and save $this->Owner = $name; $this->Save(); return true; } public function Set(string $name, mixed $value, string $user) : bool { - $this->LonaDB->Logger->Table("(".$this->file.") User '".$user."' is trying to set the variable '".$name."' to '".strval($value)."'"); + //Check if the executing user has write permissions on this table if(!$this->CheckPermission($user, "write")) return false; + //Set the variable and save $this->data[$name] = $value; $this->Save(); - $this->LonaDB->Logger->Table("(".$this->file.") User '".$user."' set the variable '".$name."' to '".strval($value)."'"); return true; } public function Delete(string $name, string $user) : bool { - $this->LonaDB->Logger->Table("(".$this->file.") User '".$user."' is trying to delete the variable '".$name."'"); + //Check if the executing user has write permissions on this table if(!$this->CheckPermission($user, "write")) return false; + //Delete the variable and save unset($this->data[$name]); $this->Save(); - $this->LonaDB->Logger->Table("(".$this->file.") User '".$user."' deleted the variable '".$name."'"); return true; } public function Get(string $name, string $user) : mixed { - $this->LonaDB->Logger->Table("(".$this->file.") User '".$user."' is trying to get the variable '".$name."'"); + //Check if the executing user has read permissions on this table if(!$this->CheckPermission($user, "read")) return null; + //Return variable's value return $this->data[$name]; } public function CheckPermission(string $user, string $permission) : bool { + //Check if the user is the table owner if($user === $this->Owner) return true; + //Check if the user is an Administrator or Superuser if($this->LonaDB->UserManager->GetRole($user) === "Administrator" || $this->LonaDB->UserManager->GetRole($user) === "Superuser") return true; + //Check if the user is an table Administrator if($this->permissions[$user]["admin"]) return true; + //Check if the user doesn't have the permission if(!$this->permissions[$user][$permission]) return false; - + //All checks have been run and the user has the permission + //Return true return true; } public function CheckVariable(string $name, string $user) : bool { + //Check if the executing user has read permissions on this table if(!$this->CheckPermission($user, 'read')) return false; + //Check if variable exists and return state if(!$this->data[$name]) return false; return true; } public function AddPermission(string $name, string $permission, string $user) : bool { - if($user !== $this->Owner && !$this->permissions[$user]["admin"]) { - $this->LonaDB->Logger->Table("(".$this->file.") Missing permission! Adding permission '".$permission."' for user '".$name."', requested by '".$user."'"); - return false; - } - if($user !== $this->Owner && $permission === "admin") { - $this->LonaDB->Logger->Table("(".$this->file.") Not the Owner! Adding permission '".$permission."' for user '".$name."', requested by '".$user."'"); - return false; - } - - $this->LonaDB->Logger->Table("(".$this->file.") Adding permission '".$permission."' for user '".$name."', requested by '".$user."'"); + //Check if user is table owner/administrator, global administrator or superuser + if($user !== $this->Owner && !$this->permissions[$user]["admin"] && $this->LonaDB->UserManager->GetRole($user) !== "Administrator" && $this->LonaDB->UserManager->GetRole($user) !== "Superuser") return false; + //Add permission and save $this->permissions[$name][$permission] = true; $this->Save(); return true; } public function RemovePermission(string $name, string $permission, string $user) : bool { - if($user !== $this->Owner && !$this->permissions[$user]["admin"]) { - $this->LonaDB->Logger->Table("(".$this->file.") Missing permission! Removing permission '".$permission."' for user '".$name."', requested by '".$user."'"); - return false; - } - - if($user !== $this->Owner && $permission === "admin") { - $this->LonaDB->Logger->Table("(".$this->file.") Not the Owner! Removing permission '".$permission."' for user '".$name."', requested by '".$user."'"); - return galse; - } - - $this->LonaDB->Logger->Table("(".$this->file.") Removing permission '".$permission."' for user '".$name."', requested by '".$user."'"); + //Check if user is table owner/administrator, global administrator or superuser + if($user !== $this->Owner && !$this->permissions[$user]["admin"] && $this->LonaDB->UserManager->GetRole($user) !== "Administrator" && $this->LonaDB->UserManager->GetRole($user) !== "Superuser") return false; + //Remove permission and save unset($this->permissions[$name][$permission]); - if($this->permissions[$name] === array()) unset($this->permissions[$name]); $this->Save(); } private function Save() : void { + //Generate IV and array to save $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(AES_256_CBC)); $save = array( "data" => $this->data, @@ -153,7 +149,9 @@ class Table{ "owner" => $this->Owner ); + //Encrypt array using IV $encrypted = openssl_encrypt(json_encode($save), AES_256_CBC, $this->LonaDB->config["encryptionKey"], 0, $iv); + //Save encrypted array and IV file_put_contents("./data/tables/".$this->file.".lona", $encrypted.":".base64_encode($iv)); } } \ No newline at end of file diff --git a/src/LonaDB/Tables/TableManager.php b/src/LonaDB/Tables/TableManager.php index 922b239..8377be2 100644 --- a/src/LonaDB/Tables/TableManager.php +++ b/src/LonaDB/Tables/TableManager.php @@ -2,83 +2,98 @@ namespace LonaDB\Tables; +//Load autoload from composer require 'vendor/autoload.php'; + +//Load Main file use LonaDB\LonaDB; class TableManager{ + //Create all variables private LonaDB $LonaDB; private array $Tables; public function __construct(LonaDB $lonaDB){ $this->LonaDB = $lonaDB; + //Initialize tables array $this->Tables = array(); + //Check if directory "data/tables/" exists, create if it doesn't if(!is_dir("data/")) mkdir("data/"); if(!is_dir("data/tables/")) mkdir("data/tables/"); + //Counter variable for counting tables and creating the Default table if none exist $counter = 0; + //For all files and folders in "data/tables/" foreach (new \DirectoryIterator('data/tables') as $fileInfo) { + //Check if file extension is ".lona" if(str_ends_with($fileInfo->getFilename(), ".lona")){ + //Initialize table instance $this->Tables[substr($fileInfo->getFilename(), 0, -5)] = new Table($this->LonaDB, false, $fileInfo->getFilename()); + //Count up $counter = $counter + 1; } } + //No table files exist if($counter === 0){ + //Create default table $this->CreateTable("Default", "root"); } } public function GetTable(string $name) : mixed { + //Check if table exists if(!$this->Tables[$name]) return false; + //Return table instance return $this->Tables[$name]; } public function ListTables(string $user = "") : array { + //Temporary array of tables to return $tables = array(); + //Check if there is a certain user we want the tables for if($user !== ""){ + //Loop through all tables foreach($this->Tables as $table){ + //Check if the user has read or write permissions on the table => Push the table to the array if($table->CheckPermission($user, "write")) array_push($tables, $table->Name); else if($table->CheckPermission($user, "read")) array_push($tables, $table->Name); } } else{ + //Loop through all tables foreach($this->Tables as $table){ + //Add the table to the array array_push($tables, $table->Name); } } + //Return temporary tables array return $tables; } public function CreateTable(string $name, string $owner) : bool { - $this->LonaDB->Logger->Table("Trying to create table '" . $name . "', owned by user '" . $owner . "'"); - if($this->Tables[$name]) { - $this->LonaDB->Logger->Error("Table '" . $name . "' already exists"); - return false; - } + //Check if there already is a table with the exact same name + if($this->Tables[$name]) return false; + + //Create table instance $this->Tables[$name] = new Table($this->LonaDB, true, $name, $owner); - $this->LonaDB->Logger->Table("Table '" . $name . "' has been created"); return true; } public function DeleteTable(string $name, string $user) : bool { - $this->LonaDB->Logger->Table("Trying to delete table '" . $name . "', requested by user '" . $user . "'"); - if(!$this->Tables[$name]) { - $this->LonaDB->Logger->Error("Table '" . $name . "' doesn't exist"); - return false; - } + //Check if the table exists + if(!$this->Tables[$name]) return false; - if($user !== $this->Tables[$name]->GetOwner() && $this->LonaDB->UserManager->GetRole($user) !== "Administrator" && $this->LonaDB->UserManager->GetRole($user) !== "Superuser") { - $this->LonaDB->Logger->Table("Not the owner! Trying to delete table '" . $name . "', requested by user '" . $user . "'"); - return false; - } + //Check if deleting user is the table owner, a global administrator or superuser + if($user !== $this->Tables[$name]->GetOwner() && $this->LonaDB->UserManager->GetRole($user) !== "Administrator" && $this->LonaDB->UserManager->GetRole($user) !== "Superuser") return false; + //Delete table file and instance from the tables array unlink("data/tables/".$name.".lona"); unset($this->Tables[$name]); - $this->LonaDB->Logger->Table("Deleted table '" . $name . "', requested by user '" . $user . "'"); return true; } } \ No newline at end of file