10 Commits

12 changed files with 137 additions and 63 deletions

View File

@@ -14,6 +14,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
require_once(__DIR__ . "/helpers.php");
class MatrixConnection { class MatrixConnection {
private $hs; private $hs;
@@ -45,12 +48,8 @@ class MatrixConnection {
$url = "https://" . $this->hs . "/_matrix/client/r0/rooms/" $url = "https://" . $this->hs . "/_matrix/client/r0/rooms/"
. urlencode($room_id) . "/send/m.room.message?access_token=" . $this->at; . urlencode($room_id) . "/send/m.room.message?access_token=" . $this->at;
$handle = curl_init($url); $handle = getCurlHandle($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_POSTFIELDS, json_encode($send_message));
curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
$response = $this->exec_curl_request($handle); $response = $this->exec_curl_request($handle);
return isset($response["event_id"]); return isset($response["event_id"]);
@@ -70,40 +69,62 @@ class MatrixConnection {
} }
$url = "https://" . $this->hs . "/_matrix/client/r0/profile/@" . $username . ":" . $this->hs; $url = "https://" . $this->hs . "/_matrix/client/r0/profile/@" . $username . ":" . $this->hs;
$handle = curl_init($url); $handle = getCurlHandle($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); $res = $this->exec_curl_request($handle);
return !(isset($res["errcode"]) && $res["errcode"] == "M_UNKNOWN"); return !(isset($res["errcode"]) && $res["errcode"] == "M_UNKNOWN");
} }
function getRegisterNonce() {
$url = "https://" . $this->hs . "/_matrix/client/r0/admin/register";
$handle = getCurlHandle($url);
try {
$response = $this->exec_curl_request($handle);
if (is_array($response) && isset($response["nonce"])) {
return $response["nonce"];
}
throw new Exception("INVALID_RESPONSE_FROM_SERVER");
} catch (Exception $e) {
if (strcmp("AUTHENTICATION_FAILED", $e->getMessage()) == 0) {
throw new Exception("WRONG_REGISTRATION_SHARED_SECRET");
} else {
throw $e;
}
}
}
function register($username, $password, $shared_secret) { function register($username, $password, $shared_secret) {
if (!$username) { if (!$username) {
error_log("no username provided"); error_log("no username provided");
} }
if (!$password) { if (!$password) {
error_log("no message to send"); error_log("no password provided");
} }
$nonce = $this->getRegisterNonce();
$mac = hash_hmac('sha1', $username, $shared_secret); //TODO allow registering of admin.
$hmac_content = $nonce . "\x00" . $username . "\x00" . $password . "\x00notadmin";
$mac = hash_hmac('sha1', $hmac_content, $shared_secret);
$data = array( $data = array(
"nonce" => $nonce,
"username" => $username, "username" => $username,
"password" => $password, "password" => $password,
"mac" => $mac, "mac" => $mac,
); );
$url = "https://" . $this->hs . "/_matrix/client/v2_alpha/register"; $url = "https://" . $this->hs . "/_matrix/client/r0/admin/register";
$handle = curl_init($url); $handle = getCurlHandle($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)); curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($data));
return $this->exec_curl_request($handle); try {
return $this->exec_curl_request($handle);
} catch (Exception $e) {
if (strcmp("AUTHENTICATION_FAILED", $e->getMessage()) == 0) {
throw new Exception("WRONG_REGISTRATION_SHARED_SECRET");
} else {
throw $e;
}
}
} }
function exec_curl_request($handle) { function exec_curl_request($handle) {
@@ -126,7 +147,7 @@ class MatrixConnection {
$response = json_decode($response, true); $response = json_decode($response, true);
error_log("Request has failed with error {$response['error']}\n"); error_log("Request has failed with error {$response['error']}\n");
if ($http_code == 401) { if ($http_code == 401) {
throw new Exception('Invalid access token provided'); throw new Exception("AUTHENTICATION_FAILED");
} }
} else { } else {
$response = json_decode($response, true); $response = json_decode($response, true);
@@ -164,7 +185,6 @@ class MatrixMessage {
function get_object() { function get_object() {
return $this->message; return $this->message;
} }
} }
?> ?>

View File

@@ -1,4 +1,6 @@
# matrix-register-bot # matrix-register-bot
![state: alpha](https://img.shields.io/badge/state-alpha-yellowgreen.svg)
[![#matrix-register-bot:msg-net.de](https://img.shields.io/badge/matrix-%23matrix--register--bot%3Amsg--net.de-brightgreen.svg)](https://matrix.to/#/#matrix-register-bot:msg-net.de)
This bot provides a two-step-registration for matrix ([synapse](https://github.com/matrix-org/synapse)). This bot provides a two-step-registration for matrix ([synapse](https://github.com/matrix-org/synapse)).
@@ -16,11 +18,20 @@ To configure synapse so that the users can login that were created via this bot
- set `operationMode=synapse` so the bot uses the register api to push the new users to synapse or - set `operationMode=synapse` so the bot uses the register api to push the new users to synapse or
- integrate it via [matrix-synapse-rest-auth](https://github.com/kamax-io/matrix-synapse-rest-auth#integrate) by configuring your system to point at `internal/login.php`. - integrate it via [matrix-synapse-rest-auth](https://github.com/kamax-io/matrix-synapse-rest-auth#integrate) by configuring your system to point at `internal/login.php`.
When using `operationMode=local` you can have the following benefits (some require [mxisd](https://github.com/kamax-io/mxisd/blob/master/docs/backends/rest.md)) When using `operationMode=local` you can have the following benefits (some require [mxisd](https://github.com/kamax-io/mxisd/blob/master/docs/stores/rest.md))
- Automatically set the display name based on first and last name on first login - Automatically set the display name based on first and last name on first login
- Use the 3PID lookup for other users (only email) - Use the 3PID lookup for other users (only email)
- Search for users that you have not seen yet - Search for users that you have not seen yet
## Requirements
- Working PHP environment with
- database connection provider \[one of sqlite, mysql, postgres\]
- curl extension to notify admins and register users (in `operationMode=synapse`)
- mail capability to interact with the users (Verification, Approval (+ initial password), Notifications)
- matrix-synapse-rest-auth when using `operationMode=local`
- some PHP capable webserver which makes the folder `public` accessible to the public and propably `internal` for server-internal access
## How to install ## How to install
- Copy `config.sample.php` to `config.php` and configure the bot as you can find there - Copy `config.sample.php` to `config.php` and configure the bot as you can find there
@@ -28,7 +39,7 @@ When using `operationMode=local` you can have the following benefits (some requi
The folder `internal` contains files that only provide API access. They can be accessed by mxisd or matrix-synapse-rest-auth The folder `internal` contains files that only provide API access. They can be accessed by mxisd or matrix-synapse-rest-auth
- To integrate with [matrix-synapse-rest-auth](https://github.com/kamax-io/matrix-synapse-rest-auth): - To integrate with [matrix-synapse-rest-auth](https://github.com/kamax-io/matrix-synapse-rest-auth):
- `/_matrix-internal/identity/v1/check_credentials` should map to `internal/login.php` - `/_matrix-internal/identity/v1/check_credentials` should map to `internal/login.php`
- To integrate with [mxisd](https://github.com/kamax-io/mxisd): Have a look at [the docs of mxisd](https://github.com/kamax-io/mxisd/blob/master/docs/backends/rest.md) and apply as follows: - To integrate with [mxisd](https://github.com/kamax-io/mxisd): Have a look at [the docs of mxisd](https://github.com/kamax-io/mxisd/blob/master/docs/stores/rest.md) and apply as follows:
| Key | file which handles that | Description | | Key | file which handles that | Description |
@@ -41,9 +52,11 @@ When using `operationMode=local` you can have the following benefits (some requi
## Further notes: ## Further notes:
### This bot sends mails ### Security: Passwords from registration form are stored in clear text
To allow the bot to verify the email address of the user and to interact with them e.g. in case of approval this bot needs a running mailserver configuration. Currently the passwords which are typed in while capturing the register request are stored in clear text.
This bot relies on php to be properly configured. The bot needs to access them to trigger a register request with correct credentials.
It is currently strongly recommended to set `"getPasswordOnRegistration" => false` in your config!
This leads to autocreating passwords which will then be send to the users directly without storing it.
### Use the ChangePasswortInterceptor (if `operationMode=local`) ### Use the ChangePasswortInterceptor (if `operationMode=local`)
@@ -58,6 +71,3 @@ Here is an example for nginx:
### The bot postpones some actions ### The bot postpones some actions
There is a cron.php which implements retries and database cleanups (e.g. to remove a username claim) There is a cron.php which implements retries and database cleanups (e.g. to remove a username claim)
For this run cron.php regularly with your system of choice. For this run cron.php regularly with your system of choice.
### Chat
For further questions, comments, feedback and more come and talk in [#matrix-register-bot:msg-net.de](https://matrix.to/#/#matrix-register-bot:msg-net.de)

View File

@@ -19,7 +19,9 @@ require_once(__DIR__ . "/language.php");
require_once(__DIR__ . "/mail_templates.php"); require_once(__DIR__ . "/mail_templates.php");
require_once(__DIR__ . "/database.php"); require_once(__DIR__ . "/database.php");
$sql = "SELECT id, first_name, last_name, username, email, state, note, verify_token, admin_token FROM registrations " $sql = "SELECT id, first_name, last_name, username, password, email,"
. " state, note, verify_token, admin_token "
. "FROM registrations "
. "WHERE state = " . RegisterState::PendingEmailSend . "WHERE state = " . RegisterState::PendingEmailSend
. " OR state = " . RegisterState::PendingAdminSend . " OR state = " . RegisterState::PendingAdminSend
. " OR state = " . RegisterState::PendingRegistration . " OR state = " . RegisterState::PendingRegistration
@@ -87,7 +89,7 @@ foreach ($mx_db->query($sql) as $row) {
break; break;
case "local": case "local":
// register by adding a user to the local database // register by adding a user to the local database
$password = $mx_db->addUser($row["first_name"], $row["last_name"], $row["username"], $row["email"]); $password = $mx_db->addUser($row["first_name"], $row["last_name"], $row["username"], $row["password"], $row["email"]);
break; break;
default: default:
throw new Exception("Unknown operationMode"); throw new Exception("Unknown operationMode");

View File

@@ -78,7 +78,7 @@ class mxDatabase {
first_name TEXT, first_name TEXT,
last_name TEXT, last_name TEXT,
username TEXT, username TEXT,
password_hash TEXT DEFAULT '', password TEXT DEFAULT '',
note TEXT, note TEXT,
email TEXT, email TEXT,
verify_token TEXT, verify_token TEXT,
@@ -98,7 +98,7 @@ class mxDatabase {
)"); )");
// make sure the bot is allowed to login // make sure the bot is allowed to login
if (!$this->userRegistered("register_bot")) { if (!$this->userRegistered("register_bot")) {
$password = $this->addUser("Register", "Bot", "register_bot", $config["register_email"]); $password = $this->addUser("Register", "Bot", "register_bot", NULL, $config["register_email"]);
$config["register_password"] = $password; $config["register_password"] = $password;
$myfile = fopen(dirname(__FILE__) . "/config.json", "w"); $myfile = fopen(dirname(__FILE__) . "/config.json", "w");
fwrite($myfile, json_encode($config, JSON_PRETTY_PRINT)); fwrite($myfile, json_encode($config, JSON_PRETTY_PRINT));
@@ -184,7 +184,7 @@ class mxDatabase {
* *
* @return ["verify_token"] * @return ["verify_token"]
*/ */
function addRegistration($first_name, $last_name, $username, $note, $email) { function addRegistration($first_name, $last_name, $username, $password, $note, $email) {
if ($this->userPendingRegistrations($username)) { if ($this->userPendingRegistrations($username)) {
throw new Exception("USERNAME_PENDING_REGISTRATION"); throw new Exception("USERNAME_PENDING_REGISTRATION");
} }
@@ -196,8 +196,9 @@ class mxDatabase {
$admin_token = bin2hex(random_bytes(16)); $admin_token = bin2hex(random_bytes(16));
$this->db->exec("INSERT INTO registrations $this->db->exec("INSERT INTO registrations
(first_name, last_name, username, note, email, verify_token, admin_token) (first_name, last_name, username, password, note, email, verify_token, admin_token)
VALUES ('" . $first_name . "','" . $last_name . "','" . $username . "','" . $note . "','" VALUES ('" . $first_name . "','" . $last_name . "','"
. $username . "','" . $password . "','" . $note . "','"
. $email . "','" . $verify_token . "','" . $admin_token . "')"); . $email . "','" . $verify_token . "','" . $admin_token . "')");
return [ return [
@@ -217,7 +218,7 @@ class mxDatabase {
$res = $this->db->query($sql); $res = $this->db->query($sql);
if ($res->fetchColumn() > 0) { if ($res->fetchColumn() > 0) {
$sql = "SELECT first_name, last_name, username, note, email FROM registrations" $sql = "SELECT first_name, last_name, username, password, note, email FROM registrations"
. " WHERE admin_token = '" . $admin_token . "'" . " WHERE admin_token = '" . $admin_token . "'"
. " AND state = " . RegisterState::PendingAdminVerify . " AND state = " . RegisterState::PendingAdminVerify
. " LIMIT 1;"; . " LIMIT 1;";
@@ -282,14 +283,16 @@ class mxDatabase {
* NULL when failed * NULL when failed
* *
*/ */
function addUser($first_name, $last_name, $username, $email) { function addUser($first_name, $last_name, $username, $password, $email) {
// check if user already exists and abort in that case // check if user already exists and abort in that case
if ($this->userRegistered($username)) { if ($this->userRegistered($username)) {
return NULL; return NULL;
} }
// generate a password with 10 characters if ($password == NULL) {
$password = bin2hex(openssl_random_pseudo_bytes(5)); // generate a password with 10 characters
$password = bin2hex(openssl_random_pseudo_bytes(5));
}
$password_hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 12]); $password_hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 12]);
$sql = "INSERT INTO logins (first_name, last_name, localpart, password_hash, email) VALUES " $sql = "INSERT INTO logins (first_name, last_name, localpart, password_hash, email) VALUES "

View File

@@ -30,4 +30,13 @@ function stripLocalpart($mxid) {
return $localpart; return $localpart;
} }
function getCurlHandle($url) {
$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"));
return $handle;
}
?> ?>

View File

@@ -31,10 +31,13 @@ $language = array(
"UNKNOWN_SESSION" => "Sitzungstoken nicht vorhanden oder ungültig.", "UNKNOWN_SESSION" => "Sitzungstoken nicht vorhanden oder ungültig.",
"UNKNOWN_USERNAME" => "Nutzername fehlt", "UNKNOWN_USERNAME" => "Nutzername fehlt",
"UNKNOWN_TOKEN" => "Token ist unbekannt", "UNKNOWN_TOKEN" => "Token ist unbekannt",
"USERNAME_LENGTH_INVALID" => "Entweder mehr als 20 oder weniger als 3 Zeichen für den Nutzernamen verwendet", "AUTHENTICATION_FAILED" => "Authentifizierung fehlgeschlagen",
"WRONG_REGISTRATION_SHARED_SECRET" => "registration_shared_secret fehlerhaft",
"USERNAME_INVALID" => "Nutzername muss aus 3 bis 20 Kleinbuchstaben bestehen",
"USERNAME_NOT_ALNUM" => "Nutzername ist nicht alphanumerisch", "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_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", "USERNAME_REGISTERED" => "Dieser Nutzername wurde bereits registriert. Bitte wähle einen anderen Nutzernamen",
"PASSWORD_NOT_PROVIDED" => "Ein oder beide Passwörter wurden nicht gesetzt",
"PASSWORD_NOT_MATCH" => "Passwörter stimmen nicht überein", "PASSWORD_NOT_MATCH" => "Passwörter stimmen nicht überein",
"NOTE_LENGTH_EXEEDED" => "Notiz ist länger als die erlaubten 50 Zeichen", "NOTE_LENGTH_EXEEDED" => "Notiz ist länger als die erlaubten 50 Zeichen",
"PLACEHOLDER_NOTE_ABOUT_YOURSELF" => "Notiz zu dir (max. 50 Zeichen)", "PLACEHOLDER_NOTE_ABOUT_YOURSELF" => "Notiz zu dir (max. 50 Zeichen)",

View File

@@ -31,10 +31,13 @@ $language = array(
"UNKNOWN_SESSION" => "Session token not found of invalid.", "UNKNOWN_SESSION" => "Session token not found of invalid.",
"UNKNOWN_USERNAME" => "username unknown", "UNKNOWN_USERNAME" => "username unknown",
"UNKNOWN_TOKEN" => "Token is unknown", "UNKNOWN_TOKEN" => "Token is unknown",
"USERNAME_LENGTH_INVALID" => "Username cpnsists pf more than 20 or less than 3 characters", "AUTHENTICATION_FAILED" => "Authentication failed",
"WRONG_REGISTRATION_SHARED_SECRET" => "wrong registration_shared_secret",
"USERNAME_INVALID" => "Username has to consist of 3 to 20 small letters",
"USERNAME_NOT_ALNUM" => "Username is not alphanumeric", "USERNAME_NOT_ALNUM" => "Username is not alphanumeric",
"USERNAME_PENDING_REGISTRATION" => "This username is locked for registration. Try again later or try again with a different username", "USERNAME_PENDING_REGISTRATION" => "This username is locked for registration. Try again later or try again with a different username",
"USERNAME_REGISTERED" => "This username is already registered. Please try again with another username", "USERNAME_REGISTERED" => "This username is already registered. Please try again with another username",
"PASSWORD_NOT_PROVIDED" => "One or both passwords are not provided",
"PASSWORD_NOT_MATCH" => "passwords do not match", "PASSWORD_NOT_MATCH" => "passwords do not match",
"NOTE_LENGTH_EXEEDED" => "Note consists of more than 50 characters", "NOTE_LENGTH_EXEEDED" => "Note consists of more than 50 characters",
"PLACEHOLDER_NOTE_ABOUT_YOURSELF" => "Note about yourself (max. 50 characters)", "PLACEHOLDER_NOTE_ABOUT_YOURSELF" => "Note about yourself (max. 50 characters)",
@@ -54,7 +57,8 @@ $language = array(
"ADMIN_REGISTER_ACCEPTED_BODY" => "The registration request got accepted. The user got notified per email.", "ADMIN_REGISTER_ACCEPTED_BODY" => "The registration request got accepted. The user got notified per email.",
"ADMIN_REGISTER_DECLINED_BODY" => "The registration request got declined. The user got notified per email.", "ADMIN_REGISTER_DECLINED_BODY" => "The registration request got declined. The user got notified per email.",
"JUMP_TO_HOMEPAGE" => "To homepage", "JUMP_TO_HOMEPAGE" => "To homepage",
"TOPIC_PLEASE_REGISTER" => "Please register for @homeserver<small>2-Step-Registration</small>", "TOPIC_PLEASE_REGISTER" => "Please register for @homeserver",
"TOPIC_PLEASE_REGISTER_NOTE" => "2-Step-Registration",
"NOTE_FOR_REGISTRATION" => "@homeserver is a closed chat network where every user has to be confirmed.<br /> "NOTE_FOR_REGISTRATION" => "@homeserver is a closed chat network where every user has to be confirmed.<br />
You will get an email once sb. approved your registration. An initial password will be send to you afterwards. You will get an email once sb. approved your registration. An initial password will be send to you afterwards.
Please leave a note about yourself (that will only be shown to the admins).<br /> Please leave a note about yourself (that will only be shown to the admins).<br />

View File

@@ -79,7 +79,7 @@ Deine Registrierungsanfrage wurde durch die Administratoren bestätigt.
Zum Anmelden kannst du folgende Zugangsdaten verwenden: Zum Anmelden kannst du folgende Zugangsdaten verwenden:
Nutzername: $username Nutzername: $username
Passwort: $password Passwort: " . (empty($password) ? "wie selbst gesetzt": $password) . "
Hinweis: Das Passwort kannst du aktuell über die App selbst ändern. Auch wenn das Passwort nirgends Hinweis: Das Passwort kannst du aktuell über die App selbst ändern. Auch wenn das Passwort nirgends
im Klartext gespeichert wird, kann jemand Zugriff auf diese Mail erlangen und so den Zugriff bekommen. im Klartext gespeichert wird, kann jemand Zugriff auf diese Mail erlangen und so den Zugriff bekommen.

View File

@@ -78,7 +78,7 @@ Your registration request got verified by the admin team.
To log in you can use the following credentials:: To log in you can use the following credentials::
Username: $username Username: $username
Password: $password Passwort: " . (empty($password) ? "as self-set": $password) . "
Important: Please change your password as soon as possible after your first login. Important: Please change your password as soon as possible after your first login.
The password is not stored in clear text on the server but people could get access to this mail The password is not stored in clear text on the server but people could get access to this mail

View File

@@ -20,7 +20,7 @@ if (!isset($_SERVER['HTTPS'])) {
} }
require_once(__DIR__ . "/../language.php"); require_once(__DIR__ . "/../language.php");
if (!file_exists("../config.php")) { if (!file_exists(__DIR__ . "/../config.php")) {
print($language["NO_CONFIGURATION"]); print($language["NO_CONFIGURATION"]);
exit(); exit();
} }
@@ -49,14 +49,18 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (!isset($_POST["username"])) { if (!isset($_POST["username"])) {
throw new Exception("UNKNOWN_USERNAME"); throw new Exception("UNKNOWN_USERNAME");
} }
if (strlen($_POST["username"] > 20 || strlen($_POST["username"]) < 3)) { if (strlen($_POST["username"]) > 20 ||
throw new Exception("USERNAME_LENGTH_INVALID"); strlen($_POST["username"]) < 3 ||
!ctype_lower($_POST["username"])) {
throw new Exception("USERNAME_INVALID");
} }
if (ctype_alnum($_POST['username']) != true) { if (ctype_alnum($_POST['username']) != true) {
throw new Exception("USERNAME_NOT_ALNUM"); throw new Exception("USERNAME_NOT_ALNUM");
} }
if (isset($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"] && if ($storePassword && (!isset($_POST["password"]) || !isset($_POST["password_confirm"]))) {
$_POST["password"] != $_POST["password_confirm"]) { throw new Exception("PASSWORD_NOT_PROVIDED");
}
if ($storePassword && $_POST["password"] != $_POST["password_confirm"]) {
throw new Exception("PASSWORD_NOT_MATCH"); throw new Exception("PASSWORD_NOT_MATCH");
} }
if (isset($_POST["note"]) && strlen($_POST["note"]) > 50) { if (isset($_POST["note"]) && strlen($_POST["note"]) > 50) {
@@ -80,6 +84,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
} }
$username = filter_var($_POST["username"], FILTER_SANITIZE_STRING); $username = filter_var($_POST["username"], FILTER_SANITIZE_STRING);
$password = "";
if ($storePassword && isset($_POST["password"])) { if ($storePassword && isset($_POST["password"])) {
$password = filter_var($_POST["password"], FILTER_SANITIZE_STRING); $password = filter_var($_POST["password"], FILTER_SANITIZE_STRING);
} }
@@ -87,7 +92,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL); $email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL);
require_once(__DIR__ . "/../database.php"); require_once(__DIR__ . "/../database.php");
$res = $mx_db->addRegistration($first_name, $last_name, $username, $note, $email); $res = $mx_db->addRegistration($first_name, $last_name, $username, $password, $note, $email);
if (!isset($res["verify_token"])) { if (!isset($res["verify_token"])) {
error_log("sth. went wrong. registration did not throw but admin_token not set"); error_log("sth. went wrong. registration did not throw but admin_token not set");
@@ -215,7 +220,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
<script type="text/javascript"> <script type="text/javascript">
var user_name = document.getElementById("username"); var user_name = document.getElementById("username");
user_name.oninvalid = function (event) { user_name.oninvalid = function (event) {
event.target.setCustomValidity("<?php echo $language["USERNAME_LENGTH_INVALID"]; ?>"); event.target.setCustomValidity("<?php echo $language["USERNAME_INVALID"]; ?>");
} }
user_name.onkeyup = function (event) { user_name.onkeyup = function (event) {
event.target.setCustomValidity(""); event.target.setCustomValidity("");

View File

@@ -51,18 +51,21 @@ try {
$email = $user["email"]; $email = $user["email"];
$admin_token = $user["admin_token"]; $admin_token = $user["admin_token"];
// we have 2 cases: first and last name or just the username
$call_name = strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username;
require_once(__DIR__ . "/../MatrixConnection.php"); require_once(__DIR__ . "/../MatrixConnection.php");
$adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $admin_token; $adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $admin_token;
$mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]);
$mxMsg = new MatrixMessage(); $mxMsg = new MatrixMessage();
$mxMsg->set_body(strtr($language["MSG_USER_WANTS_REGISTER"], [ $mxMsg->set_body(strtr($language["MSG_USER_WANTS_REGISTER"], [
"@name" => (strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username), "@name" => $call_name,
"@note" => $note, "@note" => $note,
"@adminUrl" => $adminUrl "@adminUrl" => $adminUrl
])); ]));
if (isset($language["MSG_USER_WANTS_REGISTER_FORMATTED"])) { if (isset($language["MSG_USER_WANTS_REGISTER_FORMATTED"])) {
$mxMsg->set_formatted_body(strtr($language["MSG_USER_WANTS_REGISTER_FORMATTED"], [ $mxMsg->set_formatted_body(strtr($language["MSG_USER_WANTS_REGISTER_FORMATTED"], [
"@name" => (strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username), "@name" => $call_name,
"@note" => $note, "@note" => $note,
"@adminUrl" => $adminUrl "@adminUrl" => $adminUrl
])); ]));
@@ -76,7 +79,7 @@ try {
$mx_db->setRegistrationStateVerify( $mx_db->setRegistrationStateVerify(
($response ? RegisterState::PendingAdminVerify : RegisterState::PendingAdminSend), $token); ($response ? RegisterState::PendingAdminVerify : RegisterState::PendingAdminSend), $token);
send_mail_pending_approval($config["homeserver"], $first_name . " " . $last_name, $email); send_mail_pending_approval($config["homeserver"], $call_name, $email);
print("<title>" . $language["VERIFICATION_SUCEEDED"] . "</title>"); print("<title>" . $language["VERIFICATION_SUCEEDED"] . "</title>");
print("</head><body>"); print("</head><body>");

View File

@@ -60,6 +60,9 @@ try {
$first_name = $user["first_name"]; $first_name = $user["first_name"];
$last_name = $user["last_name"]; $last_name = $user["last_name"];
$username = $user["username"]; $username = $user["username"];
// we have 2 cases: first and last name or just the username
$call_name = strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username;
$note = $user["note"]; $note = $user["note"];
$email = $user["email"]; $email = $user["email"];
@@ -71,11 +74,17 @@ try {
$mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]);
$password = NULL; $password = NULL;
$use_db_password = (isset($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"]);
if ($use_db_password && isset($user["password"]) && strlen($user["password"]) > 0) {
$password = $user["password"];
} else {
$use_db_password = false;
// generate a password with 10 characters
$password = bin2hex(openssl_random_pseudo_bytes(5));
}
switch ($config["operationMode"]) { switch ($config["operationMode"]) {
case "synapse": case "synapse":
// register with registration_shared_secret // register with registration_shared_secret
// generate a password with 10 characters
$password = bin2hex(openssl_random_pseudo_bytes(5));
$res = $mxConn->register($username, $password, $config["registration_shared_secret"]); $res = $mxConn->register($username, $password, $config["registration_shared_secret"]);
if (!$res) { if (!$res) {
// something went wrong while registering // something went wrong while registering
@@ -84,7 +93,7 @@ try {
break; break;
case "local": case "local":
// register by adding a user to the local database // register by adding a user to the local database
$password = $mx_db->addUser($first_name, $last_name, $username, $email); $password = $mx_db->addUser($first_name, $last_name, $username, $password, $email);
break; break;
default: default:
throw new Exception("Unknown operationMode"); throw new Exception("Unknown operationMode");
@@ -92,7 +101,13 @@ try {
if ($password != NULL) { if ($password != NULL) {
// send registration_success // send registration_success
$res = send_mail_registration_success( $res = send_mail_registration_success(
$config["homeserver"], $first_name . " " . $last_name, $email, $username, $password, $config["howToURL"] $config["homeserver"],
$call_name,
$email,
$username,
// only send password when auto-created
($use_db_password ? NULL : $password),
$config["howToURL"]
); );
if ($res) { if ($res) {
$mx_db->setRegistrationStateAdmin(RegisterState::AllDone, $token); $mx_db->setRegistrationStateAdmin(RegisterState::AllDone, $token);
@@ -100,11 +115,11 @@ try {
$mx_db->setRegistrationStateAdmin(RegisterState::PendingSendRegistrationMail, $token); $mx_db->setRegistrationStateAdmin(RegisterState::PendingSendRegistrationMail, $token);
} }
} else { } else {
send_mail_registration_allowed_but_failed($config["homeserver"], $first_name . " " . $last_name, $email); send_mail_registration_allowed_but_failed($config["homeserver"], $call_name, $email);
$mxMsg = new MatrixMessage(); $mxMsg = new MatrixMessage();
$mxMsg->set_type("m.text"); $mxMsg->set_type("m.text");
$mxMsg->set_body(strtr($language["REGISTRATION_FAILED_FOR"], [ $mxMsg->set_body(strtr($language["REGISTRATION_FAILED_FOR"], [
"@name" => strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username, "@name" => $call_name,
])); ]));
$mxConn->send($config["register_room"], $mxMsg); $mxConn->send($config["register_room"], $mxMsg);
throw new Exception("REGISTRATION_FAILED"); throw new Exception("REGISTRATION_FAILED");
@@ -117,7 +132,7 @@ try {
} elseif ($action == RegisterState::RegistrationDeclined) { } elseif ($action == RegisterState::RegistrationDeclined) {
$mx_db->setRegistrationStateAdmin(RegisterState::RegistrationDeclined, $token); $mx_db->setRegistrationStateAdmin(RegisterState::RegistrationDeclined, $token);
send_mail_registration_decline( send_mail_registration_decline(
$config["homeserver"], strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username, $email, $decline_reason $config["homeserver"], $call_name, $email, $decline_reason
); );
print("<title>" . $language["ADMIN_VERIFY_SITE_TITLE"] . "</title>"); print("<title>" . $language["ADMIN_VERIFY_SITE_TITLE"] . "</title>");
print("</head><body>"); print("</head><body>");