mirror of
https://github.com/sbrl/Pepperminty-Wiki.git
synced 2024-11-21 16:13:00 +00:00
Add url_stem() & email address verification system
This commit is contained in:
parent
e6fd579bf1
commit
322f956a9f
4 changed files with 152 additions and 21 deletions
|
@ -28,12 +28,27 @@ function url_origin( $s = false, $use_forwarded_host = false )
|
|||
* @param bool $use_forwarded_host Whether to take the X-Forwarded-Host header into account.
|
||||
* @return string The full url, as requested by the client.
|
||||
*/
|
||||
function full_url( $s = false, $use_forwarded_host = false )
|
||||
{
|
||||
function full_url($s = false, $use_forwarded_host = false) {
|
||||
if($s == false) $s = $_SERVER;
|
||||
return url_origin( $s, $use_forwarded_host ) . $s['REQUEST_URI'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stem URL at which this Pepperminty Wiki instance is located
|
||||
* You can just append ?get_params_here to this and it will be a valid URL.
|
||||
* Uses full_url() under the hood.
|
||||
* Note that this is based on the URL of the current request.
|
||||
* @param array $s The $_SERVER variable (defaults to $_SERVER)
|
||||
* @param boolean $use_forwarded_host Whether to use the x-forwarded-host header or ignore it.
|
||||
* @return string The stem url, as described above
|
||||
*/
|
||||
function url_stem( $s = false, $use_forwarded_host = false) {
|
||||
// Calculate the stem from the current full URL by stripping everything after the question mark ('?')
|
||||
$url_stem = full_url();
|
||||
if(mb_strrpos($url_stem, "?") !== false) $url_stem = mb_substr($url_stem, mb_strrpos($url_stem, "?"));
|
||||
return $url_stem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a filesize into a human-readable string.
|
||||
* @package core
|
||||
|
@ -698,9 +713,9 @@ function extract_user_from_userpage($userPagename) {
|
|||
* @param string $body The body of the email.
|
||||
* @return bool Whether the email was sent successfully or not. Currently, this may fail if the user doesn't have a registered email address.
|
||||
*/
|
||||
function email_user($username, $subject, $body)
|
||||
function email_user(string $username, string $subject, string $body) : boolean
|
||||
{
|
||||
global $version, $settings;
|
||||
global $version, $env, $settings;
|
||||
|
||||
static $literator = null;
|
||||
if($literator == null) $literator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
|
||||
|
@ -709,6 +724,10 @@ function email_user($username, $subject, $body)
|
|||
if(empty($settings->users->{$username}->emailAddress))
|
||||
return false;
|
||||
|
||||
// If email address verification is required but hasn't been done for this user, skip them
|
||||
if(empty($env->user_data->emailAddressVerified))
|
||||
return false;
|
||||
|
||||
|
||||
$headers = [
|
||||
"x-mailer" => ini_get("user_agent"),
|
||||
|
@ -790,3 +809,16 @@ function delete_recursive($path, $delete_self = true) {
|
|||
}
|
||||
if($delete_self) rmdir($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a crytographically-safe random id of the given length.
|
||||
* @param int $length The length of id to generate.
|
||||
* @return string The random id.
|
||||
*/
|
||||
function crypto_id(int $length) : string {
|
||||
// It *should* be the right length already, but it doesn't hurt to be safe
|
||||
return substr(strtr(
|
||||
base64_encode(random_bytes($length * 0.75)),
|
||||
[ "=" => "", "+" => "-", "/" => "_"]
|
||||
), 0, $length);
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@
|
|||
"version": "0.3.4",
|
||||
"author": "Starbeamrainbowlabs",
|
||||
"description": "Adds a user preferences page, letting people do things like change their email address and password.",
|
||||
"lastupdate": 1577140301,
|
||||
"lastupdate": 1578257121,
|
||||
"optional": false,
|
||||
"extra_data": []
|
||||
},
|
||||
|
@ -192,10 +192,10 @@
|
|||
{
|
||||
"id": "feature-watchlist",
|
||||
"name": "User watchlists",
|
||||
"version": "0.1",
|
||||
"version": "0.1.2",
|
||||
"author": "Starbeamrainbowlabs",
|
||||
"description": "Adds per-user watchlists. When a page on a user's watchlist is edited, a notification email is sent.",
|
||||
"lastupdate": 1577141571,
|
||||
"lastupdate": 1578255352,
|
||||
"optional": false,
|
||||
"extra_data": []
|
||||
},
|
||||
|
|
|
@ -66,6 +66,9 @@ register_module([
|
|||
$content .= " <label for='email-address'>Email Address:</label>\n";
|
||||
$content .= " <input type='email' id='email-address' name='email-address' placeholder='e.g. bob@bobsrockets.com' value='{$env->user_data->emailAddress}' />\n";
|
||||
$content .= " <p><small>Used to send you notifications etc. Never shared with anyone except $settings->admindetails_name, $settings->sitename's administrator.</small></p>\n";
|
||||
if($settings->email_user_verify) {
|
||||
$content .= " <p>Verification status: ".(!empty($env->user_data->emailAddressVerificationCode) || !$env->user_data->emailAddressVerificationCode ? "not " : "")."verified</p>";
|
||||
}
|
||||
$content .= " <input type='submit' value='Save Preferences' />\n";
|
||||
$content .= "</form>\n";
|
||||
$content .= "<h3>Change Password</h3\n>";
|
||||
|
@ -103,31 +106,91 @@ register_module([
|
|||
exit(page_renderer::render_main("Error Saving Preferences - $settings->sitename", "<p>You aren't logged in, so you can't save your preferences. Try <a href='?action=login&returnto=" . rawurlencode("?action=user-preferences") . "'>logging in</a> first.</p>"));
|
||||
}
|
||||
|
||||
if(isset($_POST["email-address"]))
|
||||
{
|
||||
if(mb_strlen($_POST["email-address"]) > 320)
|
||||
{
|
||||
if(isset($_POST["email-address"])) {
|
||||
if(mb_strlen($_POST["email-address"]) > 320) {
|
||||
http_response_code(413);
|
||||
exit(page_renderer::render_main("Error Saving Email Address - $settings->sitename", "<p>The email address you supplied (<code>{$_POST['email-address']}</code>) is too long. Email addresses can only be 320 characters long. <a href='javascript:window.history.back();'>Go back</a>."));
|
||||
}
|
||||
|
||||
if(mb_strpos($_POST["email-address"], "@") === false)
|
||||
{
|
||||
if(mb_strpos($_POST["email-address"], "@") === false) {
|
||||
http_response_code(422);
|
||||
exit(page_renderer::render_main("Error Saving Email Address - $settings->sitename", "<p>The email address you supplied (<code>{$_POST['email-address']}</code>) doesn't appear to be valid. <a href='javascript:window.history.back();'>Go back</a>."));
|
||||
}
|
||||
|
||||
$old_address = $env->user_data->emailAddress ?? null;
|
||||
$env->user_data->emailAddress = $_POST["email-address"];
|
||||
// If email address verification is required and the email
|
||||
// address has changed, send a verification email now
|
||||
if($settings->email_user_verify && $old_address !== $_POST["email-address"]) {
|
||||
$env->user_data->emailAddressVerified = false;
|
||||
if(!email_user_verify($env->user)) {
|
||||
http_response_code(503);
|
||||
exit(page_renderer::render_main("Server error sending verification code - $settings->sitename", "<p>$settings->sitename tried to send you an email to verify your email address, but was unable to do so. The changes to your settings have not been saved. Please contact $settings->admindetails_name, whose email address can be found at the bottom of this page.</p>"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the user's preferences
|
||||
if(!save_userdata())
|
||||
{
|
||||
if(!save_userdata()) {
|
||||
http_response_code(503);
|
||||
exit(page_renderer::render_main("Error Saving Preferences - $settings->sitename", "<p>$settings->sitename had some trouble saving your preferences! Please contact $settings->admindetails_name, $settings->sitename's administrator and tell them about this error if it still occurs in 5 minutes. They can be contacted by email at this address: <a href='mailto:" . hide_email($settings->admindetails_email) . "'>" . hide_email($settings->admindetails_email) . "</a>.</p>"));
|
||||
}
|
||||
|
||||
exit(page_renderer::render_main("Preferences Saved Successfully - $settings->sitename", "<p>Your preferences have been saved successfully! You could go back your <a href='?action=user-preferences'>preferences page</a>, or on to the <a href='?page=" . rawurlencode($settings->defaultpage) . "'>$settings->defaultpage</a>.</p>"));
|
||||
exit(page_renderer::render_main("Preferences Saved Successfully - $settings->sitename", "<p>Your preferences have been saved successfully! You could go back your <a href='?action=user-preferences'>preferences page</a>, or on to the <a href='?page=" . rawurlencode($settings->defaultpage) . "'>$settings->defaultpage</a>.</p>
|
||||
<p>If you changed your email address, a verification code will have been sent to the email address you specified. Click on the link provided to verify your new email address.</p>"));
|
||||
});
|
||||
|
||||
/**
|
||||
* @api {get} ?action=email-address-verify&code={code} Verify the current user's email address
|
||||
* @apiName EmailAddressVerify
|
||||
* @apiGroup Settings
|
||||
* @apiPermission User
|
||||
*
|
||||
* @apiParam {string} code The verfication code.
|
||||
*
|
||||
* @apiError VerificationCodeIncorrect The supplied verification code is not correct.
|
||||
*/
|
||||
add_action("email-address-verify", function() {
|
||||
global $env, $settings;
|
||||
|
||||
if(!$env->is_logged_in) {
|
||||
http_response_code(307);
|
||||
header("x-status: failed");
|
||||
header("x-problem: not-logged-in");
|
||||
exit(page_renderer::render_main("Not logged in - $settings->sitename", "<p>You aren't logged in, so you can't verify your email address. Try <a href='?action=login&returnto=".rawurlencode("?action=email-address-verify&code=".($_GET["code"]??""))."'>logging in</a>.</p>"));
|
||||
}
|
||||
|
||||
if($env->user_data->emailAddressVerified) {
|
||||
header("x-status: success");
|
||||
exit(page_renderer::render_main("Already verified - $settings->sitename", "<p>Your email address is already verified, so you don't need to verify it again.</p>\n<p> <a href='index.php'>Go to the main page</a>.</p>"));
|
||||
}
|
||||
|
||||
if(empty($_GET["code"])) {
|
||||
http_response_code(400);
|
||||
header("x-status: failed");
|
||||
header("x-problem: no-code-specified");
|
||||
exit(page_renderer::render_main("No verification code specified - $settings->sitename", "<p>No verification code specified. Do so with the <code>code</code> GET parameter.</p>"));
|
||||
}
|
||||
|
||||
if($env->user_data->emailAddressVerificationCode !== $_GET["code"]) {
|
||||
http_resonse_code(400);
|
||||
header("x-status: failed");
|
||||
header("x-problem: code-incorrect");
|
||||
exit(page_renderer::render_main("Verification code incorrect", "<p>That verification code was incorrect. Try specifying another one, or going to your <a href='?action=user-preferences'>user preferences</a> and changing your email address to re-send another code (you can change it to the same address).</p>"));
|
||||
}
|
||||
|
||||
// The code supplied must be valid
|
||||
unset($env->user_data->emailAddressVerificationCode);
|
||||
$env->user_data->emailAddressVerified = true;
|
||||
|
||||
if(!save_settings()) {
|
||||
http_response_code(503);
|
||||
header("x-status: failed");
|
||||
header("x-problem: server-error-disk-io");
|
||||
exit(page_renderer::render_main("Server error - $settings->sitename", "<p>Your verification code was correct, but $settings->sitename was unable to update your user details because it failed to write the changes to disk. Please contact $settings->admindetails_name, whose email address can be found at the bottom of the page.</p>"));
|
||||
}
|
||||
|
||||
header("x-status: success");
|
||||
exit(page_renderer::render_main("Email Address Verified - $settings->sitename", "<p>Your email address was verified successfully. <a href='index.php'>Go to the main page</p>, or <a href='?action=user-preferences'>to your user preferences</a> to make further changes.</p>"));
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -223,17 +286,52 @@ register_module([
|
|||
|
||||
// Display a help section on the user preferences, but only if the user
|
||||
// is logged in and so able to access them
|
||||
if($env->is_logged_in)
|
||||
{
|
||||
if($env->is_logged_in) {
|
||||
add_help_section("910-user-preferences", "User Preferences", "<p>As you are logged in, $settings->sitename lets you configure a selection of personal preferences. These can be viewed and tweaked to you liking over on the <a href='?action=user-preferences'>preferences page</a>, which can be accessed at any time by clicking the cog icon (it looks something like this: <a href='?action=user-preferences'>$settings->user_preferences_button_text</a>), though the administrator of $settings->sitename ($settings->admindetails_name) may have changed its appearance.</p>");
|
||||
}
|
||||
|
||||
if($settings->avatars_show)
|
||||
{
|
||||
if($settings->avatars_show) {
|
||||
add_help_section("915-avatars", "Avatars", "<p>$settings->sitename allows you to upload an avatar and have it displayed next to your name. If you don't have an avatar uploaded yet, then $settings->sitename will take a <a href='https://www.techopedia.com/definition/19744/hash-function'>hash</a> of your email address and ask <a href='https://gravatar.com'>Gravatar</a> for for your Gravatar instead. If you haven't told $settings->sitename what your email address is either, a hash of your username is used instead. If you don't have a gravatar, then $settings->sitename asks Gravatar for an identicon instead.</p>
|
||||
<p>Your avatar on $settings->sitename currently looks like this: <img class='avatar' src='?action=avatar&user=" . urlencode($env->user) . "' />" . ($settings->upload_enabled ? " - you can upload a new one by going to your <a href='?action=user-preferences'>preferences</a>, or <a href='?action=upload&avatar=yes' />clicking here</a>." : ", but $settings->sitename currently has uploads disabled, so you can't upload a new one directly to $settings->sitename. You can, however, set your email address in your <a href='?action=user-preferences'>preferences</a> and <a href='https://en.gravatar.com/'>create a Gravatar</a>, and then it should show up here on $settings->sitename shortly.") . "</p>");
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* Sends a verification email to the specified user, assuming they need to
|
||||
* verify their email address.
|
||||
* If a user does not need to verify their email address, no verification email
|
||||
* is sent and true is returned.
|
||||
* @param string $username The name of the user to send the verification code to.
|
||||
* @return boolean Whether the verification code was sent successfully. If a user does not need to verify their email address, this returns true.
|
||||
*/
|
||||
function email_user_verify(string $username) : boolean {
|
||||
global $settings;
|
||||
|
||||
$user_data = $settings->users->$username;
|
||||
|
||||
if(!empty($user_data->emailAddressVerified) &&
|
||||
$user_data->emailAddressVerified === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generate a verification code
|
||||
$user_data->emailAddressVerificationCode = crypto_id(64);
|
||||
if(!save_settings())
|
||||
return false;
|
||||
|
||||
return email_user(
|
||||
$username,
|
||||
"Verify your account - $settings->sitename",
|
||||
"Hey there! Click this link to verify your account on $settings->sitename:
|
||||
|
||||
".url_stem()."?action=email-address-verify&code=$user_data->emailAddressVerificationCode
|
||||
|
||||
$settings->sitename requires that you verify your email address in order to use it.
|
||||
|
||||
--$settings->sitename
|
||||
Powered by Pepperminty Wiki"
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -231,6 +231,7 @@
|
|||
"email_debug_dontsend": { "type": "checkbox", "description": "If set to true, emails are logged to the standard error instead of being actually sent.", "default": false },
|
||||
"email_subject_utf8": { "type": "checkbox", "description": "Whether to encode the subject of emails sent to allow them to contain unicode characters. Without this, email subjects will be transliterated to ASCII. If utf-8 email subjects are disabled, page names may not be represented properly.", "default": true },
|
||||
"email_body_utf8": { "type": "checkbox", "description": "Whether to send emails with utf-8 bodies. If set to false, email bodies will be transliterated to ASCII. If utf-8 email bodies are disabled, page names may not be represented properly.", "default": true },
|
||||
"email_user_verify": { "type": "checkbox", "description": "Whether user email addresses must be verified in order to send emails to them.", "default": true },
|
||||
"updateurl": { "type": "url", "description": "The url from which to fetch updates. Defaults to the master (development) branch. MAKE SURE THAT THIS POINTS TO A *HTTPS* URL, OTHERWISE SOMEONE COULD INJECT A VIRUS INTO YOUR WIKI!", "default": "https://raw.githubusercontent.com/sbrl/pepperminty-wiki/master/index.php" },
|
||||
"optimize_pages": { "type": "checkbox", "description": "Whether to optimise all webpages generated.", "default": true },
|
||||
"minify_pageindex": { "type": "checkbox", "description": "Whether to minify the page index when saving it. Improves performance slightly (especially on larger wikis), but can make debugging and quick ninja-edits more awkward. Note that this only takes effect when the page index is next saved.", "default": true },
|
||||
|
|
Loading…
Reference in a new issue