First running version (#2)
This currently depends of interacting with matrix_synapse_rest_auth[1] and mxisd[2]. How to integrate that is explained in the README [1] https://github.com/kamax-io/matrix-synapse-rest-auth [2] https://github.com/kamax-io/mxisd
This commit is contained in:
339
database.php
Normal file
339
database.php
Normal file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
require_once("config.php");
|
||||
if (!isset($config["databaseURI"])) {
|
||||
throw new Exception ("malformed configuration: databaseURI not defined");
|
||||
}
|
||||
|
||||
abstract class RegisterState
|
||||
{
|
||||
// Sending an E-Mail failed in the first attempt. Will retry later
|
||||
const PendingEmailSend = 0;
|
||||
// User got a mail. We wait for it to verfiy
|
||||
const PendingEmailVerify = 1;
|
||||
// Sending a message to the register room failed on first attempt
|
||||
const PendingAdminSend = 5;
|
||||
// No admin has verified the registration yet
|
||||
const PendingAdminVerify = 6;
|
||||
// Registration failed on first attempt. Will retry
|
||||
const PendingRegistration = 7;
|
||||
|
||||
// in this case we have to reset the password of the user (or should we store it for this case?)
|
||||
const PendingSendRegistrationMail = 8;
|
||||
|
||||
// State to allow persisting in the database although an admin declined it.
|
||||
// Will be removed regularly
|
||||
const RegistrationAccepted = 7;
|
||||
const RegistrationDeclined = 13;
|
||||
|
||||
// User got successfully registered. Will be cleaned up later
|
||||
const AllDone = 100;
|
||||
}
|
||||
|
||||
class mxDatabase
|
||||
{
|
||||
private $db = NULL;
|
||||
|
||||
/**
|
||||
* Creates mxDatabase object
|
||||
* @param config object which has following members:
|
||||
* databaseURI: path to the sqlite file where the credentials should be stored
|
||||
* or a param which can be used to connect to a database with PDO
|
||||
* databaseUser and databasePass when authentication is required
|
||||
* register_email which email does the register bot have (here used for providing lookup)
|
||||
*/
|
||||
function __construct($config) {
|
||||
if (empty($config)) {
|
||||
throw new Exception("config is empty");
|
||||
}
|
||||
if (!isset($config["databaseURI"])) {
|
||||
throw new Exception("'databaseURI' not defined");
|
||||
}
|
||||
$db_input = $config["databaseURI"];
|
||||
$user = '';
|
||||
$password = '';
|
||||
if (isset($config["databaseUser"]) && isset($config["databasePass"])) {
|
||||
// only use it when both are defined
|
||||
$user = $config["databaseUser"];
|
||||
$password = $config["databasePass"];
|
||||
}
|
||||
// create database file when not existent yet
|
||||
$this->db = new PDO($db_input, $user, $password);
|
||||
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->db->exec("CREATE TABLE IF NOT EXISTS registrations(
|
||||
id SERIAL PRIMARY KEY,
|
||||
state INT DEFAULT 0,
|
||||
first_name TEXT,
|
||||
last_name TEXT,
|
||||
username TEXT,
|
||||
password_hash TEXT DEFAULT '',
|
||||
note TEXT,
|
||||
email TEXT,
|
||||
verify_token TEXT,
|
||||
admin_token TEXT,
|
||||
request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)");
|
||||
$this->db->exec("CREATE TABLE IF NOT EXISTS logins (
|
||||
id SERIAL PRIMARY KEY,
|
||||
active INT DEFAULT 1,
|
||||
first_name TEXT,
|
||||
last_name TEXT,
|
||||
localpart TEXT,
|
||||
password_hash TEXT,
|
||||
email TEXT,
|
||||
create_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)");
|
||||
// make sure the bot is allowed to login
|
||||
if (!$this->userRegistered("register_bot")) {
|
||||
$password = $this->addUser("Register", "Bot", "register_bot", $config["register_email"]);
|
||||
$config["register_password"] = $password;
|
||||
$myfile = fopen(dirname(__FILE__) . "/config.json", "w");
|
||||
fwrite($myfile, json_encode($config, JSON_PRETTY_PRINT));
|
||||
fclose($myfile);
|
||||
}
|
||||
|
||||
// set writeable when not set already
|
||||
if (strpos($db_input, "sqlite") === 0) {
|
||||
$sqlite_file = substr($db_input, strlen("sqlite:"));
|
||||
if (!is_writable($sqlite_file)) {
|
||||
chmod($sqlite_file, 0660);
|
||||
}
|
||||
unset($sqlite_file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: This allows accessing the database directly.
|
||||
* This was only be added for convenience. You are advised to not use this function extensively
|
||||
*
|
||||
* @param sql String wich will be passed directly to the database
|
||||
* @return Response of PDO::query()
|
||||
*/
|
||||
function query($sql) {
|
||||
return $this->db->query($sql);
|
||||
}
|
||||
|
||||
function setRegistrationStateVerify($state, $token) {
|
||||
$sql = "UPDATE registrations SET state = " . $state
|
||||
. " WHERE verify_token = '" . $token . "';";
|
||||
|
||||
return $this->db->exec($sql);
|
||||
}
|
||||
|
||||
function setRegistrationStateById($state, $id) {
|
||||
$sql = "UPDATE registrations SET state = " . $state
|
||||
. " WHERE id = '" . $id . "';";
|
||||
|
||||
return $this->db->exec($sql);
|
||||
}
|
||||
|
||||
function setRegistrationStateAdmin($state, $token) {
|
||||
$sql = "UPDATE registrations SET state = " . $state
|
||||
. " WHERE admin_token = '" . $token . "';";
|
||||
|
||||
return $this->db->exec($sql);
|
||||
}
|
||||
|
||||
function setRegistrationState($state, $token) {
|
||||
$sql = "UPDATE registrations SET state = " . $state
|
||||
. " WHERE verify_token = '" . $token . "' OR admin_token = '" . $token . "';";
|
||||
|
||||
return $this->db->exec($sql);
|
||||
}
|
||||
|
||||
function userPendingRegistrations($username) {
|
||||
$sql = "SELECT COUNT(*) FROM registrations WHERE username = '" . $username . "' AND NOT state = "
|
||||
. RegisterState::RegistrationDeclined . " LIMIT 1;";
|
||||
$res = $this->db->query($sql);
|
||||
if ($res->fetchColumn() > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function userRegistered($username) {
|
||||
$sql = "SELECT COUNT(*) FROM logins WHERE localpart = '" . $username . "' LIMIT 1;";
|
||||
$res = $this->db->query($sql);
|
||||
if ($res->fetchColumn() > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds user to the database. Next steps should be sending a verify-mail to the user
|
||||
* @param first_name First name of the user
|
||||
* @param last_name Sirname of the user
|
||||
* @param username the future localpart of that user
|
||||
* @param note Note the user typed in to give a hint
|
||||
* @param email E-Mail-Adress which will be stored into the database.
|
||||
* This will be send to the server on first login
|
||||
*
|
||||
* @return ["verify_token"]
|
||||
*/
|
||||
function addRegistration($first_name, $last_name, $username, $note, $email) {
|
||||
if ($this->userPendingRegistrations($username)) {
|
||||
require_once("language.php");
|
||||
throw new Exception($language["USERNAME_PENDING_REGISTRATION"]." (requested)");
|
||||
}
|
||||
if ($this->userRegistered($username)) {
|
||||
require_once("language.php");
|
||||
throw new Exception($language["USERNAME_REGISTERED"] . " (registered)");
|
||||
}
|
||||
|
||||
$verify_token = bin2hex(random_bytes(16));
|
||||
$admin_token = bin2hex(random_bytes(16));
|
||||
|
||||
$this->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."')");
|
||||
|
||||
return [
|
||||
"verify_token"=> $verify_token,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user for the verify_admin page.
|
||||
*
|
||||
* @return ArrayOfUser|NULL Array with "first_name, last_name, username, note and email"
|
||||
* as members
|
||||
*/
|
||||
function getUserForApproval($admin_token) {
|
||||
$sql = "SELECT COUNT(*) FROM registrations WHERE admin_token = '" . $admin_token . "'"
|
||||
. " AND state = " . RegisterState::PendingAdminVerify . " LIMIT 1;";
|
||||
$res = $this->db->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 = '" . $admin_token . "'"
|
||||
. " AND state = " . RegisterState::PendingAdminVerify
|
||||
. " LIMIT 1;";
|
||||
foreach ($this->db->query($sql) as $row) {
|
||||
// will only be executed once
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user when it opens the page to verify its mail
|
||||
*
|
||||
* @return ArrayOfUser|NULL Array with "first_name, last_name, note, email and admin_token"
|
||||
* as members
|
||||
*/
|
||||
function getUserForVerify($verify_token) {
|
||||
$sql = "SELECT COUNT(*) FROM registrations WHERE verify_token = '" . $verify_token . "'"
|
||||
. " AND state = " . RegisterState::PendingEmailVerify . " LIMIT 1;";
|
||||
$res = $this->db->query($sql);
|
||||
$first_name = NULL; $last_name = NULL; $username = NULL; $note = NULL; $email = NULL;
|
||||
|
||||
if ($res->fetchColumn() > 0) {
|
||||
$sql = "SELECT first_name, last_name, note, email, admin_token FROM registrations "
|
||||
. " WHERE verify_token = '" . $verify_token . "'"
|
||||
. " AND state = " . RegisterState::PendingEmailVerify . " LIMIT 1;";
|
||||
foreach ($this->db->query($sql) as $row) {
|
||||
// will only be executed once
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
function getUserForLogin($localpart, $password) {
|
||||
$sql = "SELECT COUNT(*) FROM logins WHERE localpart = '" . $localpart
|
||||
. "' AND active = 1 LIMIT 1;";
|
||||
$res = $this->db->query($sql);
|
||||
|
||||
if ($res->fetchColumn() > 0) {
|
||||
$sql = "SELECT first_name, last_name, email, password_hash FROM logins "
|
||||
. " WHERE localpart = '" . $localpart . "' AND active = 1 LIMIT 1;";
|
||||
foreach ($this->db->query($sql) as $row) {
|
||||
if (password_verify($password, $row["password_hash"])) {
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds User to be able to login afterwards.
|
||||
* @param first_name First name of the user
|
||||
* @param last_name Sirname of the user
|
||||
* @param username the future localpart of that user
|
||||
* @param email E-Mail-Adress which will be stored into the database.
|
||||
* This will be send to the server on first login
|
||||
*
|
||||
* @return password|NULL with member password as this method generates a
|
||||
* password and saves that into the database
|
||||
* NULL when failed
|
||||
*
|
||||
*/
|
||||
function addUser($first_name, $last_name, $username, $email) {
|
||||
// check if user already exists and abort in that case
|
||||
if ($this->userRegistered($username)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// generate a password with 10 characters
|
||||
$password = bin2hex(openssl_random_pseudo_bytes(5));
|
||||
$password_hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>12]);
|
||||
|
||||
$sql = "INSERT INTO logins (first_name, last_name, localpart, password_hash, email) VALUES "
|
||||
. "('" . $first_name."','" . $last_name . "','" . $username . "','"
|
||||
. $password_hash . "','" . $email . "');";
|
||||
|
||||
if ($this->db->exec($sql)) {
|
||||
return $password;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
function searchUserByName($search_term) {
|
||||
$term = filter_var($search_term, FILTER_SANITIZE_STRING);
|
||||
$result = array();
|
||||
$sql = "SELECT COUNT(*) FROM logins WHERE"
|
||||
. " localpart LIKE '" . $term . "%' AND active = 1;";
|
||||
$res = $this->db->query($sql);
|
||||
|
||||
if ($res->fetchColumn() > 0) {
|
||||
$sql = "SELECT first_name, last_name, localpart FROM logins WHERE"
|
||||
. " localpart LIKE '" . $term . "%' AND active = 1;";
|
||||
foreach ($this->db->query($sql) as $row) {
|
||||
array_push($result, [
|
||||
"display_name" => $row["first_name"] . " " . $row["last_name"],
|
||||
"user_id" => $row["localpart"],
|
||||
]);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function searchUserByEmail($search_term) {
|
||||
$term = filter_var($search_term, FILTER_SANITIZE_STRING);
|
||||
$result = array();
|
||||
$sql = "SELECT COUNT(*) FROM logins WHERE"
|
||||
. " email = '" . $term . "' AND active = 1;";
|
||||
$res = $this->db->query($sql);
|
||||
|
||||
if ($res->fetchColumn() > 0) {
|
||||
$sql = "SELECT first_name, last_name, localpart FROM logins WHERE"
|
||||
. " email = '" . $term . "' AND active = 1;";
|
||||
foreach ($this->db->query($sql) as $row) {
|
||||
array_push($result, [
|
||||
"display_name" => $row["first_name"] . " " . $row["last_name"],
|
||||
"user_id" => $row["localpart"],
|
||||
]);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($mx_db)) {
|
||||
$mx_db = new mxDatabase($config);
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user