pub / rememori

Simple file-based bookmarking and notes application
git clone https://src.jayvii.de/pub/rememori.git
Home | Log | Files | Exports | Refs | README | RSS

commit da4133455749ad995882cc4ca122c10c6c51d32d
parent 1b03f4e12bf00ea93e0c66e560da667c4e40276f
Author: JayVii <jayvii[AT]posteo[DOT]de>
Date:   Fri, 25 Oct 2024 18:06:28 +0200

feat: implement rudimentary auth process

Diffstat:
Aconfig/config.php | 3+++
Mconfig/i18n.php | 4++++
Mindex.php | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mlib/edit.php | 7+++----
Mlib/helpers.php | 12+++++++++++-
Mlib/list.php | 13+++++++++----
Alib/login.php | 26++++++++++++++++++++++++++
Mlib/show.php | 17++++++++++++++---
Alib/users.php | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/write.php | 2+-
Amisc/create_initial_user.php | 18++++++++++++++++++
11 files changed, 281 insertions(+), 30 deletions(-)

diff --git a/config/config.php b/config/config.php @@ -0,0 +1,3 @@ +<?php + +$GLOBALS["pw_salt"] = "set_a_proper_salt"; diff --git a/config/i18n.php b/config/i18n.php @@ -14,6 +14,10 @@ if ($lang == "en") { $GLOBALS["i18n_edit"] = "Edit note"; $GLOBALS["i18n_new"] = "New note"; $GLOBALS["i18n_list"] = "List notes"; + $GLOBALS["i18n_login"] = "Login"; + $GLOBALS["i18n_user"] = "Username"; + $GLOBALS["i18n_pass"] = "Password"; + $GLOBALS["i18n_logout"] = "Logout"; } diff --git a/index.php b/index.php @@ -8,7 +8,6 @@ if (!array_key_exists("action", $_POST)) { $_POST["action"] = "list"; } - $_POST["user"] = "test_user"; /* Load library functions */ foreach (glob("./lib/*.php") as $lib) { @@ -20,13 +19,31 @@ include($conf); } - /* Gather POST input */ + /* Gather POST input and validate if necessary */ $action = gather_post("action"); - $category = gather_post("category"); - $filename = gather_post("filename"); + $category = validate_input_string(gather_post("category")); + $filename = validate_input_string(gather_post("filename")); $filepath_t1 = gather_post("filepath_t1"); $content = gather_post("content"); - $user = gather_post("user"); + $GLOBALS["user"] = validate_input_string(gather_post("user")); + $pass = gather_post("pass"); + $GLOBALS["token"] = gather_post("token"); + + /* if password is given, but token is not, create the token! */ + if ($GLOBALS["token"] == "") { + $GLOBALS["token"] = create_password_hash($pass); + } + + /* Check authentification of user */ + $auth = auth_user($GLOBALS["user"], $token); + if ($auth !== true) { + $action = "login"; + } + + /* ensure the given filename has a valid extension */ + if (count(preg_grep("/\.txt$/", array($filename), PREG_GREP_INVERT)) > 0) { + $filename = $filename . ".txt"; + } ?> @@ -43,6 +60,13 @@ <body> +<?php + + /* Only show Header if we are not at login */ + if ($action != "login") { + +?> + <header> <!-- Buttons --> <nav> @@ -53,7 +77,13 @@ id="user" name="user" type="hidden" - value="<?php echo $user; ?>" + value="<?php echo $GLOBALS["user"]; ?>" + > + <input + id="token" + name="token" + type="hidden" + value="<?php echo $GLOBALS["token"]; ?>" > <input id="category" @@ -78,6 +108,37 @@ value="<?php echo $GLOBALS["i18n_new"]; ?>" > </form> + <!-- Logout-Action --> + <form action="/" target="_self" method="post"> + <input + id="user" + name="user" + type="hidden" + value="" + > + <input + id="pass" + name="pass" + type="hidden" + value="" + > + <input + id="token" + name="token" + type="hidden" + value="" + > + <input + id="action" + name="action" + type="hidden" + value="list" + > + <input + type="submit" + value="<?php echo $GLOBALS["i18n_logout"]; ?>" + > + </form> </nav> <!-- Headline --> @@ -89,7 +150,13 @@ id="user" name="user" type="hidden" - value="<?php echo $user; ?>" + value="<?php echo $GLOBALS["user"]; ?>" + > + <input + id="token" + name="token" + type="hidden" + value="<?php echo $GLOBALS["token"]; ?>" > <input id="action" @@ -104,16 +171,24 @@ </form> <?php - /* Fetch all categories of the user */ - $categories_path = glob("./data/" . $user . "/*"); - foreach ($categories_path as $category_path) { + /* Fetch all categories of the user */ + $categories_path = glob("./data/" . $GLOBALS["user"] . "/*"); + foreach ($categories_path as $category_path) { + + if (count(glob($category_path . "/" . "*.txt")) > 0) { ?> <form action="/" target="_self" method="post"> <input id="user" name="user" type="hidden" - value="<?php echo $user; ?>" + value="<?php echo $GLOBALS["user"]; ?>" + > + <input + id="token" + name="token" + type="hidden" + value="<?php echo $GLOBALS["token"]; ?>" > <input id="category" @@ -133,31 +208,39 @@ > </form> <?php - } // foreach-loop + } // if-statement + } // foreach-loop ?> </header> <?php + } // if-statement + + /* Login action */ + if ($action == "login") { + show_login_form("list"); + die(); + } + /* Listing action */ if ($action == "list") { /* if no category is given, list all. otherwise only show given */ - if (!is_null($category)) { + if ($category != "") { $categories_path = array( - "./data/" . $user . "/" . $category + "./data/" . $GLOBALS["user"] . "/" . $category ); } foreach ($categories_path as $category_path) { - list_notes($user, basename($category_path)); + list_notes(basename($category_path)); } } /* Edit action */ if ($action == "edit") { edit_note( - $user, $category, $filename, $filepath_t1, @@ -170,7 +253,6 @@ /* Show action */ if ($action == "show") { show_note( - $user, $category, $filename ); diff --git a/lib/edit.php b/lib/edit.php @@ -1,7 +1,6 @@ <?php function edit_note( - string $user, string $category, string $filename, string $filepath_t1, @@ -10,7 +9,7 @@ function edit_note( /* Write note as if it was completely new */ $written = write_note( - $user, + $GLOBALS["user"], $category, $filename, $content @@ -22,12 +21,12 @@ function edit_note( } /* construct new filepath */ - $filepath_t0 = $user . "/" . $category . "/" . $filename; + $filepath_t0 = $category . "/" . $filename; /* if old and new filepaths differ, the file was likely renamed. * delete the old one and exit immediately */ if ($filepath_t1 != $filepath_t0) { - return unlink("./data/" . $filepath_t1); + return unlink("./data/" . $GLOBALS["user"] . "/" . $filepath_t1); } /* if all actions went through, return "true" */ diff --git a/lib/helpers.php b/lib/helpers.php @@ -5,10 +5,20 @@ function gather_post(string $key) { if (array_key_exists($key, $_POST)) { return $_POST[$key]; } else { - return null; + return ""; } } +function validate_input_string(string $in) { + /* Only allows alphanumeric characters and any of "-_." */ + $out = preg_replace( + "/[^A-Za-z0-9\-\_\.]/", + "_", + $in + ); + return $out; +} + function sort_by_time(array $a, array $b) { // sort from oldest to newest (reversed) return $b["time"] - $a["time"]; diff --git a/lib/list.php b/lib/list.php @@ -1,17 +1,16 @@ <?php function list_notes( - string $user, string $category ) { /* Create full path name */ - $dirpath = "./data/" . $user . "/" . $category; + $dirpath = "./data/" . $GLOBALS["user"] . "/" . $category; /* list all available notes */ $filematches = glob($dirpath . "/" . "*.txt"); - if ($filematches !== false) { + if (count($filematches) > 0) { /* Gather each file next to its edit time stamp */ $filenames = array(); @@ -80,7 +79,13 @@ function list_notes( id="user" name="user" type="hidden" - value="<?php echo $user; ?>" + value="<?php echo $GLOBALS["user"]; ?>" + > + <input + id="token" + name="token" + type="hidden" + value="<?php echo $GLOBALS["token"]; ?>" > <input id="category" diff --git a/lib/login.php b/lib/login.php @@ -0,0 +1,26 @@ +<?php + +function show_login_form(string $target) { + +?> + +<form action="/" target="_self" method="post"> + <label for="user"><?php echo $GLOBALS["i18n_user"]; ?></label> + <input id="user" name="user" type="text"> + <label for="pass"><?php echo $GLOBALS["i18n_pass"]; ?></label> + <input id="pass" name="pass" type="password"> + <input + id="action" + name="action" + type="hidden" + value="<?php echo $target; ?>" + > + <input type="submit" value="<?php echo $GLOBALS["i18n_login"]; ?>"> +</form> + +<?php + +} // function + +?> + diff --git a/lib/show.php b/lib/show.php @@ -1,13 +1,12 @@ <?php function show_note( - string $user, string $category, string $filename ) { /* Read Note: if it does not exist yet, use empty string */ - $content = read_note($user, $category, $filename); + $content = read_note($GLOBALS["user"], $category, $filename); ?> @@ -33,7 +32,7 @@ function show_note( id="filepath_t1" name="filepath_t1" type="hidden" - value="<?php echo $user . "/" . $category . "/" . $filename; ?>" + value="<?php echo $category . "/" . $filename; ?>" > <input id="action" @@ -41,6 +40,18 @@ function show_note( type="hidden" value="edit" > + <input + id="user" + name="user" + type="hidden" + value="<?php echo $GLOBALS["user"]; ?>" + > + <input + id="token" + name="token" + type="hidden" + value="<?php echo $GLOBALS["token"]; ?>" + > <input type="submit" value="<?php echo $GLOBALS["i18n_save"]; ?>"> </form> diff --git a/lib/users.php b/lib/users.php @@ -0,0 +1,93 @@ +<?php + +function create_password_hash( + string $pass +) { + + /* check if salt is set properly */ + if (!array_key_exists("pw_salt", $GLOBALS)) { + exit("Your password salt was not set properly!"); + } + + /* Create hash from given password */ + $token = hash("sha256", $pass . $GLOBALS["pw_salt"], false); + + /* return resulting token */ + return $token; +} + +function auth_user( + string $user, + string $token +) { + + /* read token file of user */ + $tokens_storage = file( + "./data/" . $user . "/.tokens", + FILE_IGNORE_NEW_LINES + ); + if ($tokens_storage === false) { + $tokens_storage = array(); + } + + /* find any matches between given hash and tokens */ + $matches = preg_grep( + "/" . $token . "/", + $tokens_storage + ); + + /* If matches between given hash and tokens were found, return "true" */ + if (count($matches) > 0 && $matches !== false) { + return true; + } else { + return false; + } +} + +function create_auth( + string $user, + string $pass +) { + + /* create hash from given password */ + $token = create_password_hash($pass); + + /* generate token path */ + $user_path = "./data/" . $user; + $token_path = $user_path . "/.tokens"; + + /* Only create new auth, if no previous token file exists already */ + if (file_exists($token_path)) { + exit("Token already exists! Use \"change_auth()\" instead!"); + } + + /* create user directory if it does not exist */ + if (opendir($user_path) === false) { + mkdir( + $user_path, /* directory */ + 0770, /* Permissions: rwxrwx--- */ + true /* recursive */ + ); + } + + /* create file-handle */ + $file = fopen( + $token_path, + "w" + ); + + /* Write to file */ + $bytes = fwrite( + $file, + $token + ); + + /* close file-hanle */ + fclose($file); + + /* return "true" on success and "false" otherwise */ + return $bytes !== false; + +} + +?> diff --git a/lib/write.php b/lib/write.php @@ -12,7 +12,7 @@ function write_note( $filepath = $dirpath . "/" . $filename; /* create user directory if it does not exist */ - if (opendir($dirpath) !== false) { + if (opendir($dirpath) === false) { mkdir( $dirpath, /* directory */ 0770, /* Permissions: rwxrwx--- */ diff --git a/misc/create_initial_user.php b/misc/create_initial_user.php @@ -0,0 +1,18 @@ +<?php + +include("./lib/users.php"); +include("./config/config.php"); + +$user = readline('Enter new username: '); +$pass = readline('Enter new password: '); + +$success = create_auth($user, $pass); + +if ($success) { + echo "Account " . $user . " created successfully!" . PHP_EOL; +} else { + exit("Account creation failed for some reason..."); +} + +?> +