mirror of
https://github.com/slawkens/myaac.git
synced 2025-10-14 17:54:55 +02:00
Compare commits
13 Commits
feature/re
...
feature/2f
Author | SHA1 | Date | |
---|---|---|---|
![]() |
abee4b3962 | ||
![]() |
fbdb6890b9 | ||
![]() |
041f58ed11 | ||
![]() |
03c7dd0002 | ||
![]() |
e435062025 | ||
![]() |
ecc9bd4042 | ||
![]() |
797377e428 | ||
![]() |
96b5df9d74 | ||
![]() |
b3dfc56c96 | ||
![]() |
96d6e04bd2 | ||
![]() |
9146eee327 | ||
![]() |
3d97fa0719 | ||
![]() |
a66cafceab |
@@ -1,21 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [1.8.2 - 26.09.2025]
|
||||
|
||||
### Added
|
||||
* Routes: Possibility to override routes with plugins pages, like characters.php - No need to define routes in plugin.json anymore (https://github.com/slawkens/myaac/commit/3f24f961b1cdeff5c60387e837ae454448bc5e1b)
|
||||
|
||||
### Changed
|
||||
* Style: Better look for myaac-table (https://github.com/slawkens/myaac/commit/a6032093b21e5bb3f0e75d2704da87d6dea6469d, https://github.com/slawkens/myaac/commit/5aa9bbf1c8e580d973ec82ac012489f8e7bc437e)
|
||||
|
||||
### Fixed
|
||||
* Install: Fix when config.local.php cannot be saved (https://github.com/slawkens/myaac/commit/4eab805d26d8c5562b29ed699769919d77dabced)
|
||||
* Create Account: Fix an exception when email cannot be sent (https://github.com/slawkens/myaac/commit/d0112d1a67e8b854b65ad131f0375b79305df8d3)
|
||||
* Login Page: Add missing csrf() - fix create account button (https://github.com/slawkens/myaac/commit/3c0cb53e17dd0b85394cfa0fdc9cf9ad8d4551df)
|
||||
* tibiacom template: Fix account lost menu (https://github.com/slawkens/myaac/commit/ed9beaf2b6ca069e304e569c52e5b9188b58f05c)
|
||||
* tibiacom template: Fix Menu div wrong tag/closing (#329) (https://github.com/slawkens/myaac/commit/85e7005fd3f0be51466151a3c122b96085fdfe68)
|
||||
* tibiacom template: Replace firstChild with firstElementChild (Thanks to @un000000) (https://github.com/slawkens/myaac/commit/df7b6e29fb8875da97f431468c81ee99116271d9)
|
||||
|
||||
## [1.8.1 - 05.09.2025]
|
||||
|
||||
### Added
|
||||
|
@@ -669,17 +669,11 @@ else if (isset($_REQUEST['search'])) {
|
||||
<div class="col-12 col-sm-12 col-lg-6">
|
||||
<label for="lastip" class="control-label">Last IP:</label>
|
||||
<input type="text" class="form-control" id="lastip" name="lastip" autocomplete="off" maxlength="10" value="<?php
|
||||
$lastIPColumnInfo = $db->getColumnInfo('players', 'lastip');
|
||||
if ($lastIPColumnInfo && is_array($lastIPColumnInfo)) {
|
||||
if (str_contains($lastIPColumnInfo['type'], 'varbinary')) {
|
||||
echo inet_ntop($player->getLastIP());
|
||||
}
|
||||
else {
|
||||
echo longToIp($player->getLastIP());
|
||||
}
|
||||
if (strlen($player->getLastIP()) > 11) {
|
||||
echo inet_ntop($player->getLastIP());
|
||||
}
|
||||
else {
|
||||
echo 'Error';
|
||||
echo longToIp($player->getLastIP());
|
||||
}
|
||||
?>" readonly/>
|
||||
</div>
|
||||
|
@@ -26,7 +26,7 @@
|
||||
if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.');
|
||||
|
||||
const MYAAC = true;
|
||||
const MYAAC_VERSION = '1.8.3-dev';
|
||||
const MYAAC_VERSION = '1.8.2-dev';
|
||||
const DATABASE_VERSION = 46;
|
||||
const TABLE_PREFIX = 'myaac_';
|
||||
define('START_TIME', microtime(true));
|
||||
|
@@ -19,7 +19,8 @@
|
||||
"symfony/var-dumper": "^6.4",
|
||||
"filp/whoops": "^2.15",
|
||||
"maximebf/debugbar": "1.*",
|
||||
"guzzlehttp/guzzle": "7.9.3"
|
||||
"guzzlehttp/guzzle": "7.9.3",
|
||||
"spomky-labs/otphp": "^11.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.10"
|
||||
|
151
composer.lock
generated
151
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5317e97a5025ebc2a977214bd3fa964c",
|
||||
"content-hash": "07419f6fe133f9bebc99557f3df843c8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
@@ -1556,6 +1556,73 @@
|
||||
},
|
||||
"time": "2018-02-13T20:26:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
"version": "v3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/constant_time_encoding.git",
|
||||
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512",
|
||||
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
"vimeo/psalm": "^4|^5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ParagonIE\\ConstantTime\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Steve 'Sc00bz' Thomas",
|
||||
"email": "steve@tobtu.com",
|
||||
"homepage": "https://www.tobtu.com",
|
||||
"role": "Original Developer"
|
||||
}
|
||||
],
|
||||
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
|
||||
"keywords": [
|
||||
"base16",
|
||||
"base32",
|
||||
"base32_decode",
|
||||
"base32_encode",
|
||||
"base64",
|
||||
"base64_decode",
|
||||
"base64_encode",
|
||||
"bin2hex",
|
||||
"encoding",
|
||||
"hex",
|
||||
"hex2bin",
|
||||
"rfc4648"
|
||||
],
|
||||
"support": {
|
||||
"email": "info@paragonie.com",
|
||||
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
|
||||
"source": "https://github.com/paragonie/constant_time_encoding"
|
||||
},
|
||||
"time": "2024-05-08T12:36:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "peppeocchi/php-cron-scheduler",
|
||||
"version": "v4.0",
|
||||
@@ -2102,6 +2169,88 @@
|
||||
},
|
||||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spomky-labs/otphp",
|
||||
"version": "11.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Spomky-Labs/otphp.git",
|
||||
"reference": "2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33",
|
||||
"reference": "2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"paragonie/constant_time_encoding": "^2.0 || ^3.0",
|
||||
"php": ">=8.1",
|
||||
"psr/clock": "^1.0",
|
||||
"symfony/deprecation-contracts": "^3.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"ekino/phpstan-banned-code": "^1.0",
|
||||
"infection/infection": "^0.26|^0.27|^0.28|^0.29",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.3",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": "^9.5.26|^10.0|^11.0",
|
||||
"qossmic/deptrac-shim": "^1.0",
|
||||
"rector/rector": "^1.0",
|
||||
"symfony/phpunit-bridge": "^6.1|^7.0",
|
||||
"symplify/easy-coding-standard": "^12.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OTPHP\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Florent Morselli",
|
||||
"homepage": "https://github.com/Spomky"
|
||||
},
|
||||
{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/Spomky-Labs/otphp/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A PHP library for generating one time passwords according to RFC 4226 (HOTP Algorithm) and the RFC 6238 (TOTP Algorithm) and compatible with Google Authenticator",
|
||||
"homepage": "https://github.com/Spomky-Labs/otphp",
|
||||
"keywords": [
|
||||
"FreeOTP",
|
||||
"RFC 4226",
|
||||
"RFC 6238",
|
||||
"google authenticator",
|
||||
"hotp",
|
||||
"otp",
|
||||
"totp"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Spomky-Labs/otphp/issues",
|
||||
"source": "https://github.com/Spomky-Labs/otphp/tree/11.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Spomky",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/FlorentMorselli",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2024-06-12T11:22:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v6.4.17",
|
||||
|
@@ -1,4 +1,4 @@
|
||||
SET @myaac_database_version = 46;
|
||||
SET @myaac_database_version = 45;
|
||||
|
||||
CREATE TABLE `myaac_account_actions`
|
||||
(
|
||||
@@ -10,12 +10,12 @@ CREATE TABLE `myaac_account_actions`
|
||||
KEY (`account_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||
|
||||
CREATE TABLE `myaac_account_emails_verify`
|
||||
CREATE TABLE `myaac_account_email_codes`
|
||||
(
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int NOT NULL,
|
||||
`hash` varchar(32) NOT NULL,
|
||||
`sent_at` int NOT NULL DEFAULT 0,
|
||||
`code` varchar(6) NOT NULL,
|
||||
`created_at` int NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||
|
||||
|
@@ -102,13 +102,18 @@ if(!$db->hasColumn('accounts', 'web_flags')) {
|
||||
success($locale['step_database_adding_field'] . ' accounts.web_flags...');
|
||||
}
|
||||
|
||||
if(!$db->hasColumn('accounts', 'email_hash')) {
|
||||
if(query("ALTER TABLE `accounts` ADD `email_hash` VARCHAR(32) NOT NULL DEFAULT '' AFTER `web_flags`;"))
|
||||
success($locale['step_database_adding_field'] . ' accounts.email_hash...');
|
||||
}
|
||||
|
||||
if(!$db->hasColumn('accounts', 'email_verified')) {
|
||||
if(query("ALTER TABLE `accounts` ADD `email_verified` TINYINT(1) NOT NULL DEFAULT 0 AFTER `web_flags`;"))
|
||||
if(query("ALTER TABLE `accounts` ADD `email_verified` TINYINT(1) NOT NULL DEFAULT 0 AFTER `email_hash`;"))
|
||||
success($locale['step_database_adding_field'] . ' accounts.email_verified...');
|
||||
}
|
||||
|
||||
if(!$db->hasColumn('accounts', 'email_new')) {
|
||||
if(query("ALTER TABLE `accounts` ADD `email_new` VARCHAR(255) NOT NULL DEFAULT '' AFTER `email_verified`;"))
|
||||
if(query("ALTER TABLE `accounts` ADD `email_new` VARCHAR(255) NOT NULL DEFAULT '' AFTER `email_hash`;"))
|
||||
success($locale['step_database_adding_field'] . ' accounts.email_new...');
|
||||
}
|
||||
|
||||
|
@@ -81,7 +81,6 @@ $deprecatedConfig = [
|
||||
'account_change_character_name_points' => 'account_change_character_name_price',
|
||||
'account_change_character_sex',
|
||||
'account_change_character_sex_points' => 'account_change_character_name_price',
|
||||
'email_lai_sec_interval' => 'mail_lost_account_interval',
|
||||
];
|
||||
|
||||
foreach ($deprecatedConfig as $key => $value) {
|
||||
|
@@ -1142,18 +1142,10 @@ function getTopPlayers($limit = 5, $skill = 'level') {
|
||||
'looktype', 'lookhead', 'lookbody', 'looklegs', 'lookfeet'
|
||||
];
|
||||
|
||||
if ($db->hasColumn('players', 'promotion')) {
|
||||
$columns[] = 'promotion';
|
||||
}
|
||||
|
||||
if ($db->hasColumn('players', 'lookaddons')) {
|
||||
$columns[] = 'lookaddons';
|
||||
}
|
||||
|
||||
if ($db->hasColumn('players', 'lookmount')) {
|
||||
$columns[] = 'lookmount';
|
||||
}
|
||||
|
||||
return Player::query()
|
||||
->select($columns)
|
||||
->withOnlineStatus()
|
||||
|
@@ -26,11 +26,10 @@ use MyAAC\Cache\Cache;
|
||||
*/
|
||||
class OTS_DB_MySQL extends OTS_Base_DB
|
||||
{
|
||||
private array $has_table_cache = [];
|
||||
private array $has_column_cache = [];
|
||||
private array $get_column_info_cache = [];
|
||||
private $has_table_cache = array();
|
||||
private $has_column_cache = array();
|
||||
|
||||
private bool $clearCacheAfter = false;
|
||||
private $clearCacheAfter = false;
|
||||
/**
|
||||
* Creates database connection.
|
||||
*
|
||||
@@ -120,11 +119,6 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
||||
if($cache->fetch('database_columns', $tmp) && $tmp) {
|
||||
$this->has_column_cache = unserialize($tmp);
|
||||
}
|
||||
|
||||
$tmp = null;
|
||||
if($cache->fetch('database_columns_info', $tmp) && $tmp) {
|
||||
$this->get_column_info_cache = unserialize($tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,13 +155,11 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
||||
if ($this->clearCacheAfter) {
|
||||
$cache->delete('database_tables');
|
||||
$cache->delete('database_columns');
|
||||
$cache->delete('database_columns_info');
|
||||
$cache->delete('database_checksum');
|
||||
}
|
||||
else {
|
||||
$cache->set('database_tables', serialize($this->has_table_cache), 3600);
|
||||
$cache->set('database_columns', serialize($this->has_column_cache), 3600);
|
||||
$cache->set('database_columns_info', serialize($this->get_column_info_cache), 3600);
|
||||
$cache->set('database_checksum', serialize(sha1($config['database_host'] . '.' . $config['database_name'])), 3600);
|
||||
}
|
||||
}
|
||||
@@ -217,8 +209,7 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public function hasTable($name): bool
|
||||
{
|
||||
public function hasTable($name) {
|
||||
if(isset($this->has_table_cache[$name])) {
|
||||
return $this->has_table_cache[$name];
|
||||
}
|
||||
@@ -226,13 +217,12 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
||||
return $this->hasTableInternal($name);
|
||||
}
|
||||
|
||||
private function hasTableInternal($name): bool
|
||||
{
|
||||
return ($this->has_table_cache[$name] = $this->query('SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = ' . $this->quote(config('database_name')) . ' AND `TABLE_NAME` = ' . $this->quote($name) . ' LIMIT 1;')->rowCount() > 0);
|
||||
private function hasTableInternal($name) {
|
||||
global $config;
|
||||
return ($this->has_table_cache[$name] = $this->query('SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = ' . $this->quote($config['database_name']) . ' AND `TABLE_NAME` = ' . $this->quote($name) . ' LIMIT 1;')->rowCount() > 0);
|
||||
}
|
||||
|
||||
public function hasColumn($table, $column): bool
|
||||
{
|
||||
public function hasColumn($table, $column) {
|
||||
if(isset($this->has_column_cache[$table . '.' . $column])) {
|
||||
return $this->has_column_cache[$table . '.' . $column];
|
||||
}
|
||||
@@ -240,8 +230,8 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
||||
return $this->hasColumnInternal($table, $column);
|
||||
}
|
||||
|
||||
private function hasColumnInternal($table, $column): bool {
|
||||
return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE " . $this->quote($column))->fetchAll()) > 0);
|
||||
private function hasColumnInternal($table, $column) {
|
||||
return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE '" . $column . "'")->fetchAll()) > 0);
|
||||
}
|
||||
|
||||
public function hasTableAndColumns(string $table, array $columns = []): bool
|
||||
@@ -257,53 +247,7 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getColumnInfo(string $table, string $column): bool|array
|
||||
{
|
||||
if(isset($this->get_column_info_cache[$table . '.' . $column])) {
|
||||
return $this->get_column_info_cache[$table . '.' . $column];
|
||||
}
|
||||
|
||||
return $this->getColumnInfoInternal($table, $column);
|
||||
}
|
||||
|
||||
private function getColumnInfoInternal(string $table, string $column): bool|array
|
||||
{
|
||||
if (!$this->hasTable($table) || !$this->hasColumn($table, $column)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$formatResult = function ($result) {
|
||||
return [
|
||||
'field' => $result['Field'],
|
||||
'type' => $result['Type'],
|
||||
'null' => strtolower($result['Null']),
|
||||
'default' => $result['Default'],
|
||||
'extra' => $result['Extra'],
|
||||
];
|
||||
};
|
||||
|
||||
$query = $this->query('SHOW COLUMNS FROM `' . $table . "` LIKE " . $this->quote($column));
|
||||
$rowCount = $query->rowCount();
|
||||
if ($rowCount > 1) {
|
||||
$tmp = [];
|
||||
|
||||
$results = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($results as $result) {
|
||||
$tmp[] = $formatResult($result);
|
||||
}
|
||||
|
||||
return ($this->get_column_info_cache[$table . '.' . $column] = $tmp);
|
||||
}
|
||||
else if ($rowCount == 1) {
|
||||
$result = $query->fetch(PDO::FETCH_ASSOC);
|
||||
return ($this->get_column_info_cache[$table . '.' . $column] = $formatResult($result));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function revalidateCache(): void
|
||||
{
|
||||
public function revalidateCache() {
|
||||
foreach($this->has_table_cache as $key => $value) {
|
||||
$this->hasTableInternal($key);
|
||||
}
|
||||
@@ -318,21 +262,6 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
||||
$this->hasColumnInternal($explode[0], $explode[1]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->get_column_info_cache as $key => $value) {
|
||||
$explode = explode('.', $key);
|
||||
if(!isset($this->has_table_cache[$explode[0]])) { // first check if table exist
|
||||
$this->hasTableInternal($explode[0]);
|
||||
}
|
||||
|
||||
if($this->has_table_cache[$explode[0]]) {
|
||||
$this->hasColumnInternal($explode[0], $explode[1]);
|
||||
}
|
||||
|
||||
if($this->has_table_cache[$explode[0]]) {
|
||||
$this->getColumnInfoInternal($explode[0], $explode[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setClearCacheAfter($clearCache)
|
||||
|
8
system/migrations/46-account_email_codes.sql
Normal file
8
system/migrations/46-account_email_codes.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE `myaac_account_email_codes`
|
||||
(
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int NOT NULL,
|
||||
`code` varchar(6) NOT NULL,
|
||||
`created_at` int NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
@@ -1,8 +0,0 @@
|
||||
CREATE TABLE `myaac_account_emails_verify`
|
||||
(
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int NOT NULL,
|
||||
`hash` varchar(32) NOT NULL,
|
||||
`sent_at` int NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
@@ -1,24 +1,27 @@
|
||||
<?php
|
||||
// add the myaac_account_email_codes
|
||||
|
||||
/**
|
||||
* @var OTS_DB_MySQL $db
|
||||
*/
|
||||
|
||||
$up = function () use ($db) {
|
||||
if ($db->hasColumn('accounts', 'email_hash')) {
|
||||
$db->dropColumn('accounts', 'email_hash');
|
||||
if (!$db->hasColumn('accounts', '2fa_type')) {
|
||||
$db->addColumn('accounts', '2fa_type', "tinyint NOT NULL DEFAULT 0 AFTER `web_flags`");
|
||||
}
|
||||
|
||||
if (!$db->hasTable(TABLE_PREFIX . 'account_emails_verify')) {
|
||||
$db->query(file_get_contents(__DIR__ . '/46-account_emails_verify.sql'));
|
||||
// add myaac_account_email_codes table
|
||||
if (!$db->hasTable(TABLE_PREFIX . 'account_email_codes')) {
|
||||
$db->exec(file_get_contents(__DIR__ . '/46-account_email_codes.sql'));
|
||||
}
|
||||
};
|
||||
|
||||
$down = function () use ($db) {
|
||||
if (!$db->hasColumn('accounts', 'email_hash')) {
|
||||
$db->addColumn('accounts', 'email_hash', "varchar(32) NOT NULL DEFAULT ''");
|
||||
if ($db->hasColumn('accounts', '2fa_type')) {
|
||||
$db->dropColumn('accounts', '2fa_type');
|
||||
}
|
||||
|
||||
if ($db->hasTable(TABLE_PREFIX . 'account_emails_verify')) {
|
||||
$db->dropTable(TABLE_PREFIX . 'account_emails_verify');
|
||||
}
|
||||
//if ($db->hasTable(TABLE_PREFIX . 'account_email_codes')) {
|
||||
// $db->dropTable(TABLE_PREFIX . 'account_email_codes');
|
||||
//}
|
||||
};
|
||||
|
124
system/pages/account/2fa.php
Normal file
124
system/pages/account/2fa.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* 2-factor authentication
|
||||
*
|
||||
* @package MyAAC
|
||||
* @author Slawkens <slawkens@gmail.com>
|
||||
* @copyright 2019 MyAAC
|
||||
* @link https://my-aac.org
|
||||
*/
|
||||
|
||||
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
|
||||
$title = 'Two Factor Authentication';
|
||||
require __DIR__ . '/base.php';
|
||||
|
||||
csrfProtect();
|
||||
|
||||
/**
|
||||
* @var OTS_Account $account_logged
|
||||
*/
|
||||
$step = $_REQUEST['step'] ?? '';
|
||||
$code = $_REQUEST['auth-code'] ?? '';
|
||||
|
||||
if ((!setting('core.mail_enabled')) && ACTION == 'email-code') {
|
||||
$twig->display('error_box.html.twig', ['errors' => ['Account two-factor e-mail authentication disabled.']]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($account_logged) || !$account_logged->isLoaded()) {
|
||||
$current_session = getSession('account');
|
||||
if($current_session) {
|
||||
$account_logged = new OTS_Account();
|
||||
$account_logged->load($current_session);
|
||||
}
|
||||
}
|
||||
|
||||
$twoFactorAuth = TwoFactorAuth::getInstance($account_logged);
|
||||
$twig->addGlobal('account_logged', $account_logged);
|
||||
|
||||
if (ACTION == 'email-code') {
|
||||
if ($step == 'resend') {
|
||||
if ($twoFactorAuth->hasRecentEmailCode(15 * 60)) {
|
||||
$errors = ['Sorry, one email per 15 minutes'];
|
||||
}
|
||||
else {
|
||||
$twoFactorAuth->resendEmailCode();
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||
}
|
||||
|
||||
$twig->display('account.2fa.email.login.html.twig');
|
||||
}
|
||||
else if ($step == 'activate') {
|
||||
if (!$twoFactorAuth->hasRecentEmailCode(15 * 60)) {
|
||||
$twoFactorAuth->resendEmailCode();
|
||||
}
|
||||
|
||||
if (isset($_POST['save'])) {
|
||||
if (!empty($code)) {
|
||||
$twoFactorAuth->setAuthGateway(TwoFactorAuth::TYPE_EMAIL);
|
||||
if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) {
|
||||
$serverName = configLua('serverName');
|
||||
|
||||
$twoFactorAuth->enable(TwoFactorAuth::TYPE_EMAIL);
|
||||
$twoFactorAuth->deleteOldCodes();
|
||||
|
||||
$twig->display('success.html.twig', [
|
||||
'title' => 'Email Code Authentication Activated',
|
||||
'description' => sprintf('You have successfully activated <b>email code authentication</b> for your account. This means an <b>email code</b> will be sent to the email address assigned to your account whenever you try to log in to the %s client or the %s website. In order to log in, you will need to enter the <b>most recent email code</b> you have received.', $serverName, $serverName)
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$errors[] = 'Invalid email code!';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||
}
|
||||
|
||||
$twig->display('account.2fa.email_code.html.twig', ['wrongCode' => count($errors) > 0]);
|
||||
}
|
||||
else if ($step == 'deactivate') {
|
||||
//if (!$twoFactorAuth->hasRecentEmailCode(15 * 60)) {
|
||||
// $twoFactorAuth->resendEmailCode();
|
||||
//}
|
||||
|
||||
/*if (isset($_POST['save'])) {
|
||||
if (!empty($code)) {
|
||||
if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) {
|
||||
*/
|
||||
$twoFactorAuth->disable();
|
||||
$twoFactorAuth->deleteOldCodes();
|
||||
|
||||
$twig->display('success.html.twig',
|
||||
[
|
||||
'title' => 'Email Code Authentication Deactivated',
|
||||
'description' => 'You have successfully <b>deactivated</b> the <b>Email Code Authentication</b> for your account.'
|
||||
]
|
||||
);
|
||||
/*
|
||||
}
|
||||
else {
|
||||
$errors[] = 'Invalid email code!';
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
if (!empty($errors)) {
|
||||
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||
}
|
||||
|
||||
$twig->display('account.2fa.email.deactivate.html.twig', ['wrongCode' => count($errors) > 0]);
|
||||
*/
|
||||
}
|
||||
}
|
@@ -17,6 +17,10 @@ if(!$logged)
|
||||
if(!empty($errors))
|
||||
$twig->display('error_box.html.twig', array('errors' => $errors));
|
||||
|
||||
if (defined('HIDE_LOGIN_BOX') && HIDE_LOGIN_BOX) {
|
||||
return;
|
||||
}
|
||||
|
||||
$twig->display('account.login.html.twig', array(
|
||||
'redirect' => $_REQUEST['redirect'] ?? null,
|
||||
'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number',
|
||||
|
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
use MyAAC\Models\Account;
|
||||
use MyAAC\Models\AccountEmailVerify;
|
||||
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
|
||||
@@ -21,20 +20,16 @@ if(empty($hash)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// by default link is valid for 30 days
|
||||
$accountEmailVerify = AccountEmailVerify::where('hash', $hash)->where('sent_at', '>', time() - 30 * 24 * 60 * 60)->first();
|
||||
if(!$accountEmailVerify) {
|
||||
note("Wrong link or link has expired.");
|
||||
if(!Account::where('email_hash', $hash)->exists()) {
|
||||
note("Your email couldn't be verified. Please contact staff to do it manually.");
|
||||
}
|
||||
else
|
||||
{
|
||||
$accountModel = Account::where('id', $accountEmailVerify->account_id)->where('email_verified', 0)->first();
|
||||
$accountModel = Account::where('email_hash', $hash)->where('email_verified', 0)->first();
|
||||
if ($accountModel) {
|
||||
$accountModel->email_verified = 1;
|
||||
$accountModel->save();
|
||||
|
||||
AccountEmailVerify::where('account_id', $accountModel->id)->delete();
|
||||
|
||||
success('You have now verified your e-mail, this will increase the security of your account. Thank you for doing this. You can now <a href=' . getLink('account/manage') . '>log in</a>.');
|
||||
|
||||
$account = new OTS_Account();
|
||||
@@ -44,6 +39,6 @@ else
|
||||
}
|
||||
}
|
||||
else {
|
||||
error('Your account is already verified.');
|
||||
error('Link has expired.');
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@
|
||||
*/
|
||||
|
||||
use MyAAC\CreateCharacter;
|
||||
use MyAAC\Models\AccountEmailVerify;
|
||||
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
$title = 'Create Account';
|
||||
@@ -245,12 +244,7 @@ if($save)
|
||||
if(setting('core.mail_enabled') && setting('core.account_mail_verify'))
|
||||
{
|
||||
$hash = md5(generateRandomString(16, true, true) . $email);
|
||||
|
||||
AccountEmailVerify::create([
|
||||
'account_id' => $new_account->getId(),
|
||||
'hash' => $hash,
|
||||
'sent_at' => time(),
|
||||
]);
|
||||
$new_account->setCustomField('email_hash', $hash);
|
||||
|
||||
$verify_url = getLink('account/confirm-email/' . $hash);
|
||||
$body_html = $twig->render('mail.account.verify.html.twig', array(
|
||||
@@ -274,10 +268,8 @@ if($save)
|
||||
}
|
||||
else
|
||||
{
|
||||
error('An error occurred while sending email! Account not created. Try again. For Admin: More info can be found in system/logs/mailer-error.log');
|
||||
error('An error occorred while sending email! Account not created. Try again. For Admin: More info can be found in system/logs/mailer-error.log');
|
||||
$new_account->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
use MyAAC\RateLimit;
|
||||
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
|
||||
@@ -48,12 +49,16 @@ if(!empty($login_account) && !empty($login_password))
|
||||
)
|
||||
{
|
||||
if (setting('core.account_mail_verify') && (int)$account_logged->getCustomField('email_verified') !== 1) {
|
||||
$link = getLink('account/resend-email-verify');
|
||||
$errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.<br/>' .
|
||||
'You can resend the Email here: <a href="' . $link . '">' . $link . '</a>';
|
||||
$errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.';
|
||||
} else {
|
||||
session_regenerate_id();
|
||||
setSession('account', $account_logged->getId());
|
||||
|
||||
$twoFactorAuth = TwoFactorAuth::getInstance($account_logged);
|
||||
if (!$twoFactorAuth->process($login_account, $login_password, $remember_me, $_POST['auth-code'] ?? '')) {
|
||||
return;
|
||||
}
|
||||
|
||||
session_regenerate_id();
|
||||
setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password));
|
||||
if($remember_me) {
|
||||
setSession('remember_me', true);
|
||||
|
@@ -8,6 +8,9 @@
|
||||
* @copyright 2019 MyAAC
|
||||
* @link https://my-aac.org
|
||||
*/
|
||||
|
||||
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
|
||||
$title = 'Account Management';
|
||||
@@ -111,6 +114,8 @@ $twig->display('account.management.html.twig', array(
|
||||
'account_registered' => $account_registered,
|
||||
'account_rlname' => $account_rlname,
|
||||
'account_location' => $account_location,
|
||||
'twoFactorViews' => TwoFactorAuth::getInstance($account_logged)->getAccountManageViews(),
|
||||
|
||||
'actions' => $actions,
|
||||
'players' => $account_players
|
||||
'players' => $account_players,
|
||||
));
|
||||
|
@@ -1,94 +0,0 @@
|
||||
<?php
|
||||
|
||||
use MyAAC\Models\AccountEmailVerify;
|
||||
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
|
||||
$title = 'Resend Email';
|
||||
|
||||
$errorWithBackButton = function ($msg) use ($twig) {
|
||||
$errors = [$msg];
|
||||
|
||||
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||
$twig->display('account.back_button.html.twig', [
|
||||
'action' => getLink('account/resend-email-verify'),
|
||||
]);
|
||||
};
|
||||
|
||||
if (!setting('core.mail_enabled') || !setting('core.account_mail_verify')) {
|
||||
$errorWithBackButton('Resending email is not possible on this server.');
|
||||
return;
|
||||
}
|
||||
|
||||
$showForm = true;
|
||||
|
||||
if (isset($_POST['submit']) && $_POST['submit'] == '1') {
|
||||
$email = $_REQUEST['email'];
|
||||
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errorWithBackButton('Please enter valid Email.');
|
||||
return;
|
||||
}
|
||||
|
||||
$account = new OTS_Account();
|
||||
$account->findByEMail($email);
|
||||
if ($account->isLoaded()) {
|
||||
if ($account->getCustomField('email_verified') == '1') {
|
||||
$errorWithBackButton('This account is already verified! You can <a href=' . getLink('account/manage') . '>log in</a> on the website.');
|
||||
return;
|
||||
}
|
||||
|
||||
$accountEmailVerify = AccountEmailVerify::where('account_id', $account->getId())->orderBy('sent_at', 'DESC')->first();
|
||||
if ($accountEmailVerify && time() - $accountEmailVerify->sent_at < 60) {
|
||||
$errorWithBackButton('Only one Email per minute is allowed. Please try again later.');
|
||||
return;
|
||||
}
|
||||
|
||||
$tmp_account = $email;
|
||||
if (!config('account_login_by_email')) {
|
||||
$tmp_account = (USE_ACCOUNT_NAME ? $account->getName() : $account->getId());
|
||||
}
|
||||
|
||||
$hash = md5(generateRandomString(16, true, true) . $email);
|
||||
|
||||
AccountEmailVerify::create([
|
||||
'account_id' => $account->getId(),
|
||||
'hash' => $hash,
|
||||
'sent_at' => time(),
|
||||
]);
|
||||
|
||||
$verify_url = getLink('account/confirm-email/' . $hash);
|
||||
$body_html = $twig->render('mail.account.resend-email-verify.html.twig', array(
|
||||
'account' => $tmp_account,
|
||||
'verify_url' => generateLink($verify_url, $verify_url, true)
|
||||
));
|
||||
|
||||
if (_mail($account->getEMail(), configLua('serverName') . ' - Verify Account', $body_html)) {
|
||||
$message = "If account with this email exists - you will become an email with verification link.";
|
||||
$showForm = false;
|
||||
} else {
|
||||
$message = "<p class='error'>An error occurred while sending email (<b>{$email}</b> )! Try again later. For Admin: More info can be found in system/logs/mailer-error.log</p>";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = "<br />If account with this email exists - you will become an email with verification link.";
|
||||
$showForm = false;
|
||||
}
|
||||
|
||||
$twig->display('success.html.twig', array(
|
||||
'title' => 'Verify Email Sent',
|
||||
'description' => $message,
|
||||
));
|
||||
}
|
||||
|
||||
//show errors if not empty
|
||||
if (!empty($errors)) {
|
||||
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||
$twig->display('account.back_button.html.twig', [
|
||||
'action' => getLink('account/resend-email-verify'),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($showForm) {
|
||||
$twig->display('account.resend-email-verify.html.twig');
|
||||
}
|
@@ -88,10 +88,8 @@ if($logged && $account_logged && $account_logged->isLoaded()) {
|
||||
/**
|
||||
* Routes loading
|
||||
*/
|
||||
$routesFinal = [];
|
||||
$dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) {
|
||||
global $cache, $routesFinal;
|
||||
|
||||
$routesFinal = [];
|
||||
foreach(getDatabasePages() as $page) {
|
||||
$routesFinal[] = ['*', $page, '__database__/' . $page, 100];
|
||||
}
|
||||
@@ -167,7 +165,7 @@ $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r)
|
||||
echo '</pre>';
|
||||
die;
|
||||
*/
|
||||
foreach ($routesFinal as &$route) {
|
||||
foreach ($routesFinal as $route) {
|
||||
if ($route[0] === '*') {
|
||||
$route[0] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'];
|
||||
}
|
||||
@@ -200,10 +198,6 @@ $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r)
|
||||
log_append('router.log', $warning);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cache->enabled()) {
|
||||
$cache->set('routes_final', serialize($routesFinal), 10 * 365 * 24 * 60 * 60); // 10 years / infinite
|
||||
}
|
||||
},
|
||||
[
|
||||
'cacheFile' => CACHE . 'route.cache',
|
||||
@@ -218,7 +212,7 @@ $found = true;
|
||||
|
||||
// old support for pages like /?subtopic=accountmanagement
|
||||
$page = $_REQUEST['p'] ?? ($_REQUEST['subtopic'] ?? '');
|
||||
if(!empty($page) && preg_match('/^[A-z0-9\/\-]+$/', $page)) {
|
||||
if(!empty($page) && preg_match('/^[A-z0-9\-]+$/', $page)) {
|
||||
if (isset($_REQUEST['p'])) { // some plugins may require this
|
||||
$_REQUEST['subtopic'] = $_REQUEST['p'];
|
||||
}
|
||||
@@ -227,26 +221,9 @@ if(!empty($page) && preg_match('/^[A-z0-9\/\-]+$/', $page)) {
|
||||
require SYSTEM . 'compat/pages.php';
|
||||
}
|
||||
|
||||
$foundRoute = false;
|
||||
|
||||
$tmp = null;
|
||||
if ($cache->enabled() && $cache->fetch('routes_final', $tmp)) {
|
||||
$routesFinal = unserialize($tmp);
|
||||
}
|
||||
|
||||
foreach ($routesFinal as $route) {
|
||||
if ($page === $route[1]) {
|
||||
$file = $route[2];
|
||||
$foundRoute = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$foundRoute) {
|
||||
$file = loadPageFromFileSystem($page, $found);
|
||||
if(!$found) {
|
||||
$file = false;
|
||||
}
|
||||
$file = loadPageFromFileSystem($page, $found);
|
||||
if(!$found) {
|
||||
$file = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@@ -115,11 +115,6 @@ class Cache
|
||||
return unserialize($value);
|
||||
}
|
||||
|
||||
// -1 for infinite cache
|
||||
if ($ttl == -1) {
|
||||
$ttl = 10 * 365 * 24 * 60 * 60; // 10 years should be enough
|
||||
}
|
||||
|
||||
$value = $callback();
|
||||
$cache->set($key, serialize($value), $ttl);
|
||||
return $value;
|
||||
|
14
system/src/Models/AccountEMailCode.php
Normal file
14
system/src/Models/AccountEMailCode.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace MyAAC\Models;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AccountEMailCode extends Model {
|
||||
|
||||
protected $table = TABLE_PREFIX . 'account_email_codes';
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = ['account_id', 'code', 'created_at'];
|
||||
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MyAAC\Models;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AccountEmailVerify extends Model
|
||||
{
|
||||
|
||||
protected $table = TABLE_PREFIX . 'account_emails_verify';
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = ['account_id', 'hash', 'sent_at'];
|
||||
|
||||
}
|
13
system/src/TwoFactorAuth/Gateway/AppAuthGateway.php
Normal file
13
system/src/TwoFactorAuth/Gateway/AppAuthGateway.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||
|
||||
use MyAAC\TwoFactorAuth\Interface\AuthGatewayInterface;
|
||||
|
||||
class AppAuthGateway extends BaseAuthGateway implements AuthGatewayInterface
|
||||
{
|
||||
public function verifyCode(string $code): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
12
system/src/TwoFactorAuth/Gateway/BaseAuthGateway.php
Normal file
12
system/src/TwoFactorAuth/Gateway/BaseAuthGateway.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||
|
||||
class BaseAuthGateway
|
||||
{
|
||||
protected \OTS_Account $account;
|
||||
|
||||
public function __construct(\OTS_Account $account) {
|
||||
$this->account = $account;
|
||||
}
|
||||
}
|
16
system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php
Normal file
16
system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||
|
||||
use MyAAC\Models\AccountEMailCode;
|
||||
use MyAAC\TwoFactorAuth\Interface\AuthGatewayInterface;
|
||||
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||
|
||||
class EmailAuthGateway extends BaseAuthGateway implements AuthGatewayInterface
|
||||
{
|
||||
public function verifyCode(string $code): bool
|
||||
{
|
||||
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('code', $code)->where('created_at', '>', time() - TwoFactorAuth::EMAIL_CODE_VALID_UNTIL)->first() !== null;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace MyAAC\TwoFactorAuth\Interface;
|
||||
|
||||
interface AuthGatewayInterface
|
||||
{
|
||||
public function __construct(\OTS_Account $account);
|
||||
public function verifyCode(string $code): bool;
|
||||
}
|
183
system/src/TwoFactorAuth/TwoFactorAuth.php
Normal file
183
system/src/TwoFactorAuth/TwoFactorAuth.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace MyAAC\TwoFactorAuth;
|
||||
|
||||
use MyAAC\Models\AccountEMailCode;
|
||||
use MyAAC\TwoFactorAuth\Gateway\AppAuthGateway;
|
||||
use MyAAC\TwoFactorAuth\Gateway\EmailAuthGateway;
|
||||
|
||||
class TwoFactorAuth
|
||||
{
|
||||
const TYPE_NONE = 0;
|
||||
const TYPE_EMAIL = 1;
|
||||
const TYPE_APP = 2;
|
||||
// maybe later
|
||||
//const TYPE_SMS = 3;
|
||||
|
||||
const EMAIL_CODE_VALID_UNTIL = 24 * 60 * 60;
|
||||
|
||||
private static self $instance;
|
||||
|
||||
private \OTS_Account $account;
|
||||
private int $authType;
|
||||
private EmailAuthGateway|AppAuthGateway $authGateway;
|
||||
|
||||
public function __construct(\OTS_Account|int $account) {
|
||||
if (is_int($account)) {
|
||||
$this->account = new \OTS_Account();
|
||||
$this->account->load($account);
|
||||
}
|
||||
else {
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
$this->authType = (int)$this->account->getCustomField('2fa_type');
|
||||
$this->setAuthGateway($this->authType);
|
||||
}
|
||||
|
||||
public static function getInstance($account = null): self
|
||||
{
|
||||
if (!isset(self::$instance)) {
|
||||
self::$instance = new self($account);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function process($login_account, $login_password, $remember_me, $code): bool
|
||||
{
|
||||
global $twig;
|
||||
|
||||
if (!$this->isActive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (empty($code)) {
|
||||
if ($this->authType == self::TYPE_EMAIL) {
|
||||
if (!$this->hasRecentEmailCode(15 * 60)) {
|
||||
$this->resendEmailCode();
|
||||
//success('Resent email.');
|
||||
}
|
||||
|
||||
define('HIDE_LOGIN_BOX', true);
|
||||
$twig->display('account.2fa.email.login.html.twig', [
|
||||
'account_login' => $login_account,
|
||||
'password_login' => $login_password,
|
||||
'remember_me' => $remember_me,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
echo 'Two Factor App Auth';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->getAuthGateway()->verifyCode($code)) {
|
||||
if ($this->authType === self::TYPE_EMAIL) {
|
||||
$this->deleteOldCodes();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (setting('core.mail_enabled')) {
|
||||
$mailBody = $twig->render('mail.account.2fa.email-code.wrong-attempt.html.twig');
|
||||
|
||||
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Failed Two-Factor Authentication Attempt', $mailBody)) {
|
||||
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
|
||||
}
|
||||
}
|
||||
|
||||
define('HIDE_LOGIN_BOX', true);
|
||||
|
||||
$errors[] = 'Invalid email code!';
|
||||
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||
|
||||
$twig->display('account.2fa.email.login.html.twig',
|
||||
[
|
||||
'account_login' => $login_account,
|
||||
'password_login' => $login_password,
|
||||
'remember_me' => $remember_me,
|
||||
|
||||
'wrongCode' => true,
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setAuthGateway(int $authType): void
|
||||
{
|
||||
if ($authType === self::TYPE_EMAIL) {
|
||||
$this->authGateway = new EmailAuthGateway($this->account);
|
||||
}
|
||||
else if ($authType === self::TYPE_APP) {
|
||||
$this->authGateway = new AppAuthGateway($this->account);
|
||||
}
|
||||
}
|
||||
|
||||
public function getAccountManageViews(): array
|
||||
{
|
||||
$twoFactorView = 'account.2fa.protected.html.twig';
|
||||
if ($this->authType == self::TYPE_EMAIL) {
|
||||
$twoFactorView2 = 'account.2fa.email.activated.html.twig';
|
||||
}
|
||||
elseif ($this->authType == self::TYPE_APP) {
|
||||
$twoFactorView2 = 'account.2fa.app.activated.html.twig';
|
||||
}
|
||||
else {
|
||||
$twoFactorView = 'account.2fa.connect.html.twig';
|
||||
$twoFactorView2 = 'account.2fa.email.activate.html.twig';
|
||||
}
|
||||
|
||||
return [$twoFactorView, $twoFactorView2];
|
||||
}
|
||||
|
||||
public function enable(int $type): void {
|
||||
$this->account->setCustomField('2fa_type', $type);
|
||||
}
|
||||
|
||||
public function disable(): void {
|
||||
$this->account->setCustomField('2fa_type', self::TYPE_NONE);
|
||||
}
|
||||
|
||||
public function isActive(): bool {
|
||||
return $this->authType != self::TYPE_NONE;
|
||||
}
|
||||
|
||||
public function getAuthType(): int {
|
||||
return $this->authType;
|
||||
}
|
||||
|
||||
public function getAuthGateway(): AppAuthGateway|EmailAuthGateway {
|
||||
return $this->authGateway;
|
||||
}
|
||||
|
||||
public function hasRecentEmailCode($since = self::EMAIL_CODE_VALID_UNTIL): bool {
|
||||
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('created_at', '>', time() - $since)->first() !== null;
|
||||
}
|
||||
|
||||
public function deleteOldCodes(): void {
|
||||
AccountEMailCode::where('account_id', '=', $this->account->getId())->delete();
|
||||
}
|
||||
|
||||
public function resendEmailCode(): void
|
||||
{
|
||||
global $twig;
|
||||
|
||||
$newCode = generateRandomString(6, true, false, true);
|
||||
AccountEMailCode::create([
|
||||
'account_id' => $this->account->getId(),
|
||||
'code' => $newCode,
|
||||
'created_at' => time(),
|
||||
]);
|
||||
|
||||
$mailBody = $twig->render('mail.account.2fa.email-code.html.twig', [
|
||||
'code' => $newCode,
|
||||
]);
|
||||
|
||||
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Requested Authentication Email Code', $mailBody)) {
|
||||
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
|
||||
}
|
||||
}
|
||||
}
|
36
system/templates/account.2fa.connect.html.twig
Normal file
36
system/templates/account.2fa.connect.html.twig
Normal file
@@ -0,0 +1,36 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableShadowContainerRightTop">
|
||||
<div class="TableShadowRightTop" style="background-image:url({{ template_path }}/images/global/content/table-shadow-rt.gif);"></div>
|
||||
</div>
|
||||
<div class="TableContentAndRightShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-rm.gif);">
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody><tr>
|
||||
<td class="LabelV"><b>Connect your {{ config.lua.serverName }} account to an authenticator app!</b>
|
||||
<div style="float: right; font-size: 1px;">
|
||||
<form action="{{ getLink('account/2fa') }}?action=email-code" method="post" style="margin: 0px; padding: 0px;">
|
||||
{{ csrf() }}
|
||||
{% set button_name = 'Request' %}
|
||||
{% include('buttons.base.html.twig') %}
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p>As a first step to connect an <b>authenticator app</b> to your account, click on "Request"! An email with a confirmation key will be sent to the email address assigned to your account.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="TableShadowContainer">
|
||||
<div class="TableBottomShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-bm.gif);">
|
||||
<div class="TableBottomLeftShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-bl.gif);"></div>
|
||||
<div class="TableBottomRightShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-br.gif);"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
37
system/templates/account.2fa.email.activate.html.twig
Normal file
37
system/templates/account.2fa.email.activate.html.twig
Normal file
@@ -0,0 +1,37 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableShadowContainerRightTop">
|
||||
<div class="TableShadowRightTop" style="background-image:url({{ template_path }}/images/global/content/table-shadow-rt.gif);"></div>
|
||||
</div>
|
||||
<div class="TableContentAndRightShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-rm.gif);">
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="LabelV"><b>Activate email code authentication for your account!</b>
|
||||
<div style="float: right; font-size: 1px;">
|
||||
<form action="{{ getLink('account/2fa') }}?action=email-code&step=activate" method="post" style="margin: 0; padding: 0;">
|
||||
{{ csrf() }}
|
||||
{% set button_name = 'Request' %}
|
||||
{% include('buttons.base.html.twig') %}
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p>As a first step to activate <b>email code authentication</b> for your account, click on "Request"! An <b>email code</b> will be sent to the email address assigned to your account. You will be asked to enter this <b>email code</b> on the next page within 24 hours.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="TableShadowContainer">
|
||||
<div class="TableBottomShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-bm.gif);">
|
||||
<div class="TableBottomLeftShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-bl.gif);"></div>
|
||||
<div class="TableBottomRightShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-br.gif);"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
26
system/templates/account.2fa.email.activated.html.twig
Normal file
26
system/templates/account.2fa.email.activated.html.twig
Normal file
@@ -0,0 +1,26 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer ">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="float: right; width: 135px;">
|
||||
<form action="{{ getLink('account/2fa') }}?action=email-code" method="post" style="padding:0;margin:0;">
|
||||
{{ csrf() }}
|
||||
<input type="hidden" name="step" value="deactivate">
|
||||
{% set button_name = 'Deactivate' %}
|
||||
{{ include('buttons.base.html.twig') }}
|
||||
</form>
|
||||
</div>
|
||||
<b>Two-Factor Email Code Authentication <span style="color: green">Activated</span>!</b>
|
||||
<p>To deactivate <b>email code authentication</b>, click on the "Deactivate" button.</p>
|
||||
<!--p>You will have to confirm the deactivation by entering an <b>email code</b> which will be sent
|
||||
to the email address assigned to your account.</p-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
109
system/templates/account.2fa.email.deactivate.html.twig
Normal file
109
system/templates/account.2fa.email.deactivate.html.twig
Normal file
@@ -0,0 +1,109 @@
|
||||
{% set title = 'Deactivate Email Code Authentication' %}
|
||||
{% set content %}
|
||||
<table style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer ">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>To deactivate <b>two-factor email code authentication</b> for your account, enter the
|
||||
received <b>email code</b> below. Note, however, that <b>email code authentication</b>
|
||||
is an important security feature which helps to prevent any unauthorised access to your
|
||||
Tibia account.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="float: right;">
|
||||
<form
|
||||
action="{{ getLink('account/2fa') }}?action=email-code&step=resend"
|
||||
method="post"
|
||||
style="padding:0;margin:0;"
|
||||
>
|
||||
{{ csrf() }}
|
||||
|
||||
{% set button_name = 'Resend Email Code' %}
|
||||
{{ include('buttons.base.html.twig') }}
|
||||
</form>
|
||||
</div>
|
||||
An <b>email code</b> has already been sent to the email address assigned to your
|
||||
account.
|
||||
Please check your email account's spam/junk filter and make sure that your mailbox is
|
||||
not
|
||||
full.<br>In case you need a new email code, you can request one by clicking on "Resend
|
||||
Email
|
||||
Code".
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>To complete the deactivation of <b>email code authentication</b>, please enter the <b>email
|
||||
code</b> you received at the email address assigned to your account.
|
||||
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||
<div class="LabelV150 {{ wrongCode ? 'red' : '' }}" style="float:left;"><label
|
||||
for="email-code">Email Code:</label></div>
|
||||
<input form="form-code" id="auth-code" name="email-code" maxlength="15"
|
||||
autocomplete="off">
|
||||
{% if wrongCode %}
|
||||
<br/>
|
||||
<div class="LabelV150" style="float:left;"> </div>
|
||||
<div class="FormFieldError">Invalid email code!</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endset %}
|
||||
{% include 'tables.headline.html.twig' %}
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr align="center" valign="top">
|
||||
<td>
|
||||
<form id="form-code" method="post" action="{{ getLink('account/2fa') }}?action=email-code">
|
||||
{{ csrf() }}
|
||||
|
||||
<input type="hidden" name="step" value="deactivate">
|
||||
<input type="hidden" name="save" value="1">
|
||||
|
||||
{% set button_name = 'Continue' %}
|
||||
{% set button_color = 'green' %}
|
||||
{{ include('buttons.submit.html.twig') }}
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;">
|
||||
{{ csrf() }}
|
||||
{% set button_color = 'blue' %}
|
||||
{{ include('buttons.back.html.twig') }}
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
92
system/templates/account.2fa.email.login.html.twig
Normal file
92
system/templates/account.2fa.email.login.html.twig
Normal file
@@ -0,0 +1,92 @@
|
||||
{% set title = 'Enter Email Code' %}
|
||||
{% set content %}
|
||||
<table style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="float: right;">
|
||||
<form
|
||||
action="{{ getLink('account/2fa') }}?action=email-code&step=resend"
|
||||
method="post"
|
||||
style="padding:0;margin:0;"
|
||||
>
|
||||
{{ csrf() }}
|
||||
|
||||
{% set button_name = 'Resend Email Code' %}
|
||||
{{ include('buttons.base.html.twig') }}
|
||||
</form>
|
||||
</div>
|
||||
An <b>email code</b> has already been sent to the email address assigned to your account.
|
||||
Please check your email account's spam/junk filter and make sure that your mailbox is not
|
||||
full.<br>In case you need a new email code, you can request one by clicking on "Resend Email
|
||||
Code".
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>Email code authentication is activated for your account.</b><br><br>Please enter the <b>most
|
||||
recent email code</b> you have received in order to log in.<br>
|
||||
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||
<div class="LabelV150 {{ wrongCode ? 'red' : '' }}" style="float:left;"><label for="email-code">Email Code:</label></div>
|
||||
<input form="form-code" id="auth-code" name="auth-code" maxlength="15" autocomplete="off">
|
||||
{% if wrongCode %}
|
||||
<br/>
|
||||
<div class="LabelV150" style="float:left;"> </div>
|
||||
<div class="FormFieldError">Invalid email code!</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endset %}
|
||||
{% include 'tables.headline.html.twig' %}
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr align="center" valign="top">
|
||||
<td>
|
||||
<form id="form-code" method="post" action="{{ getLink('account/manage') }}">
|
||||
{{ csrf() }}
|
||||
|
||||
<input type="hidden" name="account_login" value="{{ account_login ?? '' }}" />
|
||||
<input type="hidden" name="password_login" value="{{ password_login ?? '' }}" />
|
||||
{% if remember_me %}
|
||||
<input type="hidden" name="remember_me" value="true" />
|
||||
{% endif %}
|
||||
|
||||
<input type="hidden" name="step" value="verify">
|
||||
{% set button_name = 'Continue' %}
|
||||
{% set button_color = 'green' %}
|
||||
{{ include('buttons.submit.html.twig') }}
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;">
|
||||
{{ csrf() }}
|
||||
|
||||
{% set button_color = 'blue' %}
|
||||
{{ include('buttons.back.html.twig') }}
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
110
system/templates/account.2fa.email_code.html.twig
Normal file
110
system/templates/account.2fa.email_code.html.twig
Normal file
@@ -0,0 +1,110 @@
|
||||
{% set title = 'Activate Email Code Authentication' %}
|
||||
|
||||
{% set content %}
|
||||
<table style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Enter the email code below to activate <b>two-factor email code authentication</b>. Note
|
||||
that this code is only valid for 24 hours.<br><br>
|
||||
<div class="AttentionSign"><img src="{{ template_path }}/images/global/content/attentionsign.gif"></div>
|
||||
<b>Note:</b> Once you have email code authentication activated, an <b>email code</b> will be
|
||||
sent to the email address assigned to your account whenever you try to log in to the Tibia
|
||||
client or the {{ config.lua.serverName }} website. In order to log in, you will need to enter the <b>most recent
|
||||
email code</b> you have received.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="float: right;">
|
||||
<form action="{{ getLink('account/2fa') }}?action=email-code"
|
||||
method="post" style="padding:0;margin:0;">
|
||||
{{ csrf() }}
|
||||
|
||||
{% if account_logged is defined %}
|
||||
<input type="hidden" name="account_logged" value="{{ account_logged.getId() }}">
|
||||
{% endif %}
|
||||
<input type="hidden" name="step" value="resend">
|
||||
|
||||
{% set button_name = 'Resend Email Code' %}
|
||||
{% include('buttons.base.html.twig') %}
|
||||
</form>
|
||||
</div>
|
||||
An <b>email code</b> has already been sent to the email address assigned to your account.
|
||||
Please check your email account's spam/junk filter and make sure that your mailbox is not
|
||||
full.<br>In case you need a new email code, you can request one by clicking on "Resend Email
|
||||
Code".
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>To complete the activation of email code authentication for your Tibia account, please enter
|
||||
the email code you received at the email address assigned to your account.
|
||||
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||
<div class="LabelV150 {{ wrongCode ? 'red' : '' }}" style="float:left;">Email Code:</div>
|
||||
<input form="confirmActivateForm" name="auth-code" maxlength="6">
|
||||
{% if wrongCode %}
|
||||
<br/>
|
||||
<div class="LabelV150" style="float:left;"> </div>
|
||||
<div class="FormFieldError">Invalid email code!</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endset %}
|
||||
{% include 'tables.headline.html.twig' %}
|
||||
<br/>
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr align="center" valign="top">
|
||||
<td>
|
||||
<form id="confirmActivateForm" action="{{ getLink('account/2fa') }}?action=email-code" method="post" style="padding:0;margin:0;">
|
||||
{{ csrf() }}
|
||||
|
||||
<input type="hidden" name="step" value="activate">
|
||||
<input type="hidden" name="save" value="1">
|
||||
|
||||
{% set button_color = 'green' %}
|
||||
{{ include('buttons.submit.html.twig') }}
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;">
|
||||
{{ csrf() }}
|
||||
{% set button_color = 'blue' %}
|
||||
{{ include('buttons.back.html.twig') }}
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
12
system/templates/account.2fa.main.html.twig
Normal file
12
system/templates/account.2fa.main.html.twig
Normal file
@@ -0,0 +1,12 @@
|
||||
{% set title = 'Two-Factor Authentication' %}
|
||||
|
||||
{% set content %}
|
||||
<table style="width:100%;">
|
||||
<tbody>
|
||||
{{ include(twoFactorViews[0]) }}
|
||||
{{ include(twoFactorViews[1]) }}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endset %}
|
||||
{% include('tables.headline.html.twig') %}
|
||||
<br/>
|
18
system/templates/account.2fa.protected.html.twig
Normal file
18
system/templates/account.2fa.protected.html.twig
Normal file
@@ -0,0 +1,18 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="TableContentContainer ">
|
||||
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="InTableRightButtonContainer"></div>
|
||||
<b>Two-Factor Authenticator App</b>
|
||||
<p>Your account is currently protected by email code authentication. If you prefer to use a <b>two-factor
|
||||
authentication app</b>, you have to "Deactivate" email code authentication first.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
@@ -147,6 +147,9 @@
|
||||
{% include('buttons.base.html.twig') %}
|
||||
</form>
|
||||
<br/>
|
||||
|
||||
{{ include('account.2fa.main.html.twig') }}
|
||||
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}
|
||||
<a name="Account+Logs" ></a>
|
||||
<h2>Account Logs</h2>
|
||||
|
@@ -1,45 +0,0 @@
|
||||
Please enter your account Email address.<br/><br/>
|
||||
{% set title = 'Resend Email' %}
|
||||
{% set background = config('darkborder') %}
|
||||
{% set content %}
|
||||
<table style="width:100%;">
|
||||
<tr>
|
||||
<td class="LabelV" >
|
||||
<span><label for="email">Email Address:</label></span>
|
||||
</td>
|
||||
<td style="width:90%;">
|
||||
<input type="email" form="form" id="email" name="email" size="30" maxlength="50" autofocus/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endset %}
|
||||
{% include 'tables.headline.html.twig' %}
|
||||
<br/>
|
||||
<table style="width:100%;">
|
||||
<tr align="center">
|
||||
<td>
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td style="border:0;">
|
||||
<form id="form" action="{{ getLink('account/resend-email-verify') }}" method="post">
|
||||
{{ csrf() }}
|
||||
<input type="hidden" name="submit" value="1"/>
|
||||
{{ include('buttons.submit.html.twig') }}
|
||||
</form>
|
||||
</td>
|
||||
<tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td style="border:0;">
|
||||
<form action="{{ getLink('news') }}" method="post">
|
||||
{{ include('buttons.back.html.twig') }}
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
@@ -9,7 +9,7 @@
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%"><tr>
|
||||
<td><img src="{{ template_path }}/images/general/blank.gif" width="10" height="1" border="0"></td>
|
||||
<td>
|
||||
{{ hook('HOOK_CHARACTERS_BEFORE_INFORMATIONS') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_BEFORE_INFORMATIONS')) }}
|
||||
{% if canEdit %}
|
||||
<a href="{{ constant('ADMIN_URL') }}?p=players&id={{ player.getId() }}" title="Edit in Admin Panel" target="_blank">
|
||||
<img src="images/edit.png"/>Edit
|
||||
@@ -153,11 +153,11 @@
|
||||
<td>{% if account.isPremium() %}Premium Account{% else %}Free Account{% endif %}</td>
|
||||
</tr>
|
||||
</table>
|
||||
{{ hook('HOOK_CHARACTERS_AFTER_INFORMATIONS') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_AFTER_INFORMATIONS')) }}
|
||||
<br/>
|
||||
<table border="0" width="100%">
|
||||
<tr>
|
||||
{{ hook('HOOK_CHARACTERS_BEFORE_SKILLS') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_BEFORE_SKILLS')) }}
|
||||
|
||||
{% if config.characters.skills %}
|
||||
<!-- SKILLS -->
|
||||
@@ -179,7 +179,7 @@
|
||||
<!-- SKILLS_END -->
|
||||
{% endif %}
|
||||
|
||||
{{ hook('HOOK_CHARACTERS_AFTER_SKILLS') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_AFTER_SKILLS')) }}
|
||||
|
||||
{% if quests_enabled %}
|
||||
<!-- QUESTS -->
|
||||
@@ -201,7 +201,7 @@
|
||||
<!-- QUESTS_END -->
|
||||
{% endif %}
|
||||
|
||||
{{ hook('HOOK_CHARACTERS_AFTER_QUESTS') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_AFTER_QUESTS')) }}
|
||||
|
||||
{% if config.characters.equipment %}
|
||||
<!-- EQUIPMENT -->
|
||||
@@ -239,11 +239,11 @@
|
||||
<!-- EQUIPMENT_END -->
|
||||
{% endif %}
|
||||
|
||||
{{ hook('HOOK_CHARACTERS_AFTER_EQUIPMENT') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_AFTER_EQUIPMENT')) }}
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{ hook('HOOK_CHARACTERS_BEFORE_DEATHS') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_BEFORE_DEATHS')) }}
|
||||
|
||||
{% if deaths|length > 0 %}
|
||||
<!-- DEATHS -->
|
||||
@@ -283,7 +283,7 @@
|
||||
<!-- FRAGS_END -->
|
||||
{% endif %}
|
||||
|
||||
{{ hook('HOOK_CHARACTERS_BEFORE_SIGNATURE') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_BEFORE_SIGNATURE')) }}
|
||||
|
||||
{% if setting('core.signature_enabled') %}
|
||||
<!-- SIGNATURE -->
|
||||
@@ -327,7 +327,7 @@
|
||||
</table>
|
||||
<!-- SIGNATURE_END -->
|
||||
{% endif %}
|
||||
{{ hook('HOOK_CHARACTERS_AFTER_SIGNATURE') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_AFTER_SIGNATURE')) }}
|
||||
{% if not player.isHidden() %}
|
||||
{% set rows = 0 %}
|
||||
<!-- ACCOUNT_INFORMATION -->
|
||||
@@ -377,7 +377,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
<!-- ACCOUNT_INFORMATION_END -->
|
||||
{{ hook('HOOK_CHARACTERS_AFTER_ACCOUNT') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_AFTER_ACCOUNT')) }}
|
||||
<!-- CHARACTERS_LIST -->
|
||||
<br/><br/>
|
||||
<table border="0" cellspacing="1" cellpadding="4" width="100%">
|
||||
@@ -421,7 +421,7 @@
|
||||
</table>
|
||||
<!-- CHARACTERS_LIST_END -->
|
||||
{% endif %}
|
||||
{{ hook('HOOK_CHARACTERS_AFTER_CHARACTERS') }}
|
||||
{{ hook(constant('HOOK_CHARACTERS_AFTER_CHARACTERS')) }}
|
||||
{% if canEdit %}
|
||||
<a href="{{ constant('ADMIN_URL') }}?p=players&id={{ player.getId() }}" title="Edit in Admin Panel" target="_blank">
|
||||
<img src="images/edit.png"/>Edit
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<div class="AttentionSign" style="background-image:url({{ template_path }}/images/content/attentionsign.gif);"></div>
|
||||
<b>The Following Errors Have Occurred:</b><br/>
|
||||
{% for error in errors %}
|
||||
<li>{{ error|striptags('<b><a>')|raw }}</li>
|
||||
<li>{{ error|striptags('<b>')|raw }}</li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="BoxFrameHorizontal" style="background-image:url({{ template_path }}/images/content/box-frame-horizontal.gif);"></div>
|
||||
@@ -17,4 +17,4 @@
|
||||
<div class="BoxFrameEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
9
system/templates/mail.account.2fa.email-code.html.twig
Normal file
9
system/templates/mail.account.2fa.email-code.html.twig
Normal file
@@ -0,0 +1,9 @@
|
||||
Dear {{ config.lua.serverName}} player,
|
||||
<br/><br/>
|
||||
Your account is protected by email code authentication, and you requested a new email code:
|
||||
<br/><br/>
|
||||
<p>{{ code }}</p>
|
||||
<br/>
|
||||
Note that the code is only valid for 24 hours.
|
||||
<br/><br/>
|
||||
Kind Regards,
|
@@ -0,0 +1,5 @@
|
||||
Dear {{ config.lua.serverName}} player,<br/>
|
||||
<br/>
|
||||
A <strong>wrong two-factor authentication code</strong> was entered for your {{ config.lua.serverName}} account. If you simply mistyped the code, please try again.<br/>
|
||||
<br/>
|
||||
However, if this was <strong>not you</strong>, someone else may be trying to access your account. Since they already know your password, we strongly recommend that you <strong>change your password immediately</strong>.
|
@@ -1,7 +0,0 @@
|
||||
Hello {{ account }}!<br/>
|
||||
<br/>
|
||||
You requested to resend the verify Email on {{ config.lua.serverName }}!<br/>
|
||||
<br/>
|
||||
|
||||
To verify your email address please click the link below:<br/>
|
||||
{{ verify_url|raw }}
|
@@ -101,7 +101,7 @@
|
||||
|
||||
<tr>
|
||||
<td class="LabelV150"><b>Location Datacenter:</b></td>
|
||||
<td>{{ setting('core.online_datacenter')|raw }} <small>(Server date & time: - {{ "now"|date("d/m/Y H:i:s") }})</small></td>
|
||||
<td>{{ setting('core.online_datacenter') }} <small>(Server date & time: - {{ "now"|date("d/m/Y H:i:s") }})</small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="LabelV150"><b>PvP Type:</b></td>
|
||||
|
@@ -1,9 +1,6 @@
|
||||
<style>
|
||||
.myaac-table {border-spacing: 1px;}
|
||||
.myaac-table > tbody > tr:nth-child(even) {background: {{ config.lightborder }}}
|
||||
.myaac-table > tbody > tr:nth-child(odd) {background: {{ config.darkborder }}}
|
||||
.myaac-table > tbody > tr > td {padding: 4px; }
|
||||
.myaac-table > thead > tr > td {padding: 4px; background: {{ config.vdarkborder }}; color: #ffffff !important;}
|
||||
.myaac-table > thead > tr > th {padding: 4px; background: {{ config.vdarkborder }}; color: #ffffff !important;}
|
||||
.myaac-table > tfoot > tr > td {padding: 4px; background: {{ config.vdarkborder }}; color: #ffffff !important;}
|
||||
.myaac-table tbody tr:nth-child(even) {background: {{ config.lightborder }}}
|
||||
.myaac-table tbody tr:nth-child(odd) {background: {{ config.darkborder }}}
|
||||
.myaac-table thead td {background: {{ config.vdarkborder }}; color: #ffffff !important;}
|
||||
.myaac-table tfoot td {background: {{ config.vdarkborder }}; color: #ffffff !important;}
|
||||
</style>
|
||||
|
@@ -101,10 +101,6 @@ $twig->addFunction($function);
|
||||
$function = new TwigFunction('hook', function ($context, $hook, array $params = []) {
|
||||
global $hooks;
|
||||
|
||||
if (config('hooks_debug')) {
|
||||
note($hook);
|
||||
}
|
||||
|
||||
if(is_string($hook)) {
|
||||
if (defined($hook)) {
|
||||
$hook = constant($hook);
|
||||
|
@@ -130,7 +130,6 @@
|
||||
<div style="float: right; margin-top: 20px;" >
|
||||
{% apply spaceless %}
|
||||
<form class="MediumButtonForm" action="{{ getLink('account/create') }}" method="post" >
|
||||
{{ csrf() }}
|
||||
<div class="MediumButtonBackground" style="background-image:url({{ template_path }}/images/global/buttons/mediumbutton.gif)" onMouseOver="MouseOverBigButton(this);" onMouseOut="MouseOutBigButton(this);">
|
||||
<div class="MediumButtonOver" style="background-image:url({{ template_path }}/images/global/buttons/mediumbutton-over.gif)" onMouseOver="MouseOverBigButton(this);" onMouseOut="MouseOutBigButton(this);"></div>
|
||||
<input class="MediumButtonText" type="image" name="Create Account" alt="Create Account" src="{{ template_path }}/images/global/buttons/mediumbutton_createaccount.png" />
|
||||
|
@@ -290,6 +290,9 @@
|
||||
{% endset %}
|
||||
{% include 'tables.headline.html.twig' %}
|
||||
<br/>
|
||||
|
||||
{{ include('account.2fa.main.html.twig') }}
|
||||
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}
|
||||
<a name="Account+Logs" ></a>
|
||||
<div class="TopButtonContainer">
|
||||
|
@@ -943,6 +943,14 @@ img {
|
||||
font-size: 8pt;
|
||||
color: red;
|
||||
}
|
||||
.AttentionSign img {
|
||||
float: left;
|
||||
top: 3px;
|
||||
left: 8px;
|
||||
width: 15px;
|
||||
height: 13px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.SmallBox {
|
||||
position: relative;
|
||||
font-size: 1px;
|
||||
|
@@ -27,18 +27,24 @@ if(isset($config['boxes']))
|
||||
var loginStatus="<?php echo ($logged ? 'true' : 'false'); ?>";
|
||||
<?php
|
||||
if(PAGE !== 'news') {
|
||||
$tmp = str_replace('/', '_', isset($_REQUEST['subtopic']) ? escapeHtml($_REQUEST['subtopic']) : PAGE);
|
||||
$exp = explode('/', PAGE);
|
||||
if(PAGE !== 'account/create' && PAGE !== 'account/lost' && isset($exp[1])) {
|
||||
if ($exp[0] === 'account' && $exp[1] === 'lost') {
|
||||
$tmp = 'account_lost';
|
||||
} elseif ($exp[0] === 'account') {
|
||||
$tmp = 'account_manage';
|
||||
} else if ($exp[0] === 'news' && $exp[1] === 'archive') {
|
||||
$tmp = 'news_archive';
|
||||
if(isset($_REQUEST['subtopic'])) {
|
||||
$tmp = escapeHtml($_REQUEST['subtopic']);
|
||||
if($tmp === 'accountmanagement') {
|
||||
$tmp = 'accountmanage';
|
||||
}
|
||||
else if (in_array($exp[0], ['characters', 'highscores', 'guilds', 'forum'])) {
|
||||
$tmp = $exp[0];
|
||||
}
|
||||
else {
|
||||
$tmp = str_replace('/', '_', PAGE);
|
||||
$exp = explode('/', PAGE);
|
||||
if(PAGE !== 'account/create' && PAGE !== 'account/lost' && isset($exp[1])) {
|
||||
if ($exp[0] === 'account') {
|
||||
$tmp = 'account_manage';
|
||||
} else if ($exp[0] === 'news' && $exp[1] === 'archive') {
|
||||
$tmp = 'news_archive';
|
||||
}
|
||||
else if (in_array($exp[0], ['characters', 'highscores', 'guilds', 'forum'])) {
|
||||
$tmp = $exp[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,24 +90,24 @@ if(isset($config['boxes']))
|
||||
// mouse-over and click events of the loginbox
|
||||
function MouseOverLoginBoxText(source)
|
||||
{
|
||||
source.lastElementChild.style.visibility = "visible";
|
||||
source.firstElementChild.style.visibility = "hidden";
|
||||
source.lastChild.style.visibility = "visible";
|
||||
source.firstChild.style.visibility = "hidden";
|
||||
}
|
||||
function MouseOutLoginBoxText(source)
|
||||
{
|
||||
source.firstElementChild.style.visibility = "visible";
|
||||
source.lastElementChild.style.visibility = "hidden";
|
||||
source.firstChild.style.visibility = "visible";
|
||||
source.lastChild.style.visibility = "hidden";
|
||||
}
|
||||
function LoginButtonAction()
|
||||
{
|
||||
if(loginStatus === "false") {
|
||||
if(loginStatus == "false") {
|
||||
window.location = "<?php echo getLink('account/manage'); ?>";
|
||||
} else {
|
||||
window.location = "<?php echo getLink('account/manage'); ?>";
|
||||
}
|
||||
}
|
||||
function LoginstatusTextAction(source) {
|
||||
if(loginStatus === "false") {
|
||||
if(loginStatus == "false") {
|
||||
window.location = "<?php echo getLink('account/create'); ?>";
|
||||
} else {
|
||||
window.location = "<?php echo getLink('account/logout'); ?>";
|
||||
@@ -220,11 +226,11 @@ if(isset($config['boxes']))
|
||||
// mouse-over effects of menubuttons and submenuitems
|
||||
function MouseOverMenuItem(source)
|
||||
{
|
||||
source.firstElementChild.style.visibility = "visible";
|
||||
source.firstChild.style.visibility = "visible";
|
||||
}
|
||||
function MouseOutMenuItem(source)
|
||||
{
|
||||
source.firstElementChild.style.visibility = "hidden";
|
||||
source.firstChild.style.visibility = "hidden";
|
||||
}
|
||||
function MouseOverSubmenuItem(source)
|
||||
{
|
||||
@@ -330,7 +336,7 @@ if(isset($config['boxes']))
|
||||
<div id="LoginBottom" class="Loginstatus" style="background-image:url(<?php echo $template_path; ?>/images/general/box-bottom.gif)" ></div>
|
||||
</div>
|
||||
|
||||
<div id='Menu'>
|
||||
<div-- id='Menu'>
|
||||
<div id='MenuTop' style='background-image:url(<?php echo $template_path; ?>/images/general/box-top.gif);'></div>
|
||||
|
||||
<?php
|
||||
@@ -395,7 +401,6 @@ foreach($config['menu_categories'] as $id => $cat) {
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
InitializePage();
|
||||
</script>
|
||||
|
@@ -1,11 +1,11 @@
|
||||
function MouseOverBigButton(source) {
|
||||
if (source?.firstElementChild?.style) {
|
||||
source.firstElementChild.style.visibility = "visible";
|
||||
if (source?.firstChild?.style) {
|
||||
source.firstChild.style.visibility = "visible";
|
||||
}
|
||||
}
|
||||
function MouseOutBigButton(source) {
|
||||
if (source?.firstElementChild?.style) {
|
||||
source.firstElementChild.style.visibility = "hidden";
|
||||
if (source?.firstChild?.style) {
|
||||
source.firstChild.style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
function BigButtonAction(path) {
|
||||
|
Reference in New Issue
Block a user