From e88eb13d9104959af42b9b2460aea003444f4bf5 Mon Sep 17 00:00:00 2001 From: Krombel Date: Fri, 23 Feb 2018 15:28:50 +0100 Subject: [PATCH] implement hasUser(localpart), RegisterState add verify{_admin}.php - fixes --- MatrixConnection.php | 33 +++++-- config.sample.php | 7 +- database.php | 59 ++++++++---- functions.php | 1 - lang/lang.de.php | 10 ++ public/register.php | 203 +++++++++++++++++++++++----------------- public/verify.php | 86 +++++++++++++++++ public/verify_admin.php | 164 ++++++++++++++++++++++++++++++++ 8 files changed, 448 insertions(+), 115 deletions(-) create mode 100644 public/verify.php create mode 100644 public/verify_admin.php diff --git a/MatrixConnection.php b/MatrixConnection.php index 36b6d4d..21df64f 100644 --- a/MatrixConnection.php +++ b/MatrixConnection.php @@ -1,12 +1,10 @@ hs = $homeserver; - } function __construct($homeserver, $access_token) { $this->hs = $homeserver; $this->at = $access_token; @@ -24,7 +22,7 @@ class MatrixConnection } elseif(is_array($message)) { $send_message = $message; } elseif ($message instanceof MatrixMessage) { - $sendmessage = $message->get_object(); + $send_message = $message->get_object(); } else { error_log("message is of not valid type\n"); return false; @@ -36,7 +34,7 @@ class MatrixConnection curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($handle, CURLOPT_TIMEOUT, 60); - curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($message)); + curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($send_message)); curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json")); return exec_curl_request($handle); @@ -50,9 +48,26 @@ class MatrixConnection ); } + function hasUser($username) { + if (!$username) { + throw new Exception ("no user given to lookup"); + } + + $url = "https://".$this->hs."/_matrix/client/r0/profile/%40" . $username . "%3A" . $this->hs; + $handle = curl_init($url); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($handle, CURLOPT_TIMEOUT, 60); + curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json")); + curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($data)); + + $res = exec_curl_request($handle); + return ($res ? true: false); + } + function register($username, $password, $shared_secret) { if (!$username) { - error_log("no username provided") + error_log("no username provided"); } if (!$password) { error_log("no message to send"); @@ -64,8 +79,8 @@ class MatrixConnection "username" => $user, "password" => $password, "mac" => $mac, - } - $url="https://".$this->hs."/_matrix/client/v2_alpha/register"; + ); + $url = "https://".$this->hs."/_matrix/client/v2_alpha/register"; $handle = curl_init($url); curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); @@ -88,7 +103,7 @@ class MatrixMessage } function set_type($msgtype) { - $this->$message["msgtype"] = $msgtype; + $this->message["msgtype"] = $msgtype; } function set_format($format) { diff --git a/config.sample.php b/config.sample.php index 57399d6..419a682 100644 --- a/config.sample.php +++ b/config.sample.php @@ -1,6 +1,9 @@ \ No newline at end of file + +$webroot="https://myregisterdomain.net/"; +$howToURL = "https://my-url-for-storing-howTos.net"; +?> diff --git a/database.php b/database.php index 9db93e2..eee73a0 100644 --- a/database.php +++ b/database.php @@ -1,28 +1,55 @@ setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $db->exec("CREATE TABLE registrations( - id INTEGER PRIMARY KEY AUTOINCREMENT, - first_name TEXT, - last_name TEXT, - username TEXT, - note TEXT, - email TEXT, - verify_token TEXT, - request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); -} + $db = new PDO('sqlite:' . $db_file); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->exec("CREATE TABLE registrations( + id INTEGER PRIMARY KEY AUTOINCREMENT, + state INT DEFAULT 0, + first_name TEXT, + last_name TEXT, + username TEXT, + note TEXT, + email TEXT, + verify_token TEXT, + admin_token TEXT, + request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); +} else { - // establish connection - $db = new PDO('sqlite:' . $db_file); - $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + // establish connection + $db = new PDO('sqlite:' . $db_file); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } // set writeable when not set already if (!is_writable($db_file)) { - chmod($db_file, 0777); + chmod($db_file, 0777); } ?> diff --git a/functions.php b/functions.php index 949df06..5559eea 100644 --- a/functions.php +++ b/functions.php @@ -47,6 +47,5 @@ function exec_curl_request($handle) } return $response; - } ?> diff --git a/lang/lang.de.php b/lang/lang.de.php index 98ef520..0b044ef 100644 --- a/lang/lang.de.php +++ b/lang/lang.de.php @@ -3,12 +3,22 @@ $language = array( "NO_CONFIGURATION" => "Es konnte keine Konfiguration gefunden werden.", "UNKNOWN_SESSION" => "Sitzungstoken nicht vorhanden oder ungültig.", "UNKNOWN_USERNAME" => "Nutzername fehlt", + "UNKNOWN_TOKEN" => "Token ist unbekannt", "USERNAME_LENGTH_INVALID" => "Entweder mehr als 20 oder weniger als 3 Zeichen für den Nutzernamen verwendet", "USERNAME_NOT_ALNUM" => "Nutzername ist nicht alphanumerisch", + "USERNAME_PENDING_REGISTRATION" => "Dieser Nutzername wurde bereits zur Registrierung vorgemerkt. Versuche es später noch einmal oder wähle einen anderen Nutzernamen", + "USERNAME_REGISTERED" => "Dieser Nutzername wurde bereits registriert. Bitte wähle einen anderen Nutzernamen", "PASSWORD_NOT_MATCH" => "Passwörter stimmen nicht überein", "NOTE_LENGTH_EXEEDED" => "Notiz ist länger als die erlaubten 50 Zeichen", "EMAIL_INVALID_FORMAT" => "Keine valide E-Mail-Adresse angegeben", "FIRSTNAME_INVALID_FORMAT" => "Vorname hat ungültiges Format", "SIRNAME_INVALID_FORMAT" => "Nachname hat ungültiges Format", + "SEND_MAIL_FAIL" => "Senden der E-Mail fehlgeschlagen", + "SEND_MATRIX_FAIL" => "Senden einer Nachricht an die Administratoren fehlgeschlagen", + "REGISTRATION_REQUEST_FAILED" => "Registrierungsanfrage ist fehlgeschlagen", + "VERIFICATION_SUCCEEDED" => "Verifizierung erfolgreich", + "VERIFICATION_FAILED" => "Verifizierung fehlgeschlagen", + "VERIFICATION_SUCCESS_BODY" => "Vielen Dank. Die Administratoren wurden informiert", + "ADMIN_VERIFY_SITE_TITLE" => "Registrierungsanfrage bearbeiten", ); ?> diff --git a/public/register.php b/public/register.php index 4e6f0eb..8a1e2b5 100644 --- a/public/register.php +++ b/public/register.php @@ -7,6 +7,7 @@ if (!file_exists("../config.php")) { exit(); } require_once "../config.php"; +require_once "../mail_templates.php"; // enforce admin via https if (!isset($_SERVER['HTTPS'])) { @@ -16,67 +17,95 @@ if (!isset($_SERVER['HTTPS'])) { session_start(); +require_once("../database.php"); + if ($_SERVER["REQUEST_METHOD"] == "POST") { - $success = false; - if (!isset($_SESSION["token"]) || !isset($_POST["token"]) || $_SESSION["token"] != $_POST["token"]) { - // token not present or invalid - $message = $language["UNKNOWN_SESSION"]; - } - elseif (!isset($_POST["username"])) { - $message = $language["UNKNOWN_USERNAME"]; - } - elseif (strlen($_POST["username"] > 20 || strlen($_POST["username"]) < 3)) { - $message = $language["USERNAME_LENGTH_INVALID"]; - } - elseif (ctype_alnum($_POST['username']) != true) { - $message = $language["USERNAME_NOT_ALNUM"]; - } - elseif (isset($_POST["note"]) && strlen($_POST["note"]) > 50) { - $message = $language["NOTE_LENGTH_EXEEDED"]; - } - elseif (!isset($_POST["email"]) || !filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) { - $message = $language["EMAIL_INVALID_FORMAT"]; - } - elseif (isset($_POST["first_name"]) && ! preg_match("/[A-Z][a-z]+/", $_POST["first_name"])) { - $message = $language["FIRSTNAME_INVALID_FORMAT"]; - } - elseif (isset($_POST["last_name"]) && ! preg_match("/[A-Z][a-z]+/", $_POST["last_name"])) { - $message = $language["SIRNAME_INVALID_FORMAT"]; - } - else { + try { + if (!isset($_SESSION["token"]) || !isset($_POST["token"]) || $_SESSION["token"] != $_POST["token"]) { + // token not present or invalid + throw new Exception($language["UNKNOWN_SESSION"]); + } + if (!isset($_POST["username"])) { + throw new Exception($language["UNKNOWN_USERNAME"]); + } + if (strlen($_POST["username"] > 20 || strlen($_POST["username"]) < 3)) { + throw new Exception($language["USERNAME_LENGTH_INVALID"]); + } + if (ctype_alnum($_POST['username']) != true) { + throw new Exception($language["USERNAME_NOT_ALNUM"]); + } + if (isset($_POST["note"]) && strlen($_POST["note"]) > 50) { + throw new Exception($language["NOTE_LENGTH_EXEEDED"]); + } + if (!isset($_POST["email"]) || !filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) { + throw new Exception($language["EMAIL_INVALID_FORMAT"]); + } + if (isset($_POST["first_name"]) && ! preg_match("/[A-Z][a-z]+/", $_POST["first_name"])) { + throw new Exception($language["FIRSTNAME_INVALID_FORMAT"]); + } + if (isset($_POST["last_name"]) && ! preg_match("/[A-Z][a-z]+/", $_POST["last_name"])) { + throw new Exception($language["SIRNAME_INVALID_FORMAT"]); + } + // check valid password - require_once("../database.php"); - $ins_stmt = $db->prepare("INSERT INTO registrations - (first_name, last_name, note, email, username, verify_token) - VALUES (:first, :last, :note, :email, :username, :token )"); - $ins_stmt->bindParam(':first', $first); - $ins_stmt->bindParam(':last', $last); - $ins_stmt->bindParam(':note', $note); - $ins_stmt->bindParam(':email', $email); - $ins_stmt->bindParam(':username', $user); - $ins_stmt->bindParam(':token ', $vToken); + $first_name = filter_var($_POST["first_name"], FILTER_SANITIZE_STRING); + $last_name = filter_var($_POST["last_name"], FILTER_SANITIZE_STRING); + $username = filter_var($_POST["username"], FILTER_SANITIZE_STRING); + $note = filter_var($_POST["note"], FILTER_SANITIZE_STRING); + $email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL); + $verify_token = bin2hex(random_bytes(16)); + $admin_token = bin2hex(random_bytes(16)); - $first = filter_var($_POST["first_name"], FILTER_SANITIZE_STRING); - $last = filter_var($_POST["last_name"], FILTER_SANITIZE_STRING); - $user = filter_var($_POST["username"], FILTER_SANITIZE_STRING); - $note = filter_var($_POST["note"], FILTER_SANITIZE_STRING); - $email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL); - $vToken= bin2hex(random_bytes(16)); + # $first="test"; $last="test2"; $user="test3"; $note="empty"; $email="mail+test1@matthias-kesler.de"; + + $sql = "SELECT COUNT(*) FROM registrations WHERE username = '" . $username . "' LIMIT 1;"; + $res = $db->query($sql); + if ($res->fetchColumn() > 0) { + throw new Exception($language["USERNAME_PENDING_REGISTRATION"]); + } + require_once("MatrixConnection.php"); + $mxConn = new MatrixConnection($homeserver, $access_token); + if ($mxConn->hasUser($username)) { + throw new Exception($language["USERNAME_REGISTERED"]); + } + + $db->exec('INSERT INTO registrations + (first_name, last_name, username, note, email, verify_token, admin_token) + VALUES ("' . $first_name.'","' . $last_name . '","' . $username . '","' . $note . '","' + . $email.'","' .$verify_token.'","' .$admin_token.'")'); + # $ins_stmt->bindValue(':first_name', $first); + # $ins_stmt->bindValue(':last_lame', $last); + # $ins_stmt->bindValue(':username', $user); + # $ins_stmt->bindValue(':note', $note); + # $ins_stmt->bindValue(':email', $email); + # $ins_stmt->bindValue(':verify_token', $vToken); + # $ins_stmt->bindValue(':admin_token', $adminToken); + # $ins_stmt->bindValue(':now', date('Y-m-d H:i:s')); + # + # $ins_stmt->execute(); + + $verify_url = $webroot . "/verify.php?t=" . $verify_token; + $success = send_mail_pending_verification( + $homeserver, + $first_name . " " . $last_name, + $email, + $verify_url); + + $db->exec("UPDATE registrations SET state = " . + ($success ? RegisterState::PendingEmailVerify : RegisterState::PendingEmailSend) + . " WHERE verify_token = \"" . $verify_token. "\";"); - $ins_stmt->execute(); - $success = true; - } - if ($success) { print("Erfolgreich"); print(""); print("

Erfolgreich

"); print("

Bitte überprüfe deine E-Mails um deine E-Mail-Adresse zu bestätigen.

"); print("Zur Registrierungsseite"); - } else { - print("".$message.""); + } catch (Exception $e) { + print("" . $language["REGISTRATION_REQUEST_FAILED"] . ""); print(""); - print("

" . $message . "

"); - print("Zur Registrierungsseite"); + print("

" . $language["REGISTRATION_REQUEST_FAILED"] . "

"); + print("

" . $e->getMessage() . "

"); + print("Zur Registrierungsseite"); } } else { $_SESSION["token"] = bin2hex(random_bytes(16)); @@ -139,18 +168,18 @@ body{ required> -
-
- -
-
-
-
- -
-
- +
+
+
+ +
+
+
+
+ +
+
+
*/ ?> "> @@ -167,29 +196,29 @@ body{ - + + - \ No newline at end of file diff --git a/public/verify.php b/public/verify.php new file mode 100644 index 0000000..52e9896 --- /dev/null +++ b/public/verify.php @@ -0,0 +1,86 @@ + + +query($sql); + + $first_name = NULL; $last_name = NULL; $note = NULL; $email = NULL; $admin_token = NULL; + + if ($res->fetchColumn() > 0) { + $sql = "SELECT first_name, last_name, note, email, admin_token FROM registrations WHERE verify_token = '" . $token . "' LIMIT 1;"; + foreach ($db->query($sql) as $row) { + // will only be executed once + $first_name = $row["first_name"]; + $last_name = $row["last_name"]; + $note = $row["note"]; + $email = $row["email"]; + $admin_token = $row["admin_token"]; + } + } else { + throw new Exception($language["UNKNOWN_TOKEN"]); + } + + require_once("../MatrixConnection.php"); + $adminUrl = $webroot . "/admin_verify.php?t=" . $admin_token; + $mxConn = new MatrixConnection($homeserver, $access_token); + $mxMsg = new MatrixMessage(); + $mxMsg->set_body($first_name . ' ' . $last_name . "möchte sich registrieren und hat folgende Notiz hinterlassen:\r\n" + . $note . "\r\n" + . "Zum Bearbeiten hier klicken:\r\n" . $adminUrl); + $mxMsg->set_formatted_body($first_name . ' ' . $last_name . " möchte sich registrieren und hat folgende Notiz hinterlassen:
" + . $note . "
" + . "Zum Bearbeiten hier klicken"); + $mxMsg->set_type("m.text"); + $response = $mxConn->send($register_room, $mxMsg); + + if ($response) { + $message = $language["SEND_MATRIX_FAIL"]; + } + $db->exec("UPDATE registrations SET state = " . + ($response ? RegisterState::PendingAdminVerify : RegisterState::PendingAdminSend) + . " WHERE verify_token = \"" . $verify_token. "\";"); + + send_mail_pending_approval($homeserver, $first_name . " " . $last_name, $email); + + print("" . $language["VERIFICATION_SUCEEDED"] . ""); + print(""); + print("

" . $language["VERIFICATION_SUCEEDED"] . "

"); + print("

" . $language["VERIFICATION_SUCCESS_BODY"] . "

"); + print("Zur Registrierungsseite"); +} catch (Exception $e) { + print("" . $language["VERIFICATION_FAILED"] . ""); + print(""); + print("

" . $language["VERIFICATION_FAILED"] . "

"); + print("

" . $e->getMessage() . "

"); + print("Zur Registrierungsseite"); +} +?> + + diff --git a/public/verify_admin.php b/public/verify_admin.php new file mode 100644 index 0000000..fb9243a --- /dev/null +++ b/public/verify_admin.php @@ -0,0 +1,164 @@ + + +query($sql); + + $first_name = NULL; $last_name = NULL; $username = NULL; $note = NULL; $email = NULL; + + if ($res->fetchColumn() > 0) { + $sql = "SELECT first_name, last_name, username, note, email FROM registrations WHERE admin_token = '" . $token . "' LIMIT 1;"; + foreach ($db->query($sql) as $row) { + // will only be executed once + $first_name = $row["first_name"]; + $last_name = $row["last_name"]; + $username = $row["username"]; + $note = $row["note"]; + $email = $row["email"]; + } + } else { + throw new Exception($language["UNKNOWN_TOKEN"]); + } + + if ($action == RegisterState::RegistrationAccepted) { + $db->exec("UPDATE registrations SET state = " . RegisterState::RegistrationAccepted) + . " WHERE admin_token = \"" . $token. "\";"); + + // register user + require_once("MatrixConnection.php"); + $mxConn = new MatrixConnection($homeserver, $access_token); + + // generate a password with 8 characters + $password = bin2hex(openssl_random_pseudo_bytes(4)); + + $res = $mxConn->register($username, $password, $shared_secret); + + // send registration_success + send_mail_registration_success($homeserver, $first_name . " " . $last_name, $email, $username, $password, $howToURL) + } elseif ($action == RegisterState::RegistrationDeclined) { + $db->exec("UPDATE registrations SET state = " . RegisterState::RegistrationAccepted) + . " WHERE admin_token = \"" . $token. "\";"); + } + + $adminUrl = $webroot . "/admin_verify.php?t=" . $admin_token; + print("" . $language["ADMIN_VERIFY_SITE_TITLE"] . ""); +?> + + + + + + +
+
+
+
+
+

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