274 lines
8.6 KiB
PHP
274 lines
8.6 KiB
PHP
<?php
|
|
$db_file = dirname(__FILE__)."/db_file.sqlite";
|
|
|
|
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 db_file path to the sqlite file where the credentials should be stored
|
|
*/
|
|
function __construct($db_file) {
|
|
// create database file when not existent yet
|
|
if (!file_exists($db_file)) {
|
|
$this->db = new PDO('sqlite:' . $db_file);
|
|
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
$this->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)");
|
|
$this->db->exec("CREATE TABLE logins (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
active INT DEFAULT 1,
|
|
firstname TEXT,
|
|
lastname 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
|
|
require_once("config.php");
|
|
$password = $this->addUser("Register", "Bot", "register_bot", $register_email);
|
|
$config["register_password"] = $password;
|
|
$myfile = fopen("config.json", "w");
|
|
fwrite($myfile, json_encode($config, JSON_PRETTY_PRINT));
|
|
fclose($myfile);
|
|
}
|
|
else {
|
|
// establish connection
|
|
$this->db = new PDO('sqlite:' . $db_file);
|
|
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
}
|
|
|
|
// set writeable when not set already
|
|
if (!is_writable($db_file)) {
|
|
chmod($db_file, 0777);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 = $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()) {
|
|
require_once "language.php";
|
|
throw new Exception($language["USERNAME_PENDING_REGISTRATION"]);
|
|
}
|
|
if ($this->userRegistered($username)) {
|
|
require_once "language.php";
|
|
throw new Exception($language["USERNAME_REGISTERED"]);
|
|
}
|
|
|
|
$verify_token = bin2hex(random_bytes(16));
|
|
$admin_token = bin2hex(random_bytes(16));
|
|
|
|
$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 = $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 = $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 = '" . $token . "'"
|
|
. " AND state = " . RegisterState::PendingEmailVerify . " LIMIT 1;";
|
|
foreach ($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);
|
|
$first_name = NULL; $last_name = NULL; $username = NULL; $note = NULL; $email = NULL;
|
|
|
|
if ($res->fetchColumn() > 0) {
|
|
$sql = "SELECT first_name, last_name, email, password_hash FROM logins "
|
|
. " WHERE verify_token = '" . $token . "' LIMIT 1;";
|
|
foreach ($this->db->query($sql) as $row) {
|
|
// will only be executed once
|
|
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) {
|
|
// generate a password with 10 characters
|
|
$password = bin2hex(openssl_random_pseudo_bytes(5));
|
|
$password_hash = password_hash($passwort, PASSWORD_BCRYPT, ["cost"=>12]);
|
|
|
|
$sql = "INSERT INTO logins (firstname, lastname, localpart, password_hash, email) VALUES "
|
|
. '("' . $first_name.'","' . $last_name . '","' . $username . '","'
|
|
. $password_hash . '","' . $email . '")';
|
|
|
|
if ($this->db->exec($sql)) {
|
|
return $password;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
$mx_db = new mxDatabase($db_file);
|
|
?>
|