Create an insecure bank application
This commit is contained in:
40
webroot/lib/Model/Context.php
Normal file
40
webroot/lib/Model/Context.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Model;
|
||||
|
||||
class Context
|
||||
{
|
||||
public ?int $requestTime = null;
|
||||
public ?Session $session = null;
|
||||
public ?string $currentPage = null;
|
||||
public ?array $navigation = null;
|
||||
|
||||
protected function initNavigation()
|
||||
{
|
||||
$this->navigation = [];
|
||||
$this->navigation[] = new NavigationEntry('Startseite', '/');
|
||||
if ($this->session === null) {
|
||||
$this->navigation[] = new NavigationEntry('Registrieren', '/register.php');
|
||||
$this->navigation[] = new NavigationEntry('Einloggen', '/login.php');
|
||||
} else {
|
||||
$this->navigation[] = new NavigationEntry('Umsatzübersicht', '/bookings.php');
|
||||
if ($this->session->user->isAdmin) {
|
||||
$this->navigation[] = new NavigationEntry('Einzahlen', '/deposit.php');
|
||||
$this->navigation[] = new NavigationEntry('Auszahlen', '/withdraw.php');
|
||||
}
|
||||
$this->navigation[] = new NavigationEntry('Überweisen', '/transfer.php');
|
||||
$this->navigation[] = new NavigationEntry('Ausloggen', '/logout.php');
|
||||
}
|
||||
}
|
||||
|
||||
public static function init(string $url): self
|
||||
{
|
||||
$context = new self();
|
||||
$context->requestTime = time();
|
||||
$context->currentPage = $url;
|
||||
$context->session = Session::load();
|
||||
$context->initNavigation();
|
||||
return $context;
|
||||
}
|
||||
}
|
24
webroot/lib/Model/NavigationEntry.php
Normal file
24
webroot/lib/Model/NavigationEntry.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Model;
|
||||
|
||||
class NavigationEntry
|
||||
{
|
||||
public function __construct(
|
||||
public string $displayText,
|
||||
public string $url,
|
||||
) {
|
||||
}
|
||||
|
||||
public function send(Context $context): void
|
||||
{
|
||||
$displayText = htmlspecialchars($this->displayText);
|
||||
$url = htmlspecialchars($this->url);
|
||||
if ($this->url === $context->currentPage) {
|
||||
echo "<li aria-current=\"page\"><span>{$displayText}</span></li>";
|
||||
} else {
|
||||
echo "<li><a href=\"{$url}\">{$displayText}</a></li>";
|
||||
}
|
||||
}
|
||||
}
|
59
webroot/lib/Model/Session.php
Normal file
59
webroot/lib/Model/Session.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Model;
|
||||
|
||||
use Controller\Sql;
|
||||
|
||||
class Session
|
||||
{
|
||||
public ?string $tokenHash = null;
|
||||
public ?string $newSessid = null;
|
||||
public ?User $user = null;
|
||||
|
||||
public static function create(User $user): Session
|
||||
{
|
||||
$sessid = bin2hex(random_bytes(32));
|
||||
$sessidHash = hash('sha256', $sessid);
|
||||
|
||||
$sql = Sql::connection();
|
||||
$stmt = $sql->prepare('INSERT INTO session (token, user) VALUES (UNHEX(?), ?)');
|
||||
$stmt->execute([$sessidHash, $user->id]);
|
||||
|
||||
$session = new self();
|
||||
$session->newSessid = $sessid;
|
||||
$session->user = $user;
|
||||
return $session;
|
||||
}
|
||||
|
||||
public static function load(): ?self
|
||||
{
|
||||
if (!isset($_COOKIE['sessid'])) {
|
||||
return null;
|
||||
}
|
||||
$sessidHash = hash('sha256', $_COOKIE['sessid']);
|
||||
|
||||
$sql = Sql::connection();
|
||||
$stmt = $sql->prepare(
|
||||
'SELECT user.id, user.name, user.admin FROM session
|
||||
JOIN user ON session.user = user.id
|
||||
WHERE token = UNHEX(?)'
|
||||
);
|
||||
$stmt->execute([$sessidHash]);
|
||||
if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$session = new Session();
|
||||
$session->tokenHash = $sessidHash;
|
||||
$session->user = new User($row['id'], $row['name'], null, (bool) $row['admin']);
|
||||
return $session;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function destroy(): void
|
||||
{
|
||||
$sql = Sql::connection();
|
||||
$stmt = $sql->prepare('DELETE FROM session WHERE token = UNHEX(?)');
|
||||
$stmt->execute([$this->tokenHash]);
|
||||
}
|
||||
}
|
46
webroot/lib/Model/User.php
Normal file
46
webroot/lib/Model/User.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Model;
|
||||
|
||||
use Controller\Sql;
|
||||
|
||||
class User
|
||||
{
|
||||
public function __construct(
|
||||
public int $id,
|
||||
public string $name,
|
||||
public ?string $pwHash,
|
||||
public bool $isAdmin,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function create(string $name, string $pwHash): ?self
|
||||
{
|
||||
$sql = Sql::connection();
|
||||
$stmt = $sql->prepare('INSERT INTO user (name, password) VALUES (?, ?)');
|
||||
try {
|
||||
$stmt->execute([$name, $pwHash]);
|
||||
} catch (\PDOException $e) {
|
||||
if ($e->getCode() == 23000) {
|
||||
// duplicate entry, so the username is already in use
|
||||
return null;
|
||||
} else {
|
||||
// unknown error, throw out
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
return new self((int) $sql->lastInsertId(), $name, $pwHash, false);
|
||||
}
|
||||
|
||||
public static function byName(string $name): ?self
|
||||
{
|
||||
$sql = Sql::connection();
|
||||
$stmt = $sql->prepare('SELECT id, name, password, admin FROM user WHERE name = ?');
|
||||
$stmt->execute([$name]);
|
||||
if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
return new self($row['id'], $row['name'], $row['password'], (bool) $row['admin']);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user