mirror of
https://github.com/openvk/openvk
synced 2024-12-22 16:42:32 +03:00
parent
bfa26c24d1
commit
45fe270700
10 changed files with 385 additions and 10 deletions
204
Email/change-email.eml.latte
Normal file
204
Email/change-email.eml.latte
Normal file
|
@ -0,0 +1,204 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>Подтверждение изменения Email</title>
|
||||
<link rel="stylesheet" href="foundation.css" />
|
||||
<style>
|
||||
.container {
|
||||
border-top: 5px solid pink;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table class="body" data-made-with-foundation="">
|
||||
<tr>
|
||||
<td class="float-center" align="center" valign="top">
|
||||
<center>
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="container">
|
||||
<tr>
|
||||
<td>
|
||||
<table class="row header">
|
||||
<tr>
|
||||
<th class="small-12 large-12 columns first last">
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h4 class="text-center">Подтверждение изменения Email</h4>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="row">
|
||||
<tr>
|
||||
<th class="small-12 large-12 columns first last">
|
||||
<table class="row">
|
||||
<tr>
|
||||
<td>
|
||||
<center>
|
||||
<img src="pictures/lock.jpeg" align="center" class="float-center" width=128 height=128 />
|
||||
</center>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-left">
|
||||
Здравствуйте, {$name}! Вы вероятно изменили свой адрес электронной почты в OpenVK. Чтобы изменение вступило в силу, необходимо подтвердить ваш новый Email.
|
||||
</p>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="button large expand success">
|
||||
<tr>
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<center>
|
||||
<a href="http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={rawurlencode($key)}" align="center" class="float-center">Подтвердить Email!</a>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-left">
|
||||
Если кнопка не работает, вы можете попробовать скопировать и вставить эту ссылку в адресную строку вашего веб-обозревателя:
|
||||
</p>
|
||||
|
||||
<table class="callout">
|
||||
<tr>
|
||||
<th class="callout-inner primary">
|
||||
<a href="http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={$key}" style="color: #000; text-decoration: none;">
|
||||
http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={$key}
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-left">
|
||||
Обратите внимание на то, что эту ссылку нельзя:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Передавать другим людям (даже друзьям, питомцам, соседам, любимым девушкам)</li>
|
||||
<li>Использовать, если прошло более двух дней с её генерации</li>
|
||||
</ul>
|
||||
|
||||
<table class="callout">
|
||||
<tr>
|
||||
<th class="callout-inner alert">
|
||||
<p>
|
||||
Ещё раз <b>обратите внимание</b> на то, что данную ссылку или письмо <b>ни в коем случае нельзя</b> передавать другим людям! Даже если они представляются службой поддержки.<br/>
|
||||
Это письмо предназначено исключительно для одноразового, <b>непосредственного</b> использования владельцем аккаунта.
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-right">
|
||||
С уважением, овк-тян.
|
||||
</p>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-left">
|
||||
<small>
|
||||
Вы получили это письмо так как кто-то или вы изменили адрес электронной почты. Это не рассылка и от неё нельзя отписаться. Если вы всё равно хотите перестать получать подобные письма, деактивируйте ваш аккаунт.
|
||||
</small>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
15
Web/Models/Entities/EmailChangeVerification.php
Normal file
15
Web/Models/Entities/EmailChangeVerification.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Util\DateTime;
|
||||
|
||||
class EmailChangeVerification extends PasswordReset
|
||||
{
|
||||
protected $tableName = "email_change_verifications";
|
||||
|
||||
function getNewEmail(): string
|
||||
{
|
||||
return $this->getRecord()->new_email;
|
||||
}
|
||||
}
|
|
@ -877,6 +877,17 @@ class User extends RowModel
|
|||
return true;
|
||||
}
|
||||
|
||||
function setEmail(string $email): void
|
||||
{
|
||||
DatabaseConnection::i()->getContext()->table("ChandlerUsers")
|
||||
->where("id", $this->getChandlerUser()->getId())->update([
|
||||
"login" => $email
|
||||
]);
|
||||
|
||||
$this->stateChanges("email", $email);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function adminNotify(string $message): bool
|
||||
{
|
||||
$admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
||||
|
|
33
Web/Models/Repositories/EmailChangeVerifications.php
Normal file
33
Web/Models/Repositories/EmailChangeVerifications.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Entities\EmailChangeVerification;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class EmailChangeVerifications
|
||||
{
|
||||
private $context;
|
||||
private $verifications;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->verifications = $this->context->table("email_change_verifications");
|
||||
}
|
||||
|
||||
function toEmailChangeVerification(?ActiveRow $ar): ?EmailChangeVerification
|
||||
{
|
||||
return is_null($ar) ? NULL : new EmailChangeVerification($ar);
|
||||
}
|
||||
|
||||
function getByToken(string $token): ?EmailChangeVerification
|
||||
{
|
||||
return $this->toEmailChangeVerification($this->verifications->where("key", $token)->fetch());
|
||||
}
|
||||
|
||||
function getLatestByUser(User $user): ?EmailChangeVerification
|
||||
{
|
||||
return $this->toEmailChangeVerification($this->verifications->where("profile", $user->getId())->order("timestamp DESC")->fetch());
|
||||
}
|
||||
}
|
|
@ -9,12 +9,15 @@ use openvk\Web\Models\Repositories\Albums;
|
|||
use openvk\Web\Models\Repositories\Videos;
|
||||
use openvk\Web\Models\Repositories\Notes;
|
||||
use openvk\Web\Models\Repositories\Vouchers;
|
||||
use openvk\Web\Models\Repositories\EmailChangeVerifications;
|
||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||
use openvk\Web\Util\Validator;
|
||||
use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification};
|
||||
use openvk\Web\Models\Entities\EmailChangeVerification;
|
||||
use Chandler\Security\Authenticator;
|
||||
use lfkeitel\phptotp\{Base32, Totp};
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
use Nette\Database\UniqueConstraintViolationException;
|
||||
|
||||
final class UserPresenter extends OpenVKPresenter
|
||||
{
|
||||
|
@ -312,7 +315,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
if($this->postParam("old_pass") && $this->postParam("new_pass") && $this->postParam("repeat_pass")) {
|
||||
if($this->postParam("new_pass") === $this->postParam("repeat_pass")) {
|
||||
if($this->user->identity->is2faEnabled()) {
|
||||
$code = $this->postParam("code");
|
||||
$code = $this->postParam("password_change_code");
|
||||
if(!($code === (new Totp)->GenerateToken(Base32::decode($this->user->identity->get2faSecret())) || $this->user->identity->use2faBackupCode((int) $code)))
|
||||
$this->flashFail("err", tr("error"), tr("incorrect_2fa_code"));
|
||||
}
|
||||
|
@ -324,6 +327,46 @@ final class UserPresenter extends OpenVKPresenter
|
|||
}
|
||||
}
|
||||
|
||||
if($this->postParam("new_email")) {
|
||||
if(!Validator::i()->emailValid($this->postParam("new_email")))
|
||||
$this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
|
||||
|
||||
if(!Authenticator::verifyHash($this->postParam("email_change_pass"), $user->getChandlerUser()->getRaw()->passwordHash))
|
||||
$this->flashFail("err", tr("error"), tr("incorrect_password"));
|
||||
|
||||
if($user->is2faEnabled()) {
|
||||
$code = $this->postParam("email_change_code");
|
||||
if(!($code === (new Totp)->GenerateToken(Base32::decode($user->get2faSecret())) || $user->use2faBackupCode((int) $code)))
|
||||
$this->flashFail("err", tr("error"), tr("incorrect_2fa_code"));
|
||||
}
|
||||
|
||||
if($this->postParam("new_email") !== $user->getEmail()) {
|
||||
if (OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']) {
|
||||
$request = (new EmailChangeVerifications)->getLatestByUser($user);
|
||||
if(!is_null($request) && $request->isNew())
|
||||
$this->flashFail("err", tr("forbidden"), tr("email_rate_limit_error"));
|
||||
|
||||
$verification = new EmailChangeVerification;
|
||||
$verification->setProfile($user->getId());
|
||||
$verification->setNew_Email($this->postParam("new_email"));
|
||||
$verification->save();
|
||||
|
||||
$params = [
|
||||
"key" => $verification->getKey(),
|
||||
"name" => $user->getCanonicalName(),
|
||||
];
|
||||
$this->sendmail($this->postParam("new_email"), "change-email", $params); #Vulnerability possible
|
||||
$this->flashFail("succ", tr("information_-1"), tr("email_change_confirm_message"));
|
||||
}
|
||||
|
||||
try {
|
||||
$user->setEmail($this->postParam("new_email"));
|
||||
} catch(UniqueConstraintViolationException $ex) {
|
||||
$this->flashFail("err", tr("error"), tr("user_already_exists"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$user->setShortCode(empty($this->postParam("sc")) ? NULL : $this->postParam("sc")))
|
||||
$this->flashFail("err", tr("error"), tr("error_shorturl_incorrect"));
|
||||
} else if($_GET['act'] === "privacy") {
|
||||
|
@ -400,11 +443,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
throw $ex;
|
||||
}
|
||||
|
||||
$this->flash(
|
||||
"succ",
|
||||
"Изменения сохранены",
|
||||
"Новые данные появятся на вашей странице."
|
||||
);
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
}
|
||||
$this->template->mode = in_array($this->queryParam("act"), [
|
||||
"main", "privacy", "finance", "finance.top-up", "interface"
|
||||
|
@ -502,6 +541,9 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||
$this->flashFail("err", tr("error"), tr("feature_disabled"));
|
||||
|
||||
$receiverAddress = $this->postParam("receiver");
|
||||
$value = (int) $this->postParam("value");
|
||||
$message = $this->postParam("message");
|
||||
|
@ -574,4 +616,24 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
$this->flashFail("succ", tr("information_-1"), tr("rating_increase_successful", $receiver->getURL(), htmlentities($receiver->getCanonicalName()), $value));
|
||||
}
|
||||
|
||||
function renderEmailChangeFinish(): void
|
||||
{
|
||||
$request = (new EmailChangeVerifications)->getByToken(str_replace(" ", "+", $this->queryParam("key")));
|
||||
if(!$request || !$request->isStillValid()) {
|
||||
$this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
|
||||
$this->redirect("/settings");
|
||||
} else {
|
||||
$request->delete(false);
|
||||
|
||||
try {
|
||||
$request->getUser()->setEmail($request->getNewEmail());
|
||||
} catch(UniqueConstraintViolationException $ex) {
|
||||
$this->flashFail("err", tr("error"), tr("user_already_exists"));
|
||||
}
|
||||
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
$this->redirect("/settings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<span class="nobold">{_"2fa_code"}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="code" style="width: 100%;" />
|
||||
<input type="text" name="password_change_code" style="width: 100%;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -154,6 +154,38 @@
|
|||
{$user->getEmail()}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_new_email_address}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="email" name="new_email" style="width: 100%;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_password}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="password" name="email_change_pass" style="width: 100%;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr n:if="$user->is2faEnabled()">
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_"2fa_code"}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="email_change_code" style="width: 100%;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<input type="submit" value="{_save_email_address}" class="button" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br/>
|
||||
|
|
|
@ -73,6 +73,8 @@ routes:
|
|||
handler: "User->disableTwoFactorAuth"
|
||||
- url: "/settings/reset_theme"
|
||||
handler: "User->resetThemepack"
|
||||
- url: "/settings/change_email"
|
||||
handler: "User->emailChangeFinish"
|
||||
- url: "/coins_transfer"
|
||||
handler: "User->coinsTransfer"
|
||||
- url: "/increase_social_credits"
|
||||
|
|
8
install/sqls/00023-email-change.sql
Normal file
8
install/sqls/00023-email-change.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE IF NOT EXISTS `email_change_verifications` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`profile` bigint(20) unsigned NOT NULL,
|
||||
`key` char(64) COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`new_email` varchar(90) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
|
||||
`timestamp` bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
|
|
@ -444,6 +444,8 @@
|
|||
"your_page_address" = "Your address page";
|
||||
"page_address" = "Address page";
|
||||
"current_email_address" = "Current email address";
|
||||
"new_email_address" = "New email address";
|
||||
"save_email_address" = "Save email address";
|
||||
"page_id" = "Page ID";
|
||||
"you_can_also" = "You can also";
|
||||
"delete_your_page" = "delete your page";
|
||||
|
@ -458,6 +460,8 @@
|
|||
"additional_links" = "Additional links";
|
||||
"ad_poster" = "Ad poster";
|
||||
|
||||
"email_change_confirm_message" = "Please confirm your new email address for the change to take effect. We have sent instructions to it.";
|
||||
|
||||
/* Two-factor authentication */
|
||||
|
||||
"two_factor_authentication" = "Two-factor authentication";
|
||||
|
|
|
@ -472,6 +472,8 @@
|
|||
"your_page_address" = "Адрес Вашей страницы";
|
||||
"page_address" = "Адрес страницы";
|
||||
"current_email_address" = "Текущий адрес";
|
||||
"new_email_address" = "Новый адрес";
|
||||
"save_email_address" = "Сохранить адрес";
|
||||
"page_id" = "ID страницы";
|
||||
"you_can_also" = "Вы также можете";
|
||||
"delete_your_page" = "удалить свою страницу";
|
||||
|
@ -486,6 +488,8 @@
|
|||
"additional_links" = "Дополнительные ссылки";
|
||||
"ad_poster" = "Рекламный плакат";
|
||||
|
||||
"email_change_confirm_message" = "Чтобы изменение вступило в силу, подтвердите ваш новый адрес электронной почты. Мы отправили инструкции на него.";
|
||||
|
||||
/* Two-factor authentication */
|
||||
|
||||
"two_factor_authentication" = "Двухфакторная аутентификация";
|
||||
|
|
Loading…
Reference in a new issue