devshort

private self-hosted shortlink service
git clone https://git.clttr.info/devshort.git
Log (Feed) | Files | Refs (Tags) | README | LICENSE

commit 70b56eb6800bd3a35b67fe719db450ed186f57fd
parent 0eec4e2a26a7ba5dabd2a839b52578f56fc8729a
Author: Florian <flokX@users.noreply.github.com>
Date:   Thu, 26 Dec 2019 17:13:42 +0100

Implement new auth system

* Use PHP sessions
* Separate login/logout module (admin-auth.php)
* Replace installer.php with first run function in the login module

Diffstat:
Aadmin-auth.php | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Madmin.php | 17++++++++++++++++-
Mdata/config.json | 5+----
Mindex.php | 4++--
Dinstaller.php | 90-------------------------------------------------------------------------------
Mredirect.php | 2+-
6 files changed, 129 insertions(+), 98 deletions(-)

diff --git a/admin-auth.php b/admin-auth.php @@ -0,0 +1,109 @@ +<?php + +// All relevant changes can be made in the data file. Please read the docs: https://github.com/flokX/devShort/wiki + +session_start(); +$incorrect_password = false; + +$config_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "data", "config.json")); +$config_content = json_decode(file_get_contents($config_path), true); +$stats_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "data", "stats.json")); +$stats_content = json_decode(file_get_contents($stats_path), true); + +// If no password is in the config.json file, redirect to wiki page +if (!$config_content["admin_password"]) { + header("Location: https://github.com/flokX/devShort/wiki/Installation#installation"); + exit; +} + +// First run: Hash password if it's in the config.json as clear text +$admin_password = $config_content["admin_password"]; +if (password_get_info($admin_password)["algo"] === 0) { + $hash = password_hash($admin_password, PASSWORD_DEFAULT); +} else { + $hash = $admin_password; +} +$config_content["admin_password"] = $hash; +file_put_contents($config_path, json_encode($config_content, JSON_PRETTY_PRINT)); + +// Logout user in session if mode is logout +if (isset($_GET["logout"])) { + unset($_SESSION["user_authenticated"]); + header("Location: index.php"); + exit; +} + +// Login user in session if mode is login and post data is available +if (isset($_GET["login"])) { + if (password_verify($_POST["input_password"], $config_content["admin_password"])) { + $_SESSION["user_authenticated"] = true; + header("Location: admin.php"); + exit; + } else { + $incorrect_password = true; + } +} + +// Generator for page customization +$links_string = ""; +if ($config_content["settings"]["custom_links"]) { + foreach ($config_content["settings"]["custom_links"] as $name => $url) { + $links_string = $links_string . "<a href=\"$url\" class=\"badge badge-secondary\">$name</a> "; + } + $links_string = substr($links_string, 0, -1); +} + +?> + +<!doctype html> +<html class="h-100" lang="en"> + +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <meta name="robots" content="noindex, nofollow"> + <meta name="author" content="<?php echo $config_content["settings"]["author"]; ?> and the devShort team"> + <link href="<?php echo $config_content["settings"]["favicon"]; ?>" rel="icon"> + <title>Login | <?php echo $config_content["settings"]["name"]; ?></title> + <link href="assets/vendor/bootstrap/bootstrap.min.css" rel="stylesheet"> + <link href="assets/main.css" rel="stylesheet"> +</head> + +<body class="d-flex flex-column h-100"> + + <main class="flex-shrink-0"> + <div class="container"> + <nav class="mt-3" aria-label="breadcrumb"> + <ol class="breadcrumb shadow-sm"> + <li class="breadcrumb-item"><a href="<?php echo $config_content["settings"]["home_link"]; ?>">Home</a></li> + <li class="breadcrumb-item"><?php echo $config_content["settings"]["name"]; ?></li> + <li class="breadcrumb-item active" aria-current="page">Login</li> + </ol> + </nav> + <h1 class="mt-5">Login</h1> + <p class="lead">Please sign in to access the admin panel. If you need help, visit <a href="https://github.com/flokX/devShort/wiki">the devShort wiki</a>.</p> + <form action="admin-auth.php?login" method="POST"> + <div class="alert alert-danger" role="alert" <?php if (!$incorrect_password) { echo "style=\"display: none;\"" } ?>> + The given password was incorrect, please try again! + </div> + <div class="form-group"> + <label for="inputPassword">Password</label> + <input class="form-control" id="inputPassword" name="input_password" type="password"> + </div> + <button class="btn btn-primary" type="submit">Login</button> + </form> + </div> + </main> + + <footer class="footer mt-auto py-3 bg-light"> + <div class="container"> + <div class="d-flex justify-content-between align-items-center"> + <span class="text-muted">&copy; <?php echo date("Y") . " " . $config_content["settings"]["author"]; ?> and <a href="https://github.com/flokX/devShort">devShort</a></span> + <?php if ($links_string) { echo "<span class=\"text-muted\">$links_string</span>"; } ?> + </div> + </div> + </footer> + +</body> + +</html> diff --git a/admin.php b/admin.php @@ -7,6 +7,13 @@ $config_content = json_decode(file_get_contents($config_path), true); $stats_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "data", "stats.json")); $stats_content = json_decode(file_get_contents($stats_path), true); +// Check if authentication is valid +session_start(); +if (!isset($_SESSION["user_authenticated"])) { + header("Location: admin-auth.php?login"); + exit; +} + // Filter the names that the admin interface doesn't break function filter_name($nameRaw) { $name = filter_var($nameRaw, FILTER_SANITIZE_STRING); @@ -73,12 +80,14 @@ if ($config_content["settings"]["custom_links"]) { <div class="col-md-4 col-lg-3"> <div class="card d-none d-md-block mb-3"> <div class="card-body"> + <h5 class="card-title">Tools</h5> <a class="card-link" id="refresh" href="#refresh">Refresh charts</a> + <a class="card-link" href="admin-auth.php?logout">Logout</a> </div> </div> <div class="card mb-3"> <div class="card-body"> - <h5 class="card-title">Add shortlink <small class="d-md-none"><a class="card-link" id="refresh" href="#refresh">Refresh charts</a></small></h5> + <h5 class="card-title">Add shortlink</h5> <form id="add-form"> <div class="form-group"> <label for="name">Name</label> @@ -106,6 +115,12 @@ if ($config_content["settings"]["custom_links"]) { <p class="mb-0">powered by <a href="https://github.com/flokX/devShort">devShort</a> v2.4.0</p> </div> </div> + <div class="card d-md-none mb-3"> + <div class="card-body text-center"> + <a class="card-link" id="refresh" href="#refresh">Refresh charts</a> + <a class="card-link" href="admin-auth.php?logout">Logout</a> + </div> + </div> </div> <div class="col-md-8 col-lg-9"> <div class="d-flex justify-content-center"> diff --git a/data/config.json b/data/config.json @@ -1,8 +1,5 @@ { - "installer": { - "username": "admin", - "password": "123" - }, + "admin_password": "", "settings": { "name": "devShort shortlink service", "author": "The admin", diff --git a/index.php b/index.php @@ -2,7 +2,7 @@ // All relevant changes can be made in the data file. Please read the docs: https://github.com/flokX/devShort/wiki -$base_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "admin")); +$base_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "data")); $config_content = json_decode(file_get_contents($base_path . DIRECTORY_SEPARATOR . "config.json"), true); // Counts the access @@ -53,7 +53,7 @@ if ($config_content["settings"]["custom_links"]) { <li class="list-inline-item">-</li> <li class="list-inline-item"><a href="<?php echo $config_content["settings"]["home_link"]; ?>">Home page</a></li> <li class="list-inline-item">-</li> - <li class="list-inline-item"><a href="admin">Admin panel</a></li> + <li class="list-inline-item"><a href="admin.php">Admin panel</a></li> </ul> </div> </main> diff --git a/installer.php b/installer.php @@ -1,90 +0,0 @@ -<?php - -// All relevant changes can be made in the data file. Please read the docs: https://github.com/flokX/devShort/wiki - -$success = false; -$config_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "admin", "config.json")); -$config_content = json_decode(file_get_contents($config_path), true); - -if ($config_content["installer"]["password"]) { - - // Create the .htpasswd for the secure directory. If already a hashed password is in the data.json file, copy it. - $htpasswd_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "admin", ".htpasswd")); - $admin_password = $config_content["installer"]["password"]; - if (password_get_info($admin_password)["algo"] === 0) { - $hash = password_hash($admin_password, PASSWORD_DEFAULT); - } else { - $hash = $admin_password; - } - file_put_contents($htpasswd_path, $config_content["installer"]["username"] . ":" . $hash); - - // Create the .htaccess for the secure directory. - $secure_htaccess = "# Authentication -AuthType Basic -AuthName \"devShort admin area\" -AuthUserFile $htpasswd_path -require valid-user"; - file_put_contents(implode(DIRECTORY_SEPARATOR, array(__DIR__, "admin", ".htaccess")), $secure_htaccess); - - // Change password entry to the hash and remove installer file. - $config_content["installer"]["password"] = $hash; - file_put_contents($config_path, json_encode($config_content, JSON_PRETTY_PRINT)); - unlink(__DIR__ . DIRECTORY_SEPARATOR . "installer.php"); - $success = true; - -} - -?> - -<!doctype html> -<html class="h-100" lang="en"> - -<head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <meta name="robots" content="noindex, nofollow"> - <meta name="author" content="The devShort team"> - <link href="assets/icon.png" rel="icon"> - <title>Installer | devShort</title> - <link href="assets/vendor/bootstrap/bootstrap.min.css" rel="stylesheet"> - <link href="assets/main.css" rel="stylesheet"> -</head> - -<body class="d-flex flex-column h-100"> - - <main class="flex-shrink-0"> - <div class="container"> - <nav class="mt-3" aria-label="breadcrumb"> - <ol class="breadcrumb"> - <li class="breadcrumb-item">devShort</li> - <li class="breadcrumb-item active" aria-current="page">Installer</li> - </ol> - </nav> - <?php - - if ($success) { - echo "<h1 class=\"mt-5\">Successful installed!</h1> -<p class=\"lead\">Now you can start to shorten links. For more information visit the <a href=\"https://github.com/flokX/devShort/wiki\">devShort wiki</a>.</p> -<a href=\"admin\" class=\"btn btn-primary btn-block\" role=\"button\">Go to the admin panel</a>"; - } else { - echo "<h1 class=\"mt-5\">Error while installing.</h1> -<p class=\"lead\">Please configure the <i>config.json</i> as shown in the <a href=\"https://github.com/flokX/devShort/wiki/Installation#installation\">devShort wiki</a> and try again.</p> -<p>We assume that you have not yet set an admin password.</p>"; - } - - ?> - </div> - </main> - - <footer class="footer mt-auto py-3 bg-light"> - <div class="container"> - <div class="d-flex justify-content-between align-items-center"> - <span class="text-muted">&copy; <?php echo date("Y") ?> <a href="https://github.com/flokX/devShort">devShort</a></span> - <span class="text-muted"><a href="https://github.com/flokX/devShort/wiki" class="badge badge-secondary">devShort wiki</a></span> - </div> - </div> - </footer> - -</body> - -</html> diff --git a/redirect.php b/redirect.php @@ -18,7 +18,7 @@ function count_access($base_path, $name) { file_put_contents($filename, json_encode($stats, JSON_PRETTY_PRINT)); } -$base_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "admin")); +$base_path = implode(DIRECTORY_SEPARATOR, array(__DIR__, "data")); $config_content = json_decode(file_get_contents($base_path . DIRECTORY_SEPARATOR . "config.json"), true); if (array_key_exists($short, $config_content["shortlinks"])) {