Added logout and autologin
This commit is contained in:
parent
c98e1add62
commit
70ba6473e9
12
.env.sample
12
.env.sample
|
@ -15,4 +15,14 @@ DATABASE="sqlite://storage/database.sqlite"
|
||||||
|
|
||||||
## This secret value is used to encrypt secret values (eg. ssh-keys, http-auth passwords, etc)
|
## This secret value is used to encrypt secret values (eg. ssh-keys, http-auth passwords, etc)
|
||||||
## You should consider your already stored secret values lost when changing this value.
|
## You should consider your already stored secret values lost when changing this value.
|
||||||
SECRET=ImNotThatSecretSoPleaseChangeMe0123456789
|
SECRET=ImNotThatSecretSoPleaseChangeMe0123456789
|
||||||
|
ENCRYPTION_METHOD="AES-256-CBC"
|
||||||
|
HASHING_METHOD="sha256"
|
||||||
|
|
||||||
|
###############
|
||||||
|
### COOKIES ###
|
||||||
|
###############
|
||||||
|
|
||||||
|
## This secret value is used to encrypt secret values (eg. ssh-keys, http-auth passwords, etc)
|
||||||
|
## You should consider your already stored secret values lost when changing this value.
|
||||||
|
COOKIE_LIFETIME=2592000
|
|
@ -4,4 +4,5 @@ vendor/
|
||||||
webcron.old/config.inc.php
|
webcron.old/config.inc.php
|
||||||
\.idea/
|
\.idea/
|
||||||
.env
|
.env
|
||||||
storage/database.sqlite
|
storage/database.sqlite
|
||||||
|
.DS_Store
|
||||||
|
|
|
@ -4,9 +4,14 @@ default:
|
||||||
_controller: JeroenED\Webcron\Controller\JobController::defaultAction
|
_controller: JeroenED\Webcron\Controller\JobController::defaultAction
|
||||||
|
|
||||||
login:
|
login:
|
||||||
path: '/login'
|
path: '/login'
|
||||||
defaults:
|
defaults:
|
||||||
_controller: JeroenED\Webcron\Controller\SecurityController::loginAction
|
_controller: JeroenED\Webcron\Controller\SecurityController::loginAction
|
||||||
|
|
||||||
|
logout:
|
||||||
|
path: '/logout'
|
||||||
|
defaults:
|
||||||
|
_controller: JeroenED\Webcron\Controller\SecurityController::logoutAction
|
||||||
|
|
||||||
login_check:
|
login_check:
|
||||||
path: '/login_check'
|
path: '/login_check'
|
||||||
|
|
|
@ -52,7 +52,7 @@ abstract class Controller
|
||||||
public function render(string $template, array $vars = []): Response
|
public function render(string $template, array $vars = []): Response
|
||||||
{
|
{
|
||||||
if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
|
if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
|
||||||
$vars['flashes'] = $_SESSION['flashes'];
|
$vars['flashes'] = $_SESSION['flashes'] ?? [] ;
|
||||||
$_SESSION['flashes'] = [];
|
$_SESSION['flashes'] = [];
|
||||||
}
|
}
|
||||||
$response = new Response($this->twig->render($template, $vars));
|
$response = new Response($this->twig->render($template, $vars));
|
||||||
|
|
|
@ -14,20 +14,53 @@ class SecurityController extends Controller
|
||||||
{
|
{
|
||||||
if(isset($_SESSION['isAuthenticated']) && $_SESSION['isAuthenticated']) {
|
if(isset($_SESSION['isAuthenticated']) && $_SESSION['isAuthenticated']) {
|
||||||
return new RedirectResponse($this->generateRoute('default'));
|
return new RedirectResponse($this->generateRoute('default'));
|
||||||
|
} elseif(isset($_COOKIE['autologin_enable']) && $_COOKIE['autologin_enable'] == true) {
|
||||||
|
$userRepository = new User($this->getDbCon());
|
||||||
|
$userId = $userRepository->checkAuthentication($_COOKIE['autologin_user'], $_COOKIE['autologin_auth'], true);
|
||||||
|
if($userId !== false) {
|
||||||
|
$_SESSION['user.id'] = $userId;
|
||||||
|
$_SESSION['isAuthenticated'] = true;
|
||||||
|
} else {
|
||||||
|
return new RedirectResponse($this->generateRoute('logout'));
|
||||||
|
}
|
||||||
|
return new RedirectResponse($this->generateRoute('default'));
|
||||||
}
|
}
|
||||||
return $this->render('security/login.html.twig');
|
return $this->render('security/login.html.twig');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function logoutAction(): Response
|
||||||
|
{
|
||||||
|
$_SESSION['isAuthenticated'] = false;
|
||||||
|
unset($_SESSION['user.id']);
|
||||||
|
unset($_COOKIE['autologin_auth']);
|
||||||
|
unset($_COOKIE['autologin_user']);
|
||||||
|
unset($_COOKIE['autologin_enable']);
|
||||||
|
setcookie('autologin_auth', "", time() - 3600);
|
||||||
|
setcookie('autologin_user', "", time() - 3600);
|
||||||
|
setcookie('autologin_enable', "", time() - 3600);
|
||||||
|
$this->addFlash('success', 'Successfully logged out');
|
||||||
|
return new RedirectResponse($this->generateRoute('login'));
|
||||||
|
}
|
||||||
|
|
||||||
public function loginCheckAction(): Response
|
public function loginCheckAction(): Response
|
||||||
{
|
{
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$userRepository = new User($this->getDbCon());
|
$userRepository = new User($this->getDbCon());
|
||||||
$credentials = $request->request->all();
|
$credentials = $request->request->all();
|
||||||
if($userRepository->checkAuthentication($credentials['name'], $credentials['passwd'])) {
|
$userId = $userRepository->checkAuthentication($credentials['name'], $credentials['passwd']);
|
||||||
|
if($userId !== false) {
|
||||||
|
$_SESSION['user.id'] = $userId;
|
||||||
$_SESSION['isAuthenticated'] = true;
|
$_SESSION['isAuthenticated'] = true;
|
||||||
|
|
||||||
|
if(isset($credentials['autologin'])) {
|
||||||
|
$token = $userRepository->createAutologinToken($credentials['passwd']);
|
||||||
|
setcookie('autologin_auth', $token, time() + $_ENV['COOKIE_LIFETIME'], "/");
|
||||||
|
setcookie('autologin_user', $credentials['name'], time() + $_ENV['COOKIE_LIFETIME'], "/");
|
||||||
|
setcookie('autologin_enable', true, time() + $_ENV['COOKIE_LIFETIME'], "/");
|
||||||
|
}
|
||||||
return new RedirectResponse($this->generateRoute('default'));
|
return new RedirectResponse($this->generateRoute('default'));
|
||||||
}
|
}
|
||||||
$this->addFlash('danger', 'Username or password incorrect');
|
$this->addFlash('danger', 'Login Failed');
|
||||||
return new RedirectResponse($this->generateRoute('login'));
|
return new RedirectResponse($this->generateRoute('login'));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,17 +15,59 @@ class User
|
||||||
$this->dbcon = $dbcon;
|
$this->dbcon = $dbcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkAuthentication(string $user, string $password): bool
|
/**
|
||||||
|
* @param string $user
|
||||||
|
* @param string $password
|
||||||
|
* @param bool $autologin
|
||||||
|
* @return int|bool
|
||||||
|
* @throws \Doctrine\DBAL\Driver\Exception
|
||||||
|
* @throws \Doctrine\DBAL\Exception
|
||||||
|
*/
|
||||||
|
public function checkAuthentication(string $user, string $password, bool $autologin = false): int|bool
|
||||||
{
|
{
|
||||||
$userSql = "SELECT * from user WHERE email = :user";
|
$userSql = "SELECT * from user WHERE email = :user";
|
||||||
$userStmt = $this->dbcon->prepare($userSql);
|
$userStmt = $this->dbcon->prepare($userSql);
|
||||||
$userRslt = $userStmt->execute([':user' => $user]);
|
$userRslt = $userStmt->execute([':user' => $user]);
|
||||||
if($user = $userRslt->fetchAssociative()) {
|
if($user = $userRslt->fetchAssociative()) {
|
||||||
$shaPass = hash('sha256', $password);
|
if($autologin) $password = $this->getPassFromAutologinToken($password);
|
||||||
if(password_verify($shaPass, $user['password'])) {
|
|
||||||
return true;
|
$password = hash($_ENV['HASHING_METHOD'], $password);
|
||||||
|
|
||||||
|
if(password_verify($password, $user['password'])) {
|
||||||
|
return $user['id'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createAutologinToken($password): string {
|
||||||
|
$method = $_ENV['ENCRYPTION_METHOD'];
|
||||||
|
$key = hash($_ENV['HASHING_METHOD'], $_ENV['SECRET'], true);
|
||||||
|
$iv = openssl_random_pseudo_bytes(16);
|
||||||
|
$time = time();
|
||||||
|
|
||||||
|
$ciphertext = openssl_encrypt($password . substr($time, -7), $method, $key, OPENSSL_RAW_DATA, $iv);
|
||||||
|
$hash = hash_hmac($_ENV['HASHING_METHOD'], $ciphertext . $iv, $key, true);
|
||||||
|
return base64_encode(json_encode(['time' => $time, 'password' => base64_encode($iv . $hash . $ciphertext)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPassFromAutologinToken($token) {
|
||||||
|
$extracted = json_decode(base64_decode($token), true);
|
||||||
|
$method = $_ENV['ENCRYPTION_METHOD'];
|
||||||
|
$encrypted = base64_decode($extracted['password']);
|
||||||
|
$iv = substr($encrypted, 0, 16);
|
||||||
|
$hash = substr($encrypted, 16, 32);
|
||||||
|
$ciphertext = substr($encrypted, 48);
|
||||||
|
$key = hash($_ENV['HASHING_METHOD'], $_ENV['SECRET'], true);
|
||||||
|
|
||||||
|
if (!hash_equals(hash_hmac($_ENV['HASHING_METHOD'], $ciphertext . $iv, $key, true), $hash)) return null;
|
||||||
|
|
||||||
|
$decryption = openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
|
||||||
|
|
||||||
|
return (
|
||||||
|
(($extracted['time'] + $_ENV['COOKIE_LIFETIME']) > time()) &&
|
||||||
|
substr($extracted['time'], -7) == substr($decryption, -7)
|
||||||
|
)
|
||||||
|
? substr($decryption, 0, -7) : null;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container-fluid py-3">
|
<div class="container-fluid">
|
||||||
<div class="row py-3">
|
<div class="row py-3">
|
||||||
<div class="col-xs-12 col-sm-12">
|
<div class="col-xs-12 col-sm-12">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
|
@ -28,6 +28,7 @@
|
||||||
<li class="nav-item"><a class="nav-link" href="{{ path('job_index') }}">Overview</a></li>
|
<li class="nav-item"><a class="nav-link" href="{{ path('job_index') }}">Overview</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="addjob.php">Add a new cronjob</a></li>
|
<li class="nav-item"><a class="nav-link" href="addjob.php">Add a new cronjob</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="config.php">Configuration</a></li>
|
<li class="nav-item"><a class="nav-link" href="config.php">Configuration</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="{{ path('logout') }}">Logout</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-sm-10">
|
<div class="col-xs-12 col-sm-10">
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
{% for flash in flashes %}
|
{% if flashes is defined and flashes is not empty %}
|
||||||
<div class="alert alert-{{ flash.category }} alert-dismissible fade show" role="alert">
|
{% for flash in flashes %}
|
||||||
{{ flash.content }}
|
<div class="alert alert-{{ flash.category }} alert-dismissible fade show" role="alert">
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
{{ flash.content }}
|
||||||
</div>
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
{% endfor %}
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
Loading…
Reference in New Issue