diff --git a/Email/verify-email.eml.latte b/Email/verify-email.eml.latte
new file mode 100755
index 00000000..40711f94
--- /dev/null
+++ b/Email/verify-email.eml.latte
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Здравствуйте, {$name}! Вы вероятно зарегистрировались на одном из инстансов OpenVK. Чтобы ваш аккаунт активировался, необходимо подтвердить Email.
+
+
+
+
+
+
+
+
+
+ Если кнопка не работает, вы можете попробовать скопировать и вставить эту ссылку в адресную строку вашего веб-обозревателя:
+
+
+
+
+
+ Обратите внимание на то, что эту ссылку нельзя:
+
+
+
+ - Передавать другим людям (даже друзьям, питомцам, соседам, любимым девушкам)
+ - Использовать, если прошло более двух дней с её генерации
+
+
+
+
+
+
+ Ещё раз обратите внимание на то, что данную ссылку или письмо ни в коем случае нельзя передавать другим людям! Даже если они представляются службой поддержки.
+ Это письмо предназначено исключительно для одноразового, непосредственного использования владельцем аккаунта.
+
+ |
+
+
+
+
+
+
+ С уважением, овк-тян.
+
+
+
+
+
+
+
+
+
+
+ Вы получили это письмо так как кто-то или вы зарегистрировались на инстансе OpenVK. Это не рассылка и от неё нельзя отписаться. Если вы всё равно хотите перестать получать подобные письма, деактивируйте ваш аккаунт.
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
diff --git a/Web/Models/Entities/EmailVerification.php b/Web/Models/Entities/EmailVerification.php
new file mode 100755
index 00000000..cfd057f9
--- /dev/null
+++ b/Web/Models/Entities/EmailVerification.php
@@ -0,0 +1,10 @@
+getRecord()->timestamp > (time() - 301);
+ return $this->getRecord()->timestamp > (time() - (5 * MINUTE));
}
+ /**
+ * Token is valid only for 3 days.
+ */
function isStillValid(): bool
{
- return $this->getRecord()->timestamp > (time() - 172801);
+ return $this->getRecord()->timestamp > (time() - (3 * DAY));
}
function verify(string $token): bool
diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php
index 81c10695..84e48041 100644
--- a/Web/Models/Entities/User.php
+++ b/Web/Models/Entities/User.php
@@ -850,10 +850,10 @@ class User extends RowModel
function isDeleted(): bool
{
- if ($this->getRecord()->deleted == 1)
- return TRUE;
- else
- return FALSE;
+ if ($this->getRecord()->deleted == 1)
+ return TRUE;
+ else
+ return FALSE;
}
/**
@@ -882,6 +882,12 @@ class User extends RowModel
{
return $this->getRecord()->website;
}
+
+ // ты устрица
+ function isActivated(): bool
+ {
+ return (bool) $this->getRecord()->activated;
+ }
use Traits\TSubscribable;
}
diff --git a/Web/Models/Repositories/Confirmations.php b/Web/Models/Repositories/Confirmations.php
new file mode 100755
index 00000000..1553b50f
--- /dev/null
+++ b/Web/Models/Repositories/Confirmations.php
@@ -0,0 +1,33 @@
+context = DatabaseConnection::i()->getContext();
+ $this->confirmations = $this->context->table("email_verifications");
+ }
+
+ function toEmailVerification(?ActiveRow $ar): ?EmailVerification
+ {
+ return is_null($ar) ? NULL : new EmailVerification($ar);
+ }
+
+ function getByToken(string $token): ?EmailVerification
+ {
+ return $this->toEmailVerification($this->confirmations->where("key", $token)->fetch());
+ }
+
+ function getLatestByUser(User $user): ?EmailVerification
+ {
+ return $this->toEmailVerification($this->confirmations->where("profile", $user->getId())->order("timestamp DESC")->fetch());
+ }
+}
diff --git a/Web/Presenters/AboutPresenter.php b/Web/Presenters/AboutPresenter.php
index bd84efbc..e7351897 100644
--- a/Web/Presenters/AboutPresenter.php
+++ b/Web/Presenters/AboutPresenter.php
@@ -8,6 +8,7 @@ use Chandler\Session\Session;
final class AboutPresenter extends OpenVKPresenter
{
protected $banTolerant = true;
+ protected $activationTolerant = true;
function renderIndex(): void
{
diff --git a/Web/Presenters/AuthPresenter.php b/Web/Presenters/AuthPresenter.php
index 9123f12a..d6970ff1 100644
--- a/Web/Presenters/AuthPresenter.php
+++ b/Web/Presenters/AuthPresenter.php
@@ -3,9 +3,11 @@ namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\IP;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Entities\PasswordReset;
+use openvk\Web\Models\Entities\EmailVerification;
use openvk\Web\Models\Repositories\IPs;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\Repositories\Restores;
+use openvk\Web\Models\Repositories\Confirmations;
use openvk\Web\Util\Validator;
use Chandler\Session\Session;
use Chandler\Security\User as ChandlerUser;
@@ -16,19 +18,22 @@ use lfkeitel\phptotp\{Base32, Totp};
final class AuthPresenter extends OpenVKPresenter
{
protected $banTolerant = true;
+ protected $activationTolerant = true;
private $authenticator;
private $db;
private $users;
private $restores;
+ private $confirmations;
- function __construct(Users $users, Restores $restores)
+ function __construct(Users $users, Restores $restores, Confirmations $confirmations)
{
$this->authenticator = Authenticator::i();
$this->db = DatabaseConnection::i()->getContext();
$this->users = $users;
$this->restores = $restores;
+ $this->confirmations = $confirmations;
parent::__construct();
}
@@ -81,7 +86,7 @@ final class AuthPresenter extends OpenVKPresenter
$this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
if (strtotime($this->postParam("birthday")) > time())
- $this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
+ $this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
$chUser = ChandlerUser::create($this->postParam("email"), $this->postParam("password"));
if(!$chUser)
@@ -96,12 +101,25 @@ final class AuthPresenter extends OpenVKPresenter
$user->setSince(date("Y-m-d H:i:s"));
$user->setRegistering_Ip(CONNECTING_IP);
$user->setBirthday(strtotime($this->postParam("birthday")));
+ $user->setActivated((int) !OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']);
$user->save();
if(!is_null($referer)) {
$user->toggleSubscription($referer);
$referer->toggleSubscription($user);
}
+
+ if (OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']) {
+ $verifObj = new EmailVerification;
+ $verifObj->setProfile($user->getId());
+ $verifObj->save();
+
+ $params = [
+ "key" => $verifObj->getKey(),
+ "name" => $user->getCanonicalName(),
+ ];
+ $this->sendmail($user->getEmail(), "verify-email", $params); #Vulnerability possible
+ }
$this->authenticator->authenticate($chUser->getId());
$this->redirect("/id" . $user->getId(), static::REDIRECT_TEMPORARY);
@@ -253,4 +271,49 @@ final class AuthPresenter extends OpenVKPresenter
$this->flashFail("succ", tr("information_-1"), tr("password_reset_email_sent"));
}
}
+
+ function renderResendEmail(): void
+ {
+ if(!is_null($this->user) && $this->user->identity->isActivated())
+ $this->redirect("/id" . $this->user->id, static::REDIRECT_TEMPORARY);
+
+ if($_SERVER["REQUEST_METHOD"] === "POST") {
+ $user = $this->user->identity;
+ if(!$user || $user->isDeleted() || $user->isActivated())
+ $this->flashFail("err", tr("error"), tr("email_error"));
+
+ $request = $this->confirmations->getLatestByUser($user);
+ if(!is_null($request) && $request->isNew())
+ $this->flashFail("err", tr("forbidden"), tr("email_rate_limit_error"));
+
+ $verifObj = new EmailVerification;
+ $verifObj->setProfile($user->getId());
+ $verifObj->save();
+
+ $params = [
+ "key" => $verifObj->getKey(),
+ "name" => $user->getCanonicalName(),
+ ];
+ $this->sendmail($user->getEmail(), "verify-email", $params); #Vulnerability possible
+
+ $this->flashFail("succ", tr("information_-1"), tr("email_sent"));
+ }
+ }
+
+ function renderVerifyEmail(): void
+ {
+ $request = $this->confirmations->getByToken(str_replace(" ", "+", $this->queryParam("key")));
+ if(!$request || !$request->isStillValid()) {
+ $this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
+ $this->redirect("/");
+ return;
+ }else{
+ $user = $request->getUser();
+ $user->setActivated(1);
+ $user->save();
+
+ $this->flash("success", tr("email_verify_success"));
+ $this->redirect("/");
+ }
+ }
}
diff --git a/Web/Presenters/OpenVKPresenter.php b/Web/Presenters/OpenVKPresenter.php
index 51840c3d..30c0b0a6 100755
--- a/Web/Presenters/OpenVKPresenter.php
+++ b/Web/Presenters/OpenVKPresenter.php
@@ -13,6 +13,7 @@ use WhichBrowser;
abstract class OpenVKPresenter extends SimplePresenter
{
protected $banTolerant = false;
+ protected $activationTolerant = false;
protected $errorTemplate = "@error";
protected $user = NULL;
@@ -212,6 +213,27 @@ abstract class OpenVKPresenter extends SimplePresenter
$this->template->userTainted = $user->isTainted();
if($this->user->identity->isDeleted()) {
+ /*
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⠶⠶⣶⠶⠶⠶⠶⠶⠶⠶⠶⠶⢶⠶⠶⠶⠤⠤⠤⠤⣄⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⣠⡾⠋⠀⠀⠊⠀⠀⠀⠀⠀⠀⠀⠀⠒⠒⠒⠀⠀⠀⠀⠤⢤⣤⣄⠉⠉⠛⠛⠷⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⣰⠟⠀⠀⠀⠀⠀⠐⠋⢑⣤⣶⣶⣤⡢⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣄⡂⠀⠀⠶⢄⠙⢷⣤⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⣸⡿⠚⠉⡀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⢢⠀⠀⡀⣰⣿⣿⣿⣿⣦⡀⠀⠀⠡⡀⢹⡆⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⢀⣴⠏⠀⣀⣀⣀⡤⢤⣄⣠⣿⣿⣿⣿⣻⣿⣿⣷⠀⢋⣾⠈⠙⣶⠒⢿⣿⣿⣿⣿⡿⠟⠃⠀⡀⠡⠼⣧⡀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⢀⣴⣿⢃⡴⢊⢽⣶⣤⣀⠀⠊⠉⠉⡛⢿⣿⣿⣿⠿⠋⢀⡀⠁⠀⠀⢸⣁⣀⣉⣉⣉⡉⠀⠩⡡⠀⣩⣦⠀⠈⠻⣦⡀⠀⠀⠀⠀
+ ⠀⢠⡟⢡⠇⡞⢀⠆⠀⢻⣿⣿⣷⣄⠀⢀⠈⠂⠈⢁⡤⠚⡟⠉⠀⣀⣀⠀⠈⠳⣍⠓⢆⢀⡠⢀⣨⣴⣿⣿⡏⢀⡆⠀⢸⡇⠀⠀⠀⠀
+ ⠀⣾⠁⢸⠀⠀⢸⠀⠀⠀⠹⣿⣿⣿⣿⣶⣬⣦⣤⡈⠀⠀⠇⠀⠛⠉⣩⣤⣤⣤⣿⣤⣤⣴⣾⣿⣿⣿⣿⣿⣧⠞⠀⠀⢸⡇⠀⠀⠀⠀
+ ⠀⢹⣆⠸⠀⠀⢸⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣟⣛⠛⠛⣛⡛⠛⠛⣛⣋⡉⠉⣡⠶⢾⣿⣿⣿⣿⣿⣿⡇⠀⠀⢀⣾⠃⠀⠀⠀⠀
+ ⠀⠀⠻⣆⡀⠀⠈⢂⠀⠀⠀⠠⡈⢻⣿⣿⣿⣿⡟⠁⠈⢧⡼⠉⠙⣆⡞⠁⠈⢹⣴⠃⠀⢸⣿⣿⣿⣿⣿⣿⠃⠀⡆⣾⠃⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠈⢻⣇⠀⠀⠀⠀⠀⠀⢡⠀⠹⣿⣿⣿⣷⡀⠀⣸⡇⠀⠀⣿⠁⠀⠀⠘⣿⠀⠀⠘⣿⣿⣿⣿⣿⣿⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠹⣇⠀⠠⠀⠀⠀⠀⠡⠐⢬⡻⣿⣿⣿⣿⣿⣷⣶⣶⣿⣦⣤⣤⣤⣿⣦⣶⣿⣿⣿⣿⣿⣿⣿⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠹⣧⡀⠡⡀⠀⠀⠀⠑⠄⠙⢎⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⠀⢿⡇⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠈⠳⣤⡐⡄⠀⠀⠀⠈⠂⠀⠱⣌⠻⣿⣿⣿⣿⣿⣿⣿⠿⣿⠟⢻⡏⢻⣿⣿⣿⣿⣿⣿⣿⠀⢸⡇⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢮⣦⡀⠂⠀⢀⠀⠀⠈⠳⣈⠻⣿⣿⣿⡇⠘⡄⢸⠀⠀⣇⠀⣻⣿⣿⣿⣿⣿⡏⠀⠸⡇⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢶⣤⣄⡑⠄⠀⠀⠈⠑⠢⠙⠻⢷⣶⣵⣞⣑⣒⣋⣉⣁⣻⣿⠿⠟⠱⠃⡸⠀⣧⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⣷⣄⡀⠐⠢⣄⣀⡀⠀⠉⠉⠉⠉⠛⠙⠭⠭⠄⠒⠈⠀⠐⠁⢀⣿⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⢦⣤⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣒⡠⠄⣠⡾⠃⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠷⠶⣦⣤⣭⣤⣬⣭⣭⣴⠶⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀
+ */
Authenticator::i()->logout();
Session::i()->set("_su", NULL);
$this->flashFail("err", tr("error"), tr("profile_not_found"));
@@ -222,8 +244,17 @@ abstract class OpenVKPresenter extends SimplePresenter
header("HTTP/1.1 403 Forbidden");
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@banned.xml", [
"thisUser" => $this->user->identity,
- "csrfToken" => $GLOBALS["csrfToken"],
- "isTimezoned" => Session::i()->get("_timezoneOffset"),
+ "csrfToken" => $GLOBALS["csrfToken"]
+ ]);
+ exit;
+ }
+
+ // ето для емейл уже надо (и по хорошему надо бы избавится от повторяющегося кода мда)
+ if(!$this->user->identity->isActivated() && !$this->activationTolerant) {
+ header("HTTP/1.1 403 Forbidden");
+ $this->getTemplatingEngine()->render(__DIR__ . "/templates/@email.xml", [
+ "thisUser" => $this->user->identity,
+ "csrfToken" => $GLOBALS["csrfToken"]
]);
exit;
}
diff --git a/Web/Presenters/templates/@email.xml b/Web/Presenters/templates/@email.xml
new file mode 100755
index 00000000..3de8b2e1
--- /dev/null
+++ b/Web/Presenters/templates/@email.xml
@@ -0,0 +1,19 @@
+{extends "@layout.xml"}
+{block title}{_ec_header}{/block}
+
+{block header}
+{_ec_header}
+{/block}
+
+{block content}
+