TrinityCore
Loading...
Searching...
No Matches
cs_account.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/* ScriptData
19Name: account_commandscript
20%Complete: 100
21Comment: All account related commands
22Category: commandscripts
23EndScriptData */
24
25#include "AccountMgr.h"
26#include "AES.h"
27#include "Base32.h"
28#include "Chat.h"
29#include "CryptoGenerics.h"
30#include "CryptoRandom.h"
31#include "DatabaseEnv.h"
32#include "IpAddress.h"
33#include "IPLocation.h"
34#include "Language.h"
35#include "Log.h"
36#include "Player.h"
37#include "ScriptMgr.h"
38#include "SecretMgr.h"
39#include "TOTP.h"
40#include "World.h"
41#include "WorldSession.h"
42#include <unordered_map>
43
44using namespace Trinity::ChatCommands;
45
47{
48public:
49 account_commandscript() : CommandScript("account_commandscript") { }
50
52 {
53 static ChatCommandTable accountSetCommandTable =
54 {
62 };
63 static ChatCommandTable accountOnlinelistCommandTable =
64 {
70 };
71 static ChatCommandTable accountCommandTable =
72 {
79 { "onlinelist", accountOnlinelistCommandTable },
82 { "set", accountSetCommandTable },
85 };
86 static ChatCommandTable commandTable =
87 {
88 { "account", accountCommandTable },
89 };
90 return commandTable;
91 }
92
94 {
95 auto const& masterKey = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY);
96 if (!masterKey.IsAvailable())
97 {
99 handler->SetSentErrorMessage(true);
100 return false;
101 }
102
103 uint32 const accountId = handler->GetSession()->GetAccountId();
104
105 { // check if 2FA already enabled
107 stmt->setUInt32(0, accountId);
108 PreparedQueryResult result = LoginDatabase.Query(stmt);
109
110 if (!result)
111 {
112 TC_LOG_ERROR("misc", "Account {} not found in login database when processing .account 2fa setup command.", accountId);
114 handler->SetSentErrorMessage(true);
115 return false;
116 }
117
118 if (!result->Fetch()->IsNull())
119 {
121 handler->SetSentErrorMessage(true);
122 return false;
123 }
124 }
125
126 // store random suggested secrets
127 static std::unordered_map<uint32, Trinity::Crypto::TOTP::Secret> suggestions;
128 auto pair = suggestions.emplace(std::piecewise_construct, std::make_tuple(accountId), std::make_tuple(Trinity::Crypto::TOTP::RECOMMENDED_SECRET_LENGTH)); // std::vector 1-argument size_t constructor invokes resize
129 if (pair.second) // no suggestion yet, generate random secret
130 Trinity::Crypto::GetRandomBytes(pair.first->second);
131
132 if (!pair.second && token) // suggestion already existed and token specified - validate
133 {
134 if (Trinity::Crypto::TOTP::ValidateToken(pair.first->second, *token))
135 {
136 if (masterKey)
137 Trinity::Crypto::AEEncryptWithRandomIV<Trinity::Crypto::AES>(pair.first->second, *masterKey);
138
140 stmt->setBinary(0, pair.first->second);
141 stmt->setUInt32(1, accountId);
142 LoginDatabase.Execute(stmt);
143 suggestions.erase(pair.first);
145 return true;
146 }
147 else
149 }
150
151 // new suggestion, or no token specified, output TOTP parameters
153 handler->SetSentErrorMessage(true);
154 return false;
155 }
156
158 {
159 auto const& masterKey = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY);
160 if (!masterKey.IsAvailable())
161 {
163 handler->SetSentErrorMessage(true);
164 return false;
165 }
166
167 uint32 const accountId = handler->GetSession()->GetAccountId();
169 { // get current TOTP secret
171 stmt->setUInt32(0, accountId);
172 PreparedQueryResult result = LoginDatabase.Query(stmt);
173
174 if (!result)
175 {
176 TC_LOG_ERROR("misc", "Account {} not found in login database when processing .account 2fa setup command.", accountId);
178 handler->SetSentErrorMessage(true);
179 return false;
180 }
181
182 Field* field = result->Fetch();
183 if (field->IsNull())
184 { // 2FA not enabled
186 handler->SetSentErrorMessage(true);
187 return false;
188 }
189
190 secret = field->GetBinary();
191 }
192
193 if (token)
194 {
195 if (masterKey)
196 {
197 bool success = Trinity::Crypto::AEDecrypt<Trinity::Crypto::AES>(secret, *masterKey);
198 if (!success)
199 {
200 TC_LOG_ERROR("misc", "Account {} has invalid ciphertext in TOTP token.", accountId);
202 handler->SetSentErrorMessage(true);
203 return false;
204 }
205 }
206
207 if (Trinity::Crypto::TOTP::ValidateToken(secret, *token))
208 {
210 stmt->setNull(0);
211 stmt->setUInt32(1, accountId);
212 LoginDatabase.Execute(stmt);
214 return true;
215 }
216 else
218 }
219
221 handler->SetSentErrorMessage(true);
222 return false;
223 }
224
225 static bool HandleAccountAddonCommand(ChatHandler* handler, uint8 expansion)
226 {
227 if (expansion > sWorld->getIntConfig(CONFIG_EXPANSION))
228 {
230 handler->SetSentErrorMessage(true);
231 return false;
232 }
233
235
236 stmt->setUInt8(0, expansion);
237 stmt->setUInt32(1, handler->GetSession()->GetAccountId());
238
239 LoginDatabase.Execute(stmt);
240
241 handler->PSendSysMessage(LANG_ACCOUNT_ADDON, expansion);
242 return true;
243 }
244
246 static bool HandleAccountCreateCommand(ChatHandler* handler, std::string const& accountName, std::string const& password, Optional<std::string> const& email)
247 {
248 if (accountName.find('@') != std::string::npos)
249 {
251 handler->SetSentErrorMessage(true);
252 return false;
253 }
254
255 switch (sAccountMgr->CreateAccount(accountName, password, email.value_or("")))
256 {
258 handler->PSendSysMessage(LANG_ACCOUNT_CREATED, accountName);
259 if (handler->GetSession())
260 {
261 TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] {}) created Account {} (Email: '{}')",
262 handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
263 handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString(),
264 accountName, email.value_or(""));
265 }
266 break;
269 handler->SetSentErrorMessage(true);
270 return false;
273 handler->SetSentErrorMessage(true);
274 return false;
277 handler->SetSentErrorMessage(true);
278 return false;
281 handler->SetSentErrorMessage(true);
282 return false;
283 default:
284 handler->PSendSysMessage(LANG_ACCOUNT_NOT_CREATED, accountName);
285 handler->SetSentErrorMessage(true);
286 return false;
287 }
288
289 return true;
290 }
291
294 static bool HandleAccountDeleteCommand(ChatHandler* handler, std::string accountName)
295 {
296 if (!Utf8ToUpperOnlyLatin(accountName))
297 {
298 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
299 handler->SetSentErrorMessage(true);
300 return false;
301 }
302
303 uint32 accountId = AccountMgr::GetId(accountName);
304 if (!accountId)
305 {
306 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
307 handler->SetSentErrorMessage(true);
308 return false;
309 }
310
314 if (handler->HasLowerSecurityAccount(nullptr, accountId, true))
315 return false;
316
317 AccountOpResult result = AccountMgr::DeleteAccount(accountId);
318 switch (result)
319 {
321 handler->PSendSysMessage(LANG_ACCOUNT_DELETED, accountName.c_str());
322 break;
324 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
325 handler->SetSentErrorMessage(true);
326 return false;
328 handler->PSendSysMessage(LANG_ACCOUNT_NOT_DELETED_SQL_ERROR, accountName.c_str());
329 handler->SetSentErrorMessage(true);
330 return false;
331 default:
332 handler->PSendSysMessage(LANG_ACCOUNT_NOT_DELETED, accountName.c_str());
333 handler->SetSentErrorMessage(true);
334 return false;
335 }
336
337 return true;
338 }
339
342 {
343 return HandleAccountOnlineListCommandWithParameters(handler, {}, {}, {}, {});
344 }
345
346 static bool HandleAccountOnlineListWithIpFilterCommand(ChatHandler* handler, std::string ipAddress)
347 {
348 return HandleAccountOnlineListCommandWithParameters(handler, ipAddress, {}, {}, {});
349 }
350
352 {
353 return HandleAccountOnlineListCommandWithParameters(handler, {}, limit, {}, {});
354 }
355
357 {
358 return HandleAccountOnlineListCommandWithParameters(handler, {}, {}, mapId, {});
359 }
360
362 {
363 return HandleAccountOnlineListCommandWithParameters(handler, {}, {}, {}, zoneId);
364 }
365
367 {
368 size_t sessionsMatchCount = 0;
369
370 SessionMap const& sessionsMap = sWorld->GetAllSessions();
371 for (SessionMap::value_type const& sessionPair : sessionsMap)
372 {
373 WorldSession* session = sessionPair.second;
374 Player* player = session->GetPlayer();
375
376 // Ignore sessions on character selection screen
377 if (!player)
378 continue;
379
380 uint32 playerMapId = player->GetMapId();
381 uint32 playerZoneId = player->GetZoneId();
382
383 // Apply optional ipAddress filter
384 if (ipAddress && ipAddress != session->GetRemoteAddress())
385 continue;
386
387 // Apply optional mapId filter
388 if (mapId && mapId != playerMapId)
389 continue;
390
391 // Apply optional zoneId filter
392 if (zoneId && zoneId != playerZoneId)
393 continue;
394
395 if (!sessionsMatchCount)
396 {
401 }
402
404 session->GetAccountName().c_str(),
405 session->GetPlayerName().c_str(),
406 session->GetRemoteAddress().c_str(),
407 playerMapId,
408 playerZoneId,
409 session->Expansion(),
410 int32(session->GetSecurity()));
411
412 ++sessionsMatchCount;
413
414 // Apply optional count limit
415 if (limit && sessionsMatchCount >= limit)
416 break;
417 }
418
419 // Header is printed on first matched session. If it wasn't printed then no sessions matched the criteria
420 if (!sessionsMatchCount)
421 {
423 return true;
424 }
425
427 return true;
428 }
429
430 static bool HandleAccountLockCountryCommand(ChatHandler* handler, bool state)
431 {
432 if (state)
433 {
434 if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(handler->GetSession()->GetRemoteAddress()))
435 {
437 stmt->setString(0, location->CountryCode);
438 stmt->setUInt32(1, handler->GetSession()->GetAccountId());
439 LoginDatabase.Execute(stmt);
441 }
442 else
443 {
444 handler->PSendSysMessage("No IP2Location information - account not locked");
445 handler->SetSentErrorMessage(true);
446 return false;
447 }
448 }
449 else
450 {
452 stmt->setString(0, "00");
453 stmt->setUInt32(1, handler->GetSession()->GetAccountId());
454 LoginDatabase.Execute(stmt);
456 }
457 return true;
458 }
459
460 static bool HandleAccountLockIpCommand(ChatHandler* handler, bool state)
461 {
463
464 if (state)
465 {
466 stmt->setBool(0, true); // locked
468 }
469 else
470 {
471 stmt->setBool(0, false); // unlocked
473 }
474
475 stmt->setUInt32(1, handler->GetSession()->GetAccountId());
476
477 LoginDatabase.Execute(stmt);
478 return true;
479 }
480
481 static bool HandleAccountEmailCommand(ChatHandler* handler, std::string const& oldEmail, std::string const& password, std::string const& email, std::string const& emailConfirm)
482 {
483 if (!AccountMgr::CheckEmail(handler->GetSession()->GetAccountId(), oldEmail))
484 {
486 sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
487 handler->SetSentErrorMessage(true);
488 TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] {} Tried to change email, but the provided email [{}] is not equal to registration email [{}].",
489 handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
490 handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString(),
491 email, oldEmail);
492 return false;
493 }
494
495 if (!AccountMgr::CheckPassword(handler->GetSession()->GetAccountId(), password))
496 {
498 sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
499 handler->SetSentErrorMessage(true);
500 TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] {} Tried to change email, but the provided password is wrong.",
501 handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
502 handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString());
503 return false;
504 }
505
506 if (email == oldEmail)
507 {
509 sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
510 handler->SetSentErrorMessage(true);
511 return false;
512 }
513
514 if (email != emailConfirm)
515 {
517 sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
518 handler->SetSentErrorMessage(true);
519 TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] {} Tried to change email, but the confirm email does not match.",
520 handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
521 handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString());
522 return false;
523 }
524
525 AccountOpResult result = AccountMgr::ChangeEmail(handler->GetSession()->GetAccountId(), email);
526 switch (result)
527 {
530 sScriptMgr->OnEmailChange(handler->GetSession()->GetAccountId());
531 TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] {} Changed Email from [{}] to [{}].",
532 handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
533 handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString(),
534 oldEmail, email);
535 break;
538 sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
539 handler->SetSentErrorMessage(true);
540 return false;
541 default:
543 handler->SetSentErrorMessage(true);
544 return false;
545 }
546
547 return true;
548 }
549
550 static bool HandleAccountPasswordCommand(ChatHandler* handler, std::string const& oldPassword, std::string const& newPassword, std::string const& confirmPassword, Optional<std::string> const& confirmEmail)
551 {
552 // First, we check config. What security type (sec type) is it ? Depending on it, the command branches out
553 uint32 const pwConfig = sWorld->getIntConfig(CONFIG_ACC_PASSCHANGESEC); // 0 - PW_NONE, 1 - PW_EMAIL, 2 - PW_RBAC
554
555 // We compare the old, saved password to the entered old password - no chance for the unauthorized.
556 if (!AccountMgr::CheckPassword(handler->GetSession()->GetAccountId(), oldPassword))
557 {
559 sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
560 handler->SetSentErrorMessage(true);
561 TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] {} Tried to change password, but the provided old password is wrong.",
562 handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
563 handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString());
564 return false;
565 }
566
567 // This compares the old, current email to the entered email - however, only...
568 if ((pwConfig == PW_EMAIL || (pwConfig == PW_RBAC && handler->HasPermission(rbac::RBAC_PERM_EMAIL_CONFIRM_FOR_PASS_CHANGE))) // ...if either PW_EMAIL or PW_RBAC with the Permission is active...
569 && !AccountMgr::CheckEmail(handler->GetSession()->GetAccountId(), confirmEmail.value_or(""))) // ... and returns false if the comparison fails.
570 {
572 sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
573 handler->SetSentErrorMessage(true);
574 TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] {} Tried to change password, but the entered email [{}] is wrong.",
575 handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
576 handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString(),
577 confirmEmail.value_or(""));
578 return false;
579 }
580
581 // Making sure that newly entered password is correctly entered.
582 if (newPassword != confirmPassword)
583 {
585 sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
586 handler->SetSentErrorMessage(true);
587 return false;
588 }
589
590 // Changes password and prints result.
591 AccountOpResult result = AccountMgr::ChangePassword(handler->GetSession()->GetAccountId(), newPassword);
592 switch (result)
593 {
596 sScriptMgr->OnPasswordChange(handler->GetSession()->GetAccountId());
597 TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] {} changed password.",
598 handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
599 handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString());
600 break;
603 sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
604 handler->SetSentErrorMessage(true);
605 return false;
606 default:
608 handler->SetSentErrorMessage(true);
609 return false;
610 }
611
612 return true;
613 }
614
615 static bool HandleAccountCommand(ChatHandler* handler)
616 {
617 // GM Level
618 AccountTypes securityLevel = handler->GetSession()->GetSecurity();
619 handler->PSendSysMessage(LANG_ACCOUNT_LEVEL, uint32(securityLevel));
620
621 // Security level required
622 bool hasRBAC = (handler->HasPermission(rbac::RBAC_PERM_EMAIL_CONFIRM_FOR_PASS_CHANGE) ? true : false);
623 uint32 pwConfig = sWorld->getIntConfig(CONFIG_ACC_PASSCHANGESEC); // 0 - PW_NONE, 1 - PW_EMAIL, 2 - PW_RBAC
624
625 handler->PSendSysMessage(LANG_ACCOUNT_SEC_TYPE, (pwConfig == PW_NONE ? "Lowest level: No Email input required." :
626 pwConfig == PW_EMAIL ? "Highest level: Email input required." :
627 pwConfig == PW_RBAC ? "Special level: Your account may require email input depending on settings. That is the case if another line is printed." :
628 "Unknown security level: Config error?"));
629
630 // RBAC required display - is not displayed for console
631 if (pwConfig == PW_RBAC && handler->GetSession() && hasRBAC)
633
634 // Email display if sufficient rights
636 {
637 std::string emailoutput;
638 uint32 accountId = handler->GetSession()->GetAccountId();
639
641 stmt->setUInt32(0, accountId);
642 PreparedQueryResult result = LoginDatabase.Query(stmt);
643
644 if (result)
645 {
646 emailoutput = (*result)[0].GetString();
647 handler->PSendSysMessage(LANG_COMMAND_EMAIL_OUTPUT, emailoutput.c_str());
648 }
649 }
650
651 return true;
652 }
653
655 static bool HandleAccountSetAddonCommand(ChatHandler* handler, Optional<std::string> accountName, uint8 expansion)
656 {
657 uint32 accountId;
658 if (accountName)
659 {
661 if (!Utf8ToUpperOnlyLatin(*accountName))
662 {
663 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName->c_str());
664 handler->SetSentErrorMessage(true);
665 return false;
666 }
667
668 accountId = AccountMgr::GetId(*accountName);
669 if (!accountId)
670 {
671 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName->c_str());
672 handler->SetSentErrorMessage(true);
673 return false;
674 }
675
676 }
677 else
678 {
679 Player* player = handler->getSelectedPlayer();
680 if (!player)
681 return false;
682
683 accountId = player->GetSession()->GetAccountId();
684 accountName.emplace();
685 AccountMgr::GetName(accountId, *accountName);
686 }
687
688 // Let set addon state only for lesser (strong) security level
689 // or to self account
690 if (handler->GetSession() && handler->GetSession()->GetAccountId() != accountId &&
691 handler->HasLowerSecurityAccount(nullptr, accountId, true))
692 return false;
693
694 if (expansion > sWorld->getIntConfig(CONFIG_EXPANSION))
695 return false;
696
698
699 stmt->setUInt8(0, expansion);
700 stmt->setUInt32(1, accountId);
701
702 LoginDatabase.Execute(stmt);
703
704 handler->PSendSysMessage(LANG_ACCOUNT_SETADDON, accountName->c_str(), accountId, expansion);
705 return true;
706 }
707
708 static bool HandleAccountSetSecLevelCommand(ChatHandler* handler, Optional<std::string> accountName, uint8 securityLevel, Optional<int32> realmId)
709 {
710 uint32 accountId;
711 if (accountName)
712 {
713 if (!Utf8ToUpperOnlyLatin(*accountName))
714 {
715 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName->c_str());
716 handler->SetSentErrorMessage(true);
717 return false;
718 }
719
720 accountId = AccountMgr::GetId(*accountName);
721 if (!accountId)
722 {
723 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName->c_str());
724 handler->SetSentErrorMessage(true);
725 return false;
726 }
727 }
728 else
729 {
730 Player* player = handler->getSelectedPlayer();
731 if (!player)
732 return false;
733 accountId = player->GetSession()->GetAccountId();
734 accountName.emplace();
735 AccountMgr::GetName(accountId, *accountName);
736 }
737
738 if (securityLevel >= SEC_CONSOLE)
739 {
741 handler->SetSentErrorMessage(true);
742 return false;
743 }
744
745 int32 realmID = -1;
746 if (realmId)
747 realmID = *realmId;
748
749 uint32 playerSecurity;
750 if (handler->IsConsole())
751 playerSecurity = SEC_CONSOLE;
752 else
753 playerSecurity = AccountMgr::GetSecurity(handler->GetSession()->GetAccountId(), realmID);
754
755 // can set security level only for target with less security and to less security that we have
756 // This also restricts setting handler's own security.
757 uint32 targetSecurity = AccountMgr::GetSecurity(accountId, realmID);
758 if (targetSecurity >= playerSecurity || securityLevel >= playerSecurity)
759 {
761 handler->SetSentErrorMessage(true);
762 return false;
763 }
764
765 // Check and abort if the target gm has a higher rank on one of the realms and the new realm is -1
766 if (realmID == -1 && !AccountMgr::IsConsoleAccount(playerSecurity))
767 {
769
770 stmt->setUInt32(0, accountId);
771 stmt->setUInt8(1, securityLevel);
772
773 PreparedQueryResult result = LoginDatabase.Query(stmt);
774
775 if (result)
776 {
778 handler->SetSentErrorMessage(true);
779 return false;
780 }
781 }
782
783 // Check if provided realmID has a negative value other than -1
784 if (realmID < -1)
785 {
787 handler->SetSentErrorMessage(true);
788 return false;
789 }
790
791 WorldSession const* session = sWorld->FindSession(accountId);
792 sAccountMgr->UpdateAccountAccess(session ? session->GetRBACData() : nullptr, accountId, securityLevel, realmID);
793
794 handler->PSendSysMessage(LANG_YOU_CHANGE_SECURITY, accountName->c_str(), securityLevel);
795 return true;
796 }
797
799 static bool HandleAccountSetPasswordCommand(ChatHandler* handler, std::string accountName, std::string const& password, std::string const& confirmPassword)
800 {
801 if (!Utf8ToUpperOnlyLatin(accountName))
802 {
803 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
804 handler->SetSentErrorMessage(true);
805 return false;
806 }
807
808 uint32 targetAccountId = AccountMgr::GetId(accountName);
809 if (!targetAccountId)
810 {
811 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
812 handler->SetSentErrorMessage(true);
813 return false;
814 }
815
818 if (handler->HasLowerSecurityAccount(nullptr, targetAccountId, true))
819 return false;
820
821 if (password != confirmPassword)
822 {
824 handler->SetSentErrorMessage(true);
825 return false;
826 }
827
828 AccountOpResult result = AccountMgr::ChangePassword(targetAccountId, password);
829 switch (result)
830 {
833 break;
835 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
836 handler->SetSentErrorMessage(true);
837 return false;
840 handler->SetSentErrorMessage(true);
841 return false;
842 default:
844 handler->SetSentErrorMessage(true);
845 return false;
846 }
847 return true;
848 }
849
850 static bool HandleAccountSet2FACommand(ChatHandler* handler, std::string accountName, std::string secret)
851 {
852 if (!Utf8ToUpperOnlyLatin(accountName))
853 {
854 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
855 handler->SetSentErrorMessage(true);
856 return false;
857 }
858
859 uint32 targetAccountId = AccountMgr::GetId(accountName);
860 if (!targetAccountId)
861 {
862 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
863 handler->SetSentErrorMessage(true);
864 return false;
865 }
866
867 if (handler->HasLowerSecurityAccount(nullptr, targetAccountId, true))
868 return false;
869
870 if (secret == "off")
871 {
873 stmt->setNull(0);
874 stmt->setUInt32(1, targetAccountId);
875 LoginDatabase.Execute(stmt);
877 return true;
878 }
879
880 auto const& masterKey = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY);
881 if (!masterKey.IsAvailable())
882 {
884 handler->SetSentErrorMessage(true);
885 return false;
886 }
887
889 if (!decoded)
890 {
892 handler->SetSentErrorMessage(true);
893 return false;
894 }
896 {
898 handler->SetSentErrorMessage(true);
899 return false;
900 }
901
902 if (masterKey)
903 Trinity::Crypto::AEEncryptWithRandomIV<Trinity::Crypto::AES>(*decoded, *masterKey);
904
906 stmt->setBinary(0, *decoded);
907 stmt->setUInt32(1, targetAccountId);
908 LoginDatabase.Execute(stmt);
909 handler->PSendSysMessage(LANG_2FA_SECRET_SET_COMPLETE, accountName.c_str());
910 return true;
911 }
912
914 static bool HandleAccountSetEmailCommand(ChatHandler* handler, std::string accountName, std::string const& email, std::string const& confirmEmail)
915 {
916 if (!Utf8ToUpperOnlyLatin(accountName))
917 {
918 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
919 handler->SetSentErrorMessage(true);
920 return false;
921 }
922
923 uint32 targetAccountId = AccountMgr::GetId(accountName);
924 if (!targetAccountId)
925 {
926 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
927 handler->SetSentErrorMessage(true);
928 return false;
929 }
930
933 if (handler->HasLowerSecurityAccount(nullptr, targetAccountId, true))
934 return false;
935
936 if (email != confirmEmail)
937 {
939 handler->SetSentErrorMessage(true);
940 return false;
941 }
942
943 AccountOpResult result = AccountMgr::ChangeEmail(targetAccountId, email);
944 switch (result)
945 {
948 TC_LOG_INFO("entities.player.character", "ChangeEmail: Account {} [Id: {}] had it's email changed to {}.",
949 accountName, targetAccountId, email);
950 break;
952 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
953 handler->SetSentErrorMessage(true);
954 return false;
957 handler->SetSentErrorMessage(true);
958 return false;
959 default:
961 handler->SetSentErrorMessage(true);
962 return false;
963 }
964
965 return true;
966 }
967
969 static bool HandleAccountSetRegEmailCommand(ChatHandler* handler, std::string accountName, std::string const& email, std::string const& confirmEmail)
970 {
971 if (!Utf8ToUpperOnlyLatin(accountName))
972 {
973 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
974 handler->SetSentErrorMessage(true);
975 return false;
976 }
977
978 uint32 targetAccountId = AccountMgr::GetId(accountName);
979 if (!targetAccountId)
980 {
981 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
982 handler->SetSentErrorMessage(true);
983 return false;
984 }
985
988 if (handler->HasLowerSecurityAccount(nullptr, targetAccountId, true))
989 return false;
990
991 if (email != confirmEmail)
992 {
994 handler->SetSentErrorMessage(true);
995 return false;
996 }
997
998 AccountOpResult result = AccountMgr::ChangeRegEmail(targetAccountId, email);
999 switch (result)
1000 {
1003 TC_LOG_INFO("entities.player.character", "ChangeRegEmail: Account {} [Id: {}] had it's Registration Email changed to {}.",
1004 accountName, targetAccountId, email);
1005 break;
1007 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
1008 handler->SetSentErrorMessage(true);
1009 return false;
1012 handler->SetSentErrorMessage(true);
1013 return false;
1014 default:
1016 handler->SetSentErrorMessage(true);
1017 return false;
1018 }
1019
1020 return true;
1021 }
1022};
1023
@ PW_EMAIL
Definition AccountMgr.h:37
@ PW_RBAC
Definition AccountMgr.h:38
@ PW_NONE
Definition AccountMgr.h:36
AccountOpResult
Definition AccountMgr.h:24
#define sAccountMgr
Definition AccountMgr.h:96
AccountTypes
Definition Common.h:39
@ SEC_CONSOLE
Definition Common.h:44
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
uint8_t uint8
Definition Define.h:135
int32_t int32
Definition Define.h:129
uint32_t uint32
Definition Define.h:133
#define sIPLocation
Definition IPLocation.h:48
@ LANG_ACCOUNT_ALREADY_EXIST
Definition Language.h:843
@ LANG_COMMAND_ACC_LOCK_COUNTRY_HELP
Definition Language.h:434
@ LANG_COMMAND_EMAIL
Definition Language.h:823
@ LANG_2FA_ALREADY_SETUP
Definition Language.h:121
@ LANG_COMMAND_ACC_SET_2FA_HELP
Definition Language.h:424
@ LANG_ACCOUNT_USE_BNET_COMMANDS
Definition Language.h:867
@ LANG_2FA_SECRET_TOO_LONG
Definition Language.h:231
@ LANG_COMMAND_NOTCHANGEPASSWORD
Definition Language.h:57
@ LANG_COMMAND_ACCLOCKLOCKED
Definition Language.h:60
@ LANG_COMMAND_ACC_SET_SEC_REGMAIL_HELP
Definition Language.h:422
@ LANG_COMMAND_EMAIL_OUTPUT
Definition Language.h:827
@ LANG_ACCOUNT_PASS_TOO_LONG
Definition Language.h:868
@ LANG_IMPROPER_VALUE
Definition Language.h:94
@ LANG_ACCOUNT_ADDON
Definition Language.h:93
@ LANG_PASSWORD_TOO_LONG
Definition Language.h:87
@ LANG_COMMAND_ACC_EMAIL_HELP
Definition Language.h:432
@ LANG_ACCOUNT_LIST_EMPTY
Definition Language.h:851
@ LANG_YOU_CHANGE_SECURITY
Definition Language.h:458
@ LANG_COMMAND_ACC_2FA_REMOVE_HELP
Definition Language.h:428
@ LANG_ACCOUNT_LIST_LINE
Definition Language.h:850
@ LANG_2FA_SECRET_INVALID
Definition Language.h:232
@ LANG_COMMAND_WRONGOLDPASSWORD
Definition Language.h:59
@ LANG_ACCOUNT_NOT_DELETED_SQL_ERROR
Definition Language.h:839
@ LANG_ACCOUNT_LIST_HEADER
Definition Language.h:847
@ LANG_COMMAND_ACC_SET_ADDON_HELP
Definition Language.h:421
@ LANG_UNKNOWN_ERROR
Definition Language.h:119
@ LANG_COMMAND_ACCOUNT_HELP
Definition Language.h:437
@ LANG_2FA_REMOVE_NEED_TOKEN
Definition Language.h:126
@ LANG_2FA_NOT_SETUP
Definition Language.h:125
@ LANG_COMMAND_ACC_PASSWORD_HELP
Definition Language.h:436
@ LANG_2FA_INVALID_TOKEN
Definition Language.h:122
@ LANG_ACCOUNT_SETADDON
Definition Language.h:876
@ LANG_COMMAND_WRONGEMAIL
Definition Language.h:821
@ LANG_COMMAND_ACC_ADDON_HELP
Definition Language.h:429
@ LANG_ACCOUNT_LIST_BAR
Definition Language.h:849
@ LANG_COMMAND_ACC_DELETE_HELP
Definition Language.h:431
@ LANG_COMMAND_ACCLOCKUNLOCKED
Definition Language.h:61
@ LANG_COMMAND_ACC_2FA_SETUP_HELP
Definition Language.h:427
@ LANG_COMMAND_ACC_CREATE_HELP
Definition Language.h:430
@ LANG_ACCOUNT_SEC_TYPE
Definition Language.h:829
@ LANG_NEW_EMAILS_NOT_MATCH
Definition Language.h:822
@ LANG_ACCOUNT_NOT_CREATED_SQL_ERROR
Definition Language.h:844
@ LANG_2FA_COMMANDS_NOT_SETUP
Definition Language.h:120
@ LANG_2FA_SECRET_SET_COMPLETE
Definition Language.h:233
@ LANG_ACCOUNT_NOT_CREATED
Definition Language.h:845
@ LANG_COMMAND_NOTCHANGEEMAIL
Definition Language.h:825
@ LANG_YOURS_SECURITY_IS_LOW
Definition Language.h:460
@ LANG_INVALID_REALMID
Definition Language.h:1218
@ LANG_ACCOUNT_CREATED
Definition Language.h:841
@ LANG_BAD_VALUE
Definition Language.h:149
@ LANG_NEW_PASSWORDS_NOT_MATCH
Definition Language.h:86
@ LANG_2FA_SECRET_SUGGESTION
Definition Language.h:123
@ LANG_OLD_EMAIL_IS_NEW_EMAIL
Definition Language.h:826
@ LANG_ACCOUNT_NOT_EXIST
Definition Language.h:473
@ LANG_ACCOUNT_LIST_BAR_HEADER
Definition Language.h:852
@ LANG_2FA_SETUP_COMPLETE
Definition Language.h:124
@ LANG_COMMAND_ACC_SET_SEC_EMAIL_HELP
Definition Language.h:423
@ LANG_COMMAND_ACC_SET_PASSWORD_HELP
Definition Language.h:426
@ LANG_ACCOUNT_NOT_DELETED
Definition Language.h:840
@ LANG_COMMAND_ACC_SET_SECLEVEL_HELP
Definition Language.h:425
@ LANG_ACCOUNT_LEVEL
Definition Language.h:43
@ LANG_RBAC_EMAIL_REQUIRED
Definition Language.h:830
@ LANG_ACCOUNT_DELETED
Definition Language.h:838
@ LANG_COMMAND_PASSWORD
Definition Language.h:58
@ LANG_EMAIL_TOO_LONG
Definition Language.h:824
@ LANG_COMMAND_ACC_LOCK_IP_HELP
Definition Language.h:435
@ LANG_ACCOUNT_NAME_TOO_LONG
Definition Language.h:842
@ LANG_2FA_REMOVE_COMPLETE
Definition Language.h:127
@ LANG_COMMAND_ACC_ONLINELIST_HELP
Definition Language.h:433
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition Log.h:159
@ LOGIN_GET_EMAIL_BY_ID
@ LOGIN_SEL_ACCOUNT_ACCESS_SECLEVEL_TEST
@ LOGIN_UPD_ACCOUNT_TOTP_SECRET
@ LOGIN_UPD_EXPANSION
@ LOGIN_UPD_ACCOUNT_LOCK_COUNTRY
@ LOGIN_SEL_ACCOUNT_TOTP_SECRET
@ LOGIN_UPD_ACCOUNT_LOCK
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define sScriptMgr
Definition ScriptMgr.h:1168
#define sSecretMgr
Definition SecretMgr.h:73
@ SECRET_TOTP_MASTER_KEY
Definition SecretMgr.h:31
bool Utf8ToUpperOnlyLatin(std::string &utf8String)
Definition Util.cpp:610
static AccountOpResult ChangeRegEmail(uint32 accountId, std::string newEmail)
static AccountOpResult DeleteAccount(uint32 accountId)
static bool CheckPassword(uint32 accountId, std::string password)
static AccountOpResult ChangeEmail(uint32 accountId, std::string newEmail)
static uint32 GetSecurity(uint32 accountId, int32 realmId)
static AccountOpResult ChangePassword(uint32 accountId, std::string newPassword)
static bool IsConsoleAccount(uint32 gmlevel)
static bool CheckEmail(uint32 accountId, std::string newEmail)
static uint32 GetId(std::string_view username)
static bool GetName(uint32 accountId, std::string &name)
virtual bool HasPermission(uint32 permission) const
Definition Chat.cpp:41
Player * getSelectedPlayer()
Definition Chat.cpp:302
WorldSession * GetSession()
Definition Chat.h:46
void SetSentErrorMessage(bool val)
Definition Chat.h:134
void PSendSysMessage(char const *fmt, Args &&... args)
Definition Chat.h:69
virtual void SendSysMessage(std::string_view str, bool escapeCharacters=false)
Definition Chat.cpp:101
bool IsConsole() const
Definition Chat.h:45
bool HasLowerSecurityAccount(WorldSession *target, uint32 account, bool strong=false)
Definition Chat.cpp:71
Class used to access individual fields of database query result.
Definition Field.h:92
bool IsNull() const
Definition Field.h:128
std::vector< uint8 > GetBinary() const
Definition Field.cpp:149
std::string ToString() const
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
WorldSession * GetSession() const
Definition Player.h:1719
void setUInt32(uint8 index, uint32 value)
void setBool(uint8 index, bool value)
void setUInt8(uint8 index, uint8 value)
void setBinary(uint8 index, std::vector< uint8 > const &value)
void setString(uint8 index, std::string const &value)
static constexpr size_t IV_SIZE_BYTES
Definition AES.h:30
static constexpr size_t TAG_SIZE_BYTES
Definition AES.h:32
uint32 GetMapId() const
Definition Position.h:193
std::string const & GetName() const
Definition Object.h:382
uint32 GetZoneId() const
Definition Object.h:373
Player session in the World.
uint8 Expansion() const
AccountTypes GetSecurity() const
rbac::RBACData * GetRBACData() const
Player * GetPlayer() const
std::string const & GetAccountName() const
std::string const & GetRemoteAddress() const
uint32 GetAccountId() const
std::string const & GetPlayerName() const
static bool HandleAccountPasswordCommand(ChatHandler *handler, std::string const &oldPassword, std::string const &newPassword, std::string const &confirmPassword, Optional< std::string > const &confirmEmail)
static bool HandleAccount2FASetupCommand(ChatHandler *handler, Optional< uint32 > token)
ChatCommandTable GetCommands() const override
static bool HandleAccountCreateCommand(ChatHandler *handler, std::string const &accountName, std::string const &password, Optional< std::string > const &email)
Create an account.
static bool HandleAccountSetRegEmailCommand(ChatHandler *handler, std::string accountName, std::string const &email, std::string const &confirmEmail)
Change registration email for account.
static bool HandleAccountSetEmailCommand(ChatHandler *handler, std::string accountName, std::string const &email, std::string const &confirmEmail)
Set normal email for account.
static bool HandleAccountSetPasswordCommand(ChatHandler *handler, std::string accountName, std::string const &password, std::string const &confirmPassword)
Set password for account.
static bool HandleAccountSet2FACommand(ChatHandler *handler, std::string accountName, std::string secret)
static bool HandleAccountAddonCommand(ChatHandler *handler, uint8 expansion)
static bool HandleAccountOnlineListWithMapFilterCommand(ChatHandler *handler, uint32 mapId)
static bool HandleAccountEmailCommand(ChatHandler *handler, std::string const &oldEmail, std::string const &password, std::string const &email, std::string const &emailConfirm)
static bool HandleAccountOnlineListCommandWithParameters(ChatHandler *handler, Optional< std::string > ipAddress, Optional< uint32 > limit, Optional< uint32 > mapId, Optional< uint32 > zoneId)
static bool HandleAccountLockIpCommand(ChatHandler *handler, bool state)
static bool HandleAccount2FARemoveCommand(ChatHandler *handler, Optional< uint32 > token)
static bool HandleAccountOnlineListWithZoneFilterCommand(ChatHandler *handler, uint32 zoneId)
static bool HandleAccountOnlineListCommand(ChatHandler *handler)
Display info on users currently in the realm.
static bool HandleAccountOnlineListWithIpFilterCommand(ChatHandler *handler, std::string ipAddress)
static bool HandleAccountLockCountryCommand(ChatHandler *handler, bool state)
static bool HandleAccountSetAddonCommand(ChatHandler *handler, Optional< std::string > accountName, uint8 expansion)
Set/Unset the expansion level for an account.
static bool HandleAccountOnlineListWithLimitCommand(ChatHandler *handler, uint32 limit)
static bool HandleAccountDeleteCommand(ChatHandler *handler, std::string accountName)
static bool HandleAccountCommand(ChatHandler *handler)
static bool HandleAccountSetSecLevelCommand(ChatHandler *handler, Optional< std::string > accountName, uint8 securityLevel, Optional< int32 > realmId)
void AddSC_account_commandscript()
#define sWorld
Definition World.h:900
std::unordered_map< uint32, WorldSession * > SessionMap
Definition World.h:553
@ CONFIG_ACC_PASSCHANGESEC
Definition World.h:374
@ CONFIG_EXPANSION
Definition World.h:282
std::vector< ChatCommandBuilder > ChatCommandTable
Definition ChatCommand.h:50
std::array< uint8, S > GetRandomBytes()
@ RBAC_PERM_MAY_CHECK_OWN_EMAIL
Definition RBAC.h:103
@ RBAC_PERM_COMMAND_ACCOUNT_DELETE
Definition RBAC.h:136
@ RBAC_PERM_COMMAND_ACCOUNT_2FA_REMOVE
Definition RBAC.h:254
@ RBAC_PERM_COMMAND_ACCOUNT_SET_ADDON
Definition RBAC.h:143
@ RBAC_PERM_COMMAND_ACCOUNT_SET_SECLEVEL
Definition RBAC.h:144
@ RBAC_PERM_COMMAND_ACCOUNT_ADDON
Definition RBAC.h:134
@ RBAC_PERM_COMMAND_ACCOUNT_ONLINE_LIST
Definition RBAC.h:140
@ RBAC_PERM_COMMAND_ACCOUNT
Definition RBAC.h:133
@ RBAC_PERM_COMMAND_ACCOUNT_2FA_SETUP
Definition RBAC.h:253
@ RBAC_PERM_COMMAND_ACCOUNT_CREATE
Definition RBAC.h:135
@ RBAC_PERM_COMMAND_ACCOUNT_SET_SEC_EMAIL
Definition RBAC.h:181
@ RBAC_PERM_COMMAND_ACCOUNT_EMAIL
Definition RBAC.h:179
@ RBAC_PERM_COMMAND_ACCOUNT_PASSWORD
Definition RBAC.h:141
@ RBAC_PERM_COMMAND_ACCOUNT_LOCK_COUNTRY
Definition RBAC.h:138
@ RBAC_PERM_COMMAND_ACCOUNT_LOCK_IP
Definition RBAC.h:139
@ RBAC_PERM_COMMAND_ACCOUNT_SET_2FA
Definition RBAC.h:255
@ RBAC_PERM_COMMAND_ACCOUNT_SET_PASSWORD
Definition RBAC.h:145
@ RBAC_PERM_EMAIL_CONFIRM_FOR_PASS_CHANGE
Definition RBAC.h:102
@ RBAC_PERM_COMMAND_ACCOUNT_SET_SEC_REGMAIL
Definition RBAC.h:182
std::vector< uint8 > Secret
Definition TOTP.h:30
static bool ValidateToken(Secret const &key, uint32 token)
Definition TOTP.cpp:43
static constexpr size_t RECOMMENDED_SECRET_LENGTH
Definition TOTP.h:29
static Optional< std::vector< uint8 > > Decode(std::string const &data)
Definition Base32.cpp:52
static std::string Encode(std::vector< uint8 > const &data)
Definition Base32.cpp:47