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 "ChatCommand.h"
30#include "CryptoGenerics.h"
31#include "CryptoRandom.h"
32#include "DatabaseEnv.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
51 std::span<ChatCommandBuilder const> GetCommands() const override
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.c_str());
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;
280 handler->PSendSysMessage(LANG_ACCOUNT_NOT_CREATED_SQL_ERROR, accountName.c_str());
281 handler->SetSentErrorMessage(true);
282 return false;
283 default:
284 handler->PSendSysMessage(LANG_ACCOUNT_NOT_CREATED, accountName.c_str());
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_view 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->GetAccountExpansion(),
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"sv);
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 uint32 accountId = handler->GetSession()->GetAccountId();
638
640 stmt->setUInt32(0, accountId);
641 PreparedQueryResult result = LoginDatabase.Query(stmt);
642
643 if (result)
644 handler->PSendSysMessage(LANG_COMMAND_EMAIL_OUTPUT, (*result)[0].GetCString());
645 }
646
647 return true;
648 }
649
651 static bool HandleAccountSetAddonCommand(ChatHandler* handler, Optional<std::string>& accountName, uint8 expansion)
652 {
653 uint32 accountId;
654 if (accountName)
655 {
657 if (!Utf8ToUpperOnlyLatin(*accountName))
658 {
659 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName->c_str());
660 handler->SetSentErrorMessage(true);
661 return false;
662 }
663
664 accountId = AccountMgr::GetId(*accountName);
665 if (!accountId)
666 {
667 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName->c_str());
668 handler->SetSentErrorMessage(true);
669 return false;
670 }
671 }
672 else
673 {
674 Player* player = handler->getSelectedPlayer();
675 if (!player)
676 return false;
677
678 accountId = player->GetSession()->GetAccountId();
679 AccountMgr::GetName(accountId, accountName.emplace());
680 }
681
682 // Let set addon state only for lesser (strong) security level
683 // or to self account
684 if (handler->GetSession() && handler->GetSession()->GetAccountId() != accountId &&
685 handler->HasLowerSecurityAccount(nullptr, accountId, true))
686 return false;
687
688 if (expansion > sWorld->getIntConfig(CONFIG_EXPANSION))
689 return false;
690
692
693 stmt->setUInt8(0, expansion);
694 stmt->setUInt32(1, accountId);
695
696 LoginDatabase.Execute(stmt);
697
698 handler->PSendSysMessage(LANG_ACCOUNT_SETADDON, accountName->c_str(), accountId, expansion);
699 return true;
700 }
701
702 static bool HandleAccountSetSecLevelCommand(ChatHandler* handler, Optional<std::string>& accountName, uint8 securityLevel, Optional<int32> realmId)
703 {
704 uint32 accountId;
705 if (accountName)
706 {
707 if (!Utf8ToUpperOnlyLatin(*accountName))
708 {
709 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName->c_str());
710 handler->SetSentErrorMessage(true);
711 return false;
712 }
713
714 accountId = AccountMgr::GetId(*accountName);
715 if (!accountId)
716 {
717 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName->c_str());
718 handler->SetSentErrorMessage(true);
719 return false;
720 }
721 }
722 else
723 {
724 Player* player = handler->getSelectedPlayer();
725 if (!player)
726 return false;
727 accountId = player->GetSession()->GetAccountId();
728 accountName.emplace();
729 AccountMgr::GetName(accountId, *accountName);
730 }
731
732 if (securityLevel >= SEC_CONSOLE)
733 {
735 handler->SetSentErrorMessage(true);
736 return false;
737 }
738
739 int32 realmID = -1;
740 if (realmId)
741 realmID = *realmId;
742
743 uint32 playerSecurity;
744 if (handler->IsConsole())
745 playerSecurity = SEC_CONSOLE;
746 else
747 playerSecurity = AccountMgr::GetSecurity(handler->GetSession()->GetAccountId(), realmID);
748
749 // can set security level only for target with less security and to less security that we have
750 // This also restricts setting handler's own security.
751 uint32 targetSecurity = AccountMgr::GetSecurity(accountId, realmID);
752 if (targetSecurity >= playerSecurity || securityLevel >= playerSecurity)
753 {
755 handler->SetSentErrorMessage(true);
756 return false;
757 }
758
759 // Check and abort if the target gm has a higher rank on one of the realms and the new realm is -1
760 if (realmID == -1 && !AccountMgr::IsConsoleAccount(playerSecurity))
761 {
763
764 stmt->setUInt32(0, accountId);
765 stmt->setUInt8(1, securityLevel);
766
767 PreparedQueryResult result = LoginDatabase.Query(stmt);
768
769 if (result)
770 {
772 handler->SetSentErrorMessage(true);
773 return false;
774 }
775 }
776
777 // Check if provided realmID has a negative value other than -1
778 if (realmID < -1)
779 {
781 handler->SetSentErrorMessage(true);
782 return false;
783 }
784
785 sAccountMgr->UpdateAccountAccess(nullptr, accountId, securityLevel, realmID);
786
787 handler->PSendSysMessage(LANG_YOU_CHANGE_SECURITY, accountName->c_str(), securityLevel);
788 return true;
789 }
790
792 static bool HandleAccountSetPasswordCommand(ChatHandler* handler, std::string& accountName, std::string const& password, std::string const& confirmPassword)
793 {
794 if (!Utf8ToUpperOnlyLatin(accountName))
795 {
796 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
797 handler->SetSentErrorMessage(true);
798 return false;
799 }
800
801 uint32 targetAccountId = AccountMgr::GetId(accountName);
802 if (!targetAccountId)
803 {
804 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
805 handler->SetSentErrorMessage(true);
806 return false;
807 }
808
811 if (handler->HasLowerSecurityAccount(nullptr, targetAccountId, true))
812 return false;
813
814 if (password != confirmPassword)
815 {
817 handler->SetSentErrorMessage(true);
818 return false;
819 }
820
821 AccountOpResult result = AccountMgr::ChangePassword(targetAccountId, password);
822 switch (result)
823 {
826 break;
828 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
829 handler->SetSentErrorMessage(true);
830 return false;
833 handler->SetSentErrorMessage(true);
834 return false;
835 default:
837 handler->SetSentErrorMessage(true);
838 return false;
839 }
840 return true;
841 }
842
843 static bool HandleAccountSet2FACommand(ChatHandler* handler, std::string& accountName, std::string_view secret)
844 {
845 if (!Utf8ToUpperOnlyLatin(accountName))
846 {
847 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
848 handler->SetSentErrorMessage(true);
849 return false;
850 }
851
852 uint32 targetAccountId = AccountMgr::GetId(accountName);
853 if (!targetAccountId)
854 {
855 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
856 handler->SetSentErrorMessage(true);
857 return false;
858 }
859
860 if (handler->HasLowerSecurityAccount(nullptr, targetAccountId, true))
861 return false;
862
863 if (secret == "off")
864 {
866 stmt->setNull(0);
867 stmt->setUInt32(1, targetAccountId);
868 LoginDatabase.Execute(stmt);
870 return true;
871 }
872
873 auto const& masterKey = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY);
874 if (!masterKey.IsAvailable())
875 {
877 handler->SetSentErrorMessage(true);
878 return false;
879 }
880
882 if (!decoded)
883 {
885 handler->SetSentErrorMessage(true);
886 return false;
887 }
889 {
891 handler->SetSentErrorMessage(true);
892 return false;
893 }
894
895 if (masterKey)
896 Trinity::Crypto::AEEncryptWithRandomIV<Trinity::Crypto::AES>(*decoded, *masterKey);
897
899 stmt->setBinary(0, std::move(*decoded));
900 stmt->setUInt32(1, targetAccountId);
901 LoginDatabase.Execute(stmt);
902 handler->PSendSysMessage(LANG_2FA_SECRET_SET_COMPLETE, accountName.c_str());
903 return true;
904 }
905
907 static bool HandleAccountSetEmailCommand(ChatHandler* handler, std::string& accountName, std::string const& email, std::string const& confirmEmail)
908 {
909 if (!Utf8ToUpperOnlyLatin(accountName))
910 {
911 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
912 handler->SetSentErrorMessage(true);
913 return false;
914 }
915
916 uint32 targetAccountId = AccountMgr::GetId(accountName);
917 if (!targetAccountId)
918 {
919 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
920 handler->SetSentErrorMessage(true);
921 return false;
922 }
923
926 if (handler->HasLowerSecurityAccount(nullptr, targetAccountId, true))
927 return false;
928
929 if (email != confirmEmail)
930 {
932 handler->SetSentErrorMessage(true);
933 return false;
934 }
935
936 AccountOpResult result = AccountMgr::ChangeEmail(targetAccountId, email);
937 switch (result)
938 {
941 TC_LOG_INFO("entities.player.character", "ChangeEmail: Account {} [Id: {}] had it's email changed to {}.",
942 accountName, targetAccountId, email);
943 break;
945 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
946 handler->SetSentErrorMessage(true);
947 return false;
950 handler->SetSentErrorMessage(true);
951 return false;
952 default:
954 handler->SetSentErrorMessage(true);
955 return false;
956 }
957
958 return true;
959 }
960
962 static bool HandleAccountSetRegEmailCommand(ChatHandler* handler, std::string& accountName, std::string const& email, std::string const& confirmEmail)
963 {
964 if (!Utf8ToUpperOnlyLatin(accountName))
965 {
966 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
967 handler->SetSentErrorMessage(true);
968 return false;
969 }
970
971 uint32 targetAccountId = AccountMgr::GetId(accountName);
972 if (!targetAccountId)
973 {
974 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
975 handler->SetSentErrorMessage(true);
976 return false;
977 }
978
981 if (handler->HasLowerSecurityAccount(nullptr, targetAccountId, true))
982 return false;
983
984 if (email != confirmEmail)
985 {
987 handler->SetSentErrorMessage(true);
988 return false;
989 }
990
991 AccountOpResult result = AccountMgr::ChangeRegEmail(targetAccountId, email);
992 switch (result)
993 {
996 TC_LOG_INFO("entities.player.character", "ChangeRegEmail: Account {} [Id: {}] had it's Registration Email changed to {}.",
997 accountName, targetAccountId, email);
998 break;
1000 handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
1001 handler->SetSentErrorMessage(true);
1002 return false;
1005 handler->SetSentErrorMessage(true);
1006 return false;
1007 default:
1009 handler->SetSentErrorMessage(true);
1010 return false;
1011 }
1012
1013 return true;
1014 }
1015};
1016
@ PW_EMAIL
Definition AccountMgr.h:39
@ PW_RBAC
Definition AccountMgr.h:40
@ PW_NONE
Definition AccountMgr.h:38
AccountOpResult
Definition AccountMgr.h:25
#define sAccountMgr
Definition AccountMgr.h:104
AccountTypes
Definition Common.h:42
@ SEC_CONSOLE
Definition Common.h:47
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
uint8_t uint8
Definition Define.h:156
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
#define sIPLocation
Definition IPLocation.h:56
@ 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:1228
@ 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__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
@ 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:1449
#define sSecretMgr
Definition SecretMgr.h:87
@ SECRET_TOTP_MASTER_KEY
Definition SecretMgr.h:31
bool Utf8ToUpperOnlyLatin(std::string &utf8String)
Definition Util.cpp:752
static AccountOpResult ChangeRegEmail(uint32 accountId, std::string newEmail)
static AccountOpResult DeleteAccount(uint32 accountId)
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 CheckPassword(std::string username, std::string password)
static bool CheckEmail(uint32 accountId, std::string newEmail)
static uint32 GetId(std::string_view username)
static bool GetName(uint32 accountId, std::string &name)
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
virtual bool HasPermission(uint32 permission) const
Definition Chat.cpp:51
Player * getSelectedPlayer()
Definition Chat.cpp:204
WorldSession * GetSession()
Definition Chat.h:42
void SetSentErrorMessage(bool val)
Definition Chat.h:127
void PSendSysMessage(char const *fmt, Args &&... args)
Definition Chat.h:62
virtual void SendSysMessage(std::string_view str, bool escapeCharacters=false)
Definition Chat.cpp:111
bool IsConsole() const
Definition Chat.h:41
bool HasLowerSecurityAccount(WorldSession *target, uint32 account, bool strong=false)
Definition Chat.cpp:81
Class used to access individual fields of database query result.
Definition Field.h:94
std::vector< uint8 > GetBinary() const noexcept
Definition Field.cpp:125
bool IsNull() const noexcept
Definition Field.h:131
std::string ToString() const
WorldSession * GetSession() const
Definition Player.h:2272
void setBinary(uint8 index, std::vector< uint8 > &&value)
void setString(uint8 index, std::string &&value)
void setUInt32(uint8 index, uint32 value)
void setBool(uint8 index, bool value)
void setUInt8(uint8 index, uint8 value)
static constexpr size_t IV_SIZE_BYTES
Definition AES.h:31
static constexpr size_t TAG_SIZE_BYTES
Definition AES.h:33
constexpr uint32 GetMapId() const
Definition Position.h:216
std::string const & GetName() const
Definition Object.h:342
uint32 GetZoneId() const
Definition Object.h:332
Player session in the World.
AccountTypes GetSecurity() const
Player * GetPlayer() const
std::string const & GetAccountName() const
std::string const & GetRemoteAddress() const
uint32 GetAccountId() const
uint8 GetAccountExpansion() const
std::string const & GetPlayerName() const
static bool HandleAccountSetAddonCommand(ChatHandler *handler, Optional< std::string > &accountName, uint8 expansion)
Set/Unset the expansion level for an account.
static bool HandleAccountOnlineListCommandWithParameters(ChatHandler *handler, Optional< std::string_view > const &ipAddress, Optional< uint32 > limit, Optional< uint32 > mapId, Optional< uint32 > zoneId)
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)
static bool HandleAccountCreateCommand(ChatHandler *handler, std::string const &accountName, std::string const &password, Optional< std::string > const &email)
Create an account.
static bool HandleAccountSetSecLevelCommand(ChatHandler *handler, Optional< std::string > &accountName, uint8 securityLevel, Optional< int32 > realmId)
static bool HandleAccountSet2FACommand(ChatHandler *handler, std::string &accountName, std::string_view secret)
static bool HandleAccountSetPasswordCommand(ChatHandler *handler, std::string &accountName, std::string const &password, std::string const &confirmPassword)
Set password for account.
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 HandleAccountLockIpCommand(ChatHandler *handler, bool state)
static bool HandleAccount2FARemoveCommand(ChatHandler *handler, Optional< uint32 > token)
static bool HandleAccountSetEmailCommand(ChatHandler *handler, std::string &accountName, std::string const &email, std::string const &confirmEmail)
Set normal email for account.
static bool HandleAccountOnlineListWithZoneFilterCommand(ChatHandler *handler, uint32 zoneId)
std::span< ChatCommandBuilder const > GetCommands() const override
static bool HandleAccountOnlineListCommand(ChatHandler *handler)
Display info on users currently in the realm.
static bool HandleAccountLockCountryCommand(ChatHandler *handler, bool state)
static bool HandleAccountOnlineListWithLimitCommand(ChatHandler *handler, uint32 limit)
static bool HandleAccountOnlineListWithIpFilterCommand(ChatHandler *handler, std::string_view ipAddress)
static bool HandleAccountCommand(ChatHandler *handler)
static bool HandleAccountDeleteCommand(ChatHandler *handler, std::string &accountName)
static bool HandleAccountSetRegEmailCommand(ChatHandler *handler, std::string &accountName, std::string const &email, std::string const &confirmEmail)
Change registration email for account.
void AddSC_account_commandscript()
#define sWorld
Definition World.h:916
std::unordered_map< uint32, WorldSession * > SessionMap
Definition World.h:551
@ CONFIG_ACC_PASSCHANGESEC
Definition World.h:397
@ CONFIG_EXPANSION
Definition World.h:306
ChatCommandBuilder const [] ChatCommandTable
Definition ChatCommand.h:49
std::array< uint8, S > GetRandomBytes()
@ RBAC_PERM_MAY_CHECK_OWN_EMAIL
Definition RBAC.h:103
@ RBAC_PERM_COMMAND_ACCOUNT_DELETE
Definition RBAC.h:134
@ RBAC_PERM_COMMAND_ACCOUNT_2FA_REMOVE
Definition RBAC.h:252
@ RBAC_PERM_COMMAND_ACCOUNT_SET_ADDON
Definition RBAC.h:141
@ RBAC_PERM_COMMAND_ACCOUNT_SET_SECLEVEL
Definition RBAC.h:142
@ RBAC_PERM_COMMAND_ACCOUNT_ADDON
Definition RBAC.h:132
@ RBAC_PERM_COMMAND_ACCOUNT_ONLINE_LIST
Definition RBAC.h:138
@ RBAC_PERM_COMMAND_ACCOUNT
Definition RBAC.h:131
@ RBAC_PERM_COMMAND_ACCOUNT_2FA_SETUP
Definition RBAC.h:251
@ RBAC_PERM_COMMAND_ACCOUNT_CREATE
Definition RBAC.h:133
@ RBAC_PERM_COMMAND_ACCOUNT_SET_SEC_EMAIL
Definition RBAC.h:179
@ RBAC_PERM_COMMAND_ACCOUNT_EMAIL
Definition RBAC.h:177
@ RBAC_PERM_COMMAND_ACCOUNT_PASSWORD
Definition RBAC.h:139
@ RBAC_PERM_COMMAND_ACCOUNT_LOCK_COUNTRY
Definition RBAC.h:136
@ RBAC_PERM_COMMAND_ACCOUNT_LOCK_IP
Definition RBAC.h:137
@ RBAC_PERM_COMMAND_ACCOUNT_SET_2FA
Definition RBAC.h:253
@ RBAC_PERM_COMMAND_ACCOUNT_SET_PASSWORD
Definition RBAC.h:143
@ RBAC_PERM_EMAIL_CONFIRM_FOR_PASS_CHANGE
Definition RBAC.h:102
@ RBAC_PERM_COMMAND_ACCOUNT_SET_SEC_REGMAIL
Definition RBAC.h:180
std::vector< uint8 > Secret
Definition TOTP.h:30
static bool ValidateToken(Secret const &key, uint32 token)
Definition TOTP.cpp:41
static constexpr size_t RECOMMENDED_SECRET_LENGTH
Definition TOTP.h:29
static Optional< std::vector< uint8 > > Decode(std::string_view data)
Definition Base32.cpp:52
static std::string Encode(std::vector< uint8 > const &data)
Definition Base32.cpp:47