diff --git a/account.php b/account.php new file mode 100644 index 0000000..4812057 --- /dev/null +++ b/account.php @@ -0,0 +1,132 @@ +prepare('SELECT * FROM accounts WHERE id = ? LIMIT 1'); +$stmt->execute([$_SESSION['id']]); +$user = $stmt->fetch(); + + +if ($_SERVER['REQUEST_METHOD'] == "POST") { + + if (isset($_POST["old_password"]) && $_POST["old_password"] != "") { + // means password reset is wanted. + + if (!password_verify($_POST["old_password"], $user["password"])) { + $password_error = "Incorrect password. (Error 901)"; + } + + if (password_verify($_POST['new_password'], $user["password"])) { + $password_error = "New password may not be same as old password. (Error 902)"; + } + + if ($_POST['new_password'] != $_POST['repeat_new_password']) { + $password_error = "The passwords must match. (Error 900)"; + } + + + if (isset($password_error)) { + $message = $password_error; + goto skip_submit; + } + + $new_password = password_hash($_POST["new_password"], PASSWORD_DEFAULT); + + $sql = "UPDATE accounts SET password = ? WHERE id = ?"; + $pdo->prepare($sql)->execute([$new_password, $user["id"]]); + } + + if (isset($_POST["display_name"])) { + $sql = "UPDATE accounts SET display_name = ? WHERE id = ?"; + $pdo->prepare($sql)->execute([$_POST["display_name"], $user["id"]]); + } + + $message = "Updated sucessfully. Changes might take a few minutes to take effect."; + +} + +skip_submit: + +?> + +

Your account

+ +".$message.""; + } +?> + +
+ +
+ + + +
+
+ +
+
+ Profile +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ Password +

You only need to insert values here if you're resetting your password.

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+

Danger Zone

+

Sign out Delete account

+
+ diff --git a/admin_accounts.php b/admin_accounts.php new file mode 100644 index 0000000..483fcf7 --- /dev/null +++ b/admin_accounts.php @@ -0,0 +1,36 @@ +"); +} + +$sql = "SELECT * FROM accounts"; +$result = $pdo-> query($sql); +if (!$result) { + http_response_code(500); + die(""); +} + + +$count_req = $pdo->query("SELECT COUNT(*) FROM accounts"); +$count = $count_req->fetchColumn(); + + +?> + +

Admin

+

Accounts

+ +

There is currently accounts registered.

+ + + diff --git a/admin_initdatabase.php b/admin_initdatabase.php new file mode 100644 index 0000000..4ff1220 --- /dev/null +++ b/admin_initdatabase.php @@ -0,0 +1,51 @@ +"); +} + +if ($_SERVER["REQUEST_METHOD"] == "POST") { + if ($_POST['init'] == 'Init') { + echo("

Initialising DB..."); + $pdo = new PDO(DB_DSN, DB_USERNAME, DB_PASSWORD, PDO_OPTIONS); + echo "

Create table `accounts`"; + $stmt = $pdo->prepare('CREATE TABLE `accounts` ( + `id` tinytext NOT NULL, + `email` text NOT NULL,, + `display_name` text NULL, + `password` text NOT NULL, + `verified` tinyint(1) NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;'); + + try { + $stmt->execute(); + } catch (PDOException $e) { + echo('

An error occurred: '. $e->getMessage() .'. Will skip. (Most likely the table already exists.)'); + } + + echo '

Set indexes for table `accounts`'; + $stmt = $pdo->prepare('ALTER TABLE `accounts` + ADD PRIMARY KEY (`id`(7)), + ADD UNIQUE KEY `email` (`email`) USING HASH;'); + + try { + $stmt->execute(); + } catch (PDOException $e) { + echo('

An error occurred: '. $e->getMessage() .'. Most likely this is already set.'); + } + + echo "

Database initialised.

"; + } +} + +?> + +

Admin

+

Init database

+ +

Assuming you have the database config configured, you can click this button to create the tables required for this thing to function.

+ +
+ +
diff --git a/db.php b/db.php new file mode 100644 index 0000000..e69de29 diff --git a/forgot_password.php b/forgot_password.php new file mode 100644 index 0000000..15ced1a --- /dev/null +++ b/forgot_password.php @@ -0,0 +1,31 @@ + +prepare($sql); + $stmt->execute([$_POST['email']]); + $user = $stmt->fetch(); + + if ($user != null) { // account exists + mail($user['email'], "ByeCorps ID Password Reset Confirmation", "The email was sent!"); + } +} + +?> + +

Forgot password

+ +".$message."

"; ?> + +

Forgot your password? We'll send you an email to reset it.

+ +
+ + +
\ No newline at end of file diff --git a/header.php b/header.php index c9ea57e..7cae72e 100644 --- a/header.php +++ b/header.php @@ -1,5 +1,25 @@ +prepare($sql); + $stmt->execute([$_SESSION['id']]); + $name = $stmt->fetchColumn(); +} + +if ($name == '') { + $name = ''.format_bcid($_SESSION['id']).''; +} + +skip_auth: + +?> + @@ -8,8 +28,12 @@ ByeCorps ID
+ +
- +
+ +
\ No newline at end of file diff --git a/id.sql b/id.sql index c1a24cd..86a603a 100644 --- a/id.sql +++ b/id.sql @@ -30,7 +30,8 @@ SET time_zone = "+00:00"; CREATE TABLE `accounts` ( `id` tinytext NOT NULL COMMENT 'BCID', `email` text NOT NULL, - `password` text NOT NULL COMMENT 'Hashed!!!', + `display_name` text NULL, + `password` text NOT NULL, `verified` tinyint(1) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/id_handler.php b/id_handler.php index 01615cd..ac66b35 100644 --- a/id_handler.php +++ b/id_handler.php @@ -1,6 +1,6 @@ "; -echo "Random BCID (unformatted): $BCID -"; -echo "Check if BCID is valid: ".validate_bcid($BCID)." -"; + if (!validate_bcid($stripped_bcid)) { + throw new Exception('Invalid BCID.'); + } -if ($query['bcid']) { - echo "BCID provided in the query: ".$query['bcid']." -"; - echo "Checking the BCID provided in the query: ".validate_bcid($query['bcid'])." -"; + return substr($stripped_bcid, 0, 3).'-'.substr($stripped_bcid, -4, 4); } + +$BCID = generate_bcid(); ?> \ No newline at end of file diff --git a/index.php b/index.php index f2f3bed..5bb9cda 100644 --- a/index.php +++ b/index.php @@ -3,12 +3,22 @@ session_start(); include("config.php"); +include("id_handler.php"); +include("time_handler.php"); + +function does_variable_exists( $variable ) { + return (isset($$variable)) ? "true" : "false"; +} $host_string = $_SERVER['HTTP_HOST']; $host = explode('.', $host_string); $uri_string = $_SERVER['REQUEST_URI']; $query_string = explode('?', $uri_string); $path = $query_string[0]; +if (str_ends_with($path,'/') && $path != "/") { + header('Location: '.substr($path,0, -1)); + exit; +} $uri = array_values(array_filter(explode('/', $uri_string))); if(isset($query_string[1])) { @@ -21,30 +31,33 @@ if(isset($query_string[1])) { } } else { - $query = array(); + $query = array(); } +$pdo = new PDO(DB_DSN, DB_USERNAME, DB_PASSWORD, PDO_OPTIONS); + $include = "404.html"; - // routing -if (!$uri) { - // empty array means index - $include = "landing.html"; -} -else if ($path == "/signin") { - $doc_title = "Sign in"; - include("signin.php"); - exit; -} -else if ($path == "/register") { - $doc_title = "Register"; - include("register.php"); - exit; -} -else if ($path == "/tests/id") { - include("id_handler.php"); - exit; + +$paths = array( + "/" => ["landing.php"], + "/admin/init/database" => ["admin_initdatabase.php"], + "/admin/accounts" => ["admin_accounts.php"], + "/account" => ["account.php", "Your account"], + "/signin" => ["signin.php", "Sign in"], + "/signup" => ["signup.php", "Sign up"], + "/signout" => ["signout.php", "Signed out"], + "/forgot_password" => ["forgot_password.php", "Forgot password"], + "/admin/signinas" => ["signinas.php"] +); + +if (isset($paths[$path])) { + $include = $paths[$path][0]; + if (isset($paths[$path][1])) { + $doc_title = $paths[$path][1]; + } } + else { $doc_title = "404"; http_response_code(404); @@ -60,7 +73,14 @@ else {
- + "); + } + + include($include); ?>
diff --git a/landing.html b/landing.php similarity index 55% rename from landing.html rename to landing.php index 6ad7729..c60788c 100644 --- a/landing.html +++ b/landing.php @@ -3,7 +3,11 @@

ByeCorps ID

Log into ByeCorps and beyond with a single ID.

- Sign in - Create an account + + Manage account"; } + else { echo "Sign inCreate an account"; } + ?> + \ No newline at end of file diff --git a/register.php b/register.php deleted file mode 100644 index eb8ce98..0000000 --- a/register.php +++ /dev/null @@ -1,56 +0,0 @@ -setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $sql = "INSERT INTO `accounts` (`email`, `password`, `verified`) VALUES ('$email', '$password', '0')"; - try{ - $stmt = $conn->prepare($sql); - $stmt->execute($query); - $result = $stmt->fetch(); - echo "Failed successfully: $result"; - } catch (PDOException $e) { - http_response_code(500); - die("An error occured: $e"); - } - } - catch(PDOException $e) { - die ("Connection failed: " . $e->getMessage()); - } - echo '
';
-    print_r($_POST);
-
-    exit;
-}
-
-?>
-
-
-
-
-    
-
-
-    
-    
-

Sign in

-
- - - -
-
- - - \ No newline at end of file diff --git a/signin.php b/signin.php new file mode 100644 index 0000000..03614f1 --- /dev/null +++ b/signin.php @@ -0,0 +1,56 @@ +prepare($sql); + $stmt->execute(array("email"=> $email)); + $user = $stmt->fetch(); + } + catch (PDOException $e) { + die ("Something happened: ". $e->getMessage()); + } + + if (password_verify($password, $user["password"])) { + $_SESSION["id"] = $user["id"]; + $_SESSION["auth"] = true; + if (isset($query['callback'])) { + header("Location: ".$query['callback']); + } else { + header("Location: /account"); + } + + exit; + } else { + $message = "Email or password incorrect."; + } +} + +?> + + +

Sign in to ByeCorps ID

+$message"; +}?> +
+ + + +
+ +

+ New? Register for a ByeCorps ID. +

\ No newline at end of file diff --git a/signinas.php b/signinas.php new file mode 100644 index 0000000..d31fbfc --- /dev/null +++ b/signinas.php @@ -0,0 +1,12 @@ +"); +} + +$_SESSION['id'] = $query['id']; + +header ('Location: /account'); + +?> \ No newline at end of file diff --git a/signout.php b/signout.php new file mode 100644 index 0000000..fb565e5 --- /dev/null +++ b/signout.php @@ -0,0 +1,8 @@ + + +

You've been signed out successfully. You may close the page.

+

Sign back in ~ Go to home

diff --git a/signup.php b/signup.php new file mode 100644 index 0000000..9d70fca --- /dev/null +++ b/signup.php @@ -0,0 +1,46 @@ +prepare($sql); + $stmt->execute([$BCID, $email, $password, 0]); + $result = $stmt->fetch(); + echo "Failed successfully: $result"; + } catch (PDOException $e) { + http_response_code(500); + die("An error occured: $e"); + } + } + catch(PDOException $e) { + die ("Connection failed: " . $e->getMessage()); + } + + $_SESSION["auth"] = true; + $_SESSION["id"] = $BCID; + + exit; +} + +?> + +

Sign up for ByeCorps ID

+
+ + + +
\ No newline at end of file diff --git a/strings.php b/strings.php new file mode 100644 index 0000000..c0cc8c0 --- /dev/null +++ b/strings.php @@ -0,0 +1,16 @@ + "Message" + + // XX errors are generic messages + + // 9XX errors are user error + "900" => "Sorry, those passwords don't match. Please try again.", + "901" => "Incorrect password. Please check your spelling and try again." + +] + +?> \ No newline at end of file diff --git a/styles/colours.css b/styles/colours.css index 2d91167..61ee275 100644 --- a/styles/colours.css +++ b/styles/colours.css @@ -13,14 +13,31 @@ --flax: #efdd8d; --mindaro: #f4fdaf; + /* open colors: used for debugging */ + --red-5: #ff6b6b; + --red-5-transparent: #ff6b6b3a; + --red-8: #e03131; + --green-5: #51cf66; + --green-8: #2f9e44; + color-scheme: light dark; } +button, .button { + background-color: #1f302b40; + color: var(--white); +} + button.primary, .button.primary { color: var(--black-bean); background-color: var(--flax); } +button.danger, .button.danger { + color: var(--white); + background-color: var(--red-5); +} + header { background-color: var(--flax); color: var(--dark-slate-gray); @@ -30,6 +47,49 @@ header a { color: var(--dark-slate-gray); } +input { + all: unset; + padding: 1em; + text-align: start; + + border-radius: 1em; + + background-color: #c0c0c077; +} + +input[data-com-onepassword-filled="light"] { + background-color: var(--byecorps-white) !important; +} + +input[data-com-onepassword-filled="dark"] { + background-color: var(--byecorps-blue) !important; +} + +.icon-true { + color: var(--green-8); +} + +.icon-false { + color: var(--red-8); +} + +.dangerzone { + background-color: var(--red-5-transparent); + color: var(--white); + + padding: 0.5rem 1em; + border-radius: 1em; + +} + +.dangerzone h2 { + margin: 0; +} + +.dangerzone p { + margin: 0; +} + @media screen and (prefers-color-scheme: dark) { button.primary, .button.primary { color: var(--flax); @@ -44,4 +104,21 @@ header a { header a { color: var(--flax); } + + input { + + background-color: #2c2c2c77; + } + + .icon-true { + color: var(--green-5); + } + + .icon-false { + color: var(--red-5); + } + + a, a:visited, a:link { + color: var(--flax); + } } \ No newline at end of file diff --git a/styles/design.css b/styles/design.css index 7de58d3..27cc92b 100644 --- a/styles/design.css +++ b/styles/design.css @@ -28,3 +28,20 @@ input { border-radius: 1em; } + +input[type="checkbox"] { + -webkit-appearance: checkbox; + -moz-appearance: checkbox; + -ms-appearance: checkbox; + -o-appearance: checkbox; + appearance: checkbox; + + width: 1em; + height: 1em; + margin: 0 0.5em 0 0; +} + +input:disabled { + opacity: 0.75; + cursor: not-allowed; +} diff --git a/styles/global.css b/styles/global.css index 59f2674..379b764 100644 --- a/styles/global.css +++ b/styles/global.css @@ -3,10 +3,8 @@ @import url(./layout.css); @import url(./colours.css); -:root { - color-scheme: light dark; -} * { box-sizing: border-box; + } diff --git a/styles/layout.css b/styles/layout.css index 7d0829c..6e4f2b4 100644 --- a/styles/layout.css +++ b/styles/layout.css @@ -33,6 +33,7 @@ header .end { } main { + height: 100%; flex: 1; padding: 1rem 1rem; } @@ -47,10 +48,69 @@ footer { gap: 1rem; } +fieldset { + + border: #c0c0c0c0 1px solid; + border-radius: calc(1em + 10px); + + padding: 10px 10px 5px 10px; +} + +legend { + font-size: 1.25rem; + font-weight: 600; +} + +form:has(fieldset) { + + /* fit two fieldsets side by side */ + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + + +} + +form:has(fieldset) > button[type="submit"] { + /* align the button to the right */ + grid-column: span 2; +} + +form .container { + /* contains a label and an input */ + display: flex; + flex-direction: column; + gap: 0.25rem; + padding-bottom: 5px; +} + +form .container:has(input[type="checkbox"]) { + flex-direction: row; +} + +form .container label { + font-size: 0.9rem; + opacity: 0.5; +} + +form .container:has(input[type="checkbox"]) label { + margin-left: 0.5em; + opacity: 1; + font-size: 1rem; +} + footer h2 { margin: 0; } +form { + display: flex; + flex-direction: column; + gap: 1rem; +} + + + .hero { display: flex; flex-direction: column; @@ -66,4 +126,5 @@ footer h2 { .accountnav { display: flex; gap: 1rem; -} \ No newline at end of file +} + diff --git a/styles/types.css b/styles/types.css index 6fce88f..83ae2a8 100644 --- a/styles/types.css +++ b/styles/types.css @@ -1,6 +1,8 @@ /* This file deals with font types and font families. */ @import url(https://fonts.bunny.net/css?family=montserrat:400,400i,600,600i,700,700i,900,900i); +@import url(https://fonts.bunny.net/css2?family=courier+prime:wght@400;700&display=swap); /* for BCIDs */ + @import url(/fontawesome/css/all.css); html { @@ -10,6 +12,16 @@ html { -moz-osx-font-smoothing: grayscale; } +h2.subheading { + font-weight: 500; + font-size: 1.5rem; + margin-bottom: 0; +} + +h2.subheading + h1 { + margin-top: 0; +} + .bc-1 { font-weight: 700; } @@ -22,6 +34,18 @@ html { font-weight: 400; } +.bcid { + font-family: 'Courier Prime', monospace; +} + .center { text-align: center; } + +.icon-true::before { + content: "\f00c"; +} + +.icon-false::before { + content: "\f00d"; +} diff --git a/time_handler.php b/time_handler.php new file mode 100644 index 0000000..cae9b56 --- /dev/null +++ b/time_handler.php @@ -0,0 +1,47 @@ + 0) + { + $day_diff = floor($diff / 86400); + if($day_diff == 0) + { + if($diff < 60) return 'just now'; + if($diff < 120) return '1 minute ago'; + if($diff < 3600) return floor($diff / 60) . ' minutes ago'; + if($diff < 7200) return '1 hour ago'; + if($diff < 86400) return floor($diff / 3600) . ' hours ago'; + } + if($day_diff == 1) return 'Yesterday'; + if($day_diff < 7) return $day_diff . ' days ago'; + if($day_diff < 31) return ceil($day_diff / 7) . ' weeks ago'; + if($day_diff < 60) return 'last month'; + return date('F Y', $ts); + } + else + { + $diff = abs($diff); + $day_diff = floor($diff / 86400); + if($day_diff == 0) + { + if($diff < 120) return 'in a minute'; + if($diff < 3600) return 'in ' . floor($diff / 60) . ' minutes'; + if($diff < 7200) return 'in an hour'; + if($diff < 86400) return 'in ' . floor($diff / 3600) . ' hours'; + } + if($day_diff == 1) return 'Tomorrow'; + if($day_diff < 4) return date('l', $ts); + if($day_diff < 7 + (7 - date('w'))) return 'next week'; + if(ceil($day_diff / 7) < 4) return 'in ' . ceil($day_diff / 7) . ' weeks'; + if(date('n', $ts) == date('n') + 1) return 'next month'; + return date('F Y', $ts); + } +} + +?> \ No newline at end of file