From f7fa12453509b522b2588aceb02131f9b3df6699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Gr=C3=B6nwoldt?= Date: Fri, 25 Sep 2020 21:33:54 +0200 Subject: [PATCH] WIP --- composer.json | 3 +- install/db.sql | 13 +++ public/admin/.htaccess | 7 -- public/admin/index.php | 24 ----- public/index.php | 6 +- public/theme/admin/css/test.css | 22 +++++ public/theme/admin/js/test.js | 0 src/Venom/Admin/AdminController.php | 35 ++++++++ src/Venom/Admin/AdminRouterInit.php | 23 +++++ src/Venom/Admin/Routes/LoginRoute.php | 15 ++++ src/Venom/Core/ArgumentHandler.php | 12 +++ src/Venom/Helper/URLHelper.php | 5 ++ src/Venom/Models/User.php | 123 ++++++++++++++++++++++++-- src/Venom/Routing/Router.php | 11 +++ src/Venom/Security/BaseLogin.php | 40 ++++++++- src/Venom/Security/Login.php | 4 - src/Venom/Security/Security.php | 42 ++++++++- src/Venom/Venom.php | 31 ++++++- src/Venom/Views/DataLoader.php | 8 +- src/Venom/Views/VenomRenderer.php | 12 ++- tpl/admin/base.php | 10 +++ 21 files changed, 388 insertions(+), 58 deletions(-) delete mode 100644 public/admin/.htaccess delete mode 100644 public/admin/index.php create mode 100644 public/theme/admin/css/test.css create mode 100644 public/theme/admin/js/test.js create mode 100644 src/Venom/Admin/AdminController.php create mode 100644 src/Venom/Admin/AdminRouterInit.php create mode 100644 src/Venom/Admin/Routes/LoginRoute.php create mode 100644 tpl/admin/base.php diff --git a/composer.json b/composer.json index 8e4f0cb..62992e9 100755 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ } ], "require": { - "ext-pdo": "*" + "ext-pdo": "*", + "ext-http": "*" }, "autoload": { "psr-4": { diff --git a/install/db.sql b/install/db.sql index 3e89174..3dfd064 100755 --- a/install/db.sql +++ b/install/db.sql @@ -27,3 +27,16 @@ create table if not exists data datatype enum ('content', 'form') ) comment 'DataLoader File'; + +create table if not exists users +( + id int(255) auto_increment not null unique primary key, + username varchar(255) not null unique, + email varchar(255) not null, + password varchar(255) not null, + token varchar(255) not null, + salt varchar(255) not null, + roles text default 'ROLE_GUEST' not null, + isActive tinyint(1) default 1 null +) + comment 'User File'; \ No newline at end of file diff --git a/public/admin/.htaccess b/public/admin/.htaccess deleted file mode 100644 index 4d339de..0000000 --- a/public/admin/.htaccess +++ /dev/null @@ -1,7 +0,0 @@ -RewriteEngine On - -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_FILENAME} !-d - -RewriteCond %{REQUEST_URI} (/[^.]*|\.)$ [NC] -RewriteRule .* index.php [L] \ No newline at end of file diff --git a/public/admin/index.php b/public/admin/index.php deleted file mode 100644 index 7ece959..0000000 --- a/public/admin/index.php +++ /dev/null @@ -1,24 +0,0 @@ -isMaintenance()) { - echo 'Currently not available'; - exit; -} -//if devMode is on show all errors! -if ($config->isDevMode()) { - error_reporting(E_ALL); - ini_set('error_reporting', E_ALL); -} -$venom = new Venom(); -Setup::loadRouters($venom); -Setup::loadModules($venom); -$venom->run(); \ No newline at end of file diff --git a/public/index.php b/public/index.php index e1d99e3..046cb5a 100755 --- a/public/index.php +++ b/public/index.php @@ -2,10 +2,12 @@ use Venom\Core\Config; use Venom\Core\Setup; +use Venom\Helper\URLHelper; use Venom\Venom; require_once '../vendor/autoload.php'; -Setup::loadConfig(false); +session_start(); +Setup::loadConfig(URLHelper::getInstance()->isAdminUrl()); Setup::loadLanguage(); $config = Config::getInstance(); @@ -21,4 +23,4 @@ if ($config->isDevMode()) { $venom = new Venom(); Setup::loadRouters($venom); Setup::loadModules($venom); -$venom->run(); \ No newline at end of file +$venom->inject(); \ No newline at end of file diff --git a/public/theme/admin/css/test.css b/public/theme/admin/css/test.css new file mode 100644 index 0000000..cfed392 --- /dev/null +++ b/public/theme/admin/css/test.css @@ -0,0 +1,22 @@ +* { + box-sizing: border-box; +} + +html, body { + margin: 0; + font-size: 16px; + color: #fff; + background-color: #333; + font-family: sans-serif; + height: 100vh; + width: 100vw; +} + +header { + font-size: 5vw; + cursor: pointer; +} + +header:hover { + color: #ff0323; +} \ No newline at end of file diff --git a/public/theme/admin/js/test.js b/public/theme/admin/js/test.js new file mode 100644 index 0000000..e69de29 diff --git a/src/Venom/Admin/AdminController.php b/src/Venom/Admin/AdminController.php new file mode 100644 index 0000000..60ae195 --- /dev/null +++ b/src/Venom/Admin/AdminController.php @@ -0,0 +1,35 @@ +getUrl() !== '/admin/') { + http_response_code(404); + $this->tpl = 'async'; + } + return true; + } + + public function getTemplate(): string + { + return $this->tpl; + } +} \ No newline at end of file diff --git a/src/Venom/Admin/AdminRouterInit.php b/src/Venom/Admin/AdminRouterInit.php new file mode 100644 index 0000000..3443d10 --- /dev/null +++ b/src/Venom/Admin/AdminRouterInit.php @@ -0,0 +1,23 @@ +addRoutes(self::getRoutes()); + $venom->addRouter(Router::ADMIN_ROUTER, $router); + } + + public static function getRoutes(): array + { + return []; + } +} \ No newline at end of file diff --git a/src/Venom/Admin/Routes/LoginRoute.php b/src/Venom/Admin/Routes/LoginRoute.php new file mode 100644 index 0000000..c706c65 --- /dev/null +++ b/src/Venom/Admin/Routes/LoginRoute.php @@ -0,0 +1,15 @@ + $item) { $this->arguments[htmlspecialchars($key)] = htmlspecialchars($item); + $this->post[htmlspecialchars($key)] = htmlspecialchars($item); } } @@ -41,4 +43,14 @@ class ArgumentHandler { return isset($this->arguments[$key]); } + + public function getPostItem(string $key, $default = null) + { + return $this->post[$key] ?? $default; + } + + public function hasPostItem(string $key): bool + { + return isset($this->post[$key]); + } } \ No newline at end of file diff --git a/src/Venom/Helper/URLHelper.php b/src/Venom/Helper/URLHelper.php index 534632b..9891eaa 100644 --- a/src/Venom/Helper/URLHelper.php +++ b/src/Venom/Helper/URLHelper.php @@ -42,4 +42,9 @@ class URLHelper { return $url; } + + public function isAdminUrl(): bool + { + return strpos($this->parsedUrl, '/admin/') === 0; + } } \ No newline at end of file diff --git a/src/Venom/Models/User.php b/src/Venom/Models/User.php index 43f98d8..7f0b93e 100644 --- a/src/Venom/Models/User.php +++ b/src/Venom/Models/User.php @@ -4,11 +4,124 @@ namespace Venom\Models; +use Venom\Core\DatabaseHandler; + class User { - private string $username; - private string $password; - private string $salt; - private string $token; - private array $roles; + public const ADMIN_ROLE = 'ROLE_ADMIN'; + public const GUEST_ROLE = 'ROLE_GUEST'; + private string $username = 'GUEST'; + private string $email = 'GUEST'; + private string $password = '---'; + private string $salt = '---'; + private string $token = '---'; + private string $id = '-1'; + private array $roles = []; + private bool $isLoaded = false; + + public function hasRole(string $role): bool + { + return in_array($role, $this->roles, true); + } + + public function loadUser(): bool + { + if (isset($_SESSION['userID'])) { + // try to load user from id! + $user = DatabaseHandler::get()->getOne("SELECT * FROM users WHERE id = :id OR username = :name AND isActive = 1", [ + ':id' => $_SESSION['userID'], + ':name' => $this->username + ]); + if ($user) { + $this->username = $user->username ?? ''; + $this->email = $user->email ?? ''; + $this->password = $user->password ?? ''; + $this->token = $user->token ?? ''; + $this->salt = $user->salt ?? ''; + $this->id = $user->id ?? '-1'; + $this->roles = explode(',', $user->roles ?? ''); + $this->isLoaded = true; + return true; + } + } + return false; + } + + public function getUsername(): string + { + return $this->username; + } + + public function setUsername(string $username): void + { + $this->username = $username; + } + + public function getEmail(): string + { + return $this->email; + } + + public function setEmail(string $email): void + { + $this->email = $email; + } + + public function getPassword(): string + { + return $this->password; + } + + public function setPassword(string $password): void + { + $this->password = $password; + } + + public function getSalt(): string + { + return $this->salt; + } + + public function setSalt(string $salt): void + { + $this->salt = $salt; + } + + public function getToken(): string + { + return $this->token; + } + + public function setToken(string $token): void + { + $this->token = $token; + } + + public function getRoles(): array + { + return $this->roles; + } + + public function setRoles(array $roles): void + { + $this->roles = $roles; + } + + public function addRole($value): void + { + if (!in_array($value, $this->roles, true)) { + $this->roles[] = $value; + } + } + + public function isLoaded(): bool + { + return $this->isLoaded; + } + + public function getId(): string + { + return $this->id; + } + } \ No newline at end of file diff --git a/src/Venom/Routing/Router.php b/src/Venom/Routing/Router.php index 091d853..af980ea 100644 --- a/src/Venom/Routing/Router.php +++ b/src/Venom/Routing/Router.php @@ -5,9 +5,14 @@ namespace Venom\Routing; use Exception; +use Venom\Core\Config; +use Venom\Models\User; +use Venom\Security\Security; class Router { + public const DEFAULT_ROUTER = 'defaultRouter'; + public const ADMIN_ROUTER = 'adminRouter'; protected string $id = 'defaultRouter'; protected int $version; protected string $prefix = ''; @@ -66,6 +71,12 @@ class Router $subRouteFound = isset($this->routes[$url]['routes'][$subRoute]); $methodFound = isset($this->routes[$url]['routes'][$subRoute][$method]); if ($routeAvailable && $subRouteFound && $methodFound) { + if ((Config::getInstance()->getSecurity()->useSecurity || $this->id === self::ADMIN_ROUTER) && isset($this->routes[$url]['roles'])) { + $roles = $this->routes[$url]['roles']; + if (!in_array(User::GUEST_ROLE, $roles, true) && !Security::get()->hasRoles($roles)) { + return null; + } + } return [ 'cl' => $this->routes[$url]['cl'], 'fnc' => $this->routes[$url]['routes'][$subRoute][$method], diff --git a/src/Venom/Security/BaseLogin.php b/src/Venom/Security/BaseLogin.php index 1a94a5c..6fc404b 100644 --- a/src/Venom/Security/BaseLogin.php +++ b/src/Venom/Security/BaseLogin.php @@ -4,10 +4,48 @@ namespace Venom\Security; +use Venom\Core\ArgumentHandler; +use Venom\Core\Config; +use Venom\Helper\URLHelper; +use Venom\Models\User; + /** * Class that Login stupid via Password, Username */ -class BaseLogin +class BaseLogin implements Login { + private User $user; + + public function __construct(User $user) + { + $this->user = $user; + } + + public function checkCredentials(): bool + { + $handler = ArgumentHandler::get(); + return $handler->hasPostItem('USERNAME') && $handler->hasPostItem('PASSWORD'); + } + + public function redirect(): void + { + http_redirect(URLHelper::getInstance()->getUrl(), ['redirect' => 'true'], true); + } + + public function login(): bool + { + $sec = Config::getInstance()->getSecurity(); + $this->user->setUsername(ArgumentHandler::get()->getPostItem('USERNAME')); + if (!$this->user->loadUser()) { + return false; + } + $secret = $sec->secret ?? 'venom'; + $hashed = hash($sec->algo ?? 'SHA256', ArgumentHandler::get()->getPostItem('PASSWORD') . $secret . $this->user->getSalt()); + if ($this->user->getPassword() === $hashed) { + $_SESSION['userID'] = $this->user->getId(); + return true; + } + return false; + } } \ No newline at end of file diff --git a/src/Venom/Security/Login.php b/src/Venom/Security/Login.php index 853cffd..51fbd3e 100644 --- a/src/Venom/Security/Login.php +++ b/src/Venom/Security/Login.php @@ -10,10 +10,6 @@ interface Login { public function __construct(User $user); - public function checkUsername(): bool; - - public function checkPassword(): bool; - public function checkCredentials(): bool; public function login(): bool; diff --git a/src/Venom/Security/Security.php b/src/Venom/Security/Security.php index 1585dbd..3d2b971 100644 --- a/src/Venom/Security/Security.php +++ b/src/Venom/Security/Security.php @@ -3,10 +3,20 @@ namespace Venom\Security; +use http\Exception\RuntimeException; +use Venom\Core\Config; +use Venom\Models\User; class Security { private static ?Security $instance = null; + private ?User $user; + + public function __construct() + { + $this->user = new User(); + $this->user->loadData(); + } public static function get(): Security { @@ -16,15 +26,39 @@ class Security return self::$instance; } - /* @todo implement logic */ public function hasRole(string $role): bool { + return $this->user->hasRole($role); + } + + public function hasRoles(array $roles): bool + { + foreach ($roles as $role) { + if (!$this->user->hasRole($role)) { + return false; + } + } return true; } - /* @todo implement logic */ - public function hasRoles(array $roles): bool + public function login(): void { - return true; + if (!$this->user->isLoaded()) { + throw new RuntimeException('Try to re-login!'); + } + $sec = Config::getInstance()->getSecurity(); + $login = new $sec->securityClass; + if ($login instanceof Login) { + if (!$login->checkCredentials() || !$login->login()) { + http_response_code(401); + } + $login->redirect(); + } + } + + public function logout(): void + { + unset($_SESSION['userID']); + $this->user = new User(); } } diff --git a/src/Venom/Venom.php b/src/Venom/Venom.php index d27b073..9dfaa06 100755 --- a/src/Venom/Venom.php +++ b/src/Venom/Venom.php @@ -4,6 +4,8 @@ namespace Venom; +use Venom\Admin\AdminController; +use Venom\Admin\AdminRouterInit; use Venom\Core\ArgumentHandler; use Venom\Core\Config; use Venom\Core\Module; @@ -30,13 +32,22 @@ class Venom Asset::get()->setRenderer($this->renderer); } + public function inject(): void + { + $this->run(); + } + public function run(): void { $arguments = ArgumentHandler::get(); $arguments->setItem(ErrorHandler::ERROR_KEY, false); + $config = Config::getInstance(); + if ($config->isAdmin()) { + $this->initAdmin(); + } // we need to load the current controller and the current start template. // after this we can start the renderer - if (Config::getInstance()->isRouterEnabled()) { + if ($config->isRouterEnabled() || $config->isAdmin()) { $status = $this->useRouter(); if ($status['found']) { if ($status['status']) { @@ -48,18 +59,32 @@ class Venom $registry = Registry::getInstance(); $registry->getLang()->initLang(); // if site is errored then dont load via SEO - if (!$arguments->getItem(ErrorHandler::ERROR_KEY)) { + if (!$config->isAdmin() && !$arguments->getItem(ErrorHandler::ERROR_KEY)) { $registry->getSeo()->loadSite(); } $this->renderer->init($this->findController()); $this->renderer->render(); } + public function initAdmin(): void + { + $this->controllers['adminCtrl'] = AdminController::class; + AdminRouterInit::registerAdminRouters($this); + ArgumentHandler::get()->setItem('cl', 'adminCtrl'); + } + private function useRouter(): array { $url = URLHelper::getInstance()->getUrl(); + $isAdmin = Config::getInstance()->isAdmin(); /** @var Router $router */ - foreach ($this->routers as $router) { + foreach ($this->routers as $key => $router) { + if ($isAdmin && $key !== Router::ADMIN_ROUTER) { + continue; + } + if (!$isAdmin && $key === Router::ADMIN_ROUTER) { + continue; + } $route = $router->findRoute($url, $_SERVER['REQUEST_METHOD']); $status = $router->tryFunctionCall($route); if ($route !== null) { diff --git a/src/Venom/Views/DataLoader.php b/src/Venom/Views/DataLoader.php index 90405b4..aa49d9f 100755 --- a/src/Venom/Views/DataLoader.php +++ b/src/Venom/Views/DataLoader.php @@ -3,7 +3,7 @@ namespace Venom\Views; -use RuntimeException; +use \RuntimeException; use Venom\Core\DatabaseHandler; use Venom\Models\DataModel; @@ -87,8 +87,8 @@ class DataLoader private function generateID(string $id = ''): string { if ($id === '') { - $id = bin2hex(random_bytes(16)); + $id = bin2hex(random_bytes(32)); } - return $id; + return hash('SHA256', $id); } -} +} \ No newline at end of file diff --git a/src/Venom/Views/VenomRenderer.php b/src/Venom/Views/VenomRenderer.php index 5e9c51e..b8bad47 100755 --- a/src/Venom/Views/VenomRenderer.php +++ b/src/Venom/Views/VenomRenderer.php @@ -84,7 +84,7 @@ class VenomRenderer return $this->vars[$name]; } - public function deleteVar($name) + public function deleteVar($name): void { unset($this->vars[$name]); } @@ -93,7 +93,13 @@ class VenomRenderer { $this->controller = $controller; $data = Config::getInstance()->getRenderer(); - $this->baseTemplate = $data->baseFile . '.php' ?? 'base.php'; - $this->templateDir = __DIR__ . '/../../../tpl/' . $data->theme . '/'; + $theme = $data->theme; + $base = $data->baseFile ?? 'base'; + if (Config::getInstance()->isAdmin()) { + $base = 'base'; + $theme = 'admin'; + } + $this->baseTemplate = $base . '.php'; + $this->templateDir = __DIR__ . '/../../../tpl/' . $theme . '/'; } } diff --git a/tpl/admin/base.php b/tpl/admin/base.php new file mode 100644 index 0000000..626f2cf --- /dev/null +++ b/tpl/admin/base.php @@ -0,0 +1,10 @@ +hasRole(User::ADMIN_ROLE)) { + echo 'Login!'; +} else { + echo 'Admin Interface!'; +}