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)
|
||||
## 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
|
||||
\.idea/
|
||||
.env
|
||||
storage/database.sqlite
|
||||
storage/database.sqlite
|
||||
.DS_Store
|
||||
|
|
|
@ -4,9 +4,14 @@ default:
|
|||
_controller: JeroenED\Webcron\Controller\JobController::defaultAction
|
||||
|
||||
login:
|
||||
path: '/login'
|
||||
defaults:
|
||||
_controller: JeroenED\Webcron\Controller\SecurityController::loginAction
|
||||
path: '/login'
|
||||
defaults:
|
||||
_controller: JeroenED\Webcron\Controller\SecurityController::loginAction
|
||||
|
||||
logout:
|
||||
path: '/logout'
|
||||
defaults:
|
||||
_controller: JeroenED\Webcron\Controller\SecurityController::logoutAction
|
||||
|
||||
login_check:
|
||||
path: '/login_check'
|
||||
|
|
|
@ -52,7 +52,7 @@ abstract class Controller
|
|||
public function render(string $template, array $vars = []): Response
|
||||
{
|
||||
if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
|
||||
$vars['flashes'] = $_SESSION['flashes'];
|
||||
$vars['flashes'] = $_SESSION['flashes'] ?? [] ;
|
||||
$_SESSION['flashes'] = [];
|
||||
}
|
||||
$response = new Response($this->twig->render($template, $vars));
|
||||
|
|
|
@ -14,20 +14,53 @@ class SecurityController extends Controller
|
|||
{
|
||||
if(isset($_SESSION['isAuthenticated']) && $_SESSION['isAuthenticated']) {
|
||||
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');
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
$userRepository = new User($this->getDbCon());
|
||||
$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;
|
||||
|
||||
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'));
|
||||
}
|
||||
$this->addFlash('danger', 'Username or password incorrect');
|
||||
$this->addFlash('danger', 'Login Failed');
|
||||
return new RedirectResponse($this->generateRoute('login'));
|
||||
}
|
||||
}
|
|
@ -15,17 +15,59 @@ class User
|
|||
$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";
|
||||
$userStmt = $this->dbcon->prepare($userSql);
|
||||
$userRslt = $userStmt->execute([':user' => $user]);
|
||||
if($user = $userRslt->fetchAssociative()) {
|
||||
$shaPass = hash('sha256', $password);
|
||||
if(password_verify($shaPass, $user['password'])) {
|
||||
return true;
|
||||
if($autologin) $password = $this->getPassFromAutologinToken($password);
|
||||
|
||||
$password = hash($_ENV['HASHING_METHOD'], $password);
|
||||
|
||||
if(password_verify($password, $user['password'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
}
|
||||
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>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid py-3">
|
||||
<div class="container-fluid">
|
||||
<div class="row py-3">
|
||||
<div class="col-xs-12 col-sm-12">
|
||||
<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="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="{{ path('logout') }}">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-10">
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{% for flash in flashes %}
|
||||
<div class="alert alert-{{ flash.category }} alert-dismissible fade show" role="alert">
|
||||
{{ flash.content }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if flashes is defined and flashes is not empty %}
|
||||
{% for flash in flashes %}
|
||||
<div class="alert alert-{{ flash.category }} alert-dismissible fade show" role="alert">
|
||||
{{ flash.content }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
Loading…
Reference in New Issue