From 4060b1c913f6a43006ee87ad2b118201416b1786 Mon Sep 17 00:00:00 2001 From: Jenga Phoenix Date: Fri, 15 Feb 2019 08:32:58 -0600 Subject: [PATCH] Only god knows what I've done. --- about.php | 13 +- admin/index.php | 14 +- admin/private/user_create.php | 41 +++--- admin/private/user_destroy.php | 37 +++++ index.php | 18 +-- post.php | 30 ++-- res/lib/array.php | 21 ++- res/lib/blagoblag.php | 23 +++ res/lib/db.php | 22 ++- res/lib/error.php | 8 ++ res/lib/load.php | 35 ++++- res/lib/sterilize.php | 136 +++++++++++++++++- res/lib/url.php | 40 ++++++ res/lib/user.php | 60 ++++++-- res/themes/default/html/admin_index.twig.html | 53 +++++-- res/themes/default/html/error.twig.html | 3 +- res/themes/default/html/navbar.twig.html | 10 -- res/themes/default/html/user.twig.html | 3 - user.php | 31 ++-- 19 files changed, 468 insertions(+), 130 deletions(-) create mode 100644 admin/private/user_destroy.php create mode 100644 res/lib/blagoblag.php create mode 100644 res/lib/url.php diff --git a/about.php b/about.php index 558cbbf..e3b2426 100644 --- a/about.php +++ b/about.php @@ -12,17 +12,8 @@ $depth = ""; $title = "About"; include "res/lib/load.php"; +// ------------------------------------ -echo $GLOBALS['twig']->render('head.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth, - 'title' =>$title]); - -echo $GLOBALS['twig']->render('index.twig.html', - ['animal'=> "cat"]); - -echo $GLOBALS['twig']->render('foot.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth]); +display_page("about.twig.html", $depth, $title); ?> diff --git a/admin/index.php b/admin/index.php index c32d03f..dcdc550 100644 --- a/admin/index.php +++ b/admin/index.php @@ -12,18 +12,8 @@ $title = "Control Panel"; $depth = "../"; include "../res/lib/load.php"; -echo $GLOBALS['twig']->render('head.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth, - 'title' =>$title]); - - -echo $GLOBALS['twig']->render('admin_index.twig.html', []); - -echo $GLOBALS['twig']->render('foot.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth]); - +// -------------------------------------- +display_page("admin_index.twig.html", $depth, $title); ?> diff --git a/admin/private/user_create.php b/admin/private/user_create.php index 4a0f5f2..e45326e 100644 --- a/admin/private/user_create.php +++ b/admin/private/user_create.php @@ -11,7 +11,11 @@ $depth = "../../"; include "../../res/lib/load.php"; -$id = $_POST['id']; +$auth_user = $_POST['auth_user']; +$auth_pass = $_POST['auth_pass']; +$auth_user_id = user_name_to_id($auth_user); + +$id = intval($_POST['id']); $name = $_POST['name']; $full_name = $_POST['full_name']; $bio = $_POST['bio']; @@ -19,26 +23,31 @@ $email = $_POST['email']; $url = $_POST['url']; $password = $_POST['password']; $login = $_POST['login']; +$password = password_hash($password, PASSWORD_BCRYPT, array('cost' => 11)); + +// ------------------------------------- + +auth_enforce($auth_user_id, $auth_pass, + array("wizard", "archmage"), "make accounts"); $invalid = input_enforce(array($id, $name, $full_name, $bio, $email, $url, $password, $login), - array("ID", "username", "full name", "biography", "email", - "url", "password", "login"), - array("int", "string", "string", "string", "email", - "url", "password", - array("contributor", "spectator", "wizard", - "admin"))); + array("ID", "Username", "Full name", "Biography", "E-mail", + "URL", "Password", "Login class"), + array("free_user_id", "free_user_name", "string", "string", + "email", "url", "ne_string", + array("spectator", "wizard", "archmage", + "contributor"))); -if (!is_bool($invalid)) { - input_error("Some input is invalid: " . $invalid); +if (!empty($invalid)) { + input_error("Some input is invalid: " . comma_sep($invalid)); } -$result = user_create($id, $name, $password, $login, - $full_name, $email, $url, $bio); -if ($result) { - header('Location: http://localhost/blagoblag/user.php?id=' . $id); -} else { - general_error("Something went wrong."); -} +// ------------------------------------- + +user_create($id, $name, $password, $login, + $full_name, $email, $url, $bio); + +root_redirect("user.php?name=" . $name); ?> diff --git a/admin/private/user_destroy.php b/admin/private/user_destroy.php new file mode 100644 index 0000000..fc957b4 --- /dev/null +++ b/admin/private/user_destroy.php @@ -0,0 +1,37 @@ + diff --git a/index.php b/index.php index 84959d0..e3294cf 100644 --- a/index.php +++ b/index.php @@ -12,22 +12,8 @@ $depth = ""; $title = ""; include "res/lib/load.php"; -// global variable declaration -$users = user_ids(); -$user = array_map(user_data, $users); -$posts = post_ids(); -$post = array_map(post_data, $posts); +// ------------------------------------- -echo $GLOBALS['twig']->render('head.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth, - 'title' =>$title]); - -echo $GLOBALS['twig']->render('index.twig.html', []); - - -echo $GLOBALS['twig']->render('foot.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth]); +display_page("index.twig.html", $depth, $title); ?> diff --git a/post.php b/post.php index 9d33964..00f51e5 100644 --- a/post.php +++ b/post.php @@ -13,27 +13,17 @@ $title = ""; include "res/lib/load.php"; // ------------------------------------- -// post environment -$post_id = $_GET['id']; -$text = post_text($post_id); -$author = post_author($post_id); -$date = post_data($post_id); + +$id = $_GET['id']; +$text = post_text($id); +$author = post_author($id); +$date = post_data($id); + +$local_exports = array('id' => $id, 'text' => $text, 'author' => $author, + 'data' => $data); + // ------------------------------------- -echo $GLOBALS['twig']->render('head.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth, - 'title' =>$title]); - -echo $GLOBALS['twig']->render('post.twig.html', - ['id'=> $post_id, - 'text' => $text, - 'author' => $author, - 'date' => $date]); - - -echo $GLOBALS['twig']->render('foot.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth]); +display_page("post.twig.html", $depth, $title, $local_exports); ?> diff --git a/res/lib/array.php b/res/lib/array.php index 4c1b091..ef0a123 100644 --- a/res/lib/array.php +++ b/res/lib/array.php @@ -11,20 +11,35 @@ // ARRAY -- > STRING // Turn a 1D array into a comma-seperated string -function comma_sep($array) { +function comma_sep($array, $seperator=", ") { + global $string; + $string = $seperator; global $stack; $stack = ""; $comma_print = function($item) { $GLOBALS['stack'] = $GLOBALS['stack'] - . ", " . $item; + . $GLOBALS['string'] . $item; }; array_map($comma_print, $array); - $stack = preg_replace('/^, /', '', $stack); + $stack = preg_replace('/^' . $string . '/', '', $stack); return $stack; } + +// STRING STRING [ARRAY] --> ARRAY +// Return exports for Twig-- with the required global & local exports, +// along with any optional local ones. +function make_exports($depth, $title, $local = array()) { + $exports = $GLOBALS['twig_exports']; + + $exports['depth'] = $depth; + $exports['title'] = $title; + + return array_merge($exports, $local); +} + ?> diff --git a/res/lib/blagoblag.php b/res/lib/blagoblag.php new file mode 100644 index 0000000..bf7cdc8 --- /dev/null +++ b/res/lib/blagoblag.php @@ -0,0 +1,23 @@ + BOOLEAN +// Render and display a page, based on it's template-path, title, relative +// depth, and an optional array of more Twig variable exports. +function display_page($template, $depth, $title, $local_exports=array()) { + echo $GLOBALS['twig']->render("head.twig.html", + make_exports($depth, $title, $local_exports)); + echo $GLOBALS['twig']->render($template, + make_exports($depth, $title, $local_exports)); + echo $GLOBALS['twig']->render("foot.twig.html", + make_exports($depth, $title, $local_exports)); + return true; +} diff --git a/res/lib/db.php b/res/lib/db.php index 5b580b7..80713ac 100644 --- a/res/lib/db.php +++ b/res/lib/db.php @@ -30,8 +30,8 @@ if (!db_table_existant("lusers")) { db_create_table("lusers", array("id int primary key","username varchar(20)", "biography longtext","email varchar(50)","website varchar(50)", - "password_hash varchar(80)","full_name varchar(50)", - "login varchar(20)")); } + "hash char(60)","full_name varchar(50)", + "class varchar(20)")); } if (!db_table_existant("posts")) { db_create_table("posts", @@ -51,10 +51,12 @@ if (!db_table_existant("comments")) { // Execute a DB command function db_cmd($query) { $stmt = $GLOBALS['pdo']->query($query); + $stack = array(); + if (is_bool($stmt)) { return $stmt; } else { - return $stmt->fetch(); + return $stmt->fetchAll(); } } @@ -63,7 +65,17 @@ function db_cmd($query) { // STRING STRING --> ARRAY // Return all values of a specific column function db_get_columns($table, $column) { - return db_cmd("select " . $column . " from " . $table); + $result = db_cmd("select " . $column . " from " . $table); + + $result_nest = function($array) { + return $array[0]; + }; + + if (is_array($result)) { + return array_map($result_nest, $result); + } else { + return $result; + } } // STRING STRING VARYING --> ARRAY @@ -77,7 +89,7 @@ function db_get_rows($table, $identifier, $value) { // Return the value of a specific column in a given row, identified by an // 'identifier' column set to the given value function db_get_cell($table, $identifier, $value, $cell) { - return db_get_rows($table, $identifier, $value)[$cell]; + return db_get_rows($table, $identifier, $value)[0][$cell]; } // -------------------------------------- diff --git a/res/lib/error.php b/res/lib/error.php index a039aff..6229075 100644 --- a/res/lib/error.php +++ b/res/lib/error.php @@ -25,4 +25,12 @@ function input_error($string) { exit; } +// STRING --> NIL +// Print out a general error, with $string as it's error-message. +function perm_error($string) { + echo $GLOBALS['twig']->render('error.twig.html', + ['error_message'=>$string]); + exit; +} + ?> diff --git a/res/lib/load.php b/res/lib/load.php index dd73af6..897836a 100644 --- a/res/lib/load.php +++ b/res/lib/load.php @@ -15,6 +15,7 @@ function root($path) { return $GLOBALS['depth'] . $path; } + // ------------------------------------- include(root("config.php")); @@ -23,12 +24,42 @@ require_once root("vendor/autoload.php"); include(root("res/lib/string.php")); include(root("res/lib/array.php")); include(root("res/lib/user.php")); -include(root("res/lib/post.php")); -include(root("res/lib/comment.php")); +include(root("res/lib/error.php")); +include(root("res/lib/sterilize.php")); include(root("res/lib/db.php")); +include(root("res/lib/url.php")); +include(root("res/lib/blagoblag.php")); $loader= new Twig_Loader_Filesystem(root("res/themes/default/html")); $twig = new Twig_Environment($loader, ['cache' => root('cache/')]); + + +// ------------------------------------- +// global variable declaration +global $users; $users = user_ids(); +global $user; $user = array(); + +$push_user_data = function($user_id) { + $user_name = user_name($user_id); + $GLOBALS['user'][$user_id] = user_data($user_id); + $GLOBALS['user'][$user_name] = user_data($user_id); + }; + +array_map($push_user_data, $users); + +// global $posts; $posts = post_ids(); +// global $post; $post = array(); +// $post = array_map(post_data, $posts); + +// ----------------- + +global $twig_exports; +$twig_exports = array('theme' => $GLOBALS['theme'], + 'users' => $GLOBALS['users'], + 'user' => $GLOBALS['user']); + //'posts' => $GLOBALS['posts'], + //'post' => $GLOBALS['post']); + ?> diff --git a/res/lib/sterilize.php b/res/lib/sterilize.php index c22603e..1208544 100644 --- a/res/lib/sterilize.php +++ b/res/lib/sterilize.php @@ -9,8 +9,136 @@ GNU Affero General Public License for more details. */ -function input_enforce($values, $names, $types) { - $stack = ""; +// NUMBER STRING ARRAY [STRING] --> NIL +// Make sure a user is both authenticated *and* permitted to do a given task, +// aka in the right login-class. +function auth_enforce($id, $password, $permitted, $message="do that") { + if (!user_valid_password($id, $password)) { + input_error("Sorry, your user-name or password is wrong."); + } - while ($i < length($valuesw)) { - if (function + $class = user_class($id); + if (!in_array($class, $permitted)) { + perm_error("Mate, only a " . comma_sep($permitted, " or ") + . " can " . $message . "-- you hecking " + . $class . "!"); + } +} + +// ARRAY ARRAY ARRAY --> ARRAY +// Validate a list of values against a corresponding list of types; +// return a list of names (corresponding to the values) that don't +// match their types. If an empty array is returned, then, all inputs are +// valid. +// Two non-existant "pseudo-types" are accepted-- "url" and "e-mail". +// A "type" can also be an array of acceptable values. +// Example: +// input_enforce(["apple", 4, "ap@ap.co.uk", "never"], +// ["Fruit", "Age", "E-mail", "Preference"], +// ["string", "int", "email", +// ["never", "always", "now"]]); +// +// ... it's an ugly, long function, I know... +function input_enforce($values, $names, $types) { + $stack = array(); + $i = 0; + + while ($i < count($values)) { + $value = $values[$i]; + $type = $types[$i]; + $name = $names[$i]; + $res = true; + + switch ($type) { + case (is_array($type)): + if (!in_array($value, $type)) { $res = false; } + break; + + default: + $type_check = "is_" . $type; + if (!call_user_func($type_check, $value)) { + $res = false; + } + break; + } + + if (!$res) { + $stack[] = $name; + } + + $i++; + } + + + return $stack; +} + + +// ------------------------------------- +// pseudo-types + +// STRING --> BOOLEAN +// Return whether or not a given string is a valid e-mail. +function is_email($string) { + return filter_var($string, FILTER_VALIDATE_EMAIL); +} + +// STRING --> BOOLEAN +// Return whether or not a given string is a valid url. +function is_url($string) { + return filter_var($string, FILTER_VALIDATE_URL); +} + +// VARYING --> BOOLEAN +// Return whether or not a given value is a non-empty string +function is_ne_string($value) { + if (is_string($value) && !empty($value)) { + return true; + } else { + return false; + } +} + +// ------------------------------------- + +// STRING --> BOOLEAN +// Return whether or not a string is already a username +function is_user_name($username) { + if (user_name_to_id($username)) { + return true; + } else { + return false; + } +} + +// NUMBER --> BOOLEAN +// Return whether or not a number is already a user ID +function is_user_id($id) { + if (in_array($id, $GLOBALS['users'])) { + return true; + } else { + return false; + } +} + +// ------------------------------------- + +// STRING --> BOOLEAN +// Return whether or not a given string is a valid (unused) username +function is_free_user_name($username) { + if (!is_user_name($username) && is_ne_string($username)) { + return true; } else { return false; } +} + +// STRING --> BOOLEAN +// Return whether or not a given number is a valid (unused) user ID +function is_free_user_id($id) { + if (!is_user_id($id) && is_int($id)) { + return true; } else { return false; } +} + + +// ------------------------------------- + + +?> diff --git a/res/lib/url.php b/res/lib/url.php new file mode 100644 index 0000000..e5dd707 --- /dev/null +++ b/res/lib/url.php @@ -0,0 +1,40 @@ + STRING +// Turn a path (relative to root) into absolute URL +function root_url($string) { + return protocol() . "://" . $_SERVER['HTTP_HOST'] . "/" + . $GLOBALS['root'] + . $string; +} + + +// NIL --> STRING +// Return the current protocol (HTTP, HTTPS, etc) +function protocol() { + return explode('/', $_SERVER['SERVER_PROTOCOL'])[0]; +} + + +// STRING --> NIL +// Redirect to a regular URL (I.E., "https://duckduckgo.com") +function redirect($url) { + header('Location: ' . $url); + return; +} + +// STRING --> NIL +// Redirect to a URL relative to root (I.E., "res/img/") +function root_redirect($path) { + header('Location: ' . root_url($path)); + return; +} diff --git a/res/lib/user.php b/res/lib/user.php index c8bcba4..fe65fce 100644 --- a/res/lib/user.php +++ b/res/lib/user.php @@ -28,16 +28,22 @@ function user_set($id, $variable, $new_value) { // NUMBER STRING STRING [STRING STRING STRING STRING STRING] --> BOOLEAN // Create a user of the given specification. -function user_create($id, $name, $password, $login="Spectator", +function user_create($id, $name, $password, $class="Spectator", $full_name=NULL, $email=NULL, $url=NULL, $bio=NULL) { return db_insert_row("lusers", - array("id", "username", "password_hash", "login", + array("id", "username", "hash", "class", "full_name", "email", "website", "biography"), - array($id, $name, $password, $login, + array($id, $name, $password, $class, $full_name, $email, $url, $bio)); } +// NUMBER --> BOOLEAN +// Delete a user by their ID. +function user_delete($id) { + return db_cmd("delete from lusers where id = " . $id); +} + // ------------------------------------- @@ -51,7 +57,7 @@ function user_id_to_name($id) { // STRING --> NUMBER // Get a user's ID from username. function user_name_to_id($username) { - return db_get_cell("lusers", "username", $username, "id"); + return db_get_cell("lusers", "username", string_wrap($username), "id"); } @@ -61,13 +67,13 @@ function user_name_to_id($username) { // NUMBER --> STRING // Return a username from a user-ID function user_name($id) { - return user_id_to_name($id, "username"); + return user_id_to_name($id); } // NUMBER --> STRING // Return a user's login-class from ID -function user_login($id) { - return user_get($id, "login"); +function user_class($id) { + return user_get($id, "class"); } // NUMBER --> STRING @@ -96,10 +102,46 @@ function user_biography($id) { // ------------------------------------- + +// NUMBER --> ARRAY +// Fetch an array of a user's IDs +function user_ids() { + return db_get_columns("lusers", "id"); +} + + +// ------------------------------------- + + // NUMBER --> ARRAY // Fetch an array of a user's posts (by ID) -function user_posts($user_id) { - return db_get_cell("posts", "user", $user_id, "id"); +function user_posts($id) { + return db_get_cell("posts", "user", $id, "id"); +} + + +// ------------------------------------- + + +// NUMBER --> ARRAY +// Return an array filled with all of a user's relevant data. +function user_data($id) { + return array('full_name' => user_full_name($id), + 'name' => user_name($id), + 'bio' => user_biography($id), + 'email' => user_email($id), + 'website' => user_website($id), + 'posts' => user_posts($id)); +} + + +// ------------------------------------- + + +// NUMBER STRING --> BOOLEAN +// Return whether or not a given password is valid. +function user_valid_password($id, $password) { + return password_verify($password, user_get($id, "hash")); } ?> diff --git a/res/themes/default/html/admin_index.twig.html b/res/themes/default/html/admin_index.twig.html index 69f1f91..33e57b4 100644 --- a/res/themes/default/html/admin_index.twig.html +++ b/res/themes/default/html/admin_index.twig.html @@ -1,5 +1,17 @@
+ +
+

+ +

+

+ +

+ +
+ +

@@ -17,21 +29,46 @@
+

+

+
+

+ +
+ +
+ +
+
+ +
+

+ +

+

+ +

+
+ +
+ +

+

- -
-
-
-
-
-
diff --git a/res/themes/default/html/error.twig.html b/res/themes/default/html/error.twig.html index 5015ded..38943c4 100644 --- a/res/themes/default/html/error.twig.html +++ b/res/themes/default/html/error.twig.html @@ -1,3 +1,4 @@

Eek!

Something was hecked.

-

{{ error_message }} Go back and try again.

+

{{ error_message }}

+

Go back and try again.

diff --git a/res/themes/default/html/navbar.twig.html b/res/themes/default/html/navbar.twig.html index 349102c..e69de29 100644 --- a/res/themes/default/html/navbar.twig.html +++ b/res/themes/default/html/navbar.twig.html @@ -1,10 +0,0 @@ - - - - - - Index - - - -{{ url }}

{{ bio }}

diff --git a/user.php b/user.php index 558cbbf..1fc93bd 100644 --- a/user.php +++ b/user.php @@ -8,21 +8,32 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. */ + $depth = ""; -$title = "About"; include "res/lib/load.php"; +// ------------------------------------- -echo $GLOBALS['twig']->render('head.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth, - 'title' =>$title]); +$id = $_GET['id'] ?? user_name_to_id($_GET['name']); +$name = user_name($id); -echo $GLOBALS['twig']->render('index.twig.html', - ['animal'=> "cat"]); +// ------------------------------------- -echo $GLOBALS['twig']->render('foot.twig.html', - ['theme' => $GLOBALS['theme'], - 'depth' => $depth]); +if (!is_user_id($id)) { + general_error("It looks like that isn't a real user."); +} +if (empty($name)) { + general_error("It looks like that isn't a real user..."); +} + +// ------------------------------------ + +$local_exports = array('full_name' => user_full_name($id), 'name' => $name, + 'bio' => user_biography($id), 'email' => + user_email($id), 'website' => user_website($id)); + +// ------------------------------------- + +display_page("user.twig.html", $depth, $title, $local_exports); ?>