✨ admin area login and auth logic (#45)
This commit is contained in:
parent
e09c1bc410
commit
cd4f2b10b2
@ -24,5 +24,18 @@
|
|||||||
// (ssl/tls will be used otherwise)
|
// (ssl/tls will be used otherwise)
|
||||||
"starttls" => true
|
"starttls" => true
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for admin area.
|
||||||
|
*/
|
||||||
|
const ADMIN_AREA = [
|
||||||
|
// AUTHENTICATION //
|
||||||
|
// hashed auth token used for login
|
||||||
|
// generate using `php -r 'echo(password_hash("yourtokenhere", PASSWORD_DEFAULT));'`
|
||||||
|
// token should have at least 32 characters
|
||||||
|
"auth_token_hash" => '<hashed token>'
|
||||||
|
];
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
4
meta.php
4
meta.php
@ -16,6 +16,7 @@
|
|||||||
static::$ext[] = "error";
|
static::$ext[] = "error";
|
||||||
static::$ext[] = "url";
|
static::$ext[] = "url";
|
||||||
static::$ext[] = "id64";
|
static::$ext[] = "id64";
|
||||||
|
static::$ext[] = "cookieaccept";
|
||||||
|
|
||||||
|
|
||||||
// ROUTES //
|
// ROUTES //
|
||||||
@ -37,5 +38,8 @@
|
|||||||
|
|
||||||
["path" => "api/newsletter/subscribe", "target" => "api/newsletter/subscribe.php"],
|
["path" => "api/newsletter/subscribe", "target" => "api/newsletter/subscribe.php"],
|
||||||
["path" => "api/newsletter/unsubscribe", "target" => "api/newsletter/unsubscribe.php"],
|
["path" => "api/newsletter/unsubscribe", "target" => "api/newsletter/unsubscribe.php"],
|
||||||
|
|
||||||
|
["path" => "admin", "target" => "page/admin/start"],
|
||||||
|
["path" => "admin/login", "target" => "page/admin/login"],
|
||||||
];
|
];
|
||||||
?>
|
?>
|
||||||
|
25
page/admin/auth_handler.php
Normal file
25
page/admin/auth_handler.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
namespace Kimendisch\Sbgg_Jetzt;
|
||||||
|
use Flake\Url_Redirect;
|
||||||
|
use Flake\Request;
|
||||||
|
|
||||||
|
// CHECK AUTHENTICATION //
|
||||||
|
// redirect to login page when not logged in
|
||||||
|
$login = $_SESSION[__NAMESPACE__]["admin"]["login"] ?? null;
|
||||||
|
if($login !== true){
|
||||||
|
Url_Redirect::location("http" . (Request::has_ssl() ? "s" : "") . "://" . Request::domain_raw_full() . "/admin/login");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MAYBE DO LOGOUT //
|
||||||
|
if(isset($_GET["logout"])){
|
||||||
|
// unset session flag
|
||||||
|
$_SESSION[__NAMESPACE__]["admin"]["login"] = false;
|
||||||
|
|
||||||
|
// redirect to login page
|
||||||
|
Url_Redirect::location("http" . (Request::has_ssl() ? "s" : "") . "://" . Request::domain_raw_full() . "/admin/login");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
?>
|
135
page/admin/login/index.php
Normal file
135
page/admin/login/index.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
namespace Kimendisch\Sbgg_Jetzt;
|
||||||
|
use Flake\Url_Redirect;
|
||||||
|
use Flake\Request;
|
||||||
|
use Flake\Lang;
|
||||||
|
use Flake\Lang_Dict;
|
||||||
|
use Flake\Page;
|
||||||
|
use Flake\Hidden;
|
||||||
|
use Flake\File;
|
||||||
|
use Flake\Project;
|
||||||
|
use Flake\Cookieaccept;
|
||||||
|
|
||||||
|
// CHECK AUTHENTICATION //
|
||||||
|
// redirect to start page when logged in
|
||||||
|
$login = $_SESSION[__NAMESPACE__]["admin"]["login"] ?? null;
|
||||||
|
if($login === true){
|
||||||
|
Url_Redirect::location("http" . (Request::has_ssl() ? "s" : "") . "://" . Request::domain_raw_full() . "/admin");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// HANDLE LOGIN //
|
||||||
|
require(__DIR__ . "/login_handler.php");
|
||||||
|
|
||||||
|
|
||||||
|
// LANGUAGE MANAGER //
|
||||||
|
// hack: fake get param from constant
|
||||||
|
$_GET["lang"] = "en";
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
$lang = new Lang(list: ["de", "en"], default: "en");
|
||||||
|
|
||||||
|
// load dict
|
||||||
|
$dict = new Lang_Dict($lang);
|
||||||
|
require("./page/strings.php");
|
||||||
|
|
||||||
|
|
||||||
|
// PAGE INIT //
|
||||||
|
Page::start();
|
||||||
|
|
||||||
|
Page::title("SBGG.jetzt - Admin Area");
|
||||||
|
Page::icon("./asset/logo-256.png");
|
||||||
|
|
||||||
|
Page::lang($lang->get());
|
||||||
|
Page::viewport(scale: 1, zoom: true);
|
||||||
|
|
||||||
|
Page::robots(index: false, follow: false);
|
||||||
|
Page::author("Kim Endisch");
|
||||||
|
|
||||||
|
Page::$head["analytics"] = '<script defer data-domain="sbgg.jetzt" src="https://analytics.tjdev.de/js/script.js"></script>';
|
||||||
|
Page::css("./page/start/style.css");
|
||||||
|
Page::css(__DIR__ . "/style.css");
|
||||||
|
|
||||||
|
Page::font("ubuntu");
|
||||||
|
Page::font("tabler");
|
||||||
|
?>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<?php if(!Cookieaccept::is_accepted()){ ?>
|
||||||
|
<div class="cookie-notice-required">
|
||||||
|
<div class="box important">
|
||||||
|
<span class="title"><i class="ti ti-cookie"></i>Cookies</span>
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
<span>This page needs cookies to function correctly.</span>
|
||||||
|
<span>Cookies are only used for required purposes.</span>
|
||||||
|
<span>You can read more about this in our <a href="<?= $dict->get("link_privacy_policy") ?>" target="_blank"><?= $dict->get("text_privacy_policy") ?> <i class="ti ti-external-link"></i></a>.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button-list">
|
||||||
|
<a class="button" href="?cookieaccept=1">
|
||||||
|
<span class="icon ti ti-check"></span>
|
||||||
|
<span class="text">Accept</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="page">
|
||||||
|
<div class="section">
|
||||||
|
<div class="content rows">
|
||||||
|
<div id="login" class="box">
|
||||||
|
<span class="title">Admin Area</span>
|
||||||
|
|
||||||
|
<form id="login-form" method="post" action="">
|
||||||
|
<div class="key-value-pair">
|
||||||
|
<div class="key">
|
||||||
|
<span class="ti ti-key"></span>
|
||||||
|
</div>
|
||||||
|
<div class="value-list">
|
||||||
|
<input id="login-form-token" class="value" type="password" name="token" placeholder="Authentication Token" autocomplete="off" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="login-form-submit" class="button primary">
|
||||||
|
<span class="text">Login</span>
|
||||||
|
<span class="icon ti ti-chevron-right"></span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php if(isset($_GET["login_failure"])){ ?>
|
||||||
|
<span id="login-feedback-negative">Login failed</span>
|
||||||
|
<?php } ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<div class="brand">
|
||||||
|
<img src="<?= File::file("./asset/logo-256.png") ?>" alt="logo" />
|
||||||
|
<span>SBGG.jetzt</span>
|
||||||
|
<a href="https://git.tjdev.de/kimendisch/sbgg.jetzt" target="_blank"><?= $dict->get("text_sourcecode") ?> <i class="ti ti-external-link"></i></a>
|
||||||
|
<span class="version">v<?= Project::version() ?></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="legal">
|
||||||
|
<span>© 2024 Kim Endisch</span>
|
||||||
|
<span class="delimiter">|</span>
|
||||||
|
<a href="<?= $dict->get("link_imprint") ?>" target="_blank"><?= $dict->get("text_imprint") ?> <i class="ti ti-external-link"></i></a>
|
||||||
|
<span class="delimiter">|</span>
|
||||||
|
<a href="<?= $dict->get("link_privacy_policy") ?>" target="_blank"><?= $dict->get("text_privacy_policy") ?> <i class="ti ti-external-link"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
32
page/admin/login/login_handler.php
Normal file
32
page/admin/login/login_handler.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
namespace Kimendisch\Sbgg_Jetzt;
|
||||||
|
use Flake\Url_Redirect;
|
||||||
|
|
||||||
|
if(isset($_POST["token"])){
|
||||||
|
// CHECK TOKEN //
|
||||||
|
// collect token from form submit
|
||||||
|
$token = $_POST["token"];
|
||||||
|
|
||||||
|
// load token hash from env
|
||||||
|
$auth_token_hash = Env::ADMIN_AREA["auth_token_hash"];
|
||||||
|
|
||||||
|
// check
|
||||||
|
$token_valid = password_verify($token, $auth_token_hash);
|
||||||
|
|
||||||
|
|
||||||
|
// MAYBE DO LOGIN //
|
||||||
|
if($token_valid){
|
||||||
|
// set session flag
|
||||||
|
$_SESSION[__NAMESPACE__]["admin"]["login"] = true;
|
||||||
|
|
||||||
|
// reload page
|
||||||
|
Url_Redirect::query_modify(remove: ["login_failure"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// LOGIN FAILED //
|
||||||
|
// display feedback after reload
|
||||||
|
Url_Redirect::query_modify(remove: ["login_failure"], add: ["login_failure"]);
|
||||||
|
}
|
||||||
|
?>
|
50
page/admin/login/style.css
Normal file
50
page/admin/login/style.css
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/* SECTION: LOGIN */
|
||||||
|
.section > .content.rows > #login {
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
flex-basis: 32rem;
|
||||||
|
flex-grow: 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-form-token {
|
||||||
|
min-width: 22rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-feedback-negative {
|
||||||
|
color: var(--color-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* COOKIE NOTICE */
|
||||||
|
.cookie-notice-required {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 6rem;
|
||||||
|
right: 2rem;
|
||||||
|
|
||||||
|
margin-left: 2rem;
|
||||||
|
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 1000px) {
|
||||||
|
.cookie-notice-required {
|
||||||
|
bottom: 8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cookie-notice-required .box.important {
|
||||||
|
border: 0.5rem solid var(--theme);
|
||||||
|
}
|
||||||
|
.cookie-notice-required .box a:not(.button) {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--theme);
|
||||||
|
}
|
||||||
|
.cookie-notice-required .box a:not(.button):hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
15
page/admin/start/index.php
Normal file
15
page/admin/start/index.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
namespace Kimendisch\Sbgg_Jetzt;
|
||||||
|
use Flake\Url_Redirect;
|
||||||
|
use Flake\Request;
|
||||||
|
|
||||||
|
// HANDLE AUTHENTICATION //
|
||||||
|
require("./page/admin/auth_handler.php");
|
||||||
|
?>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TODO: Admin Start Page
|
Loading…
Reference in New Issue
Block a user