More fixes + authentication.

This commit is contained in:
flash 2022-02-06 17:14:13 +00:00
parent 2b7b8e7597
commit 4e80c5f0a0
12 changed files with 111 additions and 83 deletions

View file

@ -20,6 +20,7 @@ require_once AJAX_CHAT_PATH . '/src/AJAXChatFileSystem.php';
require_once AJAX_CHAT_PATH . '/src/AJAXChatHTTPHeader.php';
require_once AJAX_CHAT_PATH . '/src/AJAXChatLanguage.php';
require_once AJAX_CHAT_PATH . '/src/AJAXChatTemplate.php';
require_once AJAX_CHAT_PATH . '/src/SockChatAuth.php';
require_once AJAX_CHAT_PATH . '/src/CustomAJAXChat.php';
require_once AJAX_CHAT_PATH . '/src/CustomAJAXChatShoutBox.php';
require_once AJAX_CHAT_PATH . '/src/CustomAJAXChatInterface.php';

View file

@ -37,6 +37,9 @@ $config['dbConnection']['type'] = null;
// Database link:
$config['dbConnection']['link'] = null;
if(is_file(AJAX_CHAT_PATH . '/config/config-db.php'))
include_once AJAX_CHAT_PATH . '/config/config-db.php';
// Database table names:
$config['dbTableNames'] = array();
$config['dbTableNames']['online'] = 'ajax_chat_online';
@ -105,7 +108,7 @@ $config['forceAutoLogin'] = false;
$config['showChannelMessages'] = true;
// If enabled, the chat will only be accessible for the admin:
$config['chatClosed'] = false;
$config['chatClosed'] = true;
// Defines the timezone offset in seconds (-12*60*60 to 12*60*60) - if null, the server timezone is used:
$config['timeZoneOffset'] = null;
// Defines the hour of the day the chat is opened (0 - closingHour):

View file

@ -333,9 +333,9 @@ class AJAXChat {
case 'legacy2':
return AJAX_CHAT_PATH . '/template/loggedOut~.html';
case 'legacy3':
return AJAX_CHAT_PATH . '/template/loggedOutFA.html';
default:
return AJAX_CHAT_PATH . '/template/loggedOut.html';
default:
return AJAX_CHAT_PATH . '/template/loggedOutFA.html';
}
}
@ -2651,8 +2651,8 @@ class AJAXChat {
'',
time()-42000,
$this->getConfig('sessionCookiePath'),
$this->getConfig('sessionCookieDomain'),
$this->getConfig('sessionCookieSecure')
$this->getConfig('sessionCookieDomain') ?? '',
$this->getConfig('sessionCookieSecure') ?? false
);
}
@ -2938,8 +2938,8 @@ class AJAXChat {
$this->getLangCode(),
time()+60*60*24*$this->getConfig('sessionCookieLifeTime'),
$this->getConfig('sessionCookiePath'),
$this->getConfig('sessionCookieDomain'),
$this->getConfig('sessionCookieSecure')
$this->getConfig('sessionCookieDomain') ?? '',
$this->getConfig('sessionCookieSecure') ?? false
);
}

View file

@ -59,9 +59,9 @@ class AJAXChatDataBase {
// Method to retrieve the current DataBase name:
function getName() {
return $this->_db->getName();
//return $this->_db->getName();
//If your database has hyphens ( - ) in it, try using this instead:
//return '`'.$this->_db->getName().'`';
return '`'.$this->_db->getName().'`';
}
// Method to retrieve the last inserted ID:

View file

@ -12,73 +12,39 @@ class CustomAJAXChat extends AJAXChat {
// Returns an associative array containing userName, userID and userRole
// Returns null if login is invalid
function getValidLoginUserData() {
if($this->getRequestVar('password')) {
if(empty($_COOKIE['msz_auth'])) {
header('Location: https://flashii.net/auth/login.php');
exit;
}
$userInfo = SockChatAuth::attempt($this->getConfig('flashiiSecret'), (string)filter_input(INPUT_COOKIE, 'msz_auth'));
if($userInfo->success) {
// Check if we have a valid registered user:
$userName = $this->getRequestVar('userName');
$userName = $this->convertEncoding($userName, $this->getConfig('contentEncoding'), $this->getConfig('sourceEncoding'));
$chatUser = [
'userID' => $userInfo->user_id,
'userName' => $userInfo->username,
'userRole' => AJAX_CHAT_GUEST,
];
$password = $this->getRequestVar('password');
$password = $this->convertEncoding($password, $this->getConfig('contentEncoding'), $this->getConfig('sourceEncoding'));
// sock chat auth doesn't actually return role ids lol
if($userInfo->colour_raw === 0xEE9400)
$chatUser['userRole'] = DONATOR;
elseif($userInfo->colour_raw === 0x0099FF)
$chatUser['userRole'] = CMOD;
elseif($userInfo->colour_raw === 0x7353C4)
$chatUser['userRole'] = PURPLE;
elseif($userInfo->colour_raw === 0x9E8DA7)
$chatUser['userRole'] = BOTS;
elseif($userInfo->rank >= 10)
$chatUser['userRole'] = AJAX_CHAT_ADMIN;
elseif($userInfo->rank >= 5)
$chatUser['userRole'] = AJAX_CHAT_MODERATOR;
elseif($userInfo->rank >= 1)
$chatUser['userRole'] = AJAX_CHAT_USER;
$flashiiConfig = parse_ini_file('/www/flashii.net/config/config.ini', true, INI_SCANNER_TYPED);
if (!empty($flashiiConfig['Database'])) {
$dbConfig = $flashiiConfig['Database'];
$flashiiDb = new PDO(
"mysql:unix_socket={$dbConfig['unix_socket']};dbname={$dbConfig['database']}",
$dbConfig['username'],
$dbConfig['password'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
$getFlashiiUser = $flashiiDb->prepare('
SELECT `user_id` as `userID`, `username` as `userName`, `display_role` as `userRole`, `password`
FROM `msz_users`
WHERE LOWER(`username`) = LOWER(:username)
');
$getFlashiiUser->bindValue('username', $userName);
$flashiiUser = $getFlashiiUser->execute() ? $getFlashiiUser->fetch(PDO::FETCH_ASSOC) : [];
if (!empty($flashiiUser) && password_verify($password, $flashiiUser['password'])) {
unset($flashiiUser['password']);
// corrections, i'm not going to update the random IDs scattered about
switch ($flashiiUser['userRole']) {
case 2:
$flashiiUser['userRole'] = AJAX_CHAT_MODERATOR;
break;
case 3:
$flashiiUser['userRole'] = AJAX_CHAT_ADMIN;
break;
case 4:
$flashiiUser['userRole'] = BOTS;
break;
case 5:
$flashiiUser['userRole'] = AJAX_CHAT_GUEST;
break;
case 6:
case 7:
$flashiiUser['userRole'] = DONATOR;
break;
default:
$flashiiUser['userRole'] = AJAX_CHAT_USER;
}
/*if ($flashiiUser['userID'] === 2) {
$flashiiUser['userRole'] = CMOD;
} else*/if ($flashiiUser['userID'] === 3) {
$flashiiUser['userRole'] = AJAX_CHAT_MODERATOR;
}
return $flashiiUser;
}
}
return null;
return $chatUser;
} else {
// Guest users:
return $this->getGuestUser();

58
src/SockChatAuth.php Normal file
View file

@ -0,0 +1,58 @@
<?php
final class SockChatAuth {
private const ENDPOINT = 'https://flashii.net/_sockchat/verify';
public static function attempt(string $secret, string $cookie): object {
if(!empty($cookie)) {
$decoded = str_pad(base64_decode(str_pad(strtr($cookie, '-_', '+/'), strlen($cookie) % 4, '=', STR_PAD_RIGHT)), 37, "\0");
$unpacked = unpack('Cversion/Nuser/H*token', $decoded);
if(isset($unpacked['version']) && $unpacked['version'] === 1
&& isset($unpacked['user']) && $unpacked['user'] > 0) {
$loginRequest = [
'user_id' => $unpacked['user'],
'token' => 'SESS:' . $cookie,
'ip' => $_SERVER['REMOTE_ADDR'],
];
$loginSignature = hash_hmac('sha256', implode('#', $loginRequest), $secret);
$login = curl_init(self::ENDPOINT);
curl_setopt_array($login, [
CURLOPT_AUTOREFERER => false,
CURLOPT_FAILONERROR => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HEADER => false,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($loginRequest),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TCP_FASTOPEN => true,
CURLOPT_CONNECTTIMEOUT => 2,
CURLOPT_MAXREDIRS => 2,
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
CURLOPT_TIMEOUT => 5,
CURLOPT_USERAGENT => 'AJAX Chat',
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-SharpChat-Signature: ' . $loginSignature,
],
]);
$userInfo = json_decode(curl_exec($login));
curl_close($login);
}
}
if(empty($userInfo->success)) {
$userInfo = new stdClass;
$userInfo->success = false;
$userInfo->user_id = 0;
$userInfo->username = 'Anonymous';
$userInfo->colour_raw = 0x40000000;
$userInfo->rank = 0;
$userInfo->hierarchy = 0;
$userInfo->is_silenced = '1970-01-01T00:00:00+00:00';
$userInfo->perms = 0;
}
return $userInfo;
}
}

View file

@ -221,7 +221,7 @@ if(!$satoriSetting['enabled'])
// Bring the process of sending a message down to one if
if(isset($satoriResp)) {
foreach($satoriResp as $directives => $response) {
foreach($satoriResp as $response) {
if($response == '[s]Trigger Kick') {
$this->logout('Kicked');
} else {
@ -230,7 +230,7 @@ if(isset($satoriResp)) {
$satoriSetting['userName'],
$satoriSetting['userRank'],
$this->getChannel(),
vsprintf($response, $directives),
$response,
$satoriSetting['userIP'],
0
);

View file

@ -130,9 +130,9 @@
<input type="hidden" name="login" id="loginField" value="login"/>
<input type="hidden" name="redirect" id="redirectField" value="[REDIRECT_URL/]"/>
<div><label for="userNameField">[LANG]userName[/LANG]:</label><br />
<input type="text" name="userName" id="userNameField" maxlength="[USER_NAME_MAX_LENGTH/]"/></div>
<input disabled="disabled" type="text" name="userName" id="userNameField" maxlength="[USER_NAME_MAX_LENGTH/]"/></div>
<div><label for="passwordField">[LANG]password[/LANG]*:</label><br />
<input type="password" name="password" id="passwordField"/></div>
<input disabled="disabled" type="password" name="password" id="passwordField"/></div>
<div><label for="channelField">[LANG]channel[/LANG]:</label><br />
<select name="channelName" id="channelField">[CHANNEL_OPTIONS/]</select></div>
<div><label for="languageSelection">[LANG]language[/LANG]:</label><br />

View file

@ -55,8 +55,8 @@ div.mioboxcontent {
<div class="mioboxcontent">
<input type="hidden" name="login" id="loginField" value="login"/>
<input type="hidden" name="redirect" id="redirectField" value="[REDIRECT_URL/]"/>
<input type="text" name="userName" id="userNameField" maxlength="[USER_NAME_MAX_LENGTH/]" placeholder="[LANG]userName[/LANG]" />
<input type="password" name="password" id="passwordField" placeholder="[LANG]password[/LANG]" />
<input disabled="disabled" type="text" name="userName" id="userNameField" maxlength="[USER_NAME_MAX_LENGTH/]" placeholder="[LANG]userName[/LANG]" />
<input disabled="disabled" type="password" name="password" id="passwordField" placeholder="[LANG]password[/LANG]" />
<select name="channelName" id="channelField">[CHANNEL_OPTIONS/]</select>
<select id="languageSelection" name="lang" onchange="ajaxChat.switchLanguage(this.value);">[LANGUAGE_OPTIONS/]</select>
<input type="submit" name="submit" id="loginButton" value="[LANG]login[/LANG]"/>

View file

@ -55,7 +55,7 @@ div.mioboxcontent {
<div class="mioboxcontent">
<input type="hidden" name="login" id="loginField" value="login"/>
<input type="hidden" name="redirect" id="redirectField" value="[REDIRECT_URL/]"/>
<input type="text" id="userNameField" disabled="" style="width: 388px;" placeholder="Click 'login' to login with your Flashii Account" />
<input type="text" id="userNameField" disabled="" style="width: 388px;" placeholder="Click &quot;Login&quot; to login with your Flashii Account" />
<select name="channelName" id="channelField">[CHANNEL_OPTIONS/]</select>
<select id="languageSelection" name="lang" onchange="ajaxChat.switchLanguage(this.value);">[LANGUAGE_OPTIONS/]</select>
<input type="submit" name="submit" id="loginButton" value="[LANG]login[/LANG]"/>

View file

@ -46,8 +46,8 @@
<form id="loginForm" action="[LOGIN_URL/]" method="post" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="login" id="loginField" value="login"/>
<input type="hidden" name="redirect" id="redirectField" value="[REDIRECT_URL/]"/>
<input type="text" name="userName" id="userNameField" maxlength="[USER_NAME_MAX_LENGTH/]" placeholder="[LANG]userName[/LANG] (e.g. [CHAT_BOT_NAME/])" />
<input type="password" name="password" id="passwordField" placeholder="[LANG]password[/LANG]" />
<input disabled="disabled" type="text" name="userName" id="userNameField" maxlength="[USER_NAME_MAX_LENGTH/]" placeholder="[LANG]userName[/LANG] (e.g. [CHAT_BOT_NAME/])" />
<input disabled="disabled" type="password" name="password" id="passwordField" placeholder="[LANG]password[/LANG]" />
<select name="channelName" id="channelField">[CHANNEL_OPTIONS/]</select>
<input type="submit" name="submit" id="loginButton" value="[LANG]login[/LANG]"/>
</form>

View file

@ -55,9 +55,9 @@
<input type="hidden" name="login" id="loginField" value="login"/>
<input type="hidden" name="redirect" id="redirectField" value="[REDIRECT_URL/]"/>
<div><label for="userNameField">[LANG]userName[/LANG]:</label><br />
<input type="text" name="userName" id="userNameField" maxlength="[USER_NAME_MAX_LENGTH/]"/></div>
<input disabled="disabled" type="text" name="userName" id="userNameField" maxlength="[USER_NAME_MAX_LENGTH/]"/></div>
<div><label for="passwordField">[LANG]password[/LANG]*:</label><br />
<input type="password" name="password" id="passwordField"/></div>
<input disabled="disabled" type="password" name="password" id="passwordField"/></div>
<div><label for="channelField">[LANG]channel[/LANG]:</label><br />
<select name="channelName" id="channelField">[CHANNEL_OPTIONS/]</select></div>
<div><label for="languageSelection">[LANG]language[/LANG]:</label><br />