Second implementation with matrix_synapse_rest_auth #2

Merged
krombel merged 51 commits from second_implementation into master 2018-03-19 13:57:16 +01:00
16 changed files with 1481 additions and 2 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
config.php
db_file.sqlite

155
MatrixConnection.php Normal file
View File

@@ -0,0 +1,155 @@
<?php
class MatrixConnection
{
private $hs;
private $at;
function __construct($homeserver, $access_token) {
$this->hs = $homeserver;
$this->at = $access_token;
}
function send($room_id, $message) {
if (!$this->at) {
error_log("No access token defined");
return false;
}
$send_message = NULL;
if (!$message) {
error_log("no message to send");
return false;
} elseif(is_array($message)) {
$send_message = $message;
} elseif ($message instanceof MatrixMessage) {
$send_message = $message->get_object();
} else {
error_log("message is of not valid type\n");
return false;
}
$url="https://".$this->hs."/_matrix/client/r0/rooms/"
. urlencode($room_id) ."/send/m.room.message?access_token=".$this->at;
$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_POSTFIELDS, json_encode($send_message));
curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
$response = $this->exec_curl_request($handle);
return isset($response["event_id"]);
}
function send_msg($room_id, $message) {
return $this->send($room_id, array(
"msgtype" => "m.notice",
"body" => $message
)
);
}
function hasUser($username) {
if (!$username) {
throw new Exception ("no user given to lookup");
}
$url = "https://".$this->hs."/_matrix/client/r0/profile/@" . $username . ":" . $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"));
$res = $this->exec_curl_request($handle);
return !(isset($res["errcode"]) && $res["errcode"] == "M_UNKNOWN");
}
function register($username, $password, $shared_secret) {
if (!$username) {
error_log("no username provided");
}
if (!$password) {
error_log("no message to send");
}
$mac = hash_hmac('sha1', $username, $shared_secret);
$data = array(
"username" => $username,
"password" => $password,
"mac" => $mac,
);
$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);
curl_setopt($handle, CURLOPT_TIMEOUT, 60);
curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($data));
return $this->exec_curl_request($handle);
}
function exec_curl_request($handle) {
$response = curl_exec($handle);
if ($response === false) {
$errno = curl_errno($handle);
$error = curl_error($handle);
error_log("Curl returned error $errno: $error\n");
curl_close($handle);
return false;
}
$http_code = intval(curl_getinfo($handle, CURLINFO_HTTP_CODE));
curl_close($handle);
if ($http_code >= 500) {
// do not want to DDOS server if something goes wrong
sleep(10);
return false;
} else if ($http_code != 200) {
$response = json_decode($response, true);
error_log("Request has failed with error {$response['error']}\n");
if ($http_code == 401) {
throw new Exception('Invalid access token provided');
}
} else {
$response = json_decode($response, true);
}
return $response;
}
}
class MatrixMessage
{
private $message;
function __construct() {
$this->message = ["msgtype" => "m.notice"];
}
function set_type($msgtype) {
$this->message["msgtype"] = $msgtype;
}
function set_format($format) {
$this->message["format"] = $format;
}
function set_body($body) {
$this->message["body"] = $body;
}
function set_formatted_body($fbody, $format="org.matrix.custom.html") {
$this->message["formatted_body"] = $fbody;
$this->message["format"] = $format;
}
function get_object() {
return $this->message;
}
}
?>

View File

@@ -4,6 +4,27 @@ This bot provides a two-step-registration for matrix.
This is done in several steps: This is done in several steps:
- potential new user registers on a bot-provided side - potential new user registers on a bot-provided side
- bot sends a message to prefined room with a registration notification. - bot sends a message to predefined room with a registration notification.
- users in that room now can approve or decline the registration. - users in that room now can approve or decline the registration.
- The bot then uses the registration token to register the user or just drops the registration request. - When approved
- the bot creates credentials
- sends them to the user
- stores them encrypted in own database
- provides that credentials to [matrix-synapse-rest-auth](https://github.com/kamax-io/matrix-synapse-rest-auth#integrate) which has to be configured to query login.php
2nd step: Implement the other apis to integrade [mxisd](https://github.com/kamax-io/mxisd/blob/master/docs/backends/rest.md)
## How to install
- Copy `config.sample.php` to `config.php` and configure the bot as you can find there
- Configure your webserver to publish the folder `public` and configure.
The folder `internal` contains files that can be accessed by mxisd or matrix-synapse-rest-auth
- To integrate with matrix-synapse-rest-auth:
- `/_matrix-internal/identity/v1/check_credentials` should map to `internal/login.php`
- To integrate with mxisd: Have a look at [the docs](https://github.com/kamax-io/mxisd/blob/master/docs/backends/rest.md) and apply as follows:
| Key | file which handles that | Description |
|--------------------------------|-------------------------------|------------------------------------------------------|
| rest.endpoints.auth | internal/login.php | Validate credentials and get user profile |
| rest.endpoints.directory | internal/directory_search.php | Search for users by arbitrary input |
| rest.endpoints.identity.single | internal/identity_single.php | Endpoint to query a single 3PID |
| rest.endpoints.identity.bulk | internal/identity_bulk.php | Endpoint to query a list of 3PID |

26
config.sample.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
$config = [
"homeserver" => "example.com",
"access_token" => "To be used for sending the registration notification",
// Which e-mail-adresse shall the bot use to send e-mails?
"register_email" => 'register_bot@example.com',
// Where should the bot post registration requests to?
"register_room" => '$registerRoomID:example.com',
// Where is the public part of the bot located? make sure you have a / at the end
"webroot" => "https://myregisterdomain.net/",
// optional: Do you have a place where howTo's are located? If not leave this value out
"howToURL" => "https://my-url-for-storing-howTos.net",
// When you want to collect the password on registration set this to true
"getPasswordOnRegistration" => false,
// to define where the data should be stored:
"databaseURI" => "sqlite:" . dirname(__FILE__) . "/db_file.sqlite",
// credentials for sqlite not used
"databaseUser" => "dbUser123",
"databasePass" => "secretPassword",
]
?>

92
cron.php Normal file
View File

@@ -0,0 +1,92 @@
<?php
require_once("config.php");
require_once("mail_templates.php");
require_once("database.php");
$sql = "SELECT id, first_name, last_name, username, email, state, note, verify_token, admin_token FROM registrations "
."WHERE state = ". RegisterState::PendingEmailSend
. " OR state = " . RegisterState::PendingAdminSend
. " OR state = " . RegisterState::PendingRegistration
. " OR state = " . RegisterState::PendingSendRegistrationMail
. " OR state = " . RegisterState::RegistrationDeclined
. " OR state = " . RegisterState::AllDone . ";";
foreach ($mx_db->query($sql) as $row) {
$first_name = $row["first_name"];
$last_name = $row["last_name"];
$username = $row["username"];
$email = $row["email"];
$state = $row["state"];
try {
switch ($state) {
case RegisterState::PendingEmailSend:
$verify_url = $config["webroot"] . "/verify.php?t=" . $row["verify_token"];
$success = send_mail_pending_verification(
$config["homeserver"],
$row["first_name"] . " " . $row["last_name"],
$row["email"],
$verify_url);
if ($success) {
$mx_db->setRegistrationStateById(RegisterState::PendingEmailVerify, $row["id"]);
} else {
throw new Exception("Could not send mail to ".$row["first_name"]." ".$row["last_name"]."(".$row["id"].")");
}
break;
case RegisterState::PendingAdminSend:
require_once("MatrixConnection.php");
$adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $row["admin_token"];
$mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]);
$mxMsg = new MatrixMessage();
$mxMsg->set_body($first_name . ' ' . $last_name . " möchte sich registrieren und hat folgende Notiz hinterlassen:\r\n"
. $row["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:<br />"
. $row["note"] . "<br />"
. "Zum Bearbeiten <a href=\"". $adminUrl . "\">hier</a> klicken");
$mxMsg->set_type("m.text");
$response = $mxConn->send($config["register_room"], $mxMsg);
if ($response) {
$mx_db->setRegistrationStateById(RegisterState::PendingAdminVerify, $row["id"]);
send_mail_pending_approval($config["homeserver"], $first_name . " " . $last_name, $email);
} else {
throw new Exception("Could not send notification for ".$row["first_name"]." ".$row["last_name"]."(".$row["id"].") to admins.");
}
break;
case RegisterState::PendingRegistration:
// Registration got accepted but registration failed
$password = $mx_db->addUser($row["first_name"], $row["last_name"], $row["username"], $row["email"]);
if ($password != NULL) {
// send registration_success
$res = send_mail_registration_success($config["homeserver"], $first_name . " " . $last_name, $email, $username, $password, $config["howToURL"]);
if ($res) {
$mx_db->setRegistrationStateById(RegisterState::AllDone, $row["id"]);
} else {
$mx_db->setRegistrationStateById(RegisterState::PendingSendRegistrationMail, $row["id"]);
}
} else {
send_mail_registration_allowed_but_failed($config["homeserver"], $first_name . " " . $last_name, $email);
$mxMsg = new MatrixMessage();
$mxMsg->set_type("m.text");
$mxMsg->set_body("Fehler beim Registrieren von " . $first_name . " " . $last_name . ".");
$mxConn->send($config["register_room"], $mxMsg);
throw new Exception($language["REGISTRATION_FAILED"]);
}
break;
case RegisterState::PendingSendRegistrationMail:
print ("Error: Unhandled state: PendingSendRegistrationMail for " . $first_name . " " . $last_name . " (" . $username . ")\n");
break;
case RegisterState::RegistrationDeclined:
case RegisterState::AllDone:
// do reqular cleanup
break;
}
} catch (Exception $e) {
print("Error while handling cron for " . $first_name . " " . $last_name . " (" . $username . ")\n");
print($e->getMessage());
}
}
?>

339
database.php Normal file
View 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);
}
?>

View File

@@ -0,0 +1,36 @@
<?php
require_once("../database.php");
$response=[
"limited" => false,
"result" => [],
];
try {
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE);
if (empty($input)) {
throw new Exception('no valid json as input present');
}
if (!isset($input["by"])) {
throw new Exception('"by" is not defined');
}
if (!isset($input["search_term"])) {
throw new Exception('"search_term" is not defined');
}
switch ($input["by"]) {
case "name":
$response["result"] = $mx_db->searchUserByName($input["search_term"]);
break;
case "threepid":
$response["result"] = $mx_db->searchUserByEmail($input["search_term"]);
break;
default:
throw new Exception("unknown type for \"by\" param");
}
} catch (Exception $e) {
error_log("failed with error: " . $e->getMessage());
$response["error"] = $e->getMessage();
}
print (json_encode($response, JSON_PRETTY_PRINT) . "\n");
?>

View File

@@ -0,0 +1,53 @@
<?php
require_once("../database.php");
$response = [
"lookup" => []
];
try {
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE);
if (!isset($input)) {
throw new Exception('request body is no valid json');
}
if (!isset($input["lookup"])) {
throw new Exception('"lookup" is not defined');
}
if (!is_array($input["lookup"])) {
throw new Exception('"lookup" is not an array');
}
foreach ($input["lookup"] as $lookup) {
if (!isset($lookup["medium"])) {
throw new Exception('"lookup.medium" is not defined');
}
if (!isset($lookup["address"])) {
throw new Exception('"lookup.address" is not defined');
}
$res2 = array();
switch ($lookup["medium"]) {
case "email":
$res2 = $mx_db->searchUserByEmail($lookup["address"]);
if (!empty($res2)) {
array_push($response["lookup"], [
"medium" => $lookup["medium"],
"address" => $lookup["address"],
"id" => [
"type" => "localpart",
"value" => $res2[0]["user_id"],
]
]
);
}
break;
case "msisdn":
break;
default:
throw new Exception("unknown type for \"by\" param");
}
}
} catch (Exception $e) {
error_log("ídentity_bulk failed with error: " . $e->getMessage());
$response["error"] = $e->getMessage();
}
print (json_encode($response, JSON_PRETTY_PRINT) . "\n");
?>

View File

@@ -0,0 +1,46 @@
<?php
require_once("../database.php");
$response = new stdClass;
try {
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE);
if (empty($input)) {
throw new Exception('no valid json as input present');
}
if (!isset($input["lookup"])) {
throw new Exception('"lookup" is not defined');
}
if (!isset($input["lookup"]["medium"])) {
throw new Exception('"lookup.medium" is not defined');
}
if (!isset($input["lookup"]["address"])) {
throw new Exception('"lookup.address" is not defined');
}
$res2 = array();
switch ($input["lookup"]["medium"]) {
case "email":
$res2 = $mx_db->searchUserByEmail($input["lookup"]["address"]);
if (!empty($res2)) {
$response = [
"lookup" => [
"medium" => $input["lookup"]["medium"],
"address" => $input["lookup"]["address"],
"id" => [
"type" => "localpart",
"value" => $res2[0]["user_id"],
]
]
];
}
break;
default:
throw new Exception("unknown type for \"by\" param");
}
} catch (Exception $e) {
error_log("ídentity_bulk failed with error: " . $e->getMessage());
$response["error"] = $e->getMessage();
}
print (json_encode($response, JSON_PRETTY_PRINT) . "\n");
?>

98
internal/login.php Normal file
View File

@@ -0,0 +1,98 @@
<?php
$response = [
"auth" => [
"success" => false,
]
];
require_once("../database.php");
abstract class LoginRequester {
const UNDEFINED = 0;
const MXISD = 1;
const RestAuth = 2;
}
$loginRequester = LoginRequester::UNDEFINED;
try {
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE);
$mxid = NULL;
$localpart = NULL;
if (isset($input["user"])) {
if (isset($input["user"]["localpart"])) {
$localpart = $input["user"]["localpart"];
$loginRequester = LoginRequester::MXISD;
} elseif (isset($input["user"]["id"])) {
// compatibility for matrix-synapse-rest-auth
$mxid = $input["user"]["id"];
$loginRequester = LoginRequester::RestAuth;
} elseif (isset($input["user"]["mxid"])) {
// compatibility for mxisd
$mxid = $input["user"]["mxid"];
$loginRequester = LoginRequester::MXISD;
}
}
// prefer the localpart attribute of mxisd. But in case of matrix-synapse-rest-auth
// we have to parse it on our own
if (empty($localpart) && !empty($mxid)) {
// A mxid would start with an @ so we start at the 2. position
$sepPos = strpos($mxid,':', 1);
if ($sepPos === false) {
// : not found. Assume mxid is localpart
// TODO: further checks
$localpart = $mxid;
} else {
$localpart = substr($mxid, 1, strpos($mxid,':') - 1 );
}
}
if (empty($localpart)) {
throw new Exception ("localpart cannot be identified");
}
$password = NULL;
if (isset($input["user"]) && isset($input["user"]["password"])) {
$password = $input["user"]["password"];
}
if (empty($password)) {
throw new Exception ("password is not present");
}
$user = $mx_db->getUserForLogin($localpart, $password);
if (!$user) {
throw new Exception("user not found or password did not match");
}
$response["auth"]["success"] = true;
$response["auth"]["profile"] = [
"display_name" => $user["first_name"] . " " . $user["last_name"],
"three_pids" => [
[
"medium" => "email",
"address" => $user["email"],
],
],
];
switch ($loginRequester) {
case LoginRequester::RestAuth:
$response["auth"]["mxid"] = $mxid;
break;
case LoginRequester::MXISD;
$response["auth"]["id"] = [
"type" => "localpart",
"value" => $localpart,
];
break;
default:
// only return that it was successful.
// we do not know how the data shall be transmitted so we do nothing with it
$response["auth"]["success"] = false;
break;
}
} catch (Exception $e) {
error_log("Auth failed with error: " . $e->getMessage());
$response["auth"]["error"] = $e->getMessage();
}
print (json_encode($response, JSON_PRETTY_PRINT) . "\n");
?>

27
lang/lang.de-de.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
$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",
"REGISTRATION_FAILED" => "Registrierung ist fehlgeschlagen",
"VERIFICATION_SUCEEDED" => "Verifizierung erfolgreich",
"VERIFICATION_FAILED" => "Verifizierung fehlgeschlagen",
"VERIFICATION_SUCCESS_BODY" => "Vielen Dank. Die Administratoren wurden informiert",
"ADMIN_VERIFY_SITE_TITLE" => "Registrierungsanfrage bearbeiten",
"ADMIN_REGISTER_ACCEPTED_BODY" => "Die Registrierungsanfrage wurde akzeptiert. Der Nutzer wurde per Mail informiert.",
"ADMIN_REGISTER_DECLINED_BODY" => "Die Registrierungsanfrage wurde angelehnt. Der Nutzer wurde per Mail informiert.",
);
?>

13
language.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
$lang = "de-de";
if(isset($_GET['lang'])){
$lang = filter_var($_GET['lang'], FILTER_SANITIZE_STRING);
}
$lang_file = dirname(__FILE__) . "/lang/lang.".$lang.".php";
if (!file_exists($lang_file)) {
error_log("Translation for " . $lang . " not found. Fallback to 'de-de'");
$lang = "de-de";
}
require_once($lang_file);
unset($lang_file);
?>

108
mail_templates.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
function send_mail($receiver, $subject, $body) {
include("config.php");
$headers = "From: " . $config["register_email"] . "\r\n"
. "Content-Type: text/plain;charset=utf-8";
return mail($receiver, $subject, $body, $headers);
}
function send_mail_pending_verification($homeserver, $user, $receiver, $verify_url) {
$subject = "Bitte bestätige Registrierung auf $homeserver";
$body = "Guten Tag " . $user . ",
Du hast anscheinend versucht dich auf $homeserver zu registrieren.
Hier gibt es eine zweistufige Registrierung.
Wir möchten dich bitten, dass du kurz bestätigst, dass du die Registrierung durchgeführt hast.
Gehe dafür auf folgenden Link:
$verify_url
Erst anschließend werden die Administratoren über deine Registrierungsanfrage informiert.
Hinweis: Du hast ca. 48 Stunden Zeit um die Bestätigung durchzuführen.
Danach ist eine Re-Registrierung mit deinem gewünschten Nutzernamen für andere wieder möglich.
Vielen Dank für dein Verständnis.
Das Administratoren-Team von " . $homeserver;
return send_mail($receiver, $subject, $body );
}
function send_mail_pending_approval($homeserver, $user, $receiver) {
$subject = "Registrierung wartet auf Bestätigung durch Administratoren";
$body = "Guten Tag " . $user . ",
Deine Registrierungsanfrage wurde verifiziert und wird nun durch die Administratoren überprüft.
Du bekommst eine weitere E-Mail, sobald deine Registrierung bestätigt oder ablehnt wurde.
Vielen Dank für dein Verständnis.
Das Administratoren-Team von " . $homeserver;
return send_mail($receiver, $subject, $body );
}
function send_mail_registration_allowed_but_failed($homeserver, $user, $receiver) {
$subject = "Registrierung auf $homeserver genehmigt.";
$body = "Guten Tag " . $user . ",
Deine Registrierungsanfrage wurde durch die Administratoren bestätigt.
Leider ist beim Registrieren ein Fehler aufgetaucht. Der Registrierungversuch wird bald wiederholt.
Wir hoffen, das Problem ist bald behoben.
Wir melden uns, wenn die Registrierung erfolgreich war.
Das Administratoren-Team von " . $homeserver;
return send_mail($receiver, $subject, $body);
}
function send_mail_registration_success($homeserver, $user, $receiver, $username, $password, $howToURL) {
$subject = "Registrierung auf $homeserver erfolgreich.";
$body = "Guten Tag " . $user . ",
Deine Registrierungsanfrage wurde durch die Administratoren bestätigt.
Zum Anmelden kannst du folgende Zugangsdaten verwenden:
Nutzername: $username
Passwort: $password
Hinweis: Aktuell ist es nicht möglich, das Passwort selbst zu ändern. Sobald die Funktionalität zur
Verfügung steht, gibt es aber einen Hinweis.
";
/*
Wichtig: Bitte ändere das Passwort direkt nach der Anmeldung.
Es wird zwar von unserer Seite nicht gespeichert, doch fremde könnten Zugriff auf diese E-Mail
erhalten und so deinen Account kompromittieren.
*/
if (!empty($howToURL)) {
$body .= "
Zu weiteren Hilfestellungen findest du hier eine Auflistung von verschiedenen
Anleitungen zu verschiedenen Clients:
$howToURL\n";
}
$body .= "
Viel Spaß bei der Verwendung von $homeserver.
Bei Fragen findest du nach der Anmeldung ein paar Räume in denen du sie stellen kannst.
Das Administratoren-Team von " . $homeserver;
return send_mail($receiver, $subject, $body);
}
function send_mail_registration_decline($homeserver, $user, $receiver, $reason) {
$subject = "Registrierung auf $homeserver abgelehnt.";
$body = "Guten Tag " . $user . ",
Deine Registrierungsanfrage wurde durch die Administratoren abgelehnt.\n";
if (empty($reason)) {
$body .= "\nEs wurde kein Grund angegeben\n";
} else {
$body .= "\nAls Grund wurde folgendes angegeben:\n$reason\n";
}
$body .= "\nDas Administratoren-Team von " . $homeserver;
return send_mail($receiver, $subject, $body );
}
?>

218
public/index.php Normal file
View File

@@ -0,0 +1,218 @@
<html>
<head>
<?php
require_once "../language.php";
if (!file_exists("../config.php")) {
print($language["NO_CONFIGURATION"]);
exit();
}
require_once "../config.php";
// enforce admin via https
if (!isset($_SERVER['HTTPS'])) {
header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301);
exit();
}
session_start();
if ($_SERVER["REQUEST_METHOD"] == "POST") {
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($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"] &&
$_POST["password"] != $_POST["password_confirm"]) {
throw new Exception($language["PASSWORD_NOT_MATCH"]);
}
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"]);
}
$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);
if (isset($_POST["password"])) {
$password = filter_var($_POST["password"], FILTER_SANITIZE_STRING);
}
$note = filter_var($_POST["note"], FILTER_SANITIZE_STRING);
$email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL);
require_once("../database.php");
$res = $mx_db->addRegistration($first_name, $last_name, $username, $note, $email);
if (!isset($res["verify_token"])) {
error_log("sth. went wrong. registration did not throw but admin_token not set");
throw Exception ("Unknown Error");
}
$verify_token = $res["verify_token"];
$verify_url = $config["webroot"] . "/verify.php?t=" . $verify_token;
require_once "../mail_templates.php";
$success = send_mail_pending_verification(
$config["homeserver"],
$first_name . " " . $last_name,
$email,
$verify_url);
$mx_db->setRegistrationStateVerify(
($success ? RegisterState::PendingEmailVerify : RegisterState::PendingEmailSend),
$verify_token);
print("<title>Erfolgreich</title>");
print("</head><body>");
print("<h1>Erfolgreich</h1>");
print("<p>Bitte überprüfe deine E-Mails um deine E-Mail-Adresse zu bestätigen.</p>");
print("<a href=\"" . $config["webroot"] . "/index.php" . "\">Zur Registrierungsseite</a>");
} catch (Exception $e) {
print("<title>" . $language["REGISTRATION_REQUEST_FAILED"] . "</title>");
print("</head><body>");
print("<h1>" . $language["REGISTRATION_REQUEST_FAILED"] . "</h1>");
print("<p>" . $e->getMessage() . "</p>");
print("<a href=\"" . $config["webroot"] . "/index.php" . "\">Zur Registrierungsseite</a>");
}
} else {
$_SESSION["token"] = bin2hex(random_bytes(16));
?>
<title>Registriere dich für <?php echo $config["homeserver"]; ?></title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css" rel="stylesheet">
<style>
body{
background-color: #525252;
}
.centered-form{
margin-top: 60px;
}
.centered-form .panel{
background: rgba(255, 255, 255, 0.8);
box-shadow: rgba(0, 0, 0, 0.3) 20px 20px 20px;
}
</style>
<script type="text/javascript" src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.1.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row centered-form">
<div class="col-xs-12 col-sm-8 col-md-4 col-sm-offset-2 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Bitte für <?php echo $config["homeserver"]; ?> registrieren<small>2-Schritt-Registrierung</small></h3>
</div>
<div class="panel-body">
<form name="regForm" role="form" action="index.php" method="post">
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<input type="text" name="first_name" id="first_name" class="form-control input-sm"
placeholder="Vorname" pattern="[A-Z][a-z]+">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<input type="text" name="last_name" id="last_name" class="form-control input-sm"
placeholder="Nachname" pattern="[A-Z][a-z]+">
</div>
</div>
</div>
<div class="form-group">
<input type="email" name="email" id="email" class="form-control input-sm" placeholder="E-Mail-Adresse" required>
</div>
<div class="form-group">
<input type="text" name="note" id="note" class="form-control input-sm" placeholder="Notiz zu dir (max. 50 Zeichen)">
</div>
<div class="form-group">
<input type="text" name="username" id="username" class="form-control input-sm"
placeholder="Nutzername (für den Login)" pattern="[a-z1-9]{3,20}" required>
</div>
<?php if (isset($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"]) { ?>
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<input type="password" name="password" id="password" class="form-control input-sm" placeholder="Passwort" required>
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<input type="password" name="password_confirm" id="password_confirm" class="form-control input-sm" placeholder="Passwort bestätigen" required>
</div>
</div>
</div>
<?php } ?>
<input type="hidden" name="token" id="token" value="<?php echo $_SESSION["token"]; ?>">
<input type="submit" value="Registrieren" class="btn btn-info btn-block">
</form>
<p>Hinweis: <br />
<?php echo $config["homeserver"]; ?> ist ein geschlossenes Chat-Netzwerk in dem jeder Nutzer bestätigt werden muss.<br />
Du bekommst eine E-Mail wenn jemand deine Mitgliedschaft bestätigt hat. An diese wird auch dein initiales Passwort gesendet.
Hinterlasse also bitte einen Hinweis zu dir (der nur den entsprechenden Personen gezeigt wird).<br />
Liebe Grüße vom Team von <?php echo $config["homeserver"]; ?>
</p>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var first_name = document.getElementById("first_name");
first_name.oninvalid = function(event) {
event.target.setCustomValidity("Vorname muss das Format <Großbuchstabe><Kleinbuchstaben> haben");
}
first_name.onkeyup = function(event) {
event.target.setCustomValidity("");
}
var last_name = document.getElementById("last_name");
last_name.oninvalid = function(event) {
event.target.setCustomValidity("Nachname muss das Format <Großbuchstabe><Kleinbuchstaben> haben");
}
last_name.onkeyup = function(event) {
event.target.setCustomValidity("");
}
var user_name = document.getElementById("username");
user_name.oninvalid = function(event) {
event.target.setCustomValidity("Nutzername darf zwischen 3 und 20 kleine Buchstaben und Zahlen enthalten");
}
user_name.onkeyup = function (event) {
event.target.setCustomValidity("");
}
<?php if (isset($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"]) { ?>
var password = document.getElementById("password")
, confirm_password = document.getElementById("password_confirm");
function validatePassword(){
if(password.value != confirm_password.value) {
confirm_password.setCustomValidity("Passwörter stimmen nicht überein");
} else {
confirm_password.setCustomValidity('');
}
}
password.onchange = validatePassword;
confirm_password.onkeyup = validatePassword;
<?php } ?>
</script>
<?php } ?>
</body>
</html>

77
public/verify.php Normal file
View File

@@ -0,0 +1,77 @@
<html>
<head>
<?php
require_once "../language.php";
if (!file_exists("../config.php")) {
print($language["NO_CONFIGURATION"]);
exit();
}
require_once "../config.php";
require_once "../mail_templates.php";
// enforce admin via https
if (!isset($_SERVER['HTTPS'])) {
header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301);
exit();
}
session_start();
try {
if ($_SERVER["REQUEST_METHOD"] != "GET") {
throw new Exception("Method not allowed");
}
if (!isset($_GET["t"])) {
throw new Exception($language["UNKNOWN_TOKEN"]);
}
$token = filter_var($_GET["t"], FILTER_SANITIZE_STRING);
require_once("../database.php");
$user = $mx_db->getUserForVerify($token);
if ($user == NULL) {
throw new Exception($language["UNKNOWN_TOKEN"]);
}
$first_name = $user["first_name"];
$last_name = $user["last_name"];
$note = $user["note"];
$email = $user["email"];
$admin_token = $user["admin_token"];
require_once("../MatrixConnection.php");
$adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $admin_token;
$mxConn = new MatrixConnection($config["homeserver"], $config["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:<br />"
. $note . "<br />"
. "Zum Bearbeiten <a href=\"". $adminUrl . "\">hier</a> klicken");
$mxMsg->set_type("m.text");
$response = $mxConn->send($config["register_room"], $mxMsg);
if ($response) {
$message = $language["SEND_MATRIX_FAIL"];
}
$mx_db->setRegistrationStateVerify(
($response ? RegisterState::PendingAdminVerify : RegisterState::PendingAdminSend),
$token);
send_mail_pending_approval($config["homeserver"], $first_name . " " . $last_name, $email);
print("<title>" . $language["VERIFICATION_SUCEEDED"] . "</title>");
print("</head><body>");
print("<h1>" . $language["VERIFICATION_SUCEEDED"] . "</h1>");
print("<p>" . $language["VERIFICATION_SUCCESS_BODY"] . "</p>");
print("<a href=\"" . $config["webroot"] . "/index.php" . "\">Zur Registrierungsseite</a>");
} catch (Exception $e) {
print("<title>" . $language["VERIFICATION_FAILED"] . "</title>");
print("</head><body>");
print("<h1>" . $language["VERIFICATION_FAILED"] . "</h1>");
print("<p>" . $e->getMessage() . "</p>");
print("<a href=\"" . $config["webroot"] . "/index.php" . "\">Zur Registrierungsseite</a>");
}
?>
</body>
</html>

168
public/verify_admin.php Normal file
View File

@@ -0,0 +1,168 @@
<html>
<head>
<?php
require_once "../language.php";
if (!file_exists("../config.php")) {
print($language["NO_CONFIGURATION"]);
exit();
}
require_once "../config.php";
require_once "../mail_templates.php";
// enforce admin via https
if (!isset($_SERVER['HTTPS'])) {
header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301);
exit();
}
session_start();
try {
if ($_SERVER["REQUEST_METHOD"] != "GET") {
throw new Exception("Method not allowed");
}
if (!isset($_GET["t"])) {
throw new Exception($language["UNKNOWN_TOKEN"]);
}
$token = filter_var($_GET["t"], FILTER_SANITIZE_STRING);
require_once("../database.php");
$action = NULL;
if (isset($_GET["allow"])) {
$action = RegisterState::RegistrationAccepted;
}
$decline_reason = NULL;
if (isset($_GET["deny"])) {
$action = RegisterState::RegistrationDeclined;
if (isset($_GET["reason"])) {
$decline_reason = filter_var($_GET["reason"], FILTER_SANITIZE_STRING);
}
}
$user = $mx_db->getUserForApproval($token);
if ($user == NULL) {
throw new Exception($language["UNKNOWN_TOKEN"]);
}
$first_name = $user["first_name"];
$last_name = $user["last_name"];
$username = $user["username"];
$note = $user["note"];
$email = $user["email"];
if ($action == RegisterState::RegistrationAccepted) {
$mx_db->setRegistrationStateAdmin(RegisterState::PendingRegistration, $token);
// register user
require_once("../MatrixConnection.php");
$mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]);
// generate a password with 8 characters
$password = $mx_db->addUser($first_name, $last_name, $username, $email);
if ($password != NULL) {
// send registration_success
$res = send_mail_registration_success($config["homeserver"], $first_name . " " . $last_name, $email, $username, $password, $config["howToURL"]);
if ($res) {
$mx_db->setRegistrationStateAdmin(RegisterState::AllDone, $token);
} else {
$mx_db->setRegistrationStateAdmin(RegisterState::PendingSendRegistrationMail, $token);
}
} else {
send_mail_registration_allowed_but_failed($config["homeserver"], $first_name . " " . $last_name, $email);
$mxMsg = new MatrixMessage();
$mxMsg->set_type("m.text");
$mxMsg->set_body("Fehler beim Registrieren von " . $first_name . " " . $last_name . ".");
$mxConn->send($config["register_room"], $mxMsg);
throw new Exception($language["REGISTRATION_FAILED"]);
}
print("<title>" . $language["ADMIN_VERIFY_SITE_TITLE"] . "</title>");
print("</head><body>");
print("<h1>" . $language["ADMIN_VERIFY_SITE_TITLE"] . "</h1>");
print("<p>" . $language["ADMIN_REGISTER_ACCEPTED_BODY"] . "</p>");
} elseif ($action == RegisterState::RegistrationDeclined) {
$mx_db->setRegistrationStateAdmin(RegisterState::RegistrationDeclined, $token);
send_mail_registration_decline($config["homeserver"], $first_name . " " . $last_name, $email, $decline_reason);
print("<title>" . $language["ADMIN_VERIFY_SITE_TITLE"] . "</title>");
print("</head><body>");
print("<h1>" . $language["ADMIN_VERIFY_SITE_TITLE"] . "</h1>");
print("<p>" . $language["ADMIN_REGISTER_DECLINED_BODY"] . "</p>");
} else {
print("<title>" . $language["ADMIN_VERIFY_SITE_TITLE"] . "</title>");
?>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css" rel="stylesheet">
<style>
body{
background-color: #525252;
}
.centered-form{
margin-top: 60px;
}
.centered-form .panel{
background: rgba(255, 255, 255, 0.8);
box-shadow: rgba(0, 0, 0, 0.3) 20px 20px 20px;
}
</style>
<script type="text/javascript" src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.1.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row centered-form">
<div class="col-xs-12 col-sm-8 col-md-4 col-sm-offset-2 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><?php echo $language["ADMIN_VERIFY_SITE_TITLE"] ; ?></h3>
</div>
<div class="panel-body">
<form name="appForm" role="form" action="verify_admin.php" method="GET">
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<input type="text" id="first_name" class="form-control input-sm"
value="<?php echo $first_name; ?>" disabled=true>
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<input type="text" id="last_name" class="form-control input-sm"
value="<?php echo $last_name; ?>" disabled=true>
</div>
</div>
</div>
<div class="form-group">
<input type="text" id="note" class="form-control input-sm" value="<?php echo $note; ?>" disabled=true>
</div>
<div class="form-group">
<input type="text" id="username" class="form-control input-sm"
value="<?php echo $username; ?>" disabled=true>
</div>
<input type="hidden" name="t" id="token" value="<?php echo $token; ?>">
<input type="submit" name="allow" value="Bestätigen" class="btn btn-info btn-block">
<input type="submit" name="deny" value="Ablehnen" class="btn btn-info btn-block">
</form>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
<?php
} // else - no action provided
} catch (Exception $e) {
print("<title>" . $language["REGISTRATION_FAILED"] . "</title>");
print("</head><body>");
print("<h1>" . $language["REGISTRATION_FAILED"] . "</h1>");
print("<p>" . $e->getMessage() . "</p>");
print("<a href=\"" . $config["webroot"] . "/index.php" . "\">Zur Registrierungsseite</a>");
}
?>
</body>
</html>