397 lines
12 KiB
PHP
397 lines
12 KiB
PHP
<?php
|
|
/**
|
|
*
|
|
* @file
|
|
* @ingroup Extensions
|
|
* @author Bennet Bleßmann
|
|
* @author Einhard Leichtfuß
|
|
* @copyright © 2020 Bennet Bleßmann, Einhard Leichtfuß
|
|
* @license GNU General Public Licence 2.0 or later
|
|
*/
|
|
|
|
|
|
if (! defined('MEDIAWIKI'))
|
|
{
|
|
echo("Not a valid entry point.\n");
|
|
die(1);
|
|
}
|
|
|
|
require_once(dirname(__FILE__) . "/StudiRegistration.php");
|
|
require_once(dirname(__FILE__) . "/StudiRegistrationCommon.php");
|
|
|
|
|
|
/**
|
|
* Provides the StudiRegistration form
|
|
* @ingroup SpecialPage
|
|
*/
|
|
class SpecialStudiRegistration extends SpecialPage
|
|
{
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct()
|
|
{
|
|
parent::__construct('StudiRegistration');
|
|
}
|
|
|
|
|
|
/**
|
|
* Main execution function
|
|
*
|
|
* @param $par Mixed: Parameters passed to the page
|
|
*/
|
|
public function execute($par) {
|
|
$this->checkPermissions();
|
|
|
|
$out = $this->getOutput();
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$submit = $request->getVal('submit');
|
|
|
|
if ($request->getVal('submit') !== null)
|
|
{
|
|
$stu_name = $request->getVal('stu-name');
|
|
$password = $request->getVal('password');
|
|
$password_rep = $request->getVal('password-rep');
|
|
$ml_req = ($request->getVal('newsletter') !== null);
|
|
|
|
$this->handle_submission($out, $stu_name, $password, $password_rep,
|
|
$ml_req);
|
|
}
|
|
else if ($request->getVal('validate') !== null)
|
|
{
|
|
$token = $request->getVal('token');
|
|
|
|
$this->perform_validation($out, $token);
|
|
}
|
|
else
|
|
{
|
|
$this->print_form($out);
|
|
}
|
|
}
|
|
|
|
|
|
function handle_submission($out, $stu_name, $password, $password_rep, $ml_req) {
|
|
if ($stu_name === null)
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_error-no-username')->text());
|
|
$this->print_form($out);
|
|
return false;
|
|
}
|
|
|
|
if (! StudiRegistrationCommon::validate_stu_name($stu_name))
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_error-format-username')
|
|
->text());
|
|
$this->print_form($out, $stu_name);
|
|
return false;
|
|
}
|
|
|
|
if (mb_strlen($password) < MIN_PW_LEN)
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_error-too-short-password',
|
|
MIN_PW_LEN)->parse());
|
|
$this->print_form($out, $stu_name);
|
|
return false;
|
|
}
|
|
if (mb_strlen($password) > MAX_PW_LEN)
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_error-too-long-password',
|
|
MAX_PW_LEN)->parse());
|
|
$this->print_form($out, $stu_name);
|
|
return false;
|
|
}
|
|
|
|
if ($password === null || $password !== $password_rep)
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_error-different-passwords')
|
|
->parse());
|
|
$this->print_form($out, $stu_name);
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
$pw_hash = self::crypt_hash($password);
|
|
|
|
if (StudiRegistration::stu_exists($stu_name))
|
|
{
|
|
$out->addHTML(
|
|
$this->msg(
|
|
'studi-reg_error-format-username',
|
|
ACC_PREFIX . $stu_name
|
|
)->parse()
|
|
);
|
|
return false;
|
|
}
|
|
|
|
$token = bin2hex(random_bytes(TOKEN_BYTES));
|
|
$token_hash = hash(TOKEN_HASH_ALGO, $token);
|
|
|
|
if (! StudiRegistration::create_registration($stu_name, $pw_hash,
|
|
$token_hash, $ml_req))
|
|
{
|
|
$out->addHTML($this->msg(
|
|
'studi-reg_error-internal-registration-creation')
|
|
->parse());
|
|
$this->print_form($out, $stu_name);
|
|
return false;
|
|
}
|
|
|
|
if (! $this->send_mail($stu_name, $token))
|
|
{
|
|
$out->addHTML($this->msg(
|
|
'studi-reg_error-internal-confirmation-mail')
|
|
->parse());
|
|
$this->print_form($out, $stu_name);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_success-confirmation-mail',
|
|
$stu_name . MAIL_SUFFIX, TOKEN_EXPIRY_MINUTES)
|
|
->parse());
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception $err)
|
|
{
|
|
error_log("StudiRegistration Internal Error: {$err}");
|
|
$out->addHTML($this->msg('studi-reg_error-internal-no-desc')
|
|
->parse());
|
|
$this->print_form($out, $stu_name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
function send_mail($stu_name, $token) {
|
|
$recipient = $stu_name . MAIL_SUFFIX;
|
|
$subject = $this->msg('studi-reg_mail-subject')->plain();
|
|
|
|
$url_token = urlencode($token);
|
|
$query = $this->msg('studi-reg_mail-additional-query-params')->text();
|
|
$link = SELF_URL . "?validate=true&token={$url_token}{$query}";
|
|
|
|
$message_pre = [
|
|
$this->msg('studi-reg_mail-greeting')->plain(),
|
|
""
|
|
];
|
|
|
|
$message_main = $this->msg('studi-reg_mail-main',
|
|
ACC_PREFIX . $stu_name)->plain();
|
|
|
|
$message_post = [
|
|
"",
|
|
" {$link}",
|
|
"",
|
|
$this->msg('studi-reg_mail-note-on-services-link')->plain(),
|
|
"",
|
|
" " . $this->msg('studi-reg_mail-services-link')->plain(),
|
|
"",
|
|
$this->msg('studi-reg_mail-regards')->plain()
|
|
];
|
|
|
|
$message = implode("\r\n", $message_pre) . "\r\n"
|
|
. wordwrap($message_main, MAIL_LINE_LEN, "\r\n") . "\r\n"
|
|
. implode("\r\n", $message_post) . "\r\n";
|
|
|
|
$header = array(
|
|
'From' => 'no-reply@fs-infmath.uni-kiel.de',
|
|
'Reply-To' => 'fachschaft@fs-infmath.uni-kiel.de',
|
|
'Content-type' => 'text/plain; charset=utf-8',
|
|
|
|
// The default is 7bit, i.e. US-ASCII only.
|
|
// Note that 8bit is not required to be supported.
|
|
// An alternative would be base64, which is ugly.
|
|
'Content-Transfer-Encoding' => '8bit'
|
|
);
|
|
|
|
return mail($recipient, $subject, $message, $header);
|
|
}
|
|
|
|
|
|
function perform_validation($out, $token) {
|
|
if ($token === null)
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_error-no-token')->parse());
|
|
return false;
|
|
}
|
|
if (strlen($token) !== TOKEN_HEX_LEN)
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_error-invalid-token-length')
|
|
->parse());
|
|
return false;
|
|
}
|
|
|
|
$token_hash = hash(TOKEN_HASH_ALGO, $token);
|
|
|
|
try
|
|
{
|
|
$acc_name = StudiRegistration::complete_registration($token_hash,
|
|
$ml_error);
|
|
|
|
if ($acc_name !== false)
|
|
{
|
|
$this->print_success($out, $acc_name, $ml_error);
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
$out->addHTML($this->msg('studi-reg_error-invalid-token')
|
|
->parse());
|
|
return false;
|
|
}
|
|
}
|
|
catch(Exception $err)
|
|
{
|
|
error_log("StudiRegistration Internal Error: {$err}");
|
|
$out->addHTML($this->msg('studi-reg_error-internal-no-desc')
|
|
->parse());
|
|
$this->print_form($out);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Hash a password with a random salt using SHA512.
|
|
*/
|
|
static function crypt_hash($password) {
|
|
// SHA512 requires a salt of (up to) 16 random characters in the set
|
|
// [a-zA-Z0-9./]. See crypt(3). base64 has a very similar character
|
|
// set (and an input-to-output ratio of 3-to-4).
|
|
$salt = base64_encode(random_bytes(12));
|
|
$salt = str_replace("+", ".", $salt);
|
|
|
|
// $6$ stands for SHA512.
|
|
return crypt($password, '$6$' . $salt);
|
|
}
|
|
|
|
|
|
function print_form($out, $stu_name = "") {
|
|
// heredocs cannot handle constants.
|
|
$max_stu_len = MAX_STU_LEN;
|
|
$acc_prefix = ACC_PREFIX;
|
|
$stu_name_prefix = STU_NAME_PREFIX;
|
|
$min_pw_len = MIN_PW_LEN;
|
|
$max_pw_len = MAX_PW_LEN;
|
|
$acc_prefix = ACC_PREFIX;
|
|
|
|
$msg_title = $this->msg("studi-reg_registration-title")->parse();
|
|
$msg_otherlang_info = $this->msg("studi-reg_otherlang-info")->plain();
|
|
$msg_note_not_for_ifi =
|
|
$this->msg("studi-reg_note-not-for-ifi")->plain();
|
|
|
|
$msg_stu_number = $this->msg("studi-reg_stu-number")->parse();
|
|
$msg_password = $this->msg("studi-reg_password")->parse();
|
|
$msg_repeat = $this->msg("studi-reg_repeat")->parse();
|
|
$msg_newsletter = $this->msg("studi-reg_newsletter")->parse();
|
|
$msg_privacy_note = $this->msg("studi-reg_privacy-note")->plain();
|
|
$msg_pw_guide_url = $this->msg("studi-reg_pw-guide-url")->plain();
|
|
$msg_password_assertion = $this->msg("studi-reg_password-assertion")
|
|
->plaintextParams($msg_pw_guide_url)
|
|
->text();
|
|
$msg_notes = $this->msg("studi-reg_notes")->text();
|
|
$msg_note1 = $this->msg("studi-reg_note1", ACC_PREFIX, STU_NAME_PREFIX)
|
|
->text();
|
|
$msg_note_target_mail_address =
|
|
$this->msg("studi-reg_note-target-mail-address")->parse();
|
|
$msg_note2 = $this->msg("studi-reg_note2")
|
|
->numParams(MIN_PW_LEN, MAX_PW_LEN)
|
|
->plaintextParams($msg_pw_guide_url)
|
|
->text();
|
|
$msg_note3 = $this->msg("studi-reg_note3")->plain();
|
|
|
|
$out->addHTML(<<<"EOF"
|
|
<h2>{$msg_title}</h2>
|
|
|
|
<p><em>{$msg_otherlang_info}</em></p>
|
|
|
|
<p>{$msg_note_not_for_ifi}</p>
|
|
|
|
<br />
|
|
|
|
<form method='POST' accept-charset='UTF-8'>
|
|
<table cellspacing='10'>
|
|
<tbody>
|
|
<tr>
|
|
<td><label for='stu-name'>{$msg_stu_number}</label>:</td>
|
|
<td align='right' style='padding: 1px; padding-left: 20px;'><strong>{$acc_prefix}</strong><input id='stu-name' type='name' name='stu-name' maxlength='{$max_stu_len}' value='{$stu_name}' placeholder='{$stu_name_prefix}XXXXXX' required='required' />
|
|
</tr>
|
|
<tr>
|
|
<td><label for='password'>{$msg_password}</label>:</td>
|
|
<td align='right' style='padding: 1px; padding-left: 20px;'><input id='password' type='password' name='password' minlength='{$min_pw_len}' maxlength='{$max_pw_len}' required='required' /></td>
|
|
</tr>
|
|
<tr>
|
|
<td><label for='password-rep'>{$msg_password} ({$msg_repeat})</label>:</td>
|
|
<td align='right' style='padding: 1px; padding-left: 20px;'><input id='password-rep' type='password' name='password-rep' maxlength='{$max_pw_len}' required='required' /></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<br />
|
|
<label for='newsletter'>
|
|
<input type='checkbox' name='newsletter' value='subscribe' />
|
|
{$msg_newsletter}
|
|
</label><br />
|
|
<p>{$msg_privacy_note}</p>
|
|
<p>{$msg_password_assertion}</p>
|
|
<br />
|
|
<input type='submit' name='submit' />
|
|
<br />
|
|
|
|
<h3>{$msg_notes}:</h3>
|
|
<ul>
|
|
<li>{$msg_note1}</li>
|
|
<li>{$msg_note_target_mail_address}</li>
|
|
<li>{$msg_note2}</li>
|
|
<li>{$msg_note3}</li>
|
|
</ul>
|
|
</form>
|
|
EOF
|
|
);
|
|
|
|
}
|
|
|
|
|
|
function print_success($out, $acc_name, $ml_error) {
|
|
$stu_name = substr($acc_name, strlen(STU_NAME_PREFIX));
|
|
if ($ml_error)
|
|
{
|
|
$ml_err_msg = <<< "EOF"
|
|
<p>
|
|
{$this->msg('studi-reg_error-newsletter-subscribe')->parse()}
|
|
</p>
|
|
EOF;
|
|
}
|
|
else
|
|
{
|
|
$ml_err_msg = '';
|
|
}
|
|
|
|
|
|
$out->addHTML(<<<"EOF"
|
|
<p>
|
|
{$this->msg('studi-reg_success-confirmed-account', $acc_name)->text()}
|
|
</p>
|
|
|
|
{$ml_err_msg}
|
|
|
|
<p>
|
|
{$this->msg('studi-reg_success-confirmed-active', $acc_name)->text()}
|
|
{$this->msg('studi-reg_success-confirmed-chat', $acc_name)->text()}
|
|
{$this->msg('studi-reg_success-confirmed-list', $acc_name)->text()}
|
|
</p>
|
|
|
|
<p>
|
|
{$this->msg('studi-reg_success-confirmation-note', $acc_name, $stu_name)->text()}
|
|
</p>
|
|
EOF);
|
|
}
|
|
}
|
|
|
|
# vi: tw=80 fo+=t ts=8 sts=0 et sw=4 sta ai
|
|
?>
|