diff --git a/.env.develop b/.env.develop index 968fd4b..e6cfe04 100644 --- a/.env.develop +++ b/.env.develop @@ -1,23 +1,7 @@ # .env.development: Environment variables for local development -# composer: Set cache directory +# Set composer cache directory COMPOSER_CACHE_DIR=./.composer -# MariaDB container -MYSQL_RANDOM_ROOT_PASSWORD=yes -MYSQL_DATABASE=mailusers -MYSQL_USER=mailaccountadmin -MYSQL_PASSWORD=mailaccountadmin - -# App settings -APP_DEBUG=true - -# - Disable Twig cache +# Disable Twig cache TWIG_CACHE_DIR= -TWIG_STRICT=true - -# - Database credentials -DB_HOST=db -DB_DATABASE=mailusers -DB_USER=mailaccountadmin -DB_PASSWORD=mailaccountadmin diff --git a/docker-compose.yml b/docker-compose.yml index f742930..f91cc48 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,21 +12,3 @@ services: volumes: - ./:/var/www/ user: ${DOCKER_UID} - - db: - image: mariadb - env_file: - - .env.develop - ports: - - 13306:3306 - volumes: - - db_data:/var/lib/mysql - - ./sql/init_tables.sql:/docker-entrypoint-initdb.d/init_tables.sql:ro - - adminer: - image: adminer - ports: - - 8099:8080 - -volumes: - db_data: diff --git a/public/index.php b/public/index.php index 37641db..34eb697 100644 --- a/public/index.php +++ b/public/index.php @@ -9,13 +9,11 @@ use MailAccountAdmin\Routes; use MailAccountAdmin\Settings; use Slim\Factory\AppFactory; -session_start(); - $settings = new Settings(); $container = Dependencies::createContainer($settings); $app = AppFactory::createFromContainer($container); -Middlewares::setMiddlewares($app, $settings); +Middlewares::setMiddlewares($app); Routes::setRoutes($app); $app->run(); diff --git a/public/static/style.css b/public/static/style.css deleted file mode 100644 index 6db3384..0000000 --- a/public/static/style.css +++ /dev/null @@ -1,50 +0,0 @@ -html, body { - max-width: 100%; - margin: 0; - padding: 0; -} - -* { - box-sizing: border-box; -} - -body { - font-family: sans-serif; -} - -/* --- Header --- */ -header { - margin: 1em; -} - -header h1 { - margin: 1em; -} - -/* --- Login page --- */ -main.login_page { - margin: 2em; - padding: 1em; - border: 1px gray solid; - width: auto; -} - -main.login_page h2 { - margin: 0 0 0.5em 0; -} - -main.login_page table td { - padding: 0.2em; -} - -/* --- Text and other styling --- */ -.error { - background: #ff4444; - width: 30em; - margin: 1em 0; - padding: 1em; -} - -button { - padding: 0.2em 1em; -} diff --git a/sql/init_tables.sql b/sql/init_tables.sql deleted file mode 100644 index f45577d..0000000 --- a/sql/init_tables.sql +++ /dev/null @@ -1,50 +0,0 @@ --- Create tables - -SET NAMES utf8mb4; - --- TODO create on prod -CREATE TABLE `admin_users` -( - `admin_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `username` varchar(255) NOT NULL, - `password` varchar(255) NOT NULL, - `is_active` tinyint(3) unsigned NOT NULL DEFAULT 1, - `created_at` datetime NOT NULL DEFAULT NOW(), - `modified_at` datetime NOT NULL DEFAULT NOW(), - PRIMARY KEY (`admin_id`), - UNIQUE KEY `username` (`username`) -) DEFAULT CHARSET = utf8mb4; - --- Create initial admin user for development (password is 'admin') -INSERT INTO `admin_users` - (`username`, `password`, `is_active`, `created_at`, `modified_at`) -VALUES ('admin', '$2y$10$zaNOBUk4PBlhDZD40h35CeyUxiqixi9LTrxlAxnrckXd95hcCctl6', '1', NOW(), NOW()); - --- TODO rename on prod `users` -> `mail_users` -CREATE TABLE `mail_users` -( - `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `username` varchar(255) NOT NULL, - `password` varchar(255) NOT NULL, - `is_active` tinyint(3) unsigned NOT NULL DEFAULT 1, - `home_dir` varchar(255) NOT NULL, - `memo` text DEFAULT NULL, - `created_at` datetime NOT NULL DEFAULT NOW(), - `modified_at` datetime NOT NULL DEFAULT NOW(), - PRIMARY KEY (`user_id`), - UNIQUE KEY `username` (`username`) -) DEFAULT CHARSET = utf8mb4; - -CREATE TABLE `mail_aliases` -( - `alias_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `user_id` int(10) unsigned NOT NULL, - `mail_address` varchar(255) NOT NULL, - `created_at` datetime NOT NULL DEFAULT NOW(), - `modified_at` datetime NOT NULL DEFAULT NOW(), - PRIMARY KEY (`alias_id`), - UNIQUE KEY `mail_address` (`mail_address`), - KEY `user_id` (`user_id`), - -- TODO rename on prod `users` -> `mail_users` - CONSTRAINT `mail_aliases_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `mail_users` (`user_id`) -) DEFAULT CHARSET = utf8mb4; diff --git a/src/Auth/AuthMiddleware.php b/src/Auth/AuthMiddleware.php deleted file mode 100644 index 6738f6e..0000000 --- a/src/Auth/AuthMiddleware.php +++ /dev/null @@ -1,36 +0,0 @@ -getUri(); - - // TODO: Lots of stuff. Session middleware, auth handler class, etc... - if ($uri->getPath() !== '/login') { - // Check authorization via session - // TODO username or user ID? - if (empty($_SESSION['username'])) { - // Not logged in -> Redirect to /login - $response = new Response(); - return $response - ->withHeader('Location', '/login') - ->withStatus(303); - } - } - - return $handler->handle($request); - } -} diff --git a/src/Common/UserHelper.php b/src/Common/UserHelper.php deleted file mode 100644 index e67f56b..0000000 --- a/src/Common/UserHelper.php +++ /dev/null @@ -1,33 +0,0 @@ -adminUserRepository = $adminUserRepository; - } - - public function isLoggedIn(): bool - { - return !empty($_SESSION['username']); - } - - public function getCurrentUser(): AdminUser - { - $username = $_SESSION['username'] ?? null; - if (empty($username)) { - throw new RuntimeException('Not logged in!'); - } - return $this->adminUserRepository->getUserByName($username); - } -} diff --git a/src/Dependencies.php b/src/Dependencies.php index 9f2e901..d82b70a 100644 --- a/src/Dependencies.php +++ b/src/Dependencies.php @@ -4,20 +4,15 @@ declare(strict_types=1); namespace MailAccountAdmin; use DI\Container; -use MailAccountAdmin\Common\UserHelper; -use MailAccountAdmin\Frontend\Login\LoginController; -use MailAccountAdmin\Frontend\Dashboard\DashboardController; -use MailAccountAdmin\Repositories\AdminUserRepository; -use PDO; +use MailAccountAdmin\Login\LoginController; use Psr\Container\ContainerInterface; use Slim\Views\Twig; class Dependencies { - public const SETTINGS = 'settings'; - public const TWIG = 'view'; + private const SETTINGS = 'settings'; + private const TWIG = 'view'; private const TWIG_TEMPLATE_DIR = __DIR__ . '/../templates'; - public const DATABASE = 'database'; public static function createContainer(Settings $settings): Container { @@ -34,50 +29,10 @@ class Dependencies return Twig::create(self::TWIG_TEMPLATE_DIR, $settings->getTwigSettings()); }); - // Database connection - $container->set(self::DATABASE, function (ContainerInterface $c) { - /** @var Settings $settings */ - $settings = $c->get(self::SETTINGS); - $dbSettings = $settings->getDatabaseSettings(); - - return new PDO( - "mysql:dbname={$dbSettings['dbname']};host={$dbSettings['host']};port={$dbSettings['port']}", - $dbSettings['username'], - $dbSettings['password'], - [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ], - ); - }); - - // Repositories - $container->set(AdminUserRepository::class, function (ContainerInterface $c) { - return new AdminUserRepository( - $c->get(self::DATABASE), - ); - }); - - // Helper classes - $container->set(UserHelper::class, function (ContainerInterface $c) { - return new UserHelper( - $c->get(AdminUserRepository::class), - ); - }); - - // Login page + // Login, registration, authentication $container->set(LoginController::class, function (ContainerInterface $c) { return new LoginController( - $c->get(self::TWIG), - $c->get(UserHelper::class), - $c->get(AdminUserRepository::class), - ); - }); - - // Dashboard - $container->set(DashboardController::class, function (ContainerInterface $c) { - return new DashboardController( - $c->get(self::TWIG), - $c->get(UserHelper::class), + $c->get(self::TWIG) ); }); diff --git a/src/Exceptions/AdminUserNotFoundException.php b/src/Exceptions/AdminUserNotFoundException.php deleted file mode 100644 index 413b5ff..0000000 --- a/src/Exceptions/AdminUserNotFoundException.php +++ /dev/null @@ -1,8 +0,0 @@ -view = $view; - $this->userHelper = $userHelper; - } -} diff --git a/src/Frontend/Dashboard/DashboardController.php b/src/Frontend/Dashboard/DashboardController.php deleted file mode 100644 index 7538686..0000000 --- a/src/Frontend/Dashboard/DashboardController.php +++ /dev/null @@ -1,23 +0,0 @@ -userHelper->getCurrentUser(); - - $renderData = [ - 'username' => $currentUser->getUsername(), - 'user' => $currentUser, - ]; - - return $this->view->render($response, 'dashboard.html.twig', $renderData); - } -} diff --git a/src/Frontend/Login/LoginController.php b/src/Frontend/Login/LoginController.php deleted file mode 100644 index f486c44..0000000 --- a/src/Frontend/Login/LoginController.php +++ /dev/null @@ -1,80 +0,0 @@ -adminUserRepository = $adminUserRepository; - } - - private function renderLoginPage(Response $response, array $renderData = []): Response - { - return $this->view->render($response, 'login.html.twig', $renderData); - } - - public function showLoginPage(Request $request, Response $response): Response - { - if ($this->userHelper->isLoggedIn()) { - // Already logged in, redirect to dashboard - return $response - ->withHeader('Location', '/') - ->withStatus(303); - } - - return $this->renderLoginPage($response); - } - - public function authenticateUser(Request $request, Response $response): Response - { - $params = (array)$request->getParsedBody(); - - if (empty($params['username'])) { - return $this->renderLoginPage($response, ['error' => 'Missing username!']); - } elseif (empty($params['password'])) { - return $this->renderLoginPage($response, ['error' => 'Missing password!']); - } - - $loginUsername = $params['username']; - $loginPassword = $params['password']; - - try { - $user = $this->adminUserRepository->getUserByName($loginUsername); - } - catch (AdminUserNotFoundException $e) { - $user = null; - } - - if ($user !== null && password_verify($loginPassword, $user->getPasswordHash())) { - $_SESSION['username'] = $user->getUsername(); - return $response - ->withHeader('Location', '/') - ->withStatus(303); - } else { - return $this->renderLoginPage($response, ['error' => 'Wrong username or password!']); - } - } - - public function logoutUser(Request $request, Response $response): Response - { - session_destroy(); - - return $response - ->withHeader('Location', '/login') - ->withStatus(303); - } -} diff --git a/src/Login/LoginController.php b/src/Login/LoginController.php new file mode 100644 index 0000000..c0bc80c --- /dev/null +++ b/src/Login/LoginController.php @@ -0,0 +1,27 @@ +view = $view; + } + + public function showLoginPage(Request $request, Response $response): Response + { + $renderData = [ + ]; + + return $this->view->render($response, 'login.html', $renderData); + } +} diff --git a/src/Middlewares.php b/src/Middlewares.php index 375e481..cbadfc7 100644 --- a/src/Middlewares.php +++ b/src/Middlewares.php @@ -3,18 +3,13 @@ declare(strict_types=1); namespace MailAccountAdmin; -use MailAccountAdmin\Auth\AuthMiddleware; use Slim\App; use Slim\Views\TwigMiddleware; class Middlewares { - public static function setMiddlewares(App $app, Settings $settings): void + public static function setMiddlewares(App $app): void { - $displayErrorDetails = $settings->isDebugMode(); - - $app->addErrorMiddleware($displayErrorDetails, true, true); - $app->add(new AuthMiddleware()); $app->add(TwigMiddleware::createFromContainer($app)); } } diff --git a/src/Models/AdminUser.php b/src/Models/AdminUser.php deleted file mode 100644 index cef9c7b..0000000 --- a/src/Models/AdminUser.php +++ /dev/null @@ -1,75 +0,0 @@ -id = $id; - $this->username = $username; - $this->passwordHash = $passwordHash; - $this->active = $isActive; - $this->createdAt = $createdAt; - $this->modifiedAt = $modifiedAt; - } - - public static function createFromArray(array $data): self - { - return new self( - (int)$data['admin_id'], - $data['username'], - $data['password'], - $data['is_active'] === '1', - new DateTimeImmutable($data['created_at']), - new DateTimeImmutable($data['modified_at']), - ); - } - - public function getId(): int - { - return $this->id; - } - - public function getUsername(): string - { - return $this->username; - } - - public function getPasswordHash(): string - { - return $this->passwordHash; - } - - public function isActive(): bool - { - return $this->active; - } - - public function getCreatedAt(): DateTimeImmutable - { - return $this->createdAt; - } - - public function getModifiedAt(): DateTimeImmutable - { - return $this->modifiedAt; - } -} diff --git a/src/Repositories/AdminUserRepository.php b/src/Repositories/AdminUserRepository.php deleted file mode 100644 index 31e2a41..0000000 --- a/src/Repositories/AdminUserRepository.php +++ /dev/null @@ -1,35 +0,0 @@ -pdo = $pdo; - } - - /** - * @throws AdminUserNotFoundException - */ - public function getUserByName(string $username): AdminUser - { - $statement = $this->pdo->prepare('SELECT * FROM admin_users WHERE username = :username LIMIT 1'); - $statement->execute(['username' => $username]); - - if ($statement->rowCount() < 1) { - throw new AdminUserNotFoundException("Admin with username '$username' was not found."); - } - - $row = $statement->fetch(PDO::FETCH_ASSOC); - return AdminUser::createFromArray($row); - } -} diff --git a/src/Routes.php b/src/Routes.php index a90372f..9c7520a 100644 --- a/src/Routes.php +++ b/src/Routes.php @@ -3,20 +3,13 @@ declare(strict_types=1); namespace MailAccountAdmin; -use MailAccountAdmin\Frontend\Dashboard\DashboardController; -use MailAccountAdmin\Frontend\Login\LoginController; +use MailAccountAdmin\Login\LoginController; use Slim\App; class Routes { public static function setRoutes(App $app): void { - // Login - $app->get('/login', LoginController::class . ':showLoginPage'); - $app->post('/login', LoginController::class . ':authenticateUser'); - $app->get('/logout', LoginController::class . ':logoutUser'); - - // Dashboard - $app->get('/', DashboardController::class . ':showDashboard'); + $app->get('/', LoginController::class . ':showLoginPage'); } } diff --git a/src/Settings.php b/src/Settings.php index ca111a0..b36160f 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -5,27 +5,12 @@ namespace MailAccountAdmin; class Settings { - public function isDebugMode(): bool - { - return getenv('APP_DEBUG') === 'true'; - } - public function getTwigSettings(): array { - return [ - 'cache' => getenv('TWIG_CACHE_DIR') ?: false, - 'strict_variables' => getenv('TWIG_STRICT') === 'true', - ]; - } + $cacheDir = getenv('TWIG_CACHE_DIR'); - public function getDatabaseSettings(): array - { return [ - 'host' => getenv('DB_HOST') ?: 'localhost', - 'port' => getenv('DB_PORT') ?: 3306, - 'dbname' => getenv('DB_DATABASE') ?: '', - 'username' => getenv('DB_USER') ?: '', - 'password' => getenv('DB_PASSWORD') ?: '', + 'cache' => !empty($cacheDir) ? $cacheDir : false, ]; } } diff --git a/templates/base.html.twig b/templates/base.html.twig deleted file mode 100644 index 916fab1..0000000 --- a/templates/base.html.twig +++ /dev/null @@ -1,23 +0,0 @@ - - -
- -Hello, {{ username }}!
- -
- ID: {{ user.getId() }}
- username: {{ user.getUsername() }}
- password: {{ user.getPasswordHash() }}
- is_active: {{ user.isActive() }}
- created_at: {{ user.getCreatedAt() | date() }}
- modified_at: {{ user.getModifiedAt() | date() }}
-
-
- Logout.
-{% endblock %}
diff --git a/templates/login.html b/templates/login.html
new file mode 100644
index 0000000..e46301d
--- /dev/null
+++ b/templates/login.html
@@ -0,0 +1,19 @@
+
+
+
+
+