Removing the event system from the framework as nobody uses it.

Closes #8.
This commit is contained in:
Ilya Bakhlin 2021-12-30 11:47:59 +01:00
parent a4dd0c40ed
commit 19bf47d799
18 changed files with 124 additions and 377 deletions

6
.gitattributes vendored
View file

@ -2,6 +2,12 @@
.gitattributes text eol=lf export-ignore .gitattributes text eol=lf export-ignore
.gitignore text eol=lf export-ignore .gitignore text eol=lf export-ignore
# PHP
*.php text eol=lf
# JSON # JSON
*.json text eol=lf *.json text eol=lf
*.lock text eol=lf *.lock text eol=lf
# YAML
*.yml text eol=lf

16
.gitignore vendored
View file

@ -1,3 +1,8 @@
# Chandler
/extensions/available/
/extensions/enabled/
/chandler.yml
# Composer # Composer
/vendor/ /vendor/
/composer.phar /composer.phar
@ -8,13 +13,6 @@
# PHPUnit # PHPUnit
/tests/cache/ /tests/cache/
/chandler.yml
/extensions/available/*
/extensions/enabled/*
!/extensions/available/.gitkeep
!/extensions/enabled/.gitkeep
/tmp/cache/di_* /tmp/cache/di_*
/tmp/plugin_artifacts/* /tmp/plugin_artifacts/*
/tmp/cache/database/* /tmp/cache/database/*
@ -25,9 +23,5 @@
!/tmp/cache/templates/.gitkeep !/tmp/cache/templates/.gitkeep
!/tmp/cache/yaml/.gitkeep !/tmp/cache/yaml/.gitkeep
/htdocs/*
!/htdocs/.htaccess
!/htdocs/index.php
/logs/* /logs/*
!/logs/.gitkeep !/logs/.gitkeep

View file

@ -42,7 +42,7 @@ class Bootstrap
*/ */
private function igniteExtensions(): void private function igniteExtensions(): void
{ {
Chandler\Extensions\ExtensionManager::i(); Chandler\Extensions\ExtensionManager::getInstance();
} }
private function loadConfig(): void private function loadConfig(): void
@ -103,7 +103,7 @@ class Bootstrap
private function route(string $url): void private function route(string $url): void
{ {
ob_start(); ob_start();
$router = Chandler\MVC\Routing\Router::i(); $router = Chandler\MVC\Routing\Router::getInstance();
if (($output = $router->execute($url, null)) !== null) if (($output = $router->execute($url, null)) !== null)
echo $output; echo $output;
else else

View file

@ -3,9 +3,9 @@ use Chandler\Security\Authenticator;
return (function(): ?bool return (function(): ?bool
{ {
$auth = Authenticator::i(); $auth = Authenticator::getInstance();
$user = $auth->getUser(); $user = $auth->getUser();
if(!$user) return NULL; if(!$user) return NULL;
return $user->can("access")->model("admin")->whichBelongsTo(NULL); return $user->can("access")->model("admin")->whichBelongsTo(NULL);
}); });

View file

@ -1,49 +0,0 @@
<?php
declare(strict_types = 1);
namespace Chandler\Eventing;
use Chandler\Patterns\TSimpleSingleton;
/**
* @package Chandler\Eventing
*/
class EventDispatcher
{
/**
* @var array
*/
private $hooks = [];
/**
* @param mixed $hook
*
* @return bool
*/
function addListener($hook): bool
{
$this->hooks[] = $hook;
return true;
}
/**
* @param \Chandler\Eventing\Events\Event $event
*
* @return \Chandler\Eventing\Events\Event
*/
function pushEvent(Events\Event $event): Events\Event
{
foreach ($this->hooks as $hook) {
if ($event instanceof Events\Cancelable)
if ($event->isCancelled())
break;
$method = "on" . str_replace("Event", "", get_class($event));
if (!method_exists($hook, $method)) continue;
$hook->$method($event);
}
return $event;
}
use TSimpleSingleton;
}

View file

@ -1,15 +0,0 @@
<?php
declare(strict_types = 1);
namespace Chandler\Eventing\Events;
/**
* @package Chandler\Eventing\Events
*/
interface Cancelable
{
public function cancel(): void;
public function isCancelled(): bool;
}

View file

@ -1,74 +0,0 @@
<?php
declare(strict_types = 1);
namespace Chandler\Eventing\Events;
/**
* @package Chandler\Eventing\Events
*/
class Event
{
/**
* @var float
*/
protected $code;
/**
* @var string
*/
protected $data;
/**
* @var bool
*/
protected $pristine = true;
/**
* @var int
*/
protected $time;
/**
* @return float
*/
public function getCode(): float
{
return $this->code;
}
/**
* @return string
*/
public function getData(): string
{
return $this->data;
}
/**
* @return int
*/
public function getTime(): int
{
return $this->time;
}
/**
* @return bool
*/
public function isTainted(): bool
{
return !$this->pristine;
}
/**
* @param string $data
* @param float $code
*/
public function __construct(string $data = "", float $code = 0.0)
{
$this->data = $data;
$this->code = $code;
$this->time = time();
}
}

View file

@ -1,6 +1,5 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
namespace Chandler\Extensions; namespace Chandler\Extensions;
use Chandler\Eventing\EventDispatcher;
use Chandler\Patterns\TSimpleSingleton; use Chandler\Patterns\TSimpleSingleton;
use Chandler\MVC\Routing\Router; use Chandler\MVC\Routing\Router;
use Nette\Utils\Finder; use Nette\Utils\Finder;
@ -20,51 +19,50 @@ class ExtensionManager
private $router = NULL; private $router = NULL;
private $rootApp = NULL; private $rootApp = NULL;
private $eventLoop = NULL; private $eventLoop = NULL;
private function __construct() private function __construct()
{ {
foreach(Finder::findDirectories("*")->in(CHANDLER_EXTENSIONS_AVAILABLE) as $directory) { foreach(Finder::findDirectories("*")->in(CHANDLER_EXTENSIONS_AVAILABLE) as $directory) {
$extensionName = $directory->getFilename(); $extensionName = $directory->getFilename();
$directory = $directory->getRealPath(); $directory = $directory->getRealPath();
$config = "$directory/manifest.yml"; $config = "$directory/manifest.yml";
if(!file_exists($config)) { if(!file_exists($config)) {
trigger_error("Skipping $extensionName for not having a valid configuration file ($config is not found)", E_USER_WARNING); trigger_error("Skipping $extensionName for not having a valid configuration file ($config is not found)", E_USER_WARNING);
continue; continue;
} }
$this->extensions[$extensionName] = (object) chandler_parse_yaml($config); $this->extensions[$extensionName] = (object) chandler_parse_yaml($config);
$this->extensions[$extensionName]->id = $extensionName; $this->extensions[$extensionName]->id = $extensionName;
$this->extensions[$extensionName]->rawName = $directory; $this->extensions[$extensionName]->rawName = $directory;
$this->extensions[$extensionName]->enabled = CHANDLER_ROOT_CONF["extensions"]["allEnabled"]; $this->extensions[$extensionName]->enabled = CHANDLER_ROOT_CONF["extensions"]["allEnabled"];
} }
if(!CHANDLER_ROOT_CONF["extensions"]["allEnabled"]) { if(!CHANDLER_ROOT_CONF["extensions"]["allEnabled"]) {
foreach(Finder::find("*")->in(CHANDLER_EXTENSIONS_ENABLED) as $directory) { #findDirectories doesn't work with symlinks foreach(Finder::find("*")->in(CHANDLER_EXTENSIONS_ENABLED) as $directory) { #findDirectories doesn't work with symlinks
if(!is_dir($directory->getRealPath())) continue; if(!is_dir($directory->getRealPath())) continue;
$extension = $directory->getFilename(); $extension = $directory->getFilename();
if(!array_key_exists($extension, $this->extensions)) { if(!array_key_exists($extension, $this->extensions)) {
trigger_error("Extension $extension is enabled, but not available, skipping", E_USER_WARNING); trigger_error("Extension $extension is enabled, but not available, skipping", E_USER_WARNING);
continue; continue;
} }
$this->extensions[$extension]->enabled = true; $this->extensions[$extension]->enabled = true;
} }
} }
if(!array_key_exists(CHANDLER_ROOT_CONF["rootApp"], $this->extensions) || !$this->extensions[CHANDLER_ROOT_CONF["rootApp"]]->enabled) { if(!array_key_exists(CHANDLER_ROOT_CONF["rootApp"], $this->extensions) || !$this->extensions[CHANDLER_ROOT_CONF["rootApp"]]->enabled) {
trigger_error("Selected root app is not available", E_USER_ERROR); trigger_error("Selected root app is not available", E_USER_ERROR);
} }
$this->rootApp = CHANDLER_ROOT_CONF["rootApp"]; $this->rootApp = CHANDLER_ROOT_CONF["rootApp"];
$this->eventLoop = EventDispatcher::i(); $this->router = Router::getInstance();
$this->router = Router::i();
$this->init(); $this->init();
} }
private function init(): void private function init(): void
{ {
foreach($this->getExtensions(true) as $name => $configuration) { foreach($this->getExtensions(true) as $name => $configuration) {
@ -73,60 +71,60 @@ class ExtensionManager
include_once CHANDLER_EXTENSIONS_ENABLED . \"/\" . str_replace(\"\\\\\", \"/\", \$class) . \".php\"; include_once CHANDLER_EXTENSIONS_ENABLED . \"/\" . str_replace(\"\\\\\", \"/\", \$class) . \".php\";
")); "));
define(str_replace("-", "_", mb_strtoupper($name)) . "_ROOT", CHANDLER_EXTENSIONS_ENABLED . "/$name", false); define(str_replace("-", "_", mb_strtoupper($name)) . "_ROOT", CHANDLER_EXTENSIONS_ENABLED . "/$name", false);
define(str_replace("-", "_", mb_strtoupper($name)) . "_ROOT_CONF", chandler_parse_yaml(CHANDLER_EXTENSIONS_ENABLED . "/$name/$name.yml"), false); define(str_replace("-", "_", mb_strtoupper($name)) . "_ROOT_CONF", chandler_parse_yaml(CHANDLER_EXTENSIONS_ENABLED . "/$name/$name.yml"), false);
if(isset($configuration->init)) { if(isset($configuration->init)) {
$init = require(CHANDLER_EXTENSIONS_ENABLED . "/$name/" . $configuration->init); $init = require(CHANDLER_EXTENSIONS_ENABLED . "/$name/" . $configuration->init);
if(is_callable($init)) if(is_callable($init))
$init(); $init();
} }
if(is_dir($hooks = CHANDLER_EXTENSIONS_ENABLED . "/$name/Hooks")) { if(is_dir($hooks = CHANDLER_EXTENSIONS_ENABLED . "/$name/Hooks")) {
foreach(Finder::findFiles("*Hook.php")->in($hooks) as $hookFile) { foreach(Finder::findFiles("*Hook.php")->in($hooks) as $hookFile) {
$hookClassName = "$name\\Hooks\\" . str_replace(".php", "", end(explode("/", $hookFile))); $hookClassName = "$name\\Hooks\\" . str_replace(".php", "", end(explode("/", $hookFile)));
$hook = new $hookClassName; $hook = new $hookClassName;
$this->eventLoop->addListener($hook); $this->eventLoop->addListener($hook);
} }
} }
if(is_dir($app = CHANDLER_EXTENSIONS_ENABLED . "/$name/Web")) #"app" means "web app", thus variable is called $app if(is_dir($app = CHANDLER_EXTENSIONS_ENABLED . "/$name/Web")) #"app" means "web app", thus variable is called $app
$this->router->readRoutes("$app/routes.yml", $name, $this->rootApp !== $name); $this->router->readRoutes("$app/routes.yml", $name, $this->rootApp !== $name);
} }
} }
function getExtensions(bool $onlyEnabled = false): array function getExtensions(bool $onlyEnabled = false): array
{ {
return $onlyEnabled return $onlyEnabled
? array_filter($this->extensions, function($e) { return $e->enabled; }) ? array_filter($this->extensions, function($e) { return $e->enabled; })
: $this->extensions; : $this->extensions;
} }
function getExtension(string $name): ?object function getExtension(string $name): ?object
{ {
return @$this->extensions[$name]; return @$this->extensions[$name];
} }
function disableExtension(string $name): void function disableExtension(string $name): void
{ {
if(!array_key_exists($name, $this->getExtensions(true))) return; if(!array_key_exists($name, $this->getExtensions(true))) return;
if(!unlink(CHANDLER_EXTENSIONS_ENABLED . "/$name")) throw new \Exception("Could not disable extension"); if(!unlink(CHANDLER_EXTENSIONS_ENABLED . "/$name")) throw new \Exception("Could not disable extension");
} }
function enableExtension(string $name): void function enableExtension(string $name): void
{ {
if(CHANDLER_ROOT_CONF["extensions"]["allEnabled"]) return; if(CHANDLER_ROOT_CONF["extensions"]["allEnabled"]) return;
if(array_key_exists($name, $this->getExtensions(true))) return; if(array_key_exists($name, $this->getExtensions(true))) return;
$path = CHANDLER_EXTENSIONS_AVAILABLE . "/$name"; $path = CHANDLER_EXTENSIONS_AVAILABLE . "/$name";
if(!is_dir($path)) throw new \Exception("Extension doesn't exist"); if(!is_dir($path)) throw new \Exception("Extension doesn't exist");
if(!symlink($path, str_replace("available", "enabled", $path))) throw new \Exception("Could not enable extension"); if(!symlink($path, str_replace("available", "enabled", $path))) throw new \Exception("Could not enable extension");
} }
use TSimpleSingleton; use TSimpleSingleton;
} }

View file

@ -4,7 +4,6 @@ declare(strict_types = 1);
namespace Chandler\MVC\Routing; namespace Chandler\MVC\Routing;
use Chandler\Eventing\EventDispatcher;
use Chandler\MVC\Exceptions\InterruptedException; use Chandler\MVC\Exceptions\InterruptedException;
use Chandler\MVC\IPresenter; use Chandler\MVC\IPresenter;
use Chandler\Patterns\TSimpleSingleton; use Chandler\Patterns\TSimpleSingleton;
@ -199,7 +198,7 @@ class Router
{ {
$key = hash("snefru", CHANDLER_ROOT_CONF["security"]["secret"] . bin2hex($nonce)); $key = hash("snefru", CHANDLER_ROOT_CONF["security"]["secret"] . bin2hex($nonce));
$data = $route->namespace; $data = $route->namespace;
$data .= Session::i()->get("tok", -1); $data .= Session::getInstance()->get("tok", -1);
return hash_hmac("snefru", $data, $key) . "#" . bin2hex($nonce); return hash_hmac("snefru", $data, $key) . "#" . bin2hex($nonce);
} }
@ -257,10 +256,5 @@ class Router
return null; return null;
} }
private function __construct()
{
$this->events = EventDispatcher::i();
}
use TSimpleSingleton; use TSimpleSingleton;
} }

View file

@ -10,22 +10,22 @@ abstract class SimplePresenter implements IPresenter
const REDIRECT_TEMPORARY = 2; const REDIRECT_TEMPORARY = 2;
const REDIRECT_PERMAMENT_PRESISTENT = 8; const REDIRECT_PERMAMENT_PRESISTENT = 8;
const REDIRECT_TEMPORARY_PRESISTENT = 7; const REDIRECT_TEMPORARY_PRESISTENT = 7;
protected $mmReader; protected $mmReader;
protected $template; protected $template;
protected $errorTemplate = NULL; protected $errorTemplate = NULL;
function __construct() function __construct()
{ {
$this->template = (object) []; $this->template = (object) [];
} }
function getTemplatingEngine(): TemplatingEngine function getTemplatingEngine(): TemplatingEngine
{ {
$latte = new TemplatingEngine; $latte = new TemplatingEngine;
$macros = new \Latte\Macros\MacroSet($latte->getCompiler()); $macros = new \Latte\Macros\MacroSet($latte->getCompiler());
$latte->setTempDirectory(CHANDLER_ROOT . "/tmp/cache/templates"); $latte->setTempDirectory(CHANDLER_ROOT . "/tmp/cache/templates");
$macros->addMacro("css", ' $macros->addMacro("css", '
$domain = "' . explode("\\", static::class)[0] . '"; $domain = "' . explode("\\", static::class)[0] . '";
$file = (%node.array)[0]; $file = (%node.array)[0];
@ -62,18 +62,18 @@ abstract class SimplePresenter implements IPresenter
echo "<!-- Inclusion complete -->"; echo "<!-- Inclusion complete -->";
' '
); );
return $latte; return $latte;
} }
protected function throwError(int $code = 400, string $desc = "Bad Request", string $message = ""): void protected function throwError(int $code = 400, string $desc = "Bad Request", string $message = ""): void
{ {
if(!is_null($this->errorTemplate)) { if(!is_null($this->errorTemplate)) {
header("HTTP/1.0 $code $desc"); header("HTTP/1.0 $code $desc");
$ext = explode("\\", get_class($this))[0]; $ext = explode("\\", get_class($this))[0];
$path = CHANDLER_EXTENSIONS_ENABLED . "/$ext/Web/Presenters/templates/" . $this->errorTemplate . ".xml"; $path = CHANDLER_EXTENSIONS_ENABLED . "/$ext/Web/Presenters/templates/" . $this->errorTemplate . ".xml";
$latte = $this->getTemplatingEngine(); $latte = $this->getTemplatingEngine();
$latte->render($path, array_merge_recursive([ $latte->render($path, array_merge_recursive([
"code" => $code, "code" => $code,
@ -85,18 +85,18 @@ abstract class SimplePresenter implements IPresenter
chandler_http_panic($code, $desc, $message); chandler_http_panic($code, $desc, $message);
} }
} }
protected function assertNoCSRF(): void protected function assertNoCSRF(): void
{ {
if(!$GLOBALS["csrfCheck"]) if(!$GLOBALS["csrfCheck"])
$this->throwError(400, "Bad Request", "CSRF token is missing or invalid."); $this->throwError(400, "Bad Request", "CSRF token is missing or invalid.");
} }
protected function terminate(): void protected function terminate(): void
{ {
throw new Exceptions\InterruptedException; throw new Exceptions\InterruptedException;
} }
protected function notFound(): void protected function notFound(): void
{ {
$this->throwError( $this->throwError(
@ -105,51 +105,51 @@ abstract class SimplePresenter implements IPresenter
"The resource you are looking for has been deleted, had its name changed or doesn't exist." "The resource you are looking for has been deleted, had its name changed or doesn't exist."
); );
} }
protected function getCaller(): string protected function getCaller(): string
{ {
return $GLOBALS["parentModule"] ?? "libchandler:absolute.0"; return $GLOBALS["parentModule"] ?? "libchandler:absolute.0";
} }
protected function redirect(string $location, int $code = 2): void protected function redirect(string $location, int $code = 2): void
{ {
$code = 300 + $code; $code = 300 + $code;
if(($code <=> 300) !== 0 && $code > 399) return; if(($code <=> 300) !== 0 && $code > 399) return;
header("HTTP/1.1 $code"); header("HTTP/1.1 $code");
header("Location: $location"); header("Location: $location");
exit; exit;
} }
protected function pass(string $to, ...$args): void protected function pass(string $to, ...$args): void
{ {
$args = array_merge([$to], $args); $args = array_merge([$to], $args);
$router = \Chandler\MVC\Routing\Router::i(); $router = \Chandler\MVC\Routing\Router::getInstance();
$__out = $router->execute($router->reverse(...$args), "libchandler:absolute.0"); $__out = $router->execute($router->reverse(...$args), "libchandler:absolute.0");
exit($__out); exit($__out);
} }
protected function sendmail(string $to, string $template, array $params): void protected function sendmail(string $to, string $template, array $params): void
{ {
$emailDir = pathinfo($template, PATHINFO_DIRNAME); $emailDir = pathinfo($template, PATHINFO_DIRNAME);
$template .= ".eml.latte"; $template .= ".eml.latte";
$renderedHTML = (new TemplatingEngine)->renderToString($template, $params); $renderedHTML = (new TemplatingEngine)->renderToString($template, $params);
$document = new \DOMDocument(); $document = new \DOMDocument();
$document->loadHTML($renderedHTML, LIBXML_NOEMPTYTAG); $document->loadHTML($renderedHTML, LIBXML_NOEMPTYTAG);
$querySel = new \DOMXPath($document); $querySel = new \DOMXPath($document);
$subject = $querySel->query("//title/text()")->item(0)->data; $subject = $querySel->query("//title/text()")->item(0)->data;
foreach($querySel->query("//link[@rel='stylesheet']") as $link) { foreach($querySel->query("//link[@rel='stylesheet']") as $link) {
$style = $document->createElement("style"); $style = $document->createElement("style");
$style->setAttribute("id", uniqid("mail", true)); $style->setAttribute("id", uniqid("mail", true));
$style->appendChild(new \DOMText(file_get_contents("$emailDir/assets/css/" . $link->getAttribute("href")))); $style->appendChild(new \DOMText(file_get_contents("$emailDir/assets/css/" . $link->getAttribute("href"))));
$link->parentNode->appendChild($style); $link->parentNode->appendChild($style);
$link->parentNode->removeChild($link); $link->parentNode->removeChild($link);
} }
foreach($querySel->query("//img") as $image) { foreach($querySel->query("//img") as $image) {
$imagePath = "$emailDir/assets/res/" . $image->getAttribute("src"); $imagePath = "$emailDir/assets/res/" . $image->getAttribute("src");
$type = pathinfo($imagePath, PATHINFO_EXTENSION); $type = pathinfo($imagePath, PATHINFO_EXTENSION);
@ -157,36 +157,36 @@ abstract class SimplePresenter implements IPresenter
$image->setAttribute("src", "data:image/$type;base64,$contents"); $image->setAttribute("src", "data:image/$type;base64,$contents");
} }
\Chandler\Email\Email::send($to, $subject, $document->saveHTML()); \Chandler\Email\Email::send($to, $subject, $document->saveHTML());
} }
protected function queryParam(string $index): ?string protected function queryParam(string $index): ?string
{ {
return $_GET[$index] ?? NULL; return $_GET[$index] ?? NULL;
} }
protected function postParam(string $index, bool $csrfCheck = true): ?string protected function postParam(string $index, bool $csrfCheck = true): ?string
{ {
if($csrfCheck) if($csrfCheck)
$this->assertNoCSRF(); $this->assertNoCSRF();
return $_POST[$index] ?? NULL; return $_POST[$index] ?? NULL;
} }
protected function requestParam(string $index): ?string protected function requestParam(string $index): ?string
{ {
return $_REQUEST[$index] ?? NULL; return $_REQUEST[$index] ?? NULL;
} }
protected function jsonParam(string $index): ?string protected function jsonParam(string $index): ?string
{ {
if(!isset($GLOBALS["jsonInputCache"])) if(!isset($GLOBALS["jsonInputCache"]))
$GLOBALS["jsonInputCache"] = json_decode($this->queryParam("js") ?? file_get_contents("php://input"), true); $GLOBALS["jsonInputCache"] = json_decode($this->queryParam("js") ?? file_get_contents("php://input"), true);
return $GLOBALS["jsonInputCache"][$index] ?? NULL; return $GLOBALS["jsonInputCache"][$index] ?? NULL;
} }
protected function findParam(string $index, array $searchIn = ["json", "post", "query"]): ?string protected function findParam(string $index, array $searchIn = ["json", "post", "query"]): ?string
{ {
$res = NULL; $res = NULL;
@ -198,38 +198,38 @@ abstract class SimplePresenter implements IPresenter
else if($search === "query") else if($search === "query")
$res = $this->queryParam($index) ?? $res; $res = $this->queryParam($index) ?? $res;
} }
return $res; return $res;
} }
protected function checkbox(string $name): bool protected function checkbox(string $name): bool
{ {
return ($this->postParam($name) ?? "off") === "on"; return ($this->postParam($name) ?? "off") === "on";
} }
function getTemplateScope(): array function getTemplateScope(): array
{ {
return (array) $this->template; return (array) $this->template;
} }
function onStartup(): void function onStartup(): void
{ {
date_default_timezone_set("UTC"); date_default_timezone_set("UTC");
} }
function onBeforeRender(): void function onBeforeRender(): void
{ {
$this->template->csrfToken = $GLOBALS["csrfToken"]; $this->template->csrfToken = $GLOBALS["csrfToken"];
} }
function onAfterRender(): void function onAfterRender(): void
{} {}
function onStop(): void function onStop(): void
{} {}
function onDestruction(): void function onDestruction(): void
{} {}
use SmartObject; use SmartObject;
} }

View file

@ -17,7 +17,7 @@ trait TSimpleSingleton
/** /**
* @return static * @return static
*/ */
public static function i(): self public static function getInstance(): self
{ {
if (is_null(static::$instance)) { if (is_null(static::$instance)) {
return static::$instance = new static(); return static::$instance = new static();

View file

@ -8,18 +8,18 @@ class Authenticator
{ {
private $db; private $db;
private $session; private $session;
private function __construct() private function __construct()
{ {
$this->db = DatabaseConnection::i()->getContext(); $this->db = DatabaseConnection::i()->getContext();
$this->session = Session::i(); $this->session = Session::getInstance();
} }
private function verifySuRights(string $uId): bool private function verifySuRights(string $uId): bool
{ {
} }
private function makeToken(string $user, string $ip, string $ua): string private function makeToken(string $user, string $ip, string $ua): string
{ {
$data = ["user" => $user, "ip" => $ip, "ua" => $ua]; $data = ["user" => $user, "ip" => $ip, "ua" => $ua];
@ -27,15 +27,15 @@ class Authenticator
->table("ChandlerTokens") ->table("ChandlerTokens")
->where($data) ->where($data)
->fetch(); ->fetch();
if(!$token) { if(!$token) {
$this->db->table("ChandlerTokens")->insert($data); $this->db->table("ChandlerTokens")->insert($data);
$token = $this->db->table("ChandlerTokens")->where($data)->fetch(); $token = $this->db->table("ChandlerTokens")->where($data)->fetch();
} }
return $token->token; return $token->token;
} }
static function verifyHash(string $input, string $hash): bool static function verifyHash(string $input, string $hash): bool
{ {
try { try {
@ -54,46 +54,46 @@ class Authenticator
} catch(\SodiumException $ex) { } catch(\SodiumException $ex) {
return false; return false;
} }
return true; return true;
} }
function getUser(): ?User function getUser(): ?User
{ {
$token = $this->session->get("tok"); $token = $this->session->get("tok");
if(!$token) return null; if(!$token) return null;
$token = $this->db $token = $this->db
->table("ChandlerTokens") ->table("ChandlerTokens")
->where([ ->where([
"token" => $token, "token" => $token,
]) ])
->fetch(); ->fetch();
if(!$token) return null; if(!$token) return null;
$checksPassed = false; $checksPassed = false;
if(CHANDLER_ROOT_CONF["security"]["extendedValidation"]) if(CHANDLER_ROOT_CONF["security"]["extendedValidation"])
$checksPassed = $token->ip === CONNECTING_IP && $token->ua === $_SERVER["HTTP_USER_AGENT"]; $checksPassed = $token->ip === CONNECTING_IP && $token->ua === $_SERVER["HTTP_USER_AGENT"];
else else
$checksPassed = true; $checksPassed = true;
if($checksPassed) { if($checksPassed) {
$su = $this->session->get("_su"); $su = $this->session->get("_su");
$user = $this->db->table("ChandlerUsers")->get($su ?? $token->user); $user = $this->db->table("ChandlerUsers")->get($su ?? $token->user);
if(!$user) return null; if(!$user) return null;
return new User($user, !is_null($su)); return new User($user, !is_null($su));
} }
return null; return null;
} }
function authenticate(string $user): void function authenticate(string $user): void
{ {
$this->session->set("tok", $this->makeToken($user, CONNECTING_IP, $_SERVER["HTTP_USER_AGENT"])); $this->session->set("tok", $this->makeToken($user, CONNECTING_IP, $_SERVER["HTTP_USER_AGENT"]));
} }
function verifyCredentials(string $id, string $password): bool function verifyCredentials(string $id, string $password): bool
{ {
$user = $this->db->table("ChandlerUsers")->get($id); $user = $this->db->table("ChandlerUsers")->get($id);
@ -101,30 +101,30 @@ class Authenticator
return false; return false;
else if(!$this->verifyHash($password, $user->passwordHash)) else if(!$this->verifyHash($password, $user->passwordHash))
return false; return false;
return true; return true;
} }
function login(string $id, string $password): bool function login(string $id, string $password): bool
{ {
if(!$this->verifyCredentials($id, $password)) if(!$this->verifyCredentials($id, $password))
return false; return false;
$this->authenticate($id); $this->authenticate($id);
return true; return true;
} }
function logout(bool $revoke = false): bool function logout(bool $revoke = false): bool
{ {
$token = $this->session->get("tok"); $token = $this->session->get("tok");
if(!$token) return false; if(!$token) return false;
if($revoke) $this->db->table("ChandlerTokens")->where("id", $token)->delete(); if($revoke) $this->db->table("ChandlerTokens")->where("id", $token)->delete();
$this->session->set("tok", NULL); $this->session->set("tok", NULL);
return true; return true;
} }
use TSimpleSingleton; use TSimpleSingleton;
} }

View file

@ -1,8 +1,11 @@
{ {
"autoload": { "autoload": {
"classmap": [ "classmap": [
"chandler/Eventing/Events/Event.php" "chandler/Patterns/TSimpleSingleton.php"
] ],
"psr-4": {
"Chandler\\": "src"
}
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
@ -14,6 +17,7 @@
"minimum-stability": "stable", "minimum-stability": "stable",
"name": "openvk/chanlder", "name": "openvk/chanlder",
"require": { "require": {
"ext-yaml": "*",
"php": "^7.3", "php": "^7.3",
"nette/utils": "^3.0", "nette/utils": "^3.0",
"nette/di": "^3.0", "nette/di": "^3.0",

View file

@ -14,10 +14,10 @@ server {
listen [::]:443 ssl http2; listen [::]:443 ssl http2;
server_name domain.tld; server_name domain.tld;
root /opt/chandler/htdocs; root /opt/chandler/public;
client_max_body_size 100m; client_max_body_size 100m;
index index.php; index index.php;
ssl_certificate /path/to/fullchain.pem; ssl_certificate /path/to/fullchain.pem;

View file

@ -3,6 +3,8 @@
<coverage cacheDirectory="tests/cache" processUncoveredFiles="true"> <coverage cacheDirectory="tests/cache" processUncoveredFiles="true">
<include> <include>
<file>chandler/Eventing/Events/Event.php</file> <file>chandler/Eventing/Events/Event.php</file>
<file>chandler/Eventing/EventDispatcher.php</file>
<file>chandler/Patterns/TSimpleSingleton.php</file>
</include> </include>
</coverage> </coverage>
<testsuites> <testsuites>

View file

@ -1,5 +1,9 @@
<?php <?php
declare(strict_types = 1); declare(strict_types = 1);
require_once(dirname(__DIR__) . "/vendor/autoload.php");
$bootstrap = require("../chandler/Bootstrap.php"); $bootstrap = require("../chandler/Bootstrap.php");
$bootstrap->ignite(); $bootstrap->ignite();

View file

@ -1,71 +0,0 @@
<?php
declare(strict_types = 1);
namespace Chandler\Tests\Chandler\Eventing;
use Chandler\Eventing\EventDispatcher;
use Chandler\Eventing\Events\Event;
use PHPUnit\Framework\TestCase;
/**
* @package Chandler\Tests\Chandler\Eventing
*/
class EventDispatcherTest extends TestCase
{
/**
* @return array
*/
public function provideMethodAddListener(): array // IMPROVE: Add more values.
{
return [
[
null,
],
];
}
/**
* @return array
*/
public function provideMethodPushEvent(): array
{
return [
[
new Event(),
],
];
}
/**
* @dataProvider provideMethodAddListener
*
* @param mixed $hook
*
* @return void
*/
public function testMethodAddListener($hook): void
{
$this->assertTrue(EventDispatcher::i()->addListener($hook));
}
/**
* @return void
*/
public function testMethodI(): void
{
$this->assertSame(EventDispatcher::i(), EventDispatcher::i());
}
/**
* @dataProvider provideMethodPushEvent
*
* @param \Chandler\Eventing\Events\Event $event
*
* @return void
*/
public function testMethodPushEvent(Event $event): void
{
$this->assertSame($event, EventDispatcher::i()->pushEvent($event));
}
}

View file

@ -1,46 +0,0 @@
<?php
declare(strict_types = 1);
namespace Chandler\Tests\Chandler\Eventing\Events;
use Chandler\Eventing\Events\Event;
use PHPUnit\Framework\TestCase;
/**
* @package Chandler\Tests\Chandler\Eventing\Events
*/
class EventTest extends TestCase
{
/**
* @return void
*/
public function testPropertyCode(): void
{
$this->assertClassHasAttribute("code", Event::class);
}
/**
* @return void
*/
public function testPropertyData(): void
{
$this->assertClassHasAttribute("data", Event::class);
}
/**
* @return void
*/
public function testPropertyPristine(): void
{
$this->assertClassHasAttribute("pristine", Event::class);
}
/**
* @return void
*/
public function testPropertyTime(): void
{
$this->assertClassHasAttribute("time", Event::class);
}
}