initialer commit

This commit is contained in:
bernd 2021-03-01 16:56:53 +01:00
commit 47912a847c
15 changed files with 1371 additions and 0 deletions

32
README.md Normal file
View file

@ -0,0 +1,32 @@
# matrix-register
Das Programm befindet sich noch in der Entwicklung! Es gibt keine Garantie
für eine korrekte Funktion.
matrix-register ist eine Webanwendung, welche die Möglichkeit der
Registrierung eines Accounts bei matrix.kraut.space anbietet. Der Nutzer
beantragt über ein Webformular die Registrierung, bekommt eine
Validierungsmail zugesand und nach Bestätigung der Mail den Account
aktiviert. Nach erfolgreicher Validierung wird ein temporäres Passwort
erzeugt und auf der Webseite angezeigt. Deshalb die Empfehlung https zu
benutzen. Auch wenn das Passwort als Hash gespeichert wird - bitte umgehend
per Client ändern.
## Installation
## Konfiguration
Datenbank, Loglevel und einige Parameter der Mail werden in etc/register.ini
konfiguriert. Mit einigen Anpassungen dürfte es auch auf andere Host
übertragbar sein. Einige Erläuterungen zu den Parametern befinden sich in
der Konfigurationsdatei.
## Abhängigkeiten
matrix-register benötigt zum Arbeiten folgende Programme:
* einen Webserver mit aktiviertem php-Support
* eine Datenbank (Postgres oder SQLite)
* matrix-synapse

55
etc/register.ini.example Normal file
View file

@ -0,0 +1,55 @@
; file: register.ini.example
; desc: Konfigurationsdatei für das Programm matrix-register. Die Datei in
; register.ini umbennen und die Optionen anpassen. Das Programm sucht
; die Konfiguration im Verzeichnis ./etc
; Treiber für das php-Datenbankmodul PDO. Generell unterstütz das PDO Modul
; die folgenden Trieber:
; PDO_DBLIB (FreeTDS/Microsoft SQL Server/Sybase),
; PDO_FIREBIRD (Firebird/Interbase 6),
; PDO_IBM (IBM DB2),
; PDO_INFORMIX (IBM Informix Dynamic Server),
; PDO_MYSQL (MySQL 3.x/4.x/5.x),
; PDO_OCI (Oracle Call Interface),
; PDO_ODBC (ODBC v3 (IBM DB2, unixODBC, and win32 ODBC)),
; PDO_PGSQL (PostgreSQL),
; PDO_SQLITE (SQLite 3 and SQLite 2),
; PDO_4D (D).
; Die Verfügbarkeit hängt davon ab, ob das Modul den entsprechenden
; Treiber einkompiliert hat. Die verfügbaren Treiber lassen sich mit
; "print_r(PDO::getAvailableDrivers());" ausgeben. Je nach Datenbank
; sind unterschiedliche Optionen nötig. Der verwendete synapse-matrix Server
; unterstützt SQLite oder Postgres-Datenbanken.
driver=PDO_PGSQL
; Pfad zur Datenbank, wenn diese eine Datei ist (z.B. SQLite).
file=
; Parameter, um sich mit einem Datenbankserver zu verbinden
host=localhost
port=5432
database=requests
user=besitzer der datenbank requests
password=total geheimes passwort
; Loglevel festlegen. Derzeit sind error, warn, info und debug gültige
; Werte. Die Angabe ist nicht Case-Sesitive.
loglevel=debug
; Logdatei festlegen. Der Benutzer, unter dem der Dienst läuft braucht
; Schreibrechte für das Verzeichnis.
logfile=pfad/zur/logdatei
; Domainname des Matrixservers, bei der der Account registriert werden soll.
mxdomain="matrix.server.me"
; Parameter für den Validierungslink. Beide Parameter müssen eine valide
; URL ergeben. Der Token wird vom Programm generiert.
baseurl="https://meine.domain/"
validator="register/validation.php?token="
; Parameter für die Validierungsmail
mailfrom="From: Matrix Registrierung <meine.domain>"
mailsubject="Ihr Matrix Account bei meiner Domain"
mailclosure="Mfg"

154
index.php Normal file
View file

@ -0,0 +1,154 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
require("static/web.php");
require("lib/request.php");
$outputLogin = "";
$outputEmail = "";
$class="";
$title = "";
$message = "";
$saved = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// echo '<pre>' . htmlspecialchars(var_export($_POST, true)) . '</pre>';
$inputLogin = $_POST['login'] ?? '';
$inputEmail = $_POST['email'] ?? '';
$inputCaptcha = $_POST['captcha'] ?? '';
if ($inputCaptcha && $inputLogin && $inputEmail) {
$request = new Request();
$saved = $request->checkRequest($message);
if ($saved) {
$class = "success";
$title = "Gespeichet";
} else {
$class = "error";
$title = "Sorry";
$outputLogin = $inputLogin;
$outputEmail = $inputEmail;
}
} else {
$class = "error";
$title = "Sorry";
$message = "Something goes wrong";
}
}
?>
<!DOCTYPE HTML>
<html lang="de">
<head>
<title>matrix.kraut.space - request</title>
<?php echo HTML_META ?>
</head>
<body>
<div id="wrapper">
<?php echo HTML_HEADER ?>
<main>
<section id="matrix">
<h2>Komm in die Matrix ...</h2>
Der Krautspace ist auch in der <a href="https://matrix.org">Matrix</a> zu
erreichen. Ihr findet uns dort unter <a
href="">#krautchan:matrix.kraut.space</a> oder <a
href="">#krautchan:matrix.org</a>. Da wir uns ein dezentrales Internet
wünschen, betreiben wir auch einen eigenen Matrix-Server. Wenn Ihr dort
einen Account möchtet, schreibt eine Mail an <a
href="mailto:matrix@kraut.space">matrix@kraut.space</a> oder benutzt
das untenstehende Formular.
</section>
<section id="register">
<h2>Als Benutzer für den Matrix Chat registrieren</h2>
<p>Über dieses Formular können Sie einen Account für die Benutzung des
Matrix-Chats auf dem Server matrix.kraut.space registrieren. Der
Benutzername sollte aus einem Wort ohne Umlaute, Leer- oder Sonderzeichen
bestehen. Stellen Sie sicher, daß Sie eine gültige E-Mail-Adresse angegeben
- an diese wird die Bestätigungsmail gesendet. Nach erfolgreicher
Aktivierung Ihres Accounts bekommen Sie Ihr vorläufiges Passwort. Sie
sollte dieses danach unverzüglich über Ihren Matrix-Client ändern.</p>
</section>
<div class="<?php echo $class ?>">
<?php if ($title): ?>
<h3><?php echo $title ?></h3>
<?php endif ?>
<?php if ($message): ?>
<p><?php echo $message ?></p>
<?php endif ?>
</div>
<section id="formular">
<form id="matrix-register" method="post" accept-charset="utf-8" action="#">
<fieldset><legend>Registrieren</legend>
<div class="formrow">
<label class="formlabel" for="login">Benutzername
<input class="forminput"
id="login"
type="text"
name="login"
value="<?php echo htmlspecialchars($outputLogin)?>"
placeholder="your name"
pattern="^[a-z-\.=\_\/]+$"
size=50
maxlength="50"
required="required" />
</label>
</div>
<div class="formrow">
<label class="formlabel" for="email">E-Mail
<input class="forminput"
id="email"
type="email"
name="email"
value="<?php echo htmlspecialchars($outputEmail)?>"
placeholder="your email"
size=50
maxlength="50"
required="required" />
</label>
</div>
<div class="formrow" id="captchaframe">
<label class="captchalabel">
Der Krautspace residiert in der Krautgasse 26. Welche Hausnummer ist
das?
<input id="captchainput"
type="text"
name="captcha"
size=5
maxlength="3"
required="required" />
</label>
</div>
<input type="submit" value="Registrieren" />
</fieldset>
</form>
</section>
<section>
<p>Bitte beachten Sie bei der Auswahl des Benutzernamens, daß die
Spezifikation für den 'localpart' der Matrix-ID neben Kleinbuchstaben und
Zahlen lediglich noch die Zeichen Minus, Punkt, Gleichheitszeichen,
Unterstrich und den Schrägstrich zuläßt.</p>
</section>
</main>
<?php echo HTML_FOOTER ?>
</div> <!-- ende div wrapper -->
</body>
</html>

92
lib/base.php Normal file
View file

@ -0,0 +1,92 @@
<?php
/**
* file: lib/base.php
* date: 28.02.2021
* user: bernd@nr18.space
* desc:
*/
require("db.php");
require("config.php");
require("logger.php");
class BaseClass {
/**
* Basisklasse ... stellt die Instanzen für die Konfiguration und die
* Datenbank zur verfügung. Bildet die Grundlage für request (Anfrage
* des Accounts) und Registrator (Registrierung des Accounts).
*/
private $config_path = "/etc/matrix-register/register.ini";
public $config; // Instanz der die Klasse Config
public $log; // Instanz der Klasse Logger
public $db; // Instanz der Klasse Database
public $token = ""; // Variable für Token oder temp. Password
public function __construct() {
/**
* Beim Erstellen der Instanz wird das Einlesen der Config
* angestoßen. Sollte das Fehlschlagen, stellt die Klasse Config
* auch Defaultwerte zur Verfügung. Danach wird versucht ein
* Datenbankobjekt zu bekommen.
*/
/**
* Instanz der Klasse Logger erstellen. Nach den Erstellen der
* Instanz von Config wird noch das Loglevel angepaßt.
*/
try {
$this->log = new Logger();
} catch (Exception $e) {
throw new Exception("Can't create logger instance");
}
$this->log->d("Create a instance of BaseClass");
/**
* Instanz der Klasse Config erstellen und Konfigurationsdatei
* einlesen lassen.
*/
try {
$this->config = new Config();
$this->config->loadConfig($this->config_path);
$this->log->d("Configuration file parsed");
} catch (Exception $e) {
$this->log->e("Error: {$e->getMessage()}");
}
$this->log->setLogLevel($this->config->getLogLevel());
/**
* Instanz der Klasse Datenbank erstellen. Die Datenbank bekommt die
* Instanz der Klasse Config übergeben. Wenn nicht vorhanden wird
* die Tabelle requests angelegt.
*/
try {
$this->db = getDatabase($this->config, $this->log);
} catch (Exception $e) {
$this->log->e("Error: {$e->getMessage()}");
}
}
public function generateToken(int $length): bool {
/**
* Generiert einen Token aus zufälligen Bits der Länge 'length'.
* Speichert diesen Token in der als Refeenz übergebenen Variable.
*/
try {
$this->token = bin2hex(random_bytes($length));
} catch (Exception $e) {
$this->log->e("Token creation failed");
return false;
}
return true;
}
}
?>

100
lib/config.php Normal file
View file

@ -0,0 +1,100 @@
<?php
class Config {
private $config = [];
public function loadConfig(string $config_path) {
/**
* Einlesen der Konfiguration.
* Anmerkung: Das @ unterdrückt die ausgabe von Warnungen. Ein
* Fehlen der Konfigurationsdatei erzeugt keine Exception!
*/
$this->config = @parse_ini_file($config_path);
if ($this->config === false)
{
throw new Exception("Failed to parse configuration file");
}
}
public function getDbDriver(): string {
$driver = $this->config['driver'] ?? "PDO_PGSQL";
return $driver;
}
public function getDbFile(): string {
$file = $this->config['file'] ?? "./request.db";
return $file;
}
public function getDbHost(): string {
$host = $this->config['host'] ?? "localhost";
return $host;
}
public function getDbPort(): int {
$port = $this->config['port'] ?? 5432;
return $port;
}
public function getDbBase(): string {
$database = $this->config['database'] ?? "requests";
return $database;
}
public function getDbUser(): string {
$user = $this->config['user'] ?? "matrixadmin";
return $user;
}
public function getDbPass(): string {
$password = $this->config['password'] ?? "geheim";
return $password;
}
public function getLogLevel(): string {
$loglevel = strtoupper($this->config['loglevel']) ?? "INFO";
return $loglevel;
}
public function getLogFile(): string {
$logfile = $this->config['logfile'] ?? "log/register.log";
return $logfile;
}
public function getMxDomain(): string {
$driver = $this->config['mxdomain'] ?? "matrix.kraut.space";
return $driver;
}
public function getBaseURL(): string {
$baseurl = $this->config['baseurl'] ?? "";
return $baseurl;
}
public function getValidator(): string {
$validator = $this->config['validator'] ?? "validator.php?token=";
return $validator;
}
public function getMailFrom(): string {
$mailfrom = $this->config['mailfrom'] ?? "";
return $mailfrom;
}
public function getMailSubject(): string {
$mailsubject = $this->config['mailsubject'] ?? "Ihr Matrix Account";
return $mailsubject;
}
public function getMailClosure(): string {
$mailclosure = $this->config['mailclosure'] ?? "MfG";
return $mailclosure;
}
}
?>

340
lib/db.php Normal file
View file

@ -0,0 +1,340 @@
<?php
/**
* file: db.php
* date: 25.01.2021
* author: bernd@nr18.space
* desc: Anbindung an die (Postgres) Datenbank.
*/
function testDriver(): bool
{
print_r(PDO::getAvailableDrivers());
return true;
}
function getDatabase(&$config): Database {
/**
* Erstellt ein Datenbank Objekt und gibt dieses zurück. Im Fehlerfall
* wird eine Exception mit der Meldung der PDOException ausgelöst.
*/
try {
$pdo = Connection::get()->connect($config);
$db = new Database($pdo, $log);
} catch (PDOException $e) {
throw new Exception($e->getMessage());
}
return $db;
}
class Connection {
/**
* Stellt die Verbindung zur Datenbank her. Derzeit sind Postgres, MySQL
* und SQLite Datenbanken möglich.
*/
private static $conn;
public function connect(&$config)
{
$driver = $config->getDbDriver();
$file = $config->getDbFile();
$host = $config->getDbHost();
$port = $config->getDbPort();
$base = $config->getDbBase();
$user = $config->getDbUser();
$pass = $config->getDbPass();
try {
if ($driver === "PDO_PGSQL") {
$pdo = new PDO("pgsql:host=$host;port=$port;dbname=$base", $user, $pass);
}
else if ($driver === "PDO_SQLITE") {
$pdo = new PDO("sqlite:$file");
}
else {
throw new Exception("Wrong driver for database: {$driver}");
return false;
}
} catch (PDOException $e) {
throw new Exception($e->getMessage());
}
if (isset($pdo)) {
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return $pdo;
}
public static function get()
{
if (null === static::$conn)
{
static::$conn = new static();
}
return static::$conn;
}
public static function close()
{
static::$conn = null;
return true;
}
}
class Database {
/**
* Stellt das Datenbankobjekt und die Methoden zum Arbeiten mit der
* Datenbank zur verfügung.
*/
private $pdo;
private $log;
public function __construct($pdo, $log)
{
$this->pdo = $pdo;
$this->log = $log;
$this->log->d("Databaseobject created");
// hier vielleicht TableCreate hin
}
public function TableExists(): int
{
$stmt = "SELECT * FROM information_schema.tables WHERE
table_type = 'BASE TABLE' and
table_name = 'requests'";
$response = $this->pdo->query($stmt);
$count = $response->rowCount();
return $count;
}
public function createTable()
{
$stmt = "CREATE TABLE IF NOT EXISTS requests (
id serial PRIMARY KEY,
nick varchar(80) NOT NULL UNIQUE,
email varchar(80) NOT NULL,
token char(32) NOT NULL UNIQUE,
time integer NOT NULL);";
$this->pdo->exec($stmt);
return $this;
}
public function UserExistsInUsers(string $nick): bool
{
/**
* Sucht in der Tabelle users nach bereits angelegten Nutzern. Die
* Abfrage der Datenbank wird an die Funktion searchUser delegiert.
* Sollte diese eine Exception werfen, wird true (es gibt einen
* Benutzer) zurück gegeben. Ansonsten wird der Rückgabewert
* von der Funktion getNick ausgewertet, die aus der übergebenen
* Matrix-ID den localpart extrahiert.
*/
$this->log->d("Search for localpart {$nick} in users");
$query = "SELECT name FROM users WHERE name LIKE :nick";
$pattern = "%$nick%";
try {
$response = $this->searchUser($query, $pattern);
} catch (PDOException $e) {
$this->log->e("searchUser() returns true because PDOException");
return true;
}
$count = count($response);
if ($count == 0)
{
$this->log->d("Nothing found");
return false;
}
else
{
foreach ($response as $array) {
$uid = $this->getNick($array['name']);
$this->log->d("Compare {$nick} with {$uid}");
if ($uid === $nick) {
$this->log->i("MID localpart already exists: {$nick}");
return true;
} else {
$this->log->d("False");
}
}
}
return false;
}
public function UserExistsInRequests(string $nick): bool
{
/**
* Sucht in der Tabelle requests nach breits beantragten
* Nutzernamen. Die Abfrage der Datenbank wird an die Funktion
* searchUser delegiert. Sollte diese eine Exception werfen, wird
* true (es gibt einen benutzer) zurück gegeben. Ansonsten wird die
* Anzahl der Treffer ausgewertet.
*/
$this->log->d("Search for localpart {$nick} in requests");
$query = "SELECT nick FROM requests WHERE nick = :nick";
try {
$response = $this->searchUser($query, $nick);
} catch (PDOException $e) {
$this->log->e("searchUser() returns true because PDOException");
return true;
}
$count = count($response);
if ($count > 0) {
$this->log->d("Search for {$nick}: {$count} hit(s)");
return true;
}
$this->log->d("Nothing found");
return false;
}
private function searchUser(string $query, string $nick): array
{
/**
* Führt die Suchoperartion auf der Datenbank aus. Bekommt dafür den
* Querystring und den Nick übergeben. Sollte Abfrage der Datenbank
* fehlschlagen, wirft die Funktion die Exception an die aufrufende
* Funktion.
*/
try
{
$stmt = $this->pdo->prepare($query,
array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$stmt->execute(array(':nick' => $nick)); // gibt bool zurück
$response = $stmt->fetchAll();
return $response;
}
catch (PDOException $e)
{
$errormsg = $e->getMessage();
$this->log->e("A PDO-Exception occurres");
$this->log->e("Error: {$errormsg}");
throw new PDOException($errormsg);
}
}
private function getNick(string $mid): string
{
/**
* Extrahiert aus einer Matrix-ID den localpart.
* TODO: In eine bibliothek auslagern? (/lib/common)
*/
$this->log->d("Extract nick from {$mid}");
$uid = "";
$append = false;
$strarray = str_split($mid);
foreach ($strarray as $char)
{
if ($char == '@')
{
$append = true;
}
else if ($char == ':')
{
$this->log->d("Extracted: {$uid}");
return $uid;
}
else
{
if ($append === true)
{
$uid = $uid.$char;
}
}
}
}
public function saveRequest($token): bool
{
/**
* Speichert den gewünschten Nick, die Emailadresse, das Token und
* einen Zeitstempel in der Tabelle Requests.
* TODO: Sollten/Müssen Nick und Email noch durch htmlspecialchars()
* oder reichen die prepared Statments?
*/
$nick = $_POST['login'];
$email = $_POST['email'];
date_default_timezone_set("Europe/Berlin");
$time = time();
$this->log->d("Save request for: {$nick} with {$token} at {$time}");
try {
$stmt = $this->pdo->prepare("INSERT INTO requests
(nick, email, token, time) VALUES
(:nick, :email, :token, :time)");
$response = $stmt->execute(array(':nick' => $nick,
':email' => $email,
':token' => $token,
':time' => $time));
} catch (PDOException $e) {
$errmsg = $e->getMessage();
$this->log->e("Saving request failed");
$this->log->e("Error: {$errmsg}");
return false;
}
$this->log->i("Request saved successfull");
$this->log->d("Database returns: {$response}");
return true;
}
public function getToken(): array {
/**
* Sucht in der Tabelle requests nach dem Token. Gibt im Erfolgsfall
* ein Array mit ID, Name und Token zurück. Andernfalls ein leeres
* Array. Im Falle eines Datenbankfehlers wird eine Exception
* geworfen.
*/
$token = $_GET['token'];
$query = "SELECT id, nick, token FROM requests WHERE token = :token";
try {
$stmt = $this->pdo->prepare($query,
array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$stmt->execute(array(':token' => $token)); // gibt bool zurück
$response = $stmt->fetchAll();
} catch (PDOException $e) {
$this->log->e("PDO Exception occures");
throw new Exception($e->getMessage());
}
$this->log->d("Database operation successfull");
return $response;
}
public function removeToken(int $id): int {
/**
* Entfernt den Request aus der Tabelle requests. Wird aufgerufen,
* wenn die Registrierung des Nicks am Matrixserver erfolgreich war.
*/
$query = "DELETE FROM requests WHERE id = :id";
try {
$stmt = $this->pdo->prepare($query,
array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$stmt->execute(array(':id' => $id)); // gibt bool zurück
} catch (PDOException $e) {
$this->log->e("PDO Exception occures");
throw new Exception($e->getMessage());
return false;
}
$this->log->d("Database operation successfull");
return $stmt->rowCount();;
}
}
?>

73
lib/logger.php Normal file
View file

@ -0,0 +1,73 @@
<?php
/**
* file: logger.php
* date: 01.03.2021
* author: bernd@nr18.space
* desc: Simpler Logger
*/
class Logger {
/**
* Einfache Klasse zum Loggen von Nachrichten. Schreibt seine
* Nachrichten in eine Datei. Eleganter wäre ein Weiterreichen an den
* Logging-Daemon des Betriebssystems.
* TODO: Er ist wenig Fehlertolerant. Es wird nicht geprüft, ob der
* angefragte Key im Array existiert.
*/
private $levelnumbers = array(
"ERROR" => 10,
"WARN" => 20,
"INFO" => 30,
"DEBUG" => 40,
);
public $loglevel = "INFO";
private $logfile = "log/register.log";
private $app = "matrix-register";
public function e(string $msg) {
$this->logMsg("ERROR", $msg);
}
public function w(string $msg) {
$this->logMsg("WARN", $msg);
}
public function i(string $msg) {
$this->logMsg("INFO", $msg);
}
public function d(string $msg) {
$this->logMsg("DEBUG", $msg);
}
public function setLogLevel(string $level) {
if (array_key_exists($level, $this->levelnumbers) === true) {
$this->loglevel = $level;
$this->writeLog("INFO", "Loglevel set to {$level}");
} else {
$this->writeLog("WARN", "{$level} is not a valid loglevel.");
}
}
private function logMsg(string $label, string $msg) {
$msglevel = $this->levelnumbers[$label];
$loglevel = $this->levelnumbers[$this->loglevel];
if ($loglevel >= $msglevel) {
$this->writeLog($label, $msg);
}
}
private function writeLog(string $label, string $msg) {
$app = $this->app;
$file = $this->logfile;
error_log(date("[Y-m-d H:i:s]")." [".$label."] [".$app."] ".$msg."\n", 3, $file);
}
}
?>

113
lib/register.php Normal file
View file

@ -0,0 +1,113 @@
<?php
/**
* file: lib/register.php
* date: 28.08.2021
* user: bernd@nr18.space
* desc: Klasse Registrator - validiert einen Token und registriert einen
* User am synapse-matrix Server. Achtung: z.Z. noch keine Registrierung!
*/
require("base.php");
class Registrator extends BaseClass {
/**
* Die Klasse zum Aktivieren des Matrix Accounts. Erbt aus dem
* Konstruktor von BaseClass ein Konfigurations- und ein Datenbakobjekt
* ($this->config, $this->db), die Funktion generateToken(), sowie die
* Variable $this->token.
*/
private $dataSet = [];
public function registerUser(&$message): bool {
/**
* Hauptfunktion der Klasse Registrator - steuert die Validierung und
* das Registrieren der Anfrage.
*/
if (!isset($this->db)) {
$this->log->e("There is no database");
return false;
}
$this->log->d("Validation started for token: {$_GET['token']}");
if ($this->checkToken() === true) {
if ($this->generateToken($token, 32) === true) {
if ($this->registerMXID() === true) {
$message = "Your temporary password is {$tmp_passwd}. Please
immediately change your password!";
if ($this->removeToken() === true) {
return true;
}
}
}
}
return false;
}
private function checkToken(): bool {
/**
* Läßt in der Tabelle requests schauen, ob es $_GET['token'] gibt.
* Speichert das zurückgegebene Array in der Variable $dataSet. Gibt
* die Datenbank eine Exception zurück oder ist das Array leer, gibt
* sie False, andernfals True zurück.
*/
try {
$this->dataSet = $this->db->getToken();
} catch (Exception $e) {
$this->log->e("Error: {$e->getMessage()}");
return false;
}
$count = count($this->dataSet);
if ($count === 0) {
$this->log->d("Token {$_GET['token']} not found in database");
} else if ($count > 1) {
$this->log->e("Error: More than one token found");
} else {
$this->log->d("Token found for nick: {$this->dataSet[1]}");
return true;
}
return false;
}
private function registerMXID(string $tmp_passwd): bool {
/**
* Registriert den Nutzer am Matrixserver. Dazu wird ein zufälliger
* String erzeugt und zusammen mit dem Nick ein CLI Tool aus dem Paket
* Paket von matrix-synapse aufgerufen.
*/
// $cmd = "register_new_matrix_user -u {$this->dataSet[1]} -p {$tmp_pass}";
// $response = system($cmd);
return true;
}
private function removeToken(): bool {
/**
* Läßt den Request aus der tabelle requests entfernen.
*/
$id = $this->dataSet[0];
$nick = $this->dataSet[1];
$token = $this->dataSet[2];
try {
$response = $this->db->deleteToken($id);
} catch (Exception $e) {
$this->log->e("Error: {$e->getMessage()}");
return false;
}
return true;
}
}
?>

197
lib/request.php Normal file
View file

@ -0,0 +1,197 @@
<?php
/**
* file: lib/request.php
* date: 28.02.2021
* user: bernd@nr18.space
* desc:
*/
require("base.php");
require("static/mail.php");
class Request extends BaseClass {
/**
* Klasse zur Bearbeitung einer Anfrage nach einem Matrix Accuont. Erbt
* aus der Klasse BaseClass ein Konfigurations- und ein Datenbankobjekt
* ($this->config, $this->db), die Funktion generateToken() und sowie
* die Variable $this-token.
*/
public function checkRequest(string &$message): bool {
/**
* Hauptfunktion der Klasse Request - steuert alle Prüfungen, das
* Speichern des Requests und das Versenden der Email. Gibt True
* oder False zurück und setzt die Variable "message", welche auf
* der Webseite ausgegeben wird.
*/
if (!isset($this->db)) {
$this->log->e("There is no database");
$message = "Something goes wrong";
return false;
}
$this->log->d("Request started for nick: {$_POST['login']}");
if (false === $this->checkCaptcha()) {
$message = "Captcha invalid";
return false;
} else if (false === $this->checkEmail()) {
$message = "Email invalid";
return false;
} else if (false === $this->checkMXID($this->config->getMxDomain())) {
$message = "User ID invalid";
return false;
} else if (false === $this->checkUser()) {
$message = "User Id is already taken";
return false;
} else {
if ($this->generateToken($token, 16) === true) {
if ($this->saveRequest() === true) {
if ($this->sendVerificationMail() === true) {
return true;
}
}
}
}
return false;
}
private function saveRequest(): bool {
/**
* Veranlaßt die Speicherung der Anfrage in der Tabelle requests.
* TODO: Exceptions behandeln.
*/
$response = $this->db->saveRequest($this->token);
if (!$response) {
return false;
}
return false;
}
private function sendVerificationMail(): bool {
/**
* Verschickt die Mail mit dem Verifizierungslink.
* TODO: Reicht filter_input()? Was kann hier passieren?
*/
$mailTo = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$mxdomain = $this->config->getMxDomain();
$baseurl = $this->config->getBaseURL();
$validator = $this->config->getValidator();
$mailFrom = $this->config->getMailFrom();
$mailSubject = $this->config->getMailSubject();
$mailClosure = $this->config->getMailClosure();
$link = $baseurl . $validator . $this->token . "\r\n\r\n";
$mailbody = MAILTEXT1 . $mxdomain . MAILTEXT2 . "\r\n\r\n" . $link . $mailClosure;
if (mail($mailTo, $mailSubject, $mailbody, $mailFrom))
{
$this->log->i("Validationmail successfull send");
return true;
}
$this->log->e("Sending validation mail failed");
return false;
}
private function checkCaptcha(): bool {
/**
* Prüfen, ob das Captcha die korrekte Hausnummer abbildet.
* Filter_input gibt im Erfolgsfall einen Integer zurück.
*/
$this->log->d("Checking captcha");
$captcha = filter_input(INPUT_POST, 'captcha', FILTER_VALIDATE_INT);
if ($captcha == 26) {
$this->log->d("Captcha valid");
return true;
}
$this->log->e("Invalid captcha");
return false;
}
private function checkEmail(): bool {
/**
* Prüfen, ob die Emailadresse schematisch gültig ist. filter_input
* gibt im Erfolgsfall den Wert, im Fehlerfall false oder null, wenn
* die Variable nicht gestzt ist, zurück. Letzteres wird beim
* vorangehenden Test checkForms geprüft und kann daher nicht
* auftreten.
*/
$this->log->d("Checking email schema");
if (filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL)) {
$this->log->d("Email schema is valid");
return true;
}
$this->log->e("Email schema invalid");
return false;
}
private function checkMXID(string $mxid): bool {
/**
* Prüft, ob der gewünschte localpart nur Zeichen enthält, die von
* der Matrix-Spezifikation erlaubt sind. Die Spezifikation erlaubt
* nur Kleinbuchstaben, Ziffern, Minus, Punkt, Gleichheitszeichen,
* Unterstrich und Schrägstrich. Geprüft wird auch, ob die Länge des
* gewünschten localpart zusammen mit dem domainpart sowie dem @ und
* : die Länge von 255 Zeichen nicht überschreiten.
*/
$this->log->d("Check MXID localpart");
$str_array = str_split($_POST['login']);
$max_length = 255 - (mb_strlen($mxid) + 2);
$specials = array('-', '.', '=', '_', '/');
$numbers = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
$possibles = array_merge(range('a', 'z'), $numbers, $specials);
foreach ($str_array as $item) {
if (in_array($item, $possibles, true) === false) {
$this->log->e("MXID localpart: invalid character: {$item}");
return false;
}
}
if (mb_strlen($_POST['login']) > $max_length) {
$this->log->e("MXID localpart: too long");
return false;
}
$this->log->d("MXID localpart is valid");
return true;
}
private function checkUser(): bool {
/**
* Prüft, ob der gewünschte Nutzernamen nicht bereits vergeben ist.
* Dazu wird in den Datenbanktabellen users (bereits registrierte
* benutzer) und requests (bereits beantragte benutzer) nach einer
* eventuellen Übereinstimmung geschaut. Eine spezielle Behandlung
* des Suchstrings ist nach meiner Meinung hier nicht nötig, da der
* String vorher auf Matrix-konforme Zeichen getestet wurde und die
* Datenbankfuntionen prepared Statements verwendet.
*/
$this->log->d("Checking if username is available");
$nick = $_POST['login'];
if ($this->db->UserExistsInRequests($nick) === true) {
return false;
}
if ($this->db->UserExistsInUsers($nick) === true) {
return false;
}
return true;
}
}
?>

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

20
static/mail.php Normal file
View file

@ -0,0 +1,20 @@
<?php
/**
* file: static/mail.php
* date: 28.02.2021
* user: bernd@nr18.space
* desc:
*/
const MAILTEXT1 = <<<END
Wir freuen uns, daß Sie sich für einen Account bei
END;
const MAILTEXT2 = <<<END
entschieden haben. Rufen Sie zur Bestätigung den untenstehenden Link
im Browser auf.
END;
?>

89
static/register.css Normal file
View file

@ -0,0 +1,89 @@
/*
file: register.css
date: 04.01.2021
author: bernd@nr18.space
*/
html, body {
padding: 0;
margin: 0;
color: #333;
background-color: white;
font-family: DejaVu Sans;
font-size: 100%;
line-height: 1.4;
height: 100%;
box-sizing: border-box;
}
#wrapper {
min-height: 100%;
max-width: 800px;
margin: 0 auto;
padding: 1.5em 3%;
}
main {
padding-top: 1em;
}
.success {
display: block;
padding: 1em;
border-radius: 0.5em;
background-color: #82c385;
}
.error {
display: block;
padding: 1em;
border-radius: 0.5em;
background-color: #db6884;
}
section#formular {
margin: 2em;
}
fieldset {
max-width: 500px;
margin: auto;
padding: 2em;
display: flex;
flex-direction: column;
}
.formrow {
display: flex;
flex-direction: row;
margin-bottom: 1.4em;
}
label.formlabel {
display: block;
text-align: right;
}
input.forminput {
width: 60%;
margin-left: 1em;
}
#captchaframe {
margin-top: 1em;
border: 1px solid #333;
border-radius: 2px;
padding: 1em;
text-align: center;
}
input#captchainput {
width: 30%;
}
input[type=submit] {
width: max-content;
}

37
static/web.php Normal file
View file

@ -0,0 +1,37 @@
<?php
/**
* file: static/web.php
* date: 06.01.2021
* author: bernd@nr18.space
* desc: Definition einiger Konstanten für die Webseiten
*/
// HTML
const HTML_META = <<<END
<meta charset="utf-8" />
<meta name="robots" content="noindex/nofollow" />
<link rel="stylesheet" href="./static/register.css" type="text/css" />
<link rel="shortcut icon" href="./static/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1">
END;
const HTML_HEADER = <<<END
<header>
<h1>
<img src="./static/logo.png" alt="Logo Krautspace" width="281" height="58" />
<span id="subtitle">Hackspace Jena e.V.</span>
</h1>
</header>
END;
const HTML_FOOTER = <<<END
<footer>
<p>Krautspace Jena e.V. | 07743 Jena | Krautgasse 26</p>
<p>
<a href="">Impressum</a>
<a href="">Datenschutz</a>
</p>
</footer>
END;
?>

69
validation.php Normal file
View file

@ -0,0 +1,69 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
require("static/web.php");
require("lib/register.php");
$class = "";
$title = "";
$message = "";
$registered = false;
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$token = $_GET['token'] ?? '';
if ($token) {
$registrator = new Registrator();
$registered = $registrator->registerUser($message);
if ($registered) {
$class = "success";
$title = "Congratulations";
} else {
$class = "error";
$title = "Sorry";
$message = "Failed to register your account.";
}
} else {
$class = "error";
$title = "Sorry";
$message = "Something goes wrong.";
}
}
?>
<!DOCTYPE HTML>
<html lang="de">
<head>
<title>matrix.kraut.space - register</title>
<?php echo HTML_META ?>
</head>
<body>
<div id="wrapper">
<?php echo HTML_HEADER ?>
<main>
<div class="<?php echo $class ?>">
<?php if ($title): ?>
<h3><?php echo $title ?></h3>
<?php endif ?>
<?php if ($message): ?>
<p><?php echo $message ?></p>
<?php endif ?>
</div>
</main>
<?php echo HTML_FOOTER ?>
</div> <!-- ende div wrapper -->
</body>
</html>