1 #include "otsdaq/WebUsersUtilities/WebUsers.h"
2 #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
4 #include <openssl/sha.h>
18 #define WEB_LOGIN_BKUP_DB_PATH "bkup/"
20 #define SECURITY_FILE_NAME std::string(__ENV__("SERVICE_DATA_PATH")) + "/OtsWizardData/security.dat"
22 #define USERS_ACTIVE_SESSIONS_FILE USERS_DB_PATH + "/activeSessions.sv"
24 #define HASHES_DB_FILE HASHES_DB_PATH + "/hashes.xml"
25 #define USERS_DB_FILE USERS_DB_PATH + "/users.xml"
26 #define USERS_GLOBAL_HISTORY_FILE "__global"
27 #define USERS_LOGIN_HISTORY_FILETYPE "hist"
28 #define USERS_PREFERENCES_FILETYPE "pref"
29 #define SYSTEM_PREFERENCES_PREFIX "system.preset"
30 #define USER_WITH_LOCK_FILE WEB_LOGIN_DB_PATH + "/user_with_lock.dat"
31 #define IP_BLACKLIST_FILE WEB_LOGIN_DB_PATH + "/ip_generated_blacklist.dat"
32 #define IP_REJECT_FILE WEB_LOGIN_DB_PATH + "/ip_reject.dat"
33 #define IP_ACCEPT_FILE WEB_LOGIN_DB_PATH + "/ip_accept.dat"
34 #define USERS_LOGIN_FAILURE_FILE USERS_DB_PATH + "/loginFailureCounts.dat"
36 #define SILENCE_ALL_TOOLTIPS_FILENAME "silenceTooltips"
38 #define HASHES_DB_GLOBAL_STRING "hashData"
39 #define HASHES_DB_ENTRY_STRING "hashEntry"
40 #define USERS_DB_GLOBAL_STRING "userData"
41 #define USERS_DB_ENTRY_STRING "userEntry"
42 #define USERS_DB_NEXT_UID_STRING "nextUserId"
45 #define PREF_XML_BGCOLOR_FIELD "pref_bgcolor"
46 #define PREF_XML_DBCOLOR_FIELD "pref_dbcolor"
47 #define PREF_XML_WINCOLOR_FIELD "pref_wincolor"
48 #define PREF_XML_LAYOUT_FIELD "pref_layout"
49 #define PREF_XML_SYSLAYOUT_FIELD "pref_syslayout"
50 #define PREF_XML_ALIAS_LAYOUT_FIELD "pref_aliaslayout"
51 #define PREF_XML_SYSALIAS_LAYOUT_FIELD "pref_sysalias_layout"
52 #define PREF_XML_PERMISSIONS_FIELD "desktop_user_permissions"
53 #define PREF_XML_USERLOCK_FIELD "username_with_lock"
54 #define PREF_XML_USERNAME_FIELD "pref_username"
55 #define PREF_XML_OTS_OWNER_FIELD "ots_owner"
57 #define PREF_XML_BGCOLOR_DEFAULT "rgb(0,76,151)"
58 #define PREF_XML_DBCOLOR_DEFAULT "rgb(0,40,85)"
59 #define PREF_XML_WINCOLOR_DEFAULT "rgba(196,229,255,0.9)"
60 #define PREF_XML_LAYOUT_DEFAULT "0;0;0;0"
61 #define PREF_XML_SYSLAYOUT_DEFAULT "0;0"
63 #define PREF_XML_ACCOUNTS_FIELD "users_accounts"
64 #define PREF_XML_LOGIN_HISTORY_FIELD "login_entry"
67 const std::string WebUsers::DEFAULT_ADMIN_USERNAME =
"admin";
68 const std::string WebUsers::DEFAULT_ADMIN_DISPLAY_NAME =
"Administrator";
69 const std::string WebUsers::DEFAULT_ADMIN_EMAIL =
"root@otsdaq.fnal.gov";
70 const std::string WebUsers::DEFAULT_ITERATOR_USERNAME =
"iterator";
71 const std::string WebUsers::DEFAULT_STATECHANGER_USERNAME =
"statechanger";
72 const std::string WebUsers::DEFAULT_USER_GROUP =
"allUsers";
74 const std::string WebUsers::REQ_NO_LOGIN_RESPONSE =
"NoLogin";
75 const std::string WebUsers::REQ_NO_PERMISSION_RESPONSE =
"NoPermission";
76 const std::string WebUsers::REQ_USER_LOCKOUT_RESPONSE =
"UserLockout";
77 const std::string WebUsers::REQ_LOCK_REQUIRED_RESPONSE =
"LockRequired";
78 const std::string WebUsers::REQ_ALLOW_NO_USER =
"AllowNoUser";
80 const std::string WebUsers::SECURITY_TYPE_NONE =
"NoSecurity";
81 const std::string WebUsers::SECURITY_TYPE_DIGEST_ACCESS =
"DigestAccessAuthentication";
82 const std::string WebUsers::SECURITY_TYPE_DEFAULT = WebUsers::SECURITY_TYPE_NONE;
84 const std::vector<std::string> WebUsers::HashesDatabaseEntryFields_ = {
"hash",
"lastAccessTime"};
85 const std::vector<std::string> WebUsers::UsersDatabaseEntryFields_ = {
"username",
"displayName",
"salt",
86 "uid",
"permissions",
"lastLoginAttemptTime",
"accountCreatedTime",
87 "loginFailureCount",
"lastModifiedTime",
"lastModifierUsername",
"useremail"};
90 #define __MF_SUBJECT__ "WebUsers"
93 volatile bool WebUsers::CareAboutCookieCodes_ =
true;
94 bool WebUsers::ipBlacklistEnabled_ = (getenv(
"OTS_ENABLE_IP_BLACKLIST") && std::string(getenv(
"OTS_ENABLE_IP_BLACKLIST")) ==
"1");
104 usersNextUserId_ = 0;
105 usersUsernameWithLock_ =
"";
124 mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755);
125 mkdir(((std::string)WEB_LOGIN_DB_PATH +
"bkup/" + USERS_DB_PATH).c_str(), 0755);
126 mkdir(((std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH).c_str(), 0755);
127 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755);
128 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH).c_str(), 0755);
129 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH).c_str(), 0755);
132 __COUT__ <<
"FATAL USER DATABASE ERROR - failed to load!!!" << __E__;
134 loadSecuritySelection();
138 std::string user = DEFAULT_ADMIN_USERNAME;
139 if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
141 __SS__ <<
"user: " << user <<
" is not found. This should be impossible!"
143 __COUT_ERR__ << ss.str();
146 else if(Users_[i].salt_ ==
148 securityType_ == SECURITY_TYPE_DIGEST_ACCESS)
154 [](
const std::string& nac,
const std::string& user) {
155 WebUsers::NACDisplayThread(nac, user);
157 Users_[i].getNewAccountCode(),
166 loadLoginFailureCounts();
174 __COUT__ <<
"Done with Web Users initialization!" << __E__;
183 std::ostringstream* out,
187 std::lock_guard<std::mutex> lock(webUserMutex_);
195 userInfo.cookieCode_,
196 &userInfo.groupPermissionLevelMap_,
199 !userInfo.automatedCommand_ ,
203 &userInfo.usernameWithLock_,
206 *out << userInfo.cookieCode_;
207 goto HANDLE_ACCESS_FAILURE;
213 i = searchUsersDatabaseForUserId(userInfo.uid_);
214 if(i >= Users_.size())
216 __SS__ <<
"Illegal uid encountered in cookie codes!? " << i << __E__;
217 ss <<
"User size = " << Users_.size() << __E__;
221 userInfo.username_ = Users_[i].username_;
222 userInfo.displayName_ = Users_[i].displayName_;
225 if(userInfo.requireLock_ && userInfo.usernameWithLock_ ==
"")
227 __COUT_INFO__ <<
"Auto-taking lock for user '" << userInfo.username_
228 <<
"' because no user has the lock and lock is required." << __E__;
230 userInfo.usernameWithLock_ = userInfo.username_;
234 goto HANDLE_ACCESS_FAILURE;
238 HANDLE_ACCESS_FAILURE:
240 if(!userInfo.automatedCommand_)
241 __COUT_ERR__ <<
"Failed request (requestType = " << userInfo.requestType_
242 <<
"): " << out->str() << __E__;
253 userInfo.ip_ = cgi.getEnvironment().getRemoteAddr();
256 userInfo.username_ =
"";
257 userInfo.displayName_ =
"";
258 userInfo.usernameWithLock_ =
"";
272 std::ostringstream* out,
276 const std::string& wizardModeSequence )
282 if(userInfo.requireSecurity_ && userInfo.permissionsThreshold_ > 1)
288 if(isWizardMode && wizardModeSequence.size() < 8)
291 *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
292 __COUT__ <<
"User (@" << userInfo.ip_ <<
") has attempted requestType '"
293 << userInfo.requestType_
294 <<
"' which requires sufficient security enabled. Please enable the "
296 " sequence of at least 8 characters."
301 else if(!isWizardMode &&
302 (userInfo.username_ == WebUsers::DEFAULT_ADMIN_USERNAME ||
303 userInfo.username_ == WebUsers::DEFAULT_ITERATOR_USERNAME ||
304 userInfo.username_ == WebUsers::DEFAULT_STATECHANGER_USERNAME))
307 *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
308 __COUT__ <<
"User (@" << userInfo.ip_ <<
") has attempted requestType '"
309 << userInfo.requestType_
310 <<
"' which requires sufficient security enabled. Please enable "
312 " logins (Note: the user admin is disallowed in an attempt to "
313 "force personal accountability for edits)."
321 if(!userInfo.automatedCommand_)
323 __COUTT__ <<
"requestType ==========>>> " << userInfo.requestType_ << __E__;
324 __COUTTV__((
unsigned int)userInfo.permissionLevel_);
325 __COUTTV__((
unsigned int)userInfo.permissionsThreshold_);
329 if(!isWizardMode && !userInfo.allowNoUser_ &&
330 userInfo.cookieCode_.length() != WebUsers::COOKIE_CODE_LENGTH &&
332 userInfo.cookieCode_ ==
336 __COUT__ <<
"User (@" << userInfo.ip_
337 <<
") has invalid cookie code: " << userInfo.cookieCode_ << std::endl;
338 *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
342 if(!userInfo.allowNoUser_ &&
343 (userInfo.permissionLevel_ == 0 ||
344 userInfo.permissionsThreshold_ == 0 ||
345 userInfo.permissionLevel_ < userInfo.permissionsThreshold_))
348 *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
349 __COUT_INFO__ <<
"User (@" << userInfo.ip_
350 <<
") has insufficient permissions for requestType '"
351 << userInfo.requestType_ <<
"' : user level is "
352 << (
unsigned int)userInfo.permissionLevel_ <<
", "
353 << (
unsigned int)userInfo.permissionsThreshold_ <<
" required."
361 userInfo.username_ = WebUsers::DEFAULT_ADMIN_USERNAME;
362 userInfo.displayName_ =
"Admin";
363 userInfo.usernameWithLock_ = userInfo.username_;
371 if(userInfo.allowNoUser_)
372 xmldoc->setHeader(WebUsers::REQ_ALLOW_NO_USER);
374 xmldoc->setHeader(userInfo.cookieCode_);
377 if(userInfo.allowNoUser_)
379 if(userInfo.automatedCommand_)
380 __COUTT__ <<
"Allowing anonymous access." << __E__;
391 if((userInfo.checkLock_ || userInfo.requireLock_) &&
392 userInfo.usernameWithLock_ !=
"" &&
393 userInfo.usernameWithLock_ != userInfo.username_)
395 *out << WebUsers::REQ_USER_LOCKOUT_RESPONSE;
396 __COUT_INFO__ <<
"User '" << userInfo.username_ <<
"' is locked out. '"
397 << userInfo.usernameWithLock_ <<
"' has lock." << std::endl;
401 if(userInfo.requireLock_ && userInfo.usernameWithLock_ != userInfo.username_)
403 *out << WebUsers::REQ_LOCK_REQUIRED_RESPONSE;
404 __COUT_INFO__ <<
"User '" << userInfo.username_
405 <<
"' must have lock to proceed. ('" << userInfo.usernameWithLock_
406 <<
"' has lock.)" << std::endl;
421 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE;
422 __COUT__ << fn << __E__;
424 FILE* fp = fopen(fn.c_str(),
"w");
427 __COUT_ERR__ <<
"Error! Persistent active sessions could not be saved to file: "
433 fprintf(fp,
"%d\n", version);
434 for(
unsigned int i = 0; i < ActiveSessions_.size(); ++i)
442 fprintf(fp,
"%s\n", ActiveSessions_[i].cookieCode_.c_str());
443 fprintf(fp,
"%s\n", ActiveSessions_[i].ip_.c_str());
444 fprintf(fp,
"%lu\n", ActiveSessions_[i].userId_);
445 fprintf(fp,
"%lu\n", ActiveSessions_[i].sessionIndex_);
446 fprintf(fp,
"%ld\n", ActiveSessions_[i].startTime_);
447 fprintf(fp,
"%ld\n", ActiveSessions_[i].lastActivityTime_);
450 __COUT__ <<
"Active Sessions saved with size " << ActiveSessions_.size() << __E__;
462 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE;
463 __COUT__ << fn << __E__;
464 FILE* fp = fopen(fn.c_str(),
"r");
468 <<
"Persistent active sessions were not found to be loaded at file: " << fn
475 const int LINELEN = 1000;
477 fgets(line, LINELEN, fp);
478 sscanf(line,
"%d", &version);
481 __COUT__ <<
"Extracting active sessions (version 0, no lastActivityTime)..."
484 else if(version == 1)
486 __COUT__ <<
"Extracting active sessions (version 1)..." << __E__;
488 while(fgets(line, LINELEN, fp))
491 line[strlen(line) - 1] =
'\0';
492 if(strlen(line) != COOKIE_CODE_LENGTH)
494 __COUT__ <<
"Illegal cookie code found: " << line << __E__;
500 ActiveSessions_.back().cookieCode_ = line;
502 fgets(line, LINELEN, fp);
504 line[strlen(line) - 1] =
'\0';
505 ActiveSessions_.back().ip_ = line;
507 fgets(line, LINELEN, fp);
508 sscanf(line,
"%lu", &(ActiveSessions_.back().userId_));
510 fgets(line, LINELEN, fp);
511 sscanf(line,
"%lu", &(ActiveSessions_.back().sessionIndex_));
513 fgets(line, LINELEN, fp);
514 sscanf(line,
"%ld", &(ActiveSessions_.back().startTime_));
518 fgets(line, LINELEN, fp);
519 sscanf(line,
"%ld", &(ActiveSessions_.back().lastActivityTime_));
524 ActiveSessions_.back().lastActivityTime_ = ActiveSessions_.back().startTime_;
528 __COUT__ <<
"Active Sessions loaded with size " << ActiveSessions_.size() << __E__;
532 fp = fopen(fn.c_str(),
"w");
541 bool WebUsers::loadDatabases()
546 const unsigned int LINE_LEN = 1000;
548 unsigned int i, si, c, len, f;
559 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_FILE;
560 __COUT__ << fn << __E__;
561 fp = fopen(fn.c_str(),
"r");
564 mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str(),
566 __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str()
568 fp = fopen(fn.c_str(),
"w");
571 __COUT__ <<
"Hashes database created: " << fn << __E__;
573 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING,
"", DB_SAVE_OPEN);
574 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING,
"", DB_SAVE_CLOSE);
581 while(fgets(line, LINE_LEN, fp))
583 if(strlen(line) < SHA512_DIGEST_LENGTH)
589 for(i = 0; i < len; ++i)
597 while(i < len && line[i] !=
'<')
609 Hashes_.push_back(Hash());
610 Hashes_.back().hash_ = &line[si];
613 sscanf(&line[si],
"%ld", &Hashes_.back().accessTime_);
616 __COUT__ << Hashes_.size() <<
" Hashes found." << __E__;
630 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_FILE;
631 fp = fopen(fn.c_str(),
"r");
634 mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str(),
636 __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str()
638 fp = fopen(fn.c_str(),
"w");
641 __COUT__ <<
"Users database created: " << fn << __E__;
643 saveToDatabase(fp, USERS_DB_GLOBAL_STRING,
"", DB_SAVE_OPEN);
645 sprintf(nidStr,
"%lu", usersNextUserId_);
646 saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, nidStr, DB_SAVE_OPEN_AND_CLOSE);
647 saveToDatabase(fp, USERS_DB_GLOBAL_STRING,
"", DB_SAVE_CLOSE);
651 DEFAULT_ADMIN_DISPLAY_NAME,
652 DEFAULT_ADMIN_EMAIL);
656 __COUT__ <<
"Users database: " << fn << __E__;
660 char salt[] =
"nextUserId";
661 while(fgets(line, LINE_LEN, fp))
663 if(strlen(line) < strlen(salt) * 2)
666 for(i = 0; i < strlen(salt); ++i)
667 if(line[i + 1] != salt[i])
670 if(i == strlen(salt))
675 while(i < LINE_LEN && line[i] !=
'\0' && line[i] !=
'<')
678 sscanf(&line[si],
"%lu", &usersNextUserId_);
683 __COUT__ <<
"Found Users database next user Id: " << usersNextUserId_ << __E__;
687 while(fgets(line, LINE_LEN, fp))
689 if(strlen(line) < 30)
697 __COUT__ <<
"Line buffer too small: " << len << __E__;
703 for(i = 0; i < len; ++i)
707 if(c == 0 || c % 2 == 1)
711 while(i < len && line[i] !=
'<')
723 Users_.push_back(User());
724 Users_.back().username_ = &line[si];
727 Users_.back().displayName_ = &line[si];
729 Users_.back().salt_ = &line[si];
731 sscanf(&line[si],
"%lu", &Users_.back().userId_);
734 std::map<std::string, permissionLevel_t>& lastPermissionsMap =
735 Users_.back().permissions_;
736 StringMacros::getMapFromString<permissionLevel_t>(
737 &line[si], lastPermissionsMap);
745 if(lastPermissionsMap.find(WebUsers::DEFAULT_USER_GROUP) ==
746 lastPermissionsMap.end())
749 <<
"User '" << Users_.back().username_
750 <<
"' is not a member of the default user group '"
751 << WebUsers::DEFAULT_USER_GROUP
752 <<
".' Assuming user account is inactive (permission "
754 << WebUsers::PERMISSION_LEVEL_INACTIVE <<
")." << __E__;
755 lastPermissionsMap[WebUsers::DEFAULT_USER_GROUP] =
756 WebUsers::PERMISSION_LEVEL_INACTIVE;
759 if(Users_.back().username_ == DEFAULT_ADMIN_USERNAME)
763 std::map<std::string ,
764 WebUsers::permissionLevel_t>
765 initPermissions = {{WebUsers::DEFAULT_USER_GROUP,
768 Users_.back().permissions_ = initPermissions;
772 sscanf(&line[si],
"%ld", &Users_.back().lastLoginAttempt_);
774 sscanf(&line[si],
"%ld", &Users_.back().accountCreationTime_);
776 sscanf(&line[si],
"%hhu", &Users_.back().loginFailureCount_);
778 sscanf(&line[si],
"%ld", &Users_.back().accessModifierTime());
780 Users_.back().loadModifierUsername(&line[si]);
782 Users_.back().email_ = &line[si];
789 __COUT__ << Users_.size() <<
" Users found." << __E__;
790 for(
size_t ii = 0; ii < Users_.size(); ++ii)
793 "User [" << Users_[ii].userId_ <<
"] \tName: " << std::left
794 << std::setfill(
' ') << std::setw(20) << Users_[ii].username_
795 <<
"\tDisplay Name: " << std::left << std::setfill(
' ') << std::setw(30)
796 << Users_[ii].displayName_ <<
"\tEmail: " << std::left
797 << std::setfill(
' ') << std::setw(30) << Users_[ii].email_
798 <<
"\tNAC: " << std::left << std::setfill(
' ') << std::setw(5)
799 << Users_[ii].getNewAccountCode()
800 <<
"\tFailedCount: " << (int)Users_[ii].loginFailureCount_
821 void WebUsers::saveLoginFailureCounts()
824 (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_LOGIN_FAILURE_FILE;
826 FILE* fp = fopen(fn.c_str(),
"w");
829 __COUT_ERR__ <<
"Failed to open login failure counts file for writing: " << fn
833 for(uint64_t i = 0; i < Users_.size(); ++i)
835 if(Users_[i].loginFailureCount_ > 0)
836 fprintf(fp,
"%lu %hhu\n", Users_[i].userId_, Users_[i].loginFailureCount_);
845 void WebUsers::loadLoginFailureCounts()
848 (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_LOGIN_FAILURE_FILE;
850 FILE* fp = fopen(fn.c_str(),
"r");
853 __COUT__ <<
"No login failure counts file found (this is normal on first run): "
859 for(
auto& user : Users_)
860 user.loginFailureCount_ = 0;
864 while(fscanf(fp,
"%lu %u", &uid, &count) == 2)
866 for(
auto& user : Users_)
868 if(user.userId_ == uid)
870 user.loginFailureCount_ = (
unsigned char)count;
876 __COUT__ <<
"Loaded login failure counts from " << fn << __E__;
881 void WebUsers::saveToDatabase(FILE* fp,
882 const std::string& field,
883 const std::string& value,
890 std::string newLine = addNewLine ?
"\n" :
"";
892 if(type == DB_SAVE_OPEN_AND_CLOSE)
899 else if(type == DB_SAVE_OPEN)
900 fprintf(fp,
"<%s>%s%s", field.c_str(), value.c_str(), newLine.c_str());
901 else if(type == DB_SAVE_CLOSE)
902 fprintf(fp,
"</%s>%s", field.c_str(), newLine.c_str());
911 bool WebUsers::saveDatabaseToFile(uint8_t db)
916 (std::string)WEB_LOGIN_DB_PATH +
917 ((db == DB_USERS) ? (std::string)USERS_DB_FILE : (std::string)HASHES_DB_FILE);
919 __COUT__ <<
"Save Database Filename: " << fn << __E__;
925 sprintf(dayAppend,
".%lu.bkup", time(0) / (3600 * 24));
926 std::string bkup_fn = (std::string)WEB_LOGIN_DB_PATH +
927 (std::string)WEB_LOGIN_BKUP_DB_PATH +
928 ((db == DB_USERS) ? (std::string)USERS_DB_FILE
929 : (std::string)HASHES_DB_FILE) +
930 (std::string)dayAppend;
932 __COUT__ <<
"Backup file: " << bkup_fn << __E__;
934 std::string shell_command =
"mv " + fn +
" " + bkup_fn;
935 system(shell_command.c_str());
938 FILE* fp = fopen(fn.c_str(),
"wb");
946 saveToDatabase(fp, USERS_DB_GLOBAL_STRING,
"", DB_SAVE_OPEN);
948 sprintf(fldStr,
"%lu", usersNextUserId_);
949 saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, fldStr, DB_SAVE_OPEN_AND_CLOSE);
951 __COUT__ <<
"Saving " << Users_.size() <<
" Users." << __E__;
953 for(uint64_t i = 0; i < Users_.size(); ++i)
957 saveToDatabase(fp, USERS_DB_ENTRY_STRING,
"", DB_SAVE_OPEN,
false);
959 for(
unsigned int f = 0; f < WebUsers::UsersDatabaseEntryFields_.size(); ++f)
964 WebUsers::UsersDatabaseEntryFields_[f],
966 DB_SAVE_OPEN_AND_CLOSE,
970 WebUsers::UsersDatabaseEntryFields_[f],
971 Users_[i].displayName_,
972 DB_SAVE_OPEN_AND_CLOSE,
976 WebUsers::UsersDatabaseEntryFields_[f],
978 DB_SAVE_OPEN_AND_CLOSE,
982 sprintf(fldStr,
"%lu", Users_[i].userId_);
984 WebUsers::UsersDatabaseEntryFields_[f],
986 DB_SAVE_OPEN_AND_CLOSE,
991 WebUsers::UsersDatabaseEntryFields_[f],
995 DB_SAVE_OPEN_AND_CLOSE,
999 sprintf(fldStr,
"%lu", Users_[i].lastLoginAttempt_);
1001 WebUsers::UsersDatabaseEntryFields_[f],
1003 DB_SAVE_OPEN_AND_CLOSE,
1008 sprintf(fldStr,
"%lu", Users_[i].accountCreationTime_);
1010 WebUsers::UsersDatabaseEntryFields_[f],
1012 DB_SAVE_OPEN_AND_CLOSE,
1017 sprintf(fldStr,
"%hhu", Users_[i].loginFailureCount_);
1019 WebUsers::UsersDatabaseEntryFields_[f],
1021 DB_SAVE_OPEN_AND_CLOSE,
1026 sprintf(fldStr,
"%lu", Users_[i].getModifierTime());
1028 WebUsers::UsersDatabaseEntryFields_[f],
1030 DB_SAVE_OPEN_AND_CLOSE,
1035 WebUsers::UsersDatabaseEntryFields_[f],
1036 Users_[i].getModifierUsername(),
1037 DB_SAVE_OPEN_AND_CLOSE,
1041 WebUsers::UsersDatabaseEntryFields_[f],
1043 DB_SAVE_OPEN_AND_CLOSE,
1047 saveToDatabase(fp, USERS_DB_ENTRY_STRING,
"", DB_SAVE_CLOSE);
1050 saveToDatabase(fp, USERS_DB_GLOBAL_STRING,
"", DB_SAVE_CLOSE);
1054 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING,
"", DB_SAVE_OPEN);
1056 __COUT__ <<
"Saving " << Hashes_.size() <<
" Hashes." << __E__;
1057 for(uint64_t i = 0; i < Hashes_.size(); ++i)
1059 __COUT__ <<
"Saving " << Hashes_[i].hash_ <<
" Hash." << __E__;
1060 saveToDatabase(fp, HASHES_DB_ENTRY_STRING,
"", DB_SAVE_OPEN,
false);
1061 for(
unsigned int f = 0; f < WebUsers::HashesDatabaseEntryFields_.size(); ++f)
1065 WebUsers::HashesDatabaseEntryFields_[f],
1067 DB_SAVE_OPEN_AND_CLOSE,
1071 sprintf(fldStr,
"%lu", Hashes_[i].accessTime_);
1073 WebUsers::HashesDatabaseEntryFields_[f],
1075 DB_SAVE_OPEN_AND_CLOSE,
1079 saveToDatabase(fp, HASHES_DB_ENTRY_STRING,
"", DB_SAVE_CLOSE);
1082 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING,
"", DB_SAVE_CLOSE);
1098 const std::string& displayName,
1099 const std::string& email)
1101 __COUT__ <<
"Creating account: " << username << __E__;
1104 if((i = searchUsersDatabaseForUsername(username)) != NOT_FOUND_IN_DATABASE ||
1105 username == WebUsers::DEFAULT_ITERATOR_USERNAME ||
1106 username == WebUsers::DEFAULT_STATECHANGER_USERNAME)
1109 __SS__ <<
"Username '" << username
1110 <<
"' already exists! Please choose a unique username." << __E__;
1115 if((i = searchUsersDatabaseForDisplayName(displayName)) != NOT_FOUND_IN_DATABASE)
1118 __SS__ <<
"Display Name '" << displayName
1119 <<
"' already exists! Please choose a unique display name." << __E__;
1124 Users_.push_back(
User());
1126 Users_.back().username_ = username;
1127 Users_.back().displayName_ = displayName;
1128 Users_.back().email_ = email;
1131 std::map<std::string , WebUsers::permissionLevel_t> initPermissions = {
1132 {WebUsers::DEFAULT_USER_GROUP,
1133 (Users_.size() ? WebUsers::PERMISSION_LEVEL_NOVICE
1136 Users_.back().permissions_ = initPermissions;
1137 Users_.back().userId_ = usersNextUserId_++;
1138 if(usersNextUserId_ >= ACCOUNT_ERROR_THRESHOLD)
1140 __SS__ <<
"usersNextUserId_ wrap around!! Too many users??? Notify Admins."
1143 usersNextUserId_ = 1;
1147 Users_.back().accountCreationTime_ = time(0);
1149 if(!saveDatabaseToFile(DB_USERS))
1151 __SS__ <<
"Failed to save User DB!" << __E__;
1162 bool WebUsers::deleteAccount(
const std::string& username,
const std::string& displayName)
1164 uint64_t i = searchUsersDatabaseForUsername(username);
1165 if(i == NOT_FOUND_IN_DATABASE)
1167 if(Users_[i].displayName_ != displayName)
1172 Users_.erase(Users_.begin() + i);
1175 return saveDatabaseToFile(DB_USERS);
1179 unsigned int WebUsers::hexByteStrToInt(
const char* h)
1182 char hs[3] = {h[0], h[1],
'\0'};
1183 sscanf(hs,
"%X", &rv);
1188 void WebUsers::intToHexStr(
unsigned char i,
char* h) { sprintf(h,
"%2.2X", i); }
1201 std::string& jumbledUser,
1202 const std::string& jumbledPw,
1203 std::string& newAccountCode,
1204 const std::string& ip)
1209 __COUT_ERR__ <<
"rejected ip: " << ip << __E__;
1210 return ACCOUNT_BLACKLISTED;
1215 if(!CareAboutCookieCodes_)
1219 newAccountCode = genCookieCode();
1226 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
1228 __COUT_ERR__ <<
"Login attempt failed. Session uuid '" << uuid
1229 <<
"' is not found or inactive." << __E__;
1230 newAccountCode =
"1";
1232 incrementIpBlacklistCount(ip);
1234 return NOT_FOUND_IN_DATABASE;
1236 ++LoginSessions_[i].loginAttempts_;
1238 std::string user = dejumble(jumbledUser, LoginSessions_[i].id_);
1240 std::string pw = dejumble(jumbledPw, LoginSessions_[i].id_);
1243 if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
1245 __COUT_ERR__ <<
"user: " << user <<
" is not found" << __E__;
1247 incrementIpBlacklistCount(ip);
1249 return NOT_FOUND_IN_DATABASE;
1252 ipBlacklistCounts_[ip] = 0;
1254 Users_[i].lastLoginAttempt_ = time(0);
1256 if(isInactiveForGroup(Users_[i].permissions_))
1258 __COUT_ERR__ <<
"User '" << user
1259 <<
"' account INACTIVE (could be due to failed logins)" << __E__;
1260 return ACCOUNT_INACTIVE;
1263 if(Users_[i].salt_ ==
"")
1265 __COUT__ <<
"First login attempt for user: " << user << __E__;
1267 if(newAccountCode != Users_[i].getNewAccountCode())
1269 __COUT__ <<
"New account code did not match: "
1270 << Users_[i].getNewAccountCode() <<
" != " << newAccountCode
1275 return NOT_FOUND_IN_DATABASE;
1281 while(!addToHashesDatabase(
1282 sha512(user, pw, Users_[i].salt_)))
1287 Users_[i].salt_ =
"";
1290 __COUT__ <<
"\tHash added: " << Hashes_.back().hash_ << __E__;
1294 if(!saveDatabaseToFile(DB_USERS))
1296 __SS__ <<
"Failed to save User DB after first login salt initialization!"
1303 std::string salt = Users_[i].salt_;
1305 if(searchHashesDatabaseForHash(sha512(user, pw, salt)) == NOT_FOUND_IN_DATABASE)
1307 __COUT__ <<
"Failed login for " << user <<
" with permissions "
1311 if(++Users_[i].loginFailureCount_ != (
unsigned char)-1)
1312 ++Users_[i].loginFailureCount_;
1314 if(Users_[i].loginFailureCount_ >= USERS_MAX_LOGIN_FAILURES)
1315 Users_[i].permissions_[WebUsers::DEFAULT_USER_GROUP] =
1316 WebUsers::PERMISSION_LEVEL_INACTIVE;
1318 __COUT_INFO__ <<
"User/pw for user '" << user
1319 <<
"' was not correct (Failed Attempt #"
1320 << (int)Users_[i].loginFailureCount_ <<
" of "
1321 << (
int)USERS_MAX_LOGIN_FAILURES <<
" allowed)." << __E__;
1323 __COUTV__(isInactiveForGroup(Users_[i].permissions_));
1324 if(isInactiveForGroup(Users_[i].permissions_))
1326 __COUT_INFO__ <<
"Account '" << user
1327 <<
"' has been marked inactive due to too many failed "
1328 "login attempts (Failed Attempt #"
1329 << (int)Users_[i].loginFailureCount_
1330 <<
")! Note only admins can reactivate accounts." << __E__;
1333 saveDatabaseToFile(DB_USERS);
1337 return NOT_FOUND_IN_DATABASE;
1341 __COUT_INFO__ <<
"Login successful for: " << user << __E__;
1344 if(Users_[i].loginFailureCount_ != 0)
1346 Users_[i].loginFailureCount_ = 0;
1347 saveLoginFailureCounts();
1351 for(
int h = 0; h < 2; ++h)
1353 std::string fn = (std::string)WEB_LOGIN_DB_PATH +
1354 (std::string)USERS_LOGIN_HISTORY_PATH +
1355 (h ? USERS_GLOBAL_HISTORY_FILE : Users_[i].username_) +
"." +
1356 (std::string)USERS_LOGIN_HISTORY_FILETYPE;
1363 (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE))
1367 __COUT__ <<
"No previous login history found." << __E__;
1373 "Time=%lu Username=%s Permissions=%s UID=%lu",
1375 Users_[i].username_.c_str(),
1380 "Time=%lu displayName=%s Permissions=%s UID=%lu",
1382 Users_[i].displayName_.c_str(),
1385 histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr);
1397 jumbledUser = Users_[i].displayName_;
1398 newAccountCode = createNewActiveSession(Users_[i].userId_,
1401 __COUTTV__(ActiveSessions_.size());
1403 if(ActiveSessions_.size() == 1)
1405 __COUT__ <<
"Attempting to auto-lock for first login user '"
1406 << Users_[i].username_ <<
"'... " << __E__;
1410 return Users_[i].userId_;
1421 std::string& cookieCode,
1423 const std::string& ip)
1427 __COUT_ERR__ <<
"rejected ip: " << ip << __E__;
1428 return NOT_FOUND_IN_DATABASE;
1433 if(!CareAboutCookieCodes_)
1437 cookieCode = genCookieCode();
1443 __COUT__ <<
"Rejecting cert logon with blank fingerprint" << __E__;
1445 incrementIpBlacklistCount(ip);
1447 return NOT_FOUND_IN_DATABASE;
1453 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
1455 __COUT__ <<
"uuid: " << uuid <<
" is not found" << __E__;
1458 incrementIpBlacklistCount(ip);
1460 return NOT_FOUND_IN_DATABASE;
1462 ++LoginSessions_[i].loginAttempts_;
1464 email = getUserEmailFromFingerprint(email);
1465 __COUT__ <<
"DejumbledEmail = " << email << __E__;
1468 __COUT__ <<
"Rejecting logon with unknown fingerprint" << __E__;
1470 incrementIpBlacklistCount(ip);
1472 return NOT_FOUND_IN_DATABASE;
1476 if((i = searchUsersDatabaseForUserEmail(email)) == NOT_FOUND_IN_DATABASE)
1478 __COUT__ <<
"email: " << email <<
" is not found" << __E__;
1480 incrementIpBlacklistCount(ip);
1482 return NOT_FOUND_IN_DATABASE;
1485 ipBlacklistCounts_[ip] = 0;
1489 Users_[i].lastLoginAttempt_ = time(0);
1490 if(isInactiveForGroup(Users_[i].permissions_))
1492 __COUT__ <<
"User '" << user
1493 <<
"' account INACTIVE (could be due to failed logins)." << __E__;
1494 return NOT_FOUND_IN_DATABASE;
1497 if(Users_[i].salt_ ==
"")
1499 return NOT_FOUND_IN_DATABASE;
1502 __COUT__ <<
"Login successful for: " << user << __E__;
1505 if(Users_[i].loginFailureCount_ != 0)
1507 Users_[i].loginFailureCount_ = 0;
1508 saveLoginFailureCounts();
1512 for(
int h = 0; h < 2; ++h)
1514 std::string fn = (std::string)WEB_LOGIN_DB_PATH +
1515 (std::string)USERS_LOGIN_HISTORY_PATH +
1516 (h ? USERS_GLOBAL_HISTORY_FILE : Users_[i].username_) +
"." +
1517 (std::string)USERS_LOGIN_HISTORY_FILETYPE;
1524 (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE))
1528 __COUT__ <<
"No previous login history found." << __E__;
1534 "Time=%lu Username=%s Permissions=%s UID=%lu",
1536 Users_[i].username_.c_str(),
1541 "Time=%lu displayName=%s Permissions=%s UID=%lu",
1543 Users_[i].displayName_.c_str(),
1546 histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr);
1558 email = Users_[i].displayName_;
1559 cookieCode = createNewActiveSession(Users_[i].userId_,
1561 return Users_[i].userId_;
1567 uint64_t WebUsers::searchActiveSessionDatabaseForCookie(
1568 const std::string& cookieCode)
const
1571 for(; i < ActiveSessions_.size(); ++i)
1572 if(ActiveSessions_[i].cookieCode_ == cookieCode)
1574 return (i == ActiveSessions_.size()) ? NOT_FOUND_IN_DATABASE : i;
1592 uint64_t WebUsers::checkRemoteLoginVerification(std::string& cookieCode,
1595 const std::string& ip)
1597 __COUTVS__(2, cookieCode);
1598 remoteLoginVerificationEnabledBlackoutTime_ = 0;
1599 if(!remoteLoginVerificationSocket_)
1604 <<
"Illegal remote login verification port found in remote destination "
1606 <<
". Please check remote settings." << __E__;
1609 __COUT_INFO__ <<
"Instantiating Remote Gateway login verification socket! "
1610 "Validation requests will go to "
1614 remoteLoginVerificationSocket_ =
1615 std::make_unique<TransceiverSocket>(remoteLoginVerificationIP_);
1616 remoteLoginVerificationSocket_->initialize();
1618 remoteLoginVerificationSocketTarget_ = std::make_unique<Socket>(
1624 auto lockHandling = [
this, refresh](std::string username,
1625 uint64_t verifiedUserId) -> uint64_t {
1626 __COUTT__ <<
"lambda lockHandling()" << __E__;
1627 __COUTTV__(ActiveSessions_.size());
1628 __COUTTV__(RemoteSessions_.size());
1630 if((!CareAboutCookieCodes_)
1632 (usersUsernameWithLock_ == DEFAULT_ADMIN_USERNAME ||
1633 usersUsernameWithLock_ ==
"") &&
1634 usersUsernameWithLock_ != username)
1636 __COUT_INFO__ <<
"Overriding local user-with-lock '" << usersUsernameWithLock_
1637 <<
"' with remote user-with-lock 'Remote:" << username <<
"'"
1639 usersUsernameWithLock_ =
1643 getUserWithLock() +
" has locked REMOTE ots (overriding anonymous " +
1644 DEFAULT_ADMIN_USERNAME +
" user).");
1646 else if((ActiveSessions_.size() == 0 &&
1647 RemoteSessions_.size() == 1)
1648 && refresh && (usersUsernameWithLock_ ==
"") &&
1649 usersUsernameWithLock_ != username)
1651 __COUT_INFO__ <<
"Overriding local user-with-lock '" << usersUsernameWithLock_
1652 <<
"' with remote user-with-lock 'Remote:" << username <<
"'"
1654 usersUsernameWithLock_ =
1658 getUserWithLock() +
" has locked REMOTE ots (which was unlocked).");
1660 return verifiedUserId;
1665 __COUTTV__(cookieCode);
1666 __COUTTV__(RemoteSessions_.size());
1667 auto it = RemoteSessions_.find(cookieCode);
1668 if(it != RemoteSessions_.end())
1670 __COUTT__ <<
"cookieCode still active locally!" << __E__;
1671 __COUTTV__(it->second.userId_);
1672 uint64_t j = searchUsersDatabaseForUserId(it->second.userId_);
1673 if(j == NOT_FOUND_IN_DATABASE)
1675 __SS__ <<
"Could not find cache entry for remote user ID '"
1676 << it->second.userId_ <<
"' - notify admins." << __E__;
1679 __COUTTV__(Users_[j].username_);
1682 return lockHandling(Users_[j].username_, it->second.userId_);
1687 __COUTTV__(doNotGoRemote);
1689 return NOT_FOUND_IN_DATABASE;
1700 std::string request =
"loginVerify," + cookieCode +
"," + (refresh ?
"1" :
"0") +
1703 __COUTTV__(request);
1706 std::string requestResponseString = remoteLoginVerificationSocket_->sendAndReceive(
1707 *remoteLoginVerificationSocketTarget_, request, 10 );
1708 __COUTTV__(requestResponseString);
1711 std::vector<std::string> rxParams =
1715 if(rxParams.size() != 6)
1717 __COUTT__ <<
"Remote login response indicates rejected: " << rxParams.size()
1719 return NOT_FOUND_IN_DATABASE;
1731 __COUTTV__(rxParams[2]);
1732 __COUTTV__(usersUsernameWithLock_);
1735 std::string username = rxParams[3];
1736 __COUTTV__(username);
1737 uint64_t j = searchUsersDatabaseForUsername(username);
1738 if(j == NOT_FOUND_IN_DATABASE)
1740 __COUT_INFO__ <<
"Creating User entry for remote user '" << username
1741 <<
"' in local user list to track user preferences." << __E__;
1745 j = Users_.size() - 1;
1748 Users_[j].lastLoginAttempt_ = time(0);
1749 Users_[j].setModifier(
"REMOTE_GATEWAY");
1754 .permissions_.clear();
1756 Users_[j].permissions_);
1758 __COUTV__(Users_[j].username_);
1759 __COUTV__(Users_[j].userId_);
1763 cookieCode = rxParams[0];
1764 __COUTTV__(cookieCode);
1765 ActiveSession& newRemoteSession =
1766 RemoteSessions_[cookieCode];
1767 newRemoteSession.cookieCode_ = cookieCode;
1768 newRemoteSession.ip_ = ip;
1769 newRemoteSession.userId_ = Users_[j].userId_;
1770 sscanf(rxParams[5].c_str(),
"%lu", &newRemoteSession.sessionIndex_);
1771 newRemoteSession.startTime_ = time(0);
1774 return lockHandling(Users_[j].username_, Users_[j].userId_);
1783 if((u = searchUsersDatabaseForUsername(username)) == NOT_FOUND_IN_DATABASE)
1793 __COUTT__ <<
"isUserIdActive? " << uid << __E__;
1796 for(
const auto& remoteSession : RemoteSessions_)
1797 if(remoteSession.second.userId_ == uid)
1802 for(; i < ActiveSessions_.size(); ++i)
1803 if(ActiveSessions_[i].userId_ == uid)
1811 uint64_t WebUsers::searchUsersDatabaseForUsername(
const std::string& username)
const
1814 for(; i < Users_.size(); ++i)
1815 if(Users_[i].username_ == username)
1817 return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
1823 uint64_t WebUsers::searchUsersDatabaseForDisplayName(
const std::string& displayName)
const
1826 for(; i < Users_.size(); ++i)
1827 if(Users_[i].displayName_ == displayName)
1829 return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
1835 uint64_t WebUsers::searchUsersDatabaseForUserEmail(
const std::string& useremail)
const
1838 for(; i < Users_.size(); ++i)
1839 if(Users_[i].email_ == useremail)
1841 return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
1847 uint64_t WebUsers::searchUsersDatabaseForUserId(uint64_t uid)
const
1850 for(; i < Users_.size(); ++i)
1851 if(Users_[i].userId_ == uid)
1853 return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
1859 uint64_t WebUsers::searchLoginSessionDatabaseForUUID(
const std::string& uuid)
const
1862 for(; i < LoginSessions_.size(); ++i)
1863 if(LoginSessions_[i].uuid_ == uuid)
1865 return (i == LoginSessions_.size()) ? NOT_FOUND_IN_DATABASE : i;
1871 uint64_t WebUsers::searchHashesDatabaseForHash(
const std::string& hash)
1875 for(; i < Hashes_.size(); ++i)
1876 if(Hashes_[i].hash_ == hash)
1881 if(i < Hashes_.size())
1882 Hashes_[i].accessTime_ =
1883 ((time(0) + (rand() % 2 ? 1 : -1) * (rand() % 30 * 24 * 60 * 60)) &
1884 0x0FFFFFFFFFE000000);
1889 return (i == Hashes_.size()) ? NOT_FOUND_IN_DATABASE : i;
1896 bool WebUsers::addToHashesDatabase(
const std::string& hash)
1898 if(searchHashesDatabaseForHash(hash) != NOT_FOUND_IN_DATABASE)
1900 __COUT__ <<
"Hash collision: " << hash << __E__;
1903 Hashes_.push_back(Hash());
1904 Hashes_.back().hash_ = hash;
1905 Hashes_.back().accessTime_ =
1906 ((time(0) + (rand() % 2 ? 1 : -1) * (rand() % 30 * 24 * 60 * 60)) &
1907 0x0FFFFFFFFFE000000);
1910 return saveDatabaseToFile(DB_HASHES);
1915 std::string WebUsers::genCookieCode()
1918 std::string cc =
"";
1919 for(uint32_t i = 0; i < COOKIE_CODE_LENGTH / 2; ++i)
1921 intToHexStr(rand(), hexStr);
1931 std::string WebUsers::createNewActiveSession(uint64_t uid,
1932 const std::string& ip,
1936 ActiveSessions_.push_back(ActiveSession());
1937 ActiveSessions_.back().cookieCode_ = genCookieCode();
1938 ActiveSessions_.back().ip_ = ip;
1939 ActiveSessions_.back().userId_ = uid;
1940 ActiveSessions_.back().startTime_ = time(0);
1941 ActiveSessions_.back().lastActivityTime_ = time(0);
1944 ActiveSessions_.back().sessionIndex_ = asIndex;
1949 for(uint64_t j = 0; j < ActiveSessions_.size(); ++j)
1950 if(ActiveSessions_[j].userId_ == uid &&
1951 max < ActiveSessions_[j].sessionIndex_)
1952 max = ActiveSessions_[j].sessionIndex_;
1954 ActiveSessions_.back().sessionIndex_ = (max ? max + 1 : 1);
1957 return ActiveSessions_.back().cookieCode_;
1984 std::string WebUsers::refreshCookieCode(
unsigned int i,
bool enableRefresh)
1987 for(uint64_t j = ActiveSessions_.size() - 1; j != (uint64_t)-1;
1989 if(ActiveSessions_[j].userId_ == ActiveSessions_[i].userId_ &&
1990 ActiveSessions_[j].sessionIndex_ ==
1991 ActiveSessions_[i].sessionIndex_)
1998 ActiveSessions_[j].lastActivityTime_ = time(0);
2001 if(enableRefresh && (time(0) - ActiveSessions_[j].startTime_ >
2002 ACTIVE_SESSION_EXPIRATION_TIME / 2))
2006 ActiveSessions_[j].startTime_ =
2007 time(0) - ACTIVE_SESSION_EXPIRATION_TIME +
2008 ACTIVE_SESSION_COOKIE_OVERLAP_TIME;
2014 return createNewActiveSession(ActiveSessions_[i].userId_,
2015 ActiveSessions_[i].ip_,
2016 ActiveSessions_[i].sessionIndex_);
2019 return ActiveSessions_[j].cookieCode_;
2031 std::string& cookieCode,
2032 std::string& username)
2034 if(!CareAboutCookieCodes_)
2041 if(!ActiveSessions_.size())
2042 return NOT_FOUND_IN_DATABASE;
2047 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
2049 __COUT__ <<
"uuid not found: " << uuid << __E__;
2050 return NOT_FOUND_IN_DATABASE;
2054 dejumble(username, LoginSessions_[i].id_);
2057 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
2059 __COUT__ <<
"Cookie code not found" << __E__;
2060 return NOT_FOUND_IN_DATABASE;
2064 if((j = searchUsersDatabaseForUserId(ActiveSessions_[i].userId_)) ==
2065 NOT_FOUND_IN_DATABASE)
2067 __COUT__ <<
"User ID not found" << __E__;
2068 return NOT_FOUND_IN_DATABASE;
2072 if(Users_[j].username_ != username)
2074 __COUT__ <<
"cookieCode: " << cookieCode <<
" was.." << __E__;
2075 __COUT__ <<
"username: " << username <<
" is not found" << __E__;
2076 return NOT_FOUND_IN_DATABASE;
2079 username = Users_[j].displayName_;
2080 cookieCode = refreshCookieCode(i);
2081 return Users_[j].userId_;
2090 std::vector<uint64_t> uniqueAsi;
2093 for(i = 0; i < ActiveSessions_.size(); ++i)
2094 if(ActiveSessions_[i].userId_ == uid)
2099 for(j = 0; j < uniqueAsi.size(); ++j)
2100 if(uniqueAsi[j] == ActiveSessions_[i].sessionIndex_)
2107 uniqueAsi.push_back(ActiveSessions_[i].sessionIndex_);
2110 __COUT__ <<
"Found " << uniqueAsi.size() <<
" active sessions for uid " << uid
2113 return uniqueAsi.size();
2129 if(time(0) > ipSecurityLastLoadTime_ +
2132 ipSecurityLastLoadTime_ = time(0);
2133 loadIPAddressSecurity();
2136 for(
const auto& acceptIp : ipAccessAccept_)
2139 __COUTV__(acceptIp);
2142 for(
const auto& rejectIp : ipAccessReject_)
2145 __COUTV__(rejectIp);
2148 if(ipBlacklistEnabled_)
2149 for(
const auto& blacklistIp : ipAccessBlacklist_)
2152 __COUTV__(blacklistIp);
2162 void WebUsers::incrementIpBlacklistCount(
const std::string& ip)
2164 if(!ipBlacklistEnabled_)
2167 if(ip ==
"0" || ipAccessBlacklist_.find(ip) != ipAccessBlacklist_.end())
2171 auto it = ipBlacklistCounts_.find(ip);
2172 if(it == ipBlacklistCounts_.end())
2174 __COUT__ <<
"First error for ip '" << ip <<
"'" << __E__;
2175 ipBlacklistCounts_[ip] = 1;
2181 if(it->second >= IP_BLACKLIST_COUNT_THRESHOLD)
2183 __COUT_WARN__ <<
"Adding IP '" << ip <<
"' to blacklist!" << __E__;
2185 ipAccessBlacklist_.emplace(ip);
2186 __COUTV__(ipAccessBlacklist_.size());
2189 FILE* fp = fopen((IP_BLACKLIST_FILE).c_str(),
"a");
2192 __COUT_ERR__ <<
"IP blacklist file '" << IP_BLACKLIST_FILE
2193 <<
"' could not be opened." << __E__;
2196 fprintf(fp,
"%s\n", ip.c_str());
2207 if((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE)
2209 return Users_[i].displayName_;
2217 if((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE)
2219 return Users_[i].username_;
2234 bool logoutOtherUserSessions,
2236 const std::string& ip)
2241 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
2243 __COUT__ <<
"Cookie code not found" << __E__;
2245 incrementIpBlacklistCount(ip);
2247 return NOT_FOUND_IN_DATABASE;
2250 ipBlacklistCounts_[ip] = 0;
2253 if(ActiveSessions_[i].ip_ != ip)
2255 __COUT__ <<
"IP does not match active session" << __E__;
2256 return NOT_FOUND_IN_DATABASE;
2265 uint64_t asi = ActiveSessions_[i].sessionIndex_;
2266 uint64_t uid = ActiveSessions_[i].userId_;
2269 uint64_t logoutCount = 0;
2272 while(i < ActiveSessions_.size())
2274 if((logoutOtherUserSessions && ActiveSessions_[i].userId_ == uid &&
2275 ActiveSessions_[i].sessionIndex_ != asi) ||
2276 (!logoutOtherUserSessions && ActiveSessions_[i].userId_ == uid &&
2277 ActiveSessions_[i].sessionIndex_ == asi))
2279 __COUT__ <<
"Logging out of active session " << ActiveSessions_[i].userId_
2280 <<
"-" << ActiveSessions_[i].sessionIndex_ << __E__;
2281 ActiveSessions_.erase(ActiveSessions_.begin() + i);
2288 __COUT__ <<
"Found and removed active session count = " << logoutCount << __E__;
2291 if(CareAboutCookieCodes_ && usersUsernameWithLock_ !=
"" && !
isUserIdActive(uid) &&
2294 __COUT_INFO__ <<
"User '" << usersUsernameWithLock_
2295 <<
"' logged out while holding the lock - releasing lock." << __E__;
2296 std::string lockedUser = usersUsernameWithLock_;
2297 usersUsernameWithLock_ =
"";
2299 saveLockStateToFile();
2301 lockedUser +
" logged out and the system lock was released.");
2321 std::string& cookieCode,
2322 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>* userPermissions,
2324 const std::string& ip,
2327 std::string* userWithLock,
2328 uint64_t* userSessionIndex)
2336 __COUT_ERR__ <<
"User IP rejected." << __E__;
2337 cookieCode = REQ_NO_LOGIN_RESPONSE;
2343 uint64_t i, j, userId = NOT_FOUND_IN_DATABASE, userSession = NOT_FOUND_IN_DATABASE;
2345 __COUTTV__(CareAboutCookieCodes_);
2346 __COUTT__ <<
"refresh=" << refresh <<
", doNotGoRemote=" << doNotGoRemote << __E__;
2347 __COUTVS__(2, cookieCode);
2351 __COUTTV__(localEnableRemoteLogin);
2356 if(localEnableRemoteLogin &&
2357 time(0) > remoteLoginVerificationEnabledBlackoutTime_ &&
2358 (userId = checkRemoteLoginVerification(
2359 cookieCode, refresh, doNotGoRemote, ip)) != NOT_FOUND_IN_DATABASE)
2362 __COUTT__ <<
"Remote login session verified." << __E__;
2363 userSession = RemoteSessions_.at(cookieCode).sessionIndex_;
2368 std::string err =
"";
2373 catch(
const std::exception& e)
2378 __COUT_WARN__ <<
"Ignoring exception during remote login verification. " << err
2382 if(!CareAboutCookieCodes_ && localEnableRemoteLogin &&
2383 remoteLoginVerificationEnabledBlackoutTime_ == 0)
2386 localEnableRemoteLogin =
false;
2387 remoteLoginVerificationEnabledBlackoutTime_ = time(0) + 10;
2388 __COUT_INFO__ <<
"Disabled remote login until "
2390 remoteLoginVerificationEnabledBlackoutTime_)
2394 __COUTTV__(localEnableRemoteLogin);
2396 if(localEnableRemoteLogin && userId == NOT_FOUND_IN_DATABASE)
2397 __COUTT__ <<
"Remote login verification failed." << __E__;
2399 if(!CareAboutCookieCodes_ &&
2400 userId == NOT_FOUND_IN_DATABASE)
2404 std::map<std::string , WebUsers::permissionLevel_t>(
2409 *userWithLock = usersUsernameWithLock_;
2410 if(userSessionIndex)
2411 *userSessionIndex = 0;
2413 if(cookieCode.size() != COOKIE_CODE_LENGTH)
2414 cookieCode = genCookieCode();
2416 if(localEnableRemoteLogin)
2418 cookieCode = WebUsers::
2426 if(userId == NOT_FOUND_IN_DATABASE)
2429 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) ==
2430 NOT_FOUND_IN_DATABASE)
2432 __COUT_ERR__ <<
"Cookie code not found" << __E__;
2433 cookieCode = REQ_NO_LOGIN_RESPONSE;
2435 incrementIpBlacklistCount(ip);
2440 ipBlacklistCounts_[ip] = 0;
2443 if(ip !=
"0" && ActiveSessions_[i].ip_ != ip)
2445 __COUTV__(ActiveSessions_[i].ip_);
2447 __COUT_ERR__ <<
"IP does not match active session." << __E__;
2448 cookieCode = REQ_NO_LOGIN_RESPONSE;
2452 userId = ActiveSessions_[i].userId_;
2453 userSession = ActiveSessions_[i].sessionIndex_;
2454 cookieCode = refreshCookieCode(i, refresh);
2455 __COUTT__ <<
"Login session verified." << __E__;
2461 if((j = searchUsersDatabaseForUserId(userId)) == NOT_FOUND_IN_DATABASE)
2463 __COUT_ERR__ <<
"After login verification, User ID not found! Notify admins."
2465 cookieCode = REQ_NO_LOGIN_RESPONSE;
2469 std::map<std::string , WebUsers::permissionLevel_t> tmpPerm =
2472 if(isInactiveForGroup(tmpPerm))
2474 __COUTT__ <<
"Inactive user identified." << __E__;
2475 cookieCode = REQ_NO_PERMISSION_RESPONSE;
2481 *userPermissions = tmpPerm;
2485 *userWithLock = usersUsernameWithLock_;
2486 if(userSessionIndex)
2487 *userSessionIndex = userSession;
2504 if(loggedOutUsernames)
2506 for(i = 0; i < UsersLoggedOutUsernames_.size(); ++i)
2507 loggedOutUsernames->push_back(UsersLoggedOutUsernames_[i]);
2508 UsersLoggedOutUsernames_.clear();
2512 for(i = 0; i < LoginSessions_.size(); ++i)
2513 if(LoginSessions_[i].startTime_ + LOGIN_SESSION_EXPIRATION_TIME <
2515 LoginSessions_[i].loginAttempts_ > LOGIN_SESSION_ATTEMPTS_MAX)
2517 __COUT__ <<
"Found expired login sessions: #" << (i + 1) <<
" of "
2518 << LoginSessions_.size() << __E__;
2522 LoginSessions_.erase(LoginSessions_.begin() + i);
2535 for(i = 0; i < ActiveSessions_.size(); ++i)
2536 if(ActiveSessions_[i].startTime_ + ACTIVE_SESSION_EXPIRATION_TIME <=
2546 __COUT__ <<
"Found expired active sessions: #" << (i + 1) <<
" of "
2547 << ActiveSessions_.size() << __E__;
2548 __COUTTV__(ActiveSessions_[i].cookieCode_);
2550 tmpUid = ActiveSessions_[i].userId_;
2551 ActiveSessions_.erase(ActiveSessions_.begin() + i);
2556 if(loggedOutUsernames)
2557 loggedOutUsernames->push_back(
2558 Users_[searchUsersDatabaseForUserId(tmpUid)].username_);
2560 UsersLoggedOutUsernames_.push_back(
2561 Users_[searchUsersDatabaseForUserId(tmpUid)].username_);
2582 if(CareAboutCookieCodes_ && usersUsernameWithLock_ !=
"" &&
2591 __COUT_INFO__ <<
"User '" << usersUsernameWithLock_
2592 <<
"' session expired while holding the lock - releasing lock."
2594 std::string lockedUser = usersUsernameWithLock_;
2595 usersUsernameWithLock_ =
"";
2597 saveLockStateToFile();
2599 "*", lockedUser +
" session expired and the system lock was released.");
2601 else if(CareAboutCookieCodes_ && usersUsernameWithLock_ !=
"")
2605 time_t mostRecentActivity = 0;
2606 for(
const auto& session : ActiveSessions_)
2608 uint64_t idx = searchUsersDatabaseForUserId(session.userId_);
2609 if(idx != NOT_FOUND_IN_DATABASE &&
2610 Users_[idx].username_ == usersUsernameWithLock_ &&
2611 session.lastActivityTime_ > mostRecentActivity)
2613 mostRecentActivity = session.lastActivityTime_;
2616 __COUTS__(20) <<
"usersUsernameWithLock_ stale? " << time(0) - mostRecentActivity
2617 <<
" seconds of inactivity for user " << usersUsernameWithLock_
2619 if(mostRecentActivity > 0 &&
2620 (time(0) - mostRecentActivity) >= LOCK_INACTIVITY_TIMEOUT)
2622 __COUT_INFO__ <<
"User '" << usersUsernameWithLock_
2623 <<
"' has been inactive for " << LOCK_INACTIVITY_TIMEOUT
2624 <<
" seconds - releasing lock." << __E__;
2625 std::string lockedUser = usersUsernameWithLock_;
2626 usersUsernameWithLock_ =
"";
2628 saveLockStateToFile();
2630 lockedUser +
" has been idle for " +
2631 std::to_string(LOCK_INACTIVITY_TIMEOUT / 60) +
2632 " minutes and the system lock was released.");
2644 std::vector<std::string> toErase;
2645 for(
const auto& remoteSession : RemoteSessions_)
2646 if(remoteSession.second.startTime_ + ACTIVE_SESSION_EXPIRATION_TIME / 4 <=
2649 __COUT__ <<
"Found expired remote active sessions: #" << remoteSession.first
2650 <<
" in " << RemoteSessions_.size() << __E__;
2651 toErase.push_back(remoteSession.first);
2653 for(
const auto& eraseId : toErase)
2654 RemoteSessions_.erase(eraseId);
2665 const std::string& ip)
2671 for(; i < LoginSessions_.size(); ++i)
2672 if(LoginSessions_[i].uuid_ == UUID)
2675 if(i != LoginSessions_.size())
2677 __COUT_ERR__ <<
"UUID: " << UUID <<
" is not unique" << __E__;
2683 LoginSessions_.back().uuid_ = UUID;
2687 std::string sid =
"";
2688 for(i = 0; i < SESSION_ID_LENGTH / 2; ++i)
2690 intToHexStr(rand(), hexStr);
2693 LoginSessions_.back().id_ = sid;
2694 LoginSessions_.back().ip_ = ip;
2695 LoginSessions_.back().startTime_ = time(0);
2696 LoginSessions_.back().loginAttempts_ = 0;
2706 std::string WebUsers::sha512(
const std::string& user,
2707 const std::string& password,
2710 SHA512_CTX sha512_context;
2715 SHA512_Init(&sha512_context);
2717 for(
unsigned int i = 0; i < 8; ++i)
2718 sha512_context.h[i] += rand();
2720 for(
unsigned int i = 0; i <
sizeof(SHA512_CTX); ++i)
2722 intToHexStr((uint8_t)(((uint8_t*)(&sha512_context))[i]), hexStr);
2724 salt.append(hexStr);
2732 for(
unsigned int i = 0; i <
sizeof(SHA512_CTX); ++i)
2733 ((uint8_t*)(&sha512_context))[i] = hexByteStrToInt(&(salt.c_str()[i * 2]));
2736 std::string strToHash = salt + user + password;
2739 unsigned char hash[SHA512_DIGEST_LENGTH];
2741 char retHash[SHA512_DIGEST_LENGTH * 2 + 1];
2745 SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length());
2747 SHA512_Final(hash, &sha512_context);
2751 for(i = 0; i < SHA512_DIGEST_LENGTH; i++)
2752 sprintf(retHash + (i * 2),
"%02x", hash[i]);
2755 retHash[SHA512_DIGEST_LENGTH * 2] =
'\0';
2766 std::string WebUsers::dejumble(
const std::string& u,
const std::string& s)
2768 if(s.length() != SESSION_ID_LENGTH)
2771 const int ss = s.length() / 2;
2772 int p = hexByteStrToInt(&(s.c_str()[0])) % ss;
2773 int n = hexByteStrToInt(&(s.c_str()[p * 2])) % ss;
2774 int len = (hexByteStrToInt(&(u.c_str()[p * 2])) - p - n + ss * 3) % ss;
2776 std::vector<bool> x(ss);
2777 for(
int i = 0; i < ss; ++i)
2781 int c = hexByteStrToInt(&(u.c_str()[p * 2]));
2783 std::string user =
"";
2785 for(
int l = 0; l < len; ++l)
2787 p = (p + hexByteStrToInt(&(s.c_str()[p * 2]))) % ss;
2791 n = hexByteStrToInt(&(s.c_str()[p * 2]));
2792 user.append(1, (hexByteStrToInt(&(u.c_str()[p * 2])) - c - n + ss * 4) % ss);
2793 c = hexByteStrToInt(&(u.c_str()[p * 2]));
2802 std::map<std::string , WebUsers::permissionLevel_t>
2805 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2806 if(userIndex < Users_.size())
2807 return Users_[userIndex].permissions_;
2810 std::map<std::string , WebUsers::permissionLevel_t> retErrorMap;
2811 retErrorMap[WebUsers::DEFAULT_USER_GROUP] = WebUsers::PERMISSION_LEVEL_INACTIVE;
2848 WebUsers::permissionLevel_t WebUsers::getPermissionLevelForGroup(
2849 const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
2850 const std::string& groupName)
2852 auto it = permissionMap.find(groupName);
2853 if(it == permissionMap.end())
2855 __COUT__ <<
"Group name '" << groupName
2856 <<
"' not found - assuming inactive user in this group." << __E__;
2857 return WebUsers::PERMISSION_LEVEL_INACTIVE;
2863 bool WebUsers::isInactiveForGroup(
2864 const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
2865 const std::string& groupName)
2867 return getPermissionLevelForGroup(permissionMap, groupName) ==
2868 WebUsers::PERMISSION_LEVEL_INACTIVE;
2872 bool WebUsers::isAdminForGroup(
2873 const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
2874 const std::string& groupName)
2876 return getPermissionLevelForGroup(permissionMap, groupName) ==
2883 std::string WebUsers::getTooltipFilename(
const std::string& username,
2884 const std::string& srcFile,
2885 const std::string& srcFunc,
2886 const std::string& srcId)
2888 std::string filename = (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH +
"/";
2892 mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755);
2893 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755);
2894 mkdir(filename.c_str(), 0755);
2896 for(
const char& c : username)
2898 (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9'))
2903 mkdir(filename.c_str(), 0755);
2905 for(
const char& c : srcFile)
2907 (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9'))
2910 for(
const char& c : srcFunc)
2912 (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9'))
2915 for(
const char& c : srcId)
2917 (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9'))
2924 std::string ots::WebUsers::getUserEmailFromFingerprint(
const std::string& fingerprint)
2926 __COUT__ <<
"Checking if user fingerprint " << fingerprint <<
" is in memory database"
2928 if(certFingerprints_.count(fingerprint))
2930 return certFingerprints_[fingerprint];
2933 __COUT__ <<
"Going to read credential database " << WEB_LOGIN_CERTDATA_PATH << __E__;
2934 std::ifstream f(WEB_LOGIN_CERTDATA_PATH);
2942 if(fp !=
"NOKEY" && fp !=
"")
2944 __COUT__ <<
"Adding user " << email <<
" to list with fingerprint " << fp
2946 certFingerprints_[fp] = email;
2952 remove(WEB_LOGIN_CERTDATA_PATH.c_str());
2955 __COUT__ <<
"Checking again if fingerprint is in memory database" << __E__;
2956 if(certFingerprints_.count(fingerprint))
2958 return certFingerprints_[fingerprint];
2961 __COUT__ <<
"Could not match fingerprint, returning null email" << __E__;
2970 const std::string& srcFile,
2971 const std::string& srcFunc,
2972 const std::string& srcId,
2974 bool temporarySilence)
2976 std::string filename;
2977 bool isForAll = (srcFile ==
"ALL" && srcFunc ==
"ALL" && srcId ==
"ALL");
2981 __COUT__ <<
"Disabling ALL tooltips for user '" << username <<
"' is now set to "
2982 << doNeverShow <<
" (temporarySilence=" << temporarySilence <<
")"
2984 filename = getTooltipFilename(username, SILENCE_ALL_TOOLTIPS_FILENAME,
"",
"");
2988 filename = getTooltipFilename(username, srcFile, srcFunc, srcId);
2989 __COUT__ <<
"Setting tooltip never show for user '" << username <<
"' to "
2990 << doNeverShow <<
" (temporarySilence=" << temporarySilence <<
")"
2995 __COUTTV__(doNeverShow);
2996 __COUTTV__(temporarySilence);
2998 __COUTTV__(srcFunc);
2999 __COUTTV__(srcFile);
3000 __COUTTV__(filename);
3003 FILE* fp = fopen(filename.c_str(),
"w");
3006 if(temporarySilence)
3009 time(0) + 7 * 24 * 60 * 60);
3010 else if(!isForAll && doNeverShow && username == WebUsers::DEFAULT_ADMIN_USERNAME)
3013 fprintf(fp,
"%ld", time(0) + 30 * 24 * 60 * 60);
3015 __COUT__ <<
"User '" << username
3016 <<
"' may be a shared account, so max silence duration for tooltips "
3017 "is 30 days. Silencing now."
3021 fputc(doNeverShow ?
'1' :
'0', fp);
3025 __COUT_ERR__ <<
"Big problem with tooltips! File not accessible: " << filename
3038 const std::string& srcFile,
3039 const std::string& srcFunc,
3040 const std::string& srcId)
3042 if(srcId ==
"ALWAYS")
3045 xmldoc->addTextElementToData(
"ShowTooltip",
"1");
3050 std::string silencefilename =
3051 getTooltipFilename(username, SILENCE_ALL_TOOLTIPS_FILENAME,
"",
"");
3055 __COUTTV__(username);
3057 __COUTTV__(srcFunc);
3058 __COUTTV__(srcFile);
3059 __COUTTV__(silencefilename);
3062 FILE* silencefp = fopen(silencefilename.c_str(),
"r");
3063 if(silencefp != NULL)
3067 fgets(line, 100, silencefp);
3068 sscanf(line,
"%ld", &val);
3072 xmldoc->addTextElementToData(
"ShowTooltip",
"0");
3078 std::string filename = getTooltipFilename(username, srcFile, srcFunc, srcId);
3079 FILE* fp = fopen(filename.c_str(),
"r");
3084 fgets(line, 100, fp);
3085 sscanf(line,
"%ld", &val);
3088 __COUT__ <<
"tooltip value read = " << val <<
" vs time(0)=" << time(0) << __E__;
3092 xmldoc->addTextElementToData(
"ShowTooltip",
3093 val == 1 ?
"0" : (time(0) > val ?
"1" :
"0"));
3097 xmldoc->addTextElementToData(
"ShowTooltip",
"1");
3107 (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH +
"/" + userNeedle)
3109 __COUT__ <<
"Successfully reset Tooltips for user " << userNeedle << __E__;
3117 std::string silencefilename = getTooltipFilename(
3118 username, SILENCE_ALL_TOOLTIPS_FILENAME,
"",
"");
3120 __COUTV__(silencefilename);
3121 FILE* silencefp = fopen(silencefilename.c_str(),
"w");
3122 if(silencefp != NULL)
3124 fputs(
"1", silencefp);
3155 bool includeAccounts,
3156 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>
3159 if(permissionMap.size() == 0)
3161 __COUTT__ <<
"Getting local permissions for user " << uid << __E__;
3166 if(isInactiveForGroup(permissionMap))
3169 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
3170 __COUT__ <<
"Gettings settings for user: " << Users_[userIndex].username_ << __E__;
3172 std::string fn = (std::string)WEB_LOGIN_DB_PATH +
3173 (std::string)USERS_PREFERENCES_PATH + Users_[userIndex].username_ +
3174 "." + (std::string)USERS_PREFERENCES_FILETYPE;
3178 __COUT__ <<
"Preferences file: " << fn << __E__;
3182 __COUT__ <<
"Preferences are defaults." << __E__;
3184 xmldoc->addTextElementToData(PREF_XML_BGCOLOR_FIELD, PREF_XML_BGCOLOR_DEFAULT);
3185 xmldoc->addTextElementToData(PREF_XML_DBCOLOR_FIELD, PREF_XML_DBCOLOR_DEFAULT);
3186 xmldoc->addTextElementToData(PREF_XML_WINCOLOR_FIELD, PREF_XML_WINCOLOR_DEFAULT);
3187 xmldoc->addTextElementToData(PREF_XML_LAYOUT_FIELD, PREF_XML_LAYOUT_DEFAULT);
3191 __COUT__ <<
"Saved Preferences found." << __E__;
3196 if(includeAccounts && isAdminForGroup(permissionMap))
3198 __COUT__ <<
"Admin on our hands" << __E__;
3200 xmldoc->addTextElementToData(PREF_XML_ACCOUNTS_FIELD,
"");
3202 if(Users_.size() == 0)
3204 __COUT__ <<
"Missing users? Attempting to load database" << __E__;
3209 for(uint64_t i = 0; i < Users_.size(); ++i)
3212 "username", Users_[i].username_, PREF_XML_ACCOUNTS_FIELD);
3214 "display_name", Users_[i].displayName_, PREF_XML_ACCOUNTS_FIELD);
3216 if(Users_[i].email_.size() > i)
3219 "useremail", Users_[i].email_, PREF_XML_ACCOUNTS_FIELD);
3229 PREF_XML_ACCOUNTS_FIELD);
3232 "nac", Users_[i].getNewAccountCode().c_str(), PREF_XML_ACCOUNTS_FIELD);
3237 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
3238 (std::string)SYSTEM_PREFERENCES_PREFIX +
"." +
3239 (std::string)USERS_PREFERENCES_FILETYPE;
3242 __COUT__ <<
"System Preferences are defaults." << __E__;
3244 xmldoc->addTextElementToData(PREF_XML_SYSLAYOUT_FIELD,
3245 PREF_XML_SYSLAYOUT_DEFAULT);
3249 __COUT__ <<
"Saved System Preferences found." << __E__;
3256 xmldoc->addTextElementToData(PREF_XML_PERMISSIONS_FIELD,
3260 xmldoc->addTextElementToData(PREF_XML_USERLOCK_FIELD, usersUsernameWithLock_);
3263 xmldoc->addTextElementToData(PREF_XML_USERNAME_FIELD,
getUsersUsername(uid));
3269 xmldoc->addTextElementToData(
"ots_remote_address",
3270 remoteLoginVerificationIP_ +
":" +
3279 const std::string& preferenceName,
3280 const std::string& preferenceValue)
3282 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
3287 std::string safePreferenceName =
"";
3288 for(
const auto& c : preferenceName)
3289 if((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9') ||
3290 (c >=
'-' || c <=
'_'))
3291 safePreferenceName += c;
3293 std::string dir = (std::string)WEB_LOGIN_DB_PATH +
3294 (std::string)USERS_PREFERENCES_PATH +
"generic_" +
3295 safePreferenceName +
"/";
3298 mkdir(dir.c_str(), 0755);
3300 std::string fn = Users_[userIndex].username_ +
"_" + safePreferenceName +
"." +
3301 (std::string)USERS_PREFERENCES_FILETYPE;
3303 __COUT__ <<
"Preferences file: " << (dir + fn) << __E__;
3305 FILE* fp = fopen((dir + fn).c_str(),
"w");
3308 fprintf(fp,
"%s", preferenceValue.c_str());
3312 __COUT_ERR__ <<
"Preferences file could not be opened for writing!" << __E__;
3320 const std::string& preferenceName,
3323 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
3328 std::string safePreferenceName =
"";
3329 for(
const auto& c : preferenceName)
3330 if((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9') ||
3331 (c >=
'-' || c <=
'_'))
3332 safePreferenceName += c;
3334 std::string dir = (std::string)WEB_LOGIN_DB_PATH +
3335 (std::string)USERS_PREFERENCES_PATH +
"generic_" +
3336 safePreferenceName +
"/";
3338 std::string fn = Users_[userIndex].username_ +
"_" + safePreferenceName +
"." +
3339 (std::string)USERS_PREFERENCES_FILETYPE;
3341 __COUT__ <<
"Preferences file: " << (dir + fn) << __E__;
3344 FILE* fp = fopen((dir + fn).c_str(),
"r");
3347 fseek(fp, 0, SEEK_END);
3348 const long size = ftell(fp);
3349 char* line =
new char
3353 fread(line, 1, size, fp);
3356 std::string retVal(line, size);
3359 __COUT__ <<
"Read value (sz = " << retVal.size() <<
") " << retVal << __E__;
3361 xmldoc->addTextElementToData(safePreferenceName, retVal);
3365 __COUT__ <<
"Using default value." << __E__;
3369 xmldoc->addTextElementToData(safePreferenceName,
"");
3376 const std::string& bgcolor,
3377 const std::string& dbcolor,
3378 const std::string& wincolor,
3379 const std::string& layout,
3380 const std::string& syslayout,
3381 const std::string& aliaslayout,
3382 const std::string& sysaliaslayout)
3384 std::map<std::string , WebUsers::permissionLevel_t> permissionMap =
3386 if(isInactiveForGroup(permissionMap))
3389 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
3390 __COUT__ <<
"Changing settings for user: " << Users_[userIndex].username_ << __E__;
3392 std::string fn = (std::string)WEB_LOGIN_DB_PATH +
3393 (std::string)USERS_PREFERENCES_PATH + Users_[userIndex].username_ +
3394 "." + (std::string)USERS_PREFERENCES_FILETYPE;
3396 __COUT__ <<
"Preferences file: " << fn << __E__;
3399 prefXml.addTextElementToData(PREF_XML_BGCOLOR_FIELD, bgcolor);
3400 prefXml.addTextElementToData(PREF_XML_DBCOLOR_FIELD, dbcolor);
3401 prefXml.addTextElementToData(PREF_XML_WINCOLOR_FIELD, wincolor);
3402 prefXml.addTextElementToData(PREF_XML_LAYOUT_FIELD, layout);
3403 prefXml.addTextElementToData(PREF_XML_ALIAS_LAYOUT_FIELD, aliaslayout);
3408 if(!isAdminForGroup(permissionMap))
3412 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
3413 (std::string)SYSTEM_PREFERENCES_PREFIX +
"." +
3414 (std::string)USERS_PREFERENCES_FILETYPE;
3417 sysPrefXml.addTextElementToData(PREF_XML_SYSLAYOUT_FIELD, syslayout);
3418 sysPrefXml.addTextElementToData(PREF_XML_SYSALIAS_LAYOUT_FIELD, sysaliaslayout);
3426 void WebUsers::saveLockStateToFile()
3428 std::string securityFileName = USER_WITH_LOCK_FILE;
3429 FILE* fp = fopen(securityFileName.c_str(),
"w");
3432 fprintf(fp,
"%s", usersUsernameWithLock_.c_str());
3436 __COUT_INFO__ <<
"USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE
3437 <<
" could not be written. Ignoring." << __E__;
3447 std::map<std::string , WebUsers::permissionLevel_t> permissionMap =
3453 __COUTV__(actingUser);
3455 __COUTV__(usersUsernameWithLock_);
3457 __COUTV__(username);
3458 __COUTV__(isUserActive);
3461 (isUserActive || !WebUsers::CareAboutCookieCodes_))
3463 if(!WebUsers::CareAboutCookieCodes_ && !isUserActive &&
3465 DEFAULT_ADMIN_USERNAME)
3468 <<
"User '" << actingUser
3469 <<
"' tried to lock for a user other than admin in wiz mode. Not allowed."
3473 else if(!isAdminForGroup(permissionMap) &&
3474 actingUser != username)
3476 __COUT_ERR__ <<
"A non-admin user '" << actingUser
3477 <<
"' tried to lock for a user other than self. Not allowed."
3481 usersUsernameWithLock_ = username;
3483 else if(!lock && usersUsernameWithLock_ == username)
3484 usersUsernameWithLock_ =
"";
3488 __COUT_INFO__ <<
"User '" << username <<
"' is inactive so not giving lock."
3491 __COUT_ERR__ <<
"Failed to lock for user '" << username <<
".'" << __E__;
3495 __COUT_INFO__ <<
"User '" << username <<
"' has locked out the system!" << __E__;
3498 saveLockStateToFile();
3506 const std::string& username,
3507 const std::string& displayname,
3508 const std::string& email,
3509 const std::string& permissions)
3511 std::map<std::string , WebUsers::permissionLevel_t> permissionMap =
3513 if(!isAdminForGroup(permissionMap))
3516 __SS__ <<
"Only admins can modify user settings." << __E__;
3520 uint64_t i = searchUsersDatabaseForUserId(actingUid);
3521 uint64_t modi = searchUsersDatabaseForUsername(username);
3526 __COUT_INFO__ <<
"Admin password reset." << __E__;
3527 Users_[modi].setModifier(Users_[i].username_);
3528 Users_[modi].salt_ =
"";
3529 Users_[modi].loginFailureCount_ = 0;
3530 saveDatabaseToFile(DB_USERS);
3533 __SS__ <<
"Cannot modify first user" << __E__;
3537 if(username.length() < USERNAME_LENGTH)
3539 __SS__ <<
"Invalid Username, must be length " << USERNAME_LENGTH << __E__;
3542 if(displayname.length() < DISPLAY_NAME_LENGTH)
3544 __SS__ <<
"Invalid Display Name; must be length " << DISPLAY_NAME_LENGTH << __E__;
3548 __COUT__ <<
"Input Permissions: " << permissions << __E__;
3549 std::map<std::string , WebUsers::permissionLevel_t> newPermissionsMap;
3553 case MOD_TYPE_UPDATE:
3555 __COUT__ <<
"MOD_TYPE_UPDATE " << username <<
" := " << permissions << __E__;
3557 if(modi == NOT_FOUND_IN_DATABASE)
3559 __SS__ <<
"User not found!? Should not happen." << __E__;
3565 for(uint64_t i = 0; i < Users_.size(); ++i)
3568 else if(Users_[i].displayName_ == displayname)
3570 __SS__ <<
"Display Name '" << displayname
3571 <<
"' already exists! Please choose a unique display name."
3577 Users_[modi].displayName_ = displayname;
3578 Users_[modi].email_ = email;
3582 bool wasInactive = isInactiveForGroup(Users_[modi].permissions_);
3585 if(newPermissionsMap.size() == 0)
3586 Users_[modi].permissions_[WebUsers::DEFAULT_USER_GROUP] =
3587 std::atoi(permissions.c_str());
3588 else if(newPermissionsMap.size() == 1 &&
3589 newPermissionsMap.find(WebUsers::DEFAULT_USER_GROUP) ==
3590 newPermissionsMap.end())
3592 if(newPermissionsMap.begin()->first ==
"")
3593 Users_[modi].permissions_[WebUsers::DEFAULT_USER_GROUP] =
3594 newPermissionsMap.begin()->second;
3597 newPermissionsMap[WebUsers::DEFAULT_USER_GROUP] =
3598 newPermissionsMap.begin()->second;
3599 Users_[modi].permissions_ = newPermissionsMap;
3603 Users_[modi].permissions_ = newPermissionsMap;
3608 !isInactiveForGroup(Users_[modi].permissions_))
3610 __COUT__ <<
"Reactivating " << username << __E__;
3611 Users_[modi].loginFailureCount_ = 0;
3612 Users_[modi].salt_ =
"";
3618 if(i == NOT_FOUND_IN_DATABASE)
3620 __SS__ <<
"Master User not found!? Should not happen." << __E__;
3623 Users_[modi].setModifier(Users_[i].username_);
3629 __COUT__ <<
"MOD_TYPE_ADD " << username <<
" - " << displayname << __E__;
3634 if(i == NOT_FOUND_IN_DATABASE)
3636 __SS__ <<
"Master User not found!? Should not happen." << __E__;
3639 Users_.back().setModifier(Users_[i].username_);
3642 if(permissions.size())
3645 actingUid, MOD_TYPE_UPDATE, username, displayname, email, permissions);
3649 case MOD_TYPE_DELETE:
3650 __COUT__ <<
"MOD_TYPE_DELETE " << username <<
" - " << displayname << __E__;
3651 deleteAccount(username, displayname);
3654 __SS__ <<
"Undefined command - do nothing " << username << __E__;
3658 saveDatabaseToFile(DB_USERS);
3659 loadSecuritySelection();
3667 std::set<unsigned int> activeUserIndices;
3668 for(uint64_t i = 0; i < ActiveSessions_.size(); ++i)
3669 activeUserIndices.emplace(
3670 searchUsersDatabaseForUserId(ActiveSessions_[i].userId_));
3672 for(
const auto& sessionPair : RemoteSessions_)
3673 activeUserIndices.emplace(
3674 searchUsersDatabaseForUserId(sessionPair.second.userId_));
3675 return activeUserIndices.size();
3683 std::set<unsigned int> activeUserIndices;
3684 for(uint64_t i = 0; i < ActiveSessions_.size(); ++i)
3685 activeUserIndices.emplace(
3686 searchUsersDatabaseForUserId(ActiveSessions_[i].userId_));
3688 for(
const auto& sessionPair : RemoteSessions_)
3689 activeUserIndices.emplace(
3690 searchUsersDatabaseForUserId(sessionPair.second.userId_));
3692 std::string activeUsersString =
"";
3693 bool addComma =
false;
3694 for(
const auto& i : activeUserIndices)
3696 if(i >= Users_.size())
3700 activeUsersString +=
",";
3704 activeUsersString += Users_[i].displayName_;
3706 if(activeUserIndices.size() == 0 &&
3708 WebUsers::SECURITY_TYPE_NONE)
3709 activeUsersString += WebUsers::DEFAULT_ADMIN_DISPLAY_NAME;
3711 __COUTVS__(20, activeUsersString);
3712 return activeUsersString;
3720 std::set<unsigned int> activeUserIndices;
3721 for(uint64_t i = 0; i < ActiveSessions_.size(); ++i)
3722 activeUserIndices.emplace(
3723 searchUsersDatabaseForUserId(ActiveSessions_[i].userId_));
3725 for(
const auto& sessionPair : RemoteSessions_)
3726 activeUserIndices.emplace(
3727 searchUsersDatabaseForUserId(sessionPair.second.userId_));
3729 std::string activeUsersString =
"";
3730 bool addComma =
false;
3731 for(
const auto& i : activeUserIndices)
3733 if(i >= Users_.size())
3737 activeUsersString +=
",";
3741 activeUsersString += Users_[i].username_;
3743 if(activeUserIndices.size() == 0 &&
3745 WebUsers::SECURITY_TYPE_NONE)
3746 activeUsersString += WebUsers::DEFAULT_ADMIN_USERNAME;
3748 __COUTVS__(20, activeUsersString);
3749 return activeUsersString;
3757 uint64_t uid = searchUsersDatabaseForUsername(DEFAULT_ADMIN_USERNAME);
3764 void WebUsers::loadUserWithLock()
3766 char username[300] =
"";
3768 std::string securityFileName = USER_WITH_LOCK_FILE;
3769 FILE* fp = fopen(securityFileName.c_str(),
"r");
3772 __COUT_INFO__ <<
"USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE
3773 <<
" not found. Defaulting to admin lock." << __E__;
3776 sprintf(username,
"%s", DEFAULT_ADMIN_USERNAME.c_str());
3780 fgets(username, 300, fp);
3787 __COUT__ <<
"Attempting to load username with lock: " << username << __E__;
3789 if(strlen(username) == 0)
3791 __COUT_INFO__ <<
"Loaded state for user-with-lock is unlocked." << __E__;
3795 uint64_t i = searchUsersDatabaseForUsername(username);
3796 if(i == NOT_FOUND_IN_DATABASE)
3798 __COUT_INFO__ <<
"username " << username <<
" not found in database. Ignoring."
3802 __COUT__ <<
"Setting lock" << __E__;
3810 const std::string& message)
3819 const std::string& subject,
3820 const std::string& message,
3823 std::vector<std::string> targetUsers;
3833 const std::string& subject,
3834 const std::string& message,
3837 systemMessageCleanup();
3839 std::string fullMessage = StringMacros::encodeURIComponent(
3840 (subject ==
"" ?
"" : (subject +
": ")) + message);
3843 std::cout << __COUT_HDR_FL__ <<
"addSystemMessage() fullMessage: " << fullMessage
3847 std::set<std::string> targetEmails;
3849 for(
const auto& targetUser : targetUsers)
3853 if(targetUser ==
"" || (targetUser !=
"*" && targetUser.size() < 3))
3855 __COUT__ <<
"Illegal username '" << targetUser <<
"'" << __E__;
3858 __COUTTV__(targetUser);
3862 if(doEmail && targetUser ==
"*")
3865 for(
const auto& user : Users_)
3867 if(user.email_.size() > 5 &&
3868 user.email_.find(
'@') != std::string::npos &&
3869 user.email_.find(
'.') != std::string::npos)
3871 __COUT__ <<
"Adding " << user.displayName_
3872 <<
" email: " << user.email_ << __E__;
3873 targetEmails.emplace(user.email_);
3878 else if(targetUser.find(
':') != std::string::npos)
3881 __COUT__ <<
"Treating as group email target: " << targetUser << __E__;
3883 std::map<std::string, WebUsers::permissionLevel_t> targetGroupMap;
3890 if(targetGroupMap.size() == 1)
3895 for(
const auto& user : Users_)
3897 WebUsers::permissionLevel_t userLevel =
3899 targetGroupMap.begin()->first);
3903 __COUTV__((
int)userLevel);
3904 __COUTV__(targetGroupMap.begin()->first);
3906 if(userLevel != WebUsers::PERMISSION_LEVEL_INACTIVE &&
3907 userLevel >= targetGroupMap.begin()->second &&
3908 user.email_.size() > 5 &&
3909 user.email_.find(
'@') != std::string::npos &&
3910 user.email_.find(
'.') != std::string::npos)
3914 targetEmails.emplace(user.email_);
3915 __COUT__ <<
"Adding " << user.displayName_
3916 <<
" email: " << user.email_ << __E__;
3918 addSystemMessageToMap(user.displayName_, fullMessage);
3923 __COUT__ <<
"target Group Map from '" << targetUser <<
"' is empty."
3931 addSystemMessageToMap(targetUser, fullMessage);
3935 for(
const auto& user : Users_)
3937 if(user.displayName_ == targetUser)
3939 if(user.email_.size() > 5 &&
3940 user.email_.find(
'@') != std::string::npos &&
3941 user.email_.find(
'.') != std::string::npos)
3943 targetEmails.emplace(user.email_);
3944 __COUT__ <<
"Adding " << user.displayName_
3945 <<
" email: " << user.email_ << __E__;
3954 __COUTV__(targetEmails.size());
3956 if(doEmail && targetEmails.size())
3960 std::string toList =
"";
3961 bool addComma =
false;
3962 for(
const auto& email : targetEmails)
3971 std::string filename = (std::string)WEB_LOGIN_DB_PATH +
3972 (std::string)USERS_DB_PATH +
"/.tmp_email.txt";
3973 FILE* fp = fopen(filename.c_str(),
"w");
3976 __SS__ <<
"Could not open email file: " << filename << __E__;
3986 fprintf(fp,
"To: %s\n", toList.c_str());
3987 fprintf(fp,
"Subject: %s\n", subject.c_str());
3988 fprintf(fp,
"Content-Type: text/html\n");
3989 fprintf(fp,
"\n<html><pre>%s</pre></html>", message.c_str());
3995 __COUT_WARN__ <<
"Do email was attempted, but no target users had email "
3996 "addresses specified!"
4005 void WebUsers::addSystemMessageToMap(
const std::string& targetUser,
4006 const std::string& fullMessage)
4009 std::lock_guard<std::mutex> lock(systemMessageLock_);
4011 __COUTT__ <<
"Before number of users with system messages: " << systemMessages_.size()
4012 <<
", first user has "
4013 << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
4014 <<
" messages." << __E__;
4016 auto it = systemMessages_.find(targetUser);
4019 if(it != systemMessages_.end() && it->second.size() &&
4020 it->second[it->second.size() - 1].message_ == fullMessage)
4023 if(it == systemMessages_.end())
4025 systemMessages_.emplace(
4026 std::pair<std::string , std::vector<SystemMessage>>(
4027 targetUser, std::vector<SystemMessage>({SystemMessage(fullMessage)})));
4028 __COUTT__ << targetUser <<
" Current System Messages count = " << 1 << __E__;
4033 it->second.push_back(SystemMessage(fullMessage));
4034 __COUTT__ << it->first <<
" Current System Messages count = " << it->second.size()
4038 __COUTT__ <<
"After number of users with system messages: " << systemMessages_.size()
4039 <<
", first user has "
4040 << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
4041 <<
" messages." << __E__;
4050 std::lock_guard<std::mutex> lock(systemMessageLock_);
4052 __COUTT__ <<
"GetLast number of users with system messages: "
4053 << systemMessages_.size() <<
", first user has "
4054 << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
4055 <<
" messages." << __E__;
4057 auto it = systemMessages_.find(
"*");
4058 if(it == systemMessages_.end() || it->second.size() == 0)
4059 return std::make_pair(
"", 0);
4061 return std::make_pair(it->second.back().message_, it->second.back().creationTime_);
4070 std::string retStr =
"";
4073 std::lock_guard<std::mutex> lock(systemMessageLock_);
4075 for(
auto& userSysMessages : systemMessages_)
4077 for(
auto& userSysMessage : userSysMessages.second)
4079 if(userSysMessage.deliveredRemote_)
4084 retStr += userSysMessages.first;
4085 retStr +=
"|" + std::to_string(userSysMessage.creationTime_);
4086 retStr +=
"|" + userSysMessage.message_;
4087 userSysMessage.deliveredRemote_ =
true;
4102 __COUTS__(20) <<
"Current System Messages: " << targetUser << __E__;
4103 std::string retStr =
"";
4109 std::lock_guard<std::mutex> lock(systemMessageLock_);
4111 __COUTS__(20) <<
"Number of users with system messages: "
4112 << systemMessages_.size() << __E__;
4115 auto it = systemMessages_.find(
"*");
4116 for(uint64_t i = 0; it != systemMessages_.end() && i < it->second.size(); ++i)
4121 sprintf(tmp,
"%lu", it->second[i].creationTime_);
4122 retStr += std::string(tmp) +
"|" + it->second[i].message_;
4128 __COUTVS__(20, targetUser);
4129 it = systemMessages_.find(targetUser);
4132 for(
auto systemMessagePair : systemMessages_)
4133 __COUTS__(20) << systemMessagePair.first <<
" "
4134 << systemMessagePair.second.size() <<
" "
4135 << (systemMessagePair.second.size()
4136 ? systemMessagePair.second[0].message_
4140 if(it != systemMessages_.end())
4142 __COUTS__(20) <<
"Message count: " << it->second.size() <<
", Last Message: "
4143 << (it->second.size() ? it->second.back().message_ :
"")
4147 for(uint64_t i = 0; it != systemMessages_.end() && i < it->second.size(); ++i)
4152 sprintf(tmp,
"%lu", it->second[i].creationTime_);
4153 retStr += std::string(tmp) +
"|" + it->second[i].message_;
4156 if(!it->second[i].delivered_)
4157 it->second[i].firstDeliveryTime_ = time(0);
4158 it->second[i].delivered_ =
true;
4163 __COUTS__(20) <<
"retStr: " << retStr << __E__;
4165 systemMessageCleanup();
4173 void WebUsers::systemMessageCleanup()
4176 std::lock_guard<std::mutex> lock(systemMessageLock_);
4178 const time_t now = time(0);
4180 __COUTT__ <<
"Before cleanup number of users with system messages: "
4181 << systemMessages_.size() <<
", first user has "
4182 << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
4183 <<
" messages." << __E__;
4184 for(
auto& userMessagesPair : systemMessages_)
4186 for(uint64_t i = 0; i < userMessagesPair.second.size(); ++i)
4188 bool shouldRemove =
false;
4190 if(userMessagesPair.first ==
"*")
4195 shouldRemove =
true;
4202 if(userMessagesPair.second[i].delivered_ &&
4203 userMessagesPair.second[i].firstDeliveryTime_ +
4206 shouldRemove =
true;
4208 else if(userMessagesPair.second[i].creationTime_ +
4211 shouldRemove =
true;
4216 __COUTT__ << userMessagesPair.first
4217 <<
" at time: " << userMessagesPair.second[i].creationTime_
4218 <<
" system messages: " << userMessagesPair.second.size()
4222 userMessagesPair.second.erase(userMessagesPair.second.begin() + i);
4227 __COUTT__ <<
"User '" << userMessagesPair.first
4228 <<
"' remaining system messages: " << userMessagesPair.second.size()
4231 __COUTT__ <<
"After cleanup number of users with system messages: "
4232 << systemMessages_.size() <<
", first user has "
4233 << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
4234 <<
" messages." << __E__;
4242 void WebUsers::loadSecuritySelection()
4244 std::string securityFileName = SECURITY_FILE_NAME;
4245 FILE* fp = fopen(securityFileName.c_str(),
"r");
4246 char line[100] =
"";
4248 fgets(line, 100, fp);
4252 while(i < strlen(line) && line[i] >=
'A' && line[i] <=
'z')
4256 if(strcmp(line, SECURITY_TYPE_NONE.c_str()) == 0 ||
4257 strcmp(line, SECURITY_TYPE_DIGEST_ACCESS.c_str()) == 0)
4258 securityType_ = line;
4260 securityType_ = SECURITY_TYPE_DEFAULT;
4262 __COUT__ <<
"The current security type is " << securityType_ << __E__;
4267 if(securityType_ == SECURITY_TYPE_NONE)
4268 CareAboutCookieCodes_ =
false;
4270 CareAboutCookieCodes_ =
true;
4272 __COUT__ <<
"CareAboutCookieCodes_: " << CareAboutCookieCodes_ << __E__;
4274 loadIPAddressSecurity();
4280 void WebUsers::loadIPAddressSecurity()
4282 ipAccessAccept_.clear();
4283 ipAccessReject_.clear();
4284 ipAccessBlacklist_.clear();
4286 FILE* fp = fopen((IP_ACCEPT_FILE).c_str(),
"r");
4292 while(fgets(line, 300, fp))
4296 if(len > 2 && line[len - 1] ==
'\n')
4297 line[len - 1] =
'\0';
4298 ipAccessAccept_.emplace(line);
4305 __COUTV__(ipAccessAccept_.size());
4307 fp = fopen((IP_REJECT_FILE).c_str(),
"r");
4310 while(fgets(line, 300, fp))
4314 if(len > 2 && line[len - 1] ==
'\n')
4315 line[len - 1] =
'\0';
4316 ipAccessReject_.emplace(line);
4323 __COUTV__(ipAccessReject_.size());
4325 fp = fopen((IP_BLACKLIST_FILE).c_str(),
"r");
4328 while(fgets(line, 300, fp))
4332 if(len > 2 && line[len - 1] ==
'\n')
4333 line[len - 1] =
'\0';
4334 ipAccessBlacklist_.emplace(line);
4341 __COUTV__(ipAccessBlacklist_.size());
4345 void WebUsers::NACDisplayThread(
const std::string& nac,
const std::string& user)
4356 std::this_thread::sleep_for(std::chrono::seconds(2));
4358 <<
"\n******************************************************************** "
4361 <<
"\n******************************************************************** "
4363 __COUT__ <<
"\n\nNew account code = " << nac <<
" for user: " << user <<
"\n"
4366 <<
"\n******************************************************************** "
4369 <<
"\n******************************************************************** "
4375 void WebUsers::deleteUserData()
4377 __COUT__ <<
"$$$$$$$$$$$$$$ Deleting ALL service user data... $$$$$$$$$$$$" << __E__;
4381 (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH +
"/*").c_str());
4383 (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH +
"/*").c_str());
4385 (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH +
"/*")
4388 (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH +
"/*")
4390 std::system((
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH).c_str());
4392 std::string serviceDataPath = __ENV__(
"SERVICE_DATA_PATH");
4394 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/MacroData/").c_str());
4395 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/MacroHistory/").c_str());
4396 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/MacroExport/").c_str());
4400 (
"rm -rf " + std::string(serviceDataPath) +
"/ConsolePreferences/").c_str());
4403 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/CodeEditorData/").c_str());
4406 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/OtsWizardData/").c_str());
4409 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/ProgressBarData/").c_str());
4412 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/RunNumber/").c_str());
4413 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/RunControlData/").c_str());
4416 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/VisualizerData/").c_str());
4423 std::system((
"rm -rf " + std::string(__ENV__(
"LOGBOOK_DATA_PATH")) +
"/").c_str());
4425 __COUT__ <<
"$$$$$$$$$$$$$$ Successfully deleted ALL service user data $$$$$$$$$$$$"
void copyDataChildren(HttpXmlDocument &document)
void removeDataElement(unsigned int dataChildIndex=0)
default to first child
bool loadXmlDocument(const std::string &filePath)
unsigned int getChildrenCount(xercesc::DOMElement *parent=0)
void addSystemMessage(const std::string &targetUsersCSV, const std::string &message)
const std::string & getSecurity(void)
WebUsers::getSecurity.
std::string getGenericPreference(uint64_t uid, const std::string &preferenceName, HttpXmlDocument *xmldoc=0) const
bool setUserWithLock(uint64_t actingUid, bool lock, const std::string &username)
static bool checkRequestAccess(cgicc::Cgicc &cgi, std::ostringstream *out, HttpXmlDocument *xmldoc, WebUsers::RequestUserInfo &userInfo, bool isWizardMode=false, const std::string &wizardModeSequence="")
static void silenceAllUserTooltips(const std::string &username)
void insertSettingsForUser(uint64_t uid, HttpXmlDocument *xmldoc, bool includeAccounts=false, std::map< std::string, WebUsers::permissionLevel_t > permissionMap={})
if empty, fetches local permissions; if provided, overrides with given permissions (e....
size_t getActiveUserCount(void)
std::string getActiveUsernamesString(void)
All active usernames.
std::map< std::string, WebUsers::permissionLevel_t > getPermissionsForUser(uint64_t uid)
from Gateway, use public version which considers remote users
uint64_t attemptActiveSession(const std::string &uuid, std::string &jumbledUser, const std::string &jumbledPw, std::string &newAccountCode, const std::string &ip)
void setGenericPreference(uint64_t uid, const std::string &preferenceName, const std::string &preferenceValue)
std::string getAllSystemMessages(void)
void cleanupExpiredEntries(std::vector< std::string > *loggedOutUsernames=0)
uint64_t isCookieCodeActiveForLogin(const std::string &uuid, std::string &cookieCode, std::string &username)
std::string createNewLoginSession(const std::string &uuid, const std::string &ip)
void createNewAccount(const std::string &username, const std::string &displayName, const std::string &email)
void modifyAccountSettings(uint64_t actingUid, uint8_t cmd_type, const std::string &username, const std::string &displayname, const std::string &email, const std::string &permissions)
WebUsers::modifyAccountSettings.
int remoteLoginVerificationPort_
Port of remote Gateway to be used for login verification.
bool isUsernameActive(const std::string &username) const
bool isUserIdActive(uint64_t uid) const
void saveActiveSessions(void)
std::string getActiveUserDisplayNamesString(void)
All active display names.
static std::atomic< bool > remoteLoginVerificationEnabled_
true if this supervisor is under control of a remote supervisor
uint64_t getAdminUserID(void)
@ SYS_CLEANUP_WILDCARD_TIME
300 seconds
@ SYS_CLEANUP_USER_MESSAGE_TIME
15 seconds - allows multiple browser tabs/devices to receive the same message
std::string getUsersUsername(uint64_t uid)
from Gateway, use public version which considers remote users
static void initializeRequestUserInfo(cgicc::Cgicc &cgi, WebUsers::RequestUserInfo &userInfo)
used by gateway and other supervisors to verify requests consistently
bool checkIpAccess(const std::string &ip)
bool xmlRequestOnGateway(cgicc::Cgicc &cgi, std::ostringstream *out, HttpXmlDocument *xmldoc, WebUsers::RequestUserInfo &userInfo)
uint64_t cookieCodeLogout(const std::string &cookieCode, bool logoutOtherUserSessions, uint64_t *uid=0, const std::string &ip="0")
std::string getSystemMessage(const std::string &targetUser)
uint64_t getActiveSessionCountForUser(uint64_t uid)
static void resetAllUserTooltips(const std::string &userNeedle="*")
WebUsers::resetAllUserTooltips.
static void tooltipSetNeverShowForUsername(const std::string &username, HttpXmlDocument *xmldoc, const std::string &srcFile, const std::string &srcFunc, const std::string &srcId, bool doNeverShow, bool temporarySilence)
void cleanupExpiredRemoteEntries(void)
std::string getUsersDisplayName(uint64_t uid)
from Gateway, use public version which considers remote users
void loadActiveSessions(void)
std::pair< std::string, time_t > getLastSystemMessage(void)
uint64_t attemptActiveSessionWithCert(const std::string &uuid, std::string &jumbledEmail, std::string &cookieCode, std::string &username, const std::string &ip)
static const std::string OTS_OWNER
defined by environment variable, e.g. experiment name
static void tooltipCheckForUsername(const std::string &username, HttpXmlDocument *xmldoc, const std::string &srcFile, const std::string &srcFunc, const std::string &srcId)
std::string remoteGatewaySelfName_
IP of remote Gateway to be used for login verification.
bool cookieCodeIsActiveForRequest(std::string &cookieCode, std::map< std::string, WebUsers::permissionLevel_t > *userPermissions=0, uint64_t *uid=0, const std::string &ip="0", bool refresh=true, bool doNotGoRemote=false, std::string *userWithLock=0, uint64_t *userSessionIndex=0)
void changeSettingsForUser(uint64_t uid, const std::string &bgcolor, const std::string &dbcolor, const std::string &wincolor, const std::string &layout, const std::string &syslayout, const std::string &aliaslayout, const std::string &sysaliaslayout)
WebUsers::changeSettingsForUser.
@ PERMISSION_LEVEL_ADMIN
max permission level!
xercesc::DOMElement * addTextElementToParent(const std::string &childName, const std::string &childText, xercesc::DOMElement *parent)
void saveXmlDocument(const std::string &filePath)
defines used also by OtsConfigurationWizardSupervisor
void INIT_MF(const char *name)
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string exec(const char *cmd)
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
vectorToString ~
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
getMapFromString ~
static bool wildCardMatch(const std::string &needle, const std::string &haystack, unsigned int *priorityIndex=0)
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)
uint64_t userSessionIndex_
can use session index to track a user's session on multiple devices/browsers
const WebUsers::permissionLevel_t & getGroupPermissionLevel()
bool setGroupPermissionLevels(const std::string &groupPermissionLevelsString)
end setGroupPermissionLevels()