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"
35 #define SILENCE_ALL_TOOLTIPS_FILENAME "silenceTooltips"
37 #define HASHES_DB_GLOBAL_STRING "hashData"
38 #define HASHES_DB_ENTRY_STRING "hashEntry"
39 #define USERS_DB_GLOBAL_STRING "userData"
40 #define USERS_DB_ENTRY_STRING "userEntry"
41 #define USERS_DB_NEXT_UID_STRING "nextUserId"
44 #define PREF_XML_BGCOLOR_FIELD "pref_bgcolor" // -background color
45 #define PREF_XML_DBCOLOR_FIELD "pref_dbcolor" // -dashboard color
46 #define PREF_XML_WINCOLOR_FIELD "pref_wincolor" // -window color
47 #define PREF_XML_LAYOUT_FIELD "pref_layout" // -3 defaults window layouts(and current)
48 #define PREF_XML_SYSLAYOUT_FIELD "pref_syslayout" // -2 defaults window layouts
49 #define PREF_XML_PERMISSIONS_FIELD "desktop_user_permissions" // 0-255 permissions value (255 is admin super user)
50 #define PREF_XML_USERLOCK_FIELD "username_with_lock" // user with lock (to lockout others)
51 #define PREF_XML_USERNAME_FIELD "pref_username" // user with lock (to lockout others)
52 #define PREF_XML_OTS_OWNER_FIELD "ots_owner" // e.g. the experiment name
54 #define PREF_XML_BGCOLOR_DEFAULT "rgb(0,76,151)" // -background color
55 #define PREF_XML_DBCOLOR_DEFAULT "rgb(0,40,85)" // -dashboard color
56 #define PREF_XML_WINCOLOR_DEFAULT "rgba(196,229,255,0.9)" // -window color
57 #define PREF_XML_LAYOUT_DEFAULT "0;0;0;0" // 3 default window layouts(and current)
58 #define PREF_XML_SYSLAYOUT_DEFAULT "0;0" // 2 system default window layouts
60 #define PREF_XML_ACCOUNTS_FIELD "users_accounts" // user accounts field for super users
61 #define PREF_XML_LOGIN_HISTORY_FIELD "login_entry" // login history field for user login history data
63 const std::string WebUsers::OTS_OWNER = getenv(
"OTS_OWNER")?getenv(
"OTS_OWNER"):
"";
64 const std::string WebUsers::DEFAULT_ADMIN_USERNAME =
"admin";
65 const std::string WebUsers::DEFAULT_ADMIN_DISPLAY_NAME =
"Administrator";
66 const std::string WebUsers::DEFAULT_ADMIN_EMAIL =
"root@otsdaq.fnal.gov";
67 const std::string WebUsers::DEFAULT_ITERATOR_USERNAME =
"iterator";
68 const std::string WebUsers::DEFAULT_STATECHANGER_USERNAME =
"statechanger";
69 const std::string WebUsers::DEFAULT_USER_GROUP =
"allUsers";
71 const std::string WebUsers::REQ_NO_LOGIN_RESPONSE =
"NoLogin";
72 const std::string WebUsers::REQ_NO_PERMISSION_RESPONSE =
"NoPermission";
73 const std::string WebUsers::REQ_USER_LOCKOUT_RESPONSE =
"UserLockout";
74 const std::string WebUsers::REQ_LOCK_REQUIRED_RESPONSE =
"LockRequired";
75 const std::string WebUsers::REQ_ALLOW_NO_USER =
"AllowNoUser";
77 const std::string WebUsers::SECURITY_TYPE_NONE =
"NoSecurity";
78 const std::string WebUsers::SECURITY_TYPE_DIGEST_ACCESS =
"DigestAccessAuthentication";
79 const std::string WebUsers::SECURITY_TYPE_DEFAULT = WebUsers::SECURITY_TYPE_NONE;
81 const std::vector<std::string> WebUsers::HashesDatabaseEntryFields_ = {
"hash",
"lastAccessTime"};
82 const std::vector<std::string> WebUsers::UsersDatabaseEntryFields_ = {
"username",
"displayName",
"salt",
83 "uid",
"permissions",
"lastLoginAttemptTime",
"accountCreatedTime",
84 "loginFailureCount",
"lastModifiedTime",
"lastModifierUsername",
"useremail"};
87 #define __MF_SUBJECT__ "WebUsers"
98 usersUsernameWithLock_ =
"";
117 mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755);
118 mkdir(((std::string)WEB_LOGIN_DB_PATH +
"bkup/" + USERS_DB_PATH).c_str(), 0755);
119 mkdir(((std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH).c_str(), 0755);
120 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755);
121 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH).c_str(), 0755);
122 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH).c_str(), 0755);
125 __COUT__ <<
"FATAL USER DATABASE ERROR - failed to load!!!" << __E__;
127 loadSecuritySelection();
131 std::string user = DEFAULT_ADMIN_USERNAME;
132 if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
134 __SS__ <<
"user: " << user <<
" is not found. This should be impossible!" << __E__;
135 __COUT_ERR__ << ss.str();
138 else if(Users_[i].salt_ ==
"" &&
139 securityType_ == SECURITY_TYPE_DIGEST_ACCESS)
144 std::thread([](
const std::string& nac,
const std::string& user) { WebUsers::NACDisplayThread(nac, user); }, Users_[i].getNewAccountCode(), user)
149 loadActiveSessions();
157 __COUT__ <<
"Done with Web Users initialization!" << __E__;
167 std::lock_guard<std::mutex> lock(webUserMutex_);
170 WebUsers::initializeRequestUserInfo(cgi, userInfo);
174 if(!cookieCodeIsActiveForRequest(userInfo.cookieCode_,
175 &userInfo.groupPermissionLevelMap_,
178 !userInfo.automatedCommand_ ,
179 &userInfo.usernameWithLock_,
180 &userInfo.activeUserSessionIndex_))
182 *out << userInfo.cookieCode_;
183 goto HANDLE_ACCESS_FAILURE;
187 userInfo.getGroupPermissionLevel();
189 i = searchUsersDatabaseForUserId(userInfo.uid_);
190 if(i >= Users_.size())
192 __SS__ <<
"Illegal uid encountered in cookie codes!? " << i << __E__;
193 ss <<
"User size = " << Users_.size() << __E__;
197 userInfo.username_ = Users_[i].username_;
198 userInfo.displayName_ = Users_[i].displayName_;
200 if(!WebUsers::checkRequestAccess(cgi, out, xmldoc, userInfo))
201 goto HANDLE_ACCESS_FAILURE;
205 HANDLE_ACCESS_FAILURE:
207 if(!userInfo.automatedCommand_)
208 __COUT_ERR__ <<
"Failed request (requestType = " << userInfo.requestType_ <<
"): " << out->str() << __E__;
218 userInfo.ip_ = cgi.getEnvironment().getRemoteAddr();
221 userInfo.username_ =
"";
222 userInfo.displayName_ =
"";
223 userInfo.usernameWithLock_ =
"";
224 userInfo.activeUserSessionIndex_ = -1;
225 userInfo.setGroupPermissionLevels(
"");
235 bool WebUsers::checkRequestAccess(cgicc::Cgicc& ,
236 std::ostringstream* out,
240 const std::string& wizardModeSequence )
246 if(userInfo.requireSecurity_)
251 if(isWizardMode && wizardModeSequence.size() < 8)
254 *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
255 __COUT__ <<
"User (@" << userInfo.ip_ <<
") has attempted requestType '" << userInfo.requestType_
256 <<
"' which requires sufficient security enabled. Please enable the "
258 " sequence of at least 8 characters."
263 else if(!isWizardMode && (userInfo.username_ == WebUsers::DEFAULT_ADMIN_USERNAME || userInfo.username_ == WebUsers::DEFAULT_ITERATOR_USERNAME ||
264 userInfo.username_ == WebUsers::DEFAULT_STATECHANGER_USERNAME))
267 *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
268 __COUT__ <<
"User (@" << userInfo.ip_ <<
") has attempted requestType '" << userInfo.requestType_
269 <<
"' which requires sufficient security enabled. Please enable "
271 " logins (Note: the user admin is disallowed in an attempt to force personal accountability for edits)."
279 if(!userInfo.automatedCommand_)
281 __COUTT__ <<
"requestType ==========>>> " << userInfo.requestType_ << __E__;
282 __COUTTV__((
unsigned int)userInfo.permissionLevel_);
283 __COUTTV__((
unsigned int)userInfo.permissionsThreshold_);
287 if(!isWizardMode && !userInfo.allowNoUser_ && userInfo.cookieCode_.length() != WebUsers::COOKIE_CODE_LENGTH)
289 __COUT__ <<
"User (@" << userInfo.ip_ <<
") has invalid cookie code: " << userInfo.cookieCode_ << std::endl;
290 *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
294 if(!userInfo.allowNoUser_ && (userInfo.permissionLevel_ == 0 ||
295 userInfo.permissionLevel_ < userInfo.permissionsThreshold_))
297 *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
298 __COUT__ <<
"User (@" << userInfo.ip_ <<
") has insufficient permissions for requestType '" << userInfo.requestType_
299 <<
"' : " << (
unsigned int)userInfo.permissionLevel_ <<
"<" << (
unsigned int)userInfo.permissionsThreshold_ << std::endl;
306 userInfo.username_ = WebUsers::DEFAULT_ADMIN_USERNAME;
307 userInfo.displayName_ =
"Admin";
308 userInfo.usernameWithLock_ = userInfo.username_;
309 userInfo.activeUserSessionIndex_ = 0;
316 if(userInfo.allowNoUser_)
317 xmldoc->setHeader(WebUsers::REQ_ALLOW_NO_USER);
319 xmldoc->setHeader(userInfo.cookieCode_);
322 if(userInfo.allowNoUser_)
324 if(userInfo.automatedCommand_)
325 __COUT__ <<
"Allowing anonymous access." << __E__;
336 if((userInfo.checkLock_ || userInfo.requireLock_) && userInfo.usernameWithLock_ !=
"" && userInfo.usernameWithLock_ != userInfo.username_)
338 *out << WebUsers::REQ_USER_LOCKOUT_RESPONSE;
339 __COUT__ <<
"User '" << userInfo.username_ <<
"' is locked out. '" << userInfo.usernameWithLock_ <<
"' has lock." << std::endl;
343 if(userInfo.requireLock_ && userInfo.usernameWithLock_ != userInfo.username_)
345 *out << WebUsers::REQ_LOCK_REQUIRED_RESPONSE;
346 __COUT__ <<
"User '" << userInfo.username_ <<
"' must have lock to proceed. ('" << userInfo.usernameWithLock_ <<
"' has lock.)" << std::endl;
358 void WebUsers::saveActiveSessions()
362 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE;
363 __COUT__ << fn << __E__;
365 FILE* fp = fopen(fn.c_str(),
"w");
368 __COUT_ERR__ <<
"Error! Persistent active sessions could not be saved to file: " << fn << __E__;
373 fprintf(fp,
"%d\n", version);
374 for(
unsigned int i = 0; i < ActiveSessions_.size(); ++i)
382 fprintf(fp,
"%s\n", ActiveSessions_[i].cookieCode_.c_str());
383 fprintf(fp,
"%s\n", ActiveSessions_[i].ip_.c_str());
384 fprintf(fp,
"%lu\n", ActiveSessions_[i].userId_);
385 fprintf(fp,
"%lu\n", ActiveSessions_[i].sessionIndex_);
386 fprintf(fp,
"%ld\n", ActiveSessions_[i].startTime_);
389 __COUT__ <<
"Active Sessions saved with size " << ActiveSessions_.size() << __E__;
397 void WebUsers::loadActiveSessions()
401 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE;
402 __COUT__ << fn << __E__;
403 FILE* fp = fopen(fn.c_str(),
"r");
406 __COUT_INFO__ <<
"Persistent active sessions were not found to be loaded at file: " << fn << __E__;
412 const int LINELEN = 1000;
414 fgets(line, LINELEN, fp);
415 sscanf(line,
"%d", &version);
418 __COUT__ <<
"Extracting active sessions..." << __E__;
421 while(fgets(line, LINELEN, fp))
424 line[strlen(line) - 1] =
'\0';
425 if(strlen(line) != COOKIE_CODE_LENGTH)
427 __COUT__ <<
"Illegal cookie code found: " << line << __E__;
432 ActiveSessions_.push_back(ActiveSession());
433 ActiveSessions_.back().cookieCode_ = line;
435 fgets(line, LINELEN, fp);
437 line[strlen(line) - 1] =
'\0';
438 ActiveSessions_.back().ip_ = line;
440 fgets(line, LINELEN, fp);
441 sscanf(line,
"%lu", &(ActiveSessions_.back().userId_));
443 fgets(line, LINELEN, fp);
444 sscanf(line,
"%lu", &(ActiveSessions_.back().sessionIndex_));
446 fgets(line, LINELEN, fp);
447 sscanf(line,
"%ld", &(ActiveSessions_.back().startTime_));
452 __COUT__ <<
"Active Sessions loaded with size " << ActiveSessions_.size() << __E__;
456 fp = fopen(fn.c_str(),
"w");
465 bool WebUsers::loadDatabases()
470 const unsigned int LINE_LEN = 1000;
472 unsigned int i, si, c, len, f;
483 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_FILE;
484 __COUT__ << fn << __E__;
485 fp = fopen(fn.c_str(),
"r");
488 mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str(), 0755);
489 __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str() << __E__;
490 fp = fopen(fn.c_str(), "w");
493 __COUT__ << "Hashes database created: " << fn << __E__;
495 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_OPEN);
496 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE);
503 while(fgets(line, LINE_LEN, fp))
505 if(strlen(line) < SHA512_DIGEST_LENGTH)
510 for(i = 0; i < len; ++i)
518 while(i < len && line[i] !=
'<')
530 Hashes_.push_back(Hash());
531 Hashes_.back().hash_ = &line[si];
534 sscanf(&line[si],
"%ld", &Hashes_.back().accessTime_);
537 __COUT__ << Hashes_.size() <<
" Hashes found." << __E__;
551 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_FILE;
552 fp = fopen(fn.c_str(),
"r");
555 mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str(), 0755);
556 __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str() << __E__;
557 fp = fopen(fn.c_str(), "w");
560 __COUT__ << "Users database created: " << fn << __E__;
562 saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_OPEN);
564 sprintf(nidStr, "%lu", usersNextUserId_);
565 saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, nidStr, DB_SAVE_OPEN_AND_CLOSE);
566 saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE);
569 createNewAccount(DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_DISPLAY_NAME,
570 DEFAULT_ADMIN_EMAIL);
574 __COUT__ <<
"Users database: " << fn << __E__;
578 char salt[] =
"nextUserId";
579 while(fgets(line, LINE_LEN, fp))
581 if(strlen(line) < strlen(salt) * 2)
584 for(i = 0; i < strlen(salt); ++i)
585 if(line[i + 1] != salt[i])
588 if(i == strlen(salt))
593 while(i < LINE_LEN && line[i] !=
'\0' && line[i] !=
'<')
596 sscanf(&line[si],
"%lu", &usersNextUserId_);
601 __COUT__ <<
"Found Users database next user Id: " << usersNextUserId_ << __E__;
605 while(fgets(line, LINE_LEN, fp))
607 if(strlen(line) < 30)
614 __COUT__ <<
"Line buffer too small: " << len << __E__;
620 for(i = 0; i < len; ++i)
624 if(c == 0 || c % 2 == 1)
628 while(i < len && line[i] !=
'<')
640 Users_.push_back(User());
641 Users_.back().username_ = &line[si];
644 Users_.back().displayName_ = &line[si];
646 Users_.back().salt_ = &line[si];
648 sscanf(&line[si],
"%lu", &Users_.back().userId_);
651 std::map<std::string, permissionLevel_t>& lastPermissionsMap = Users_.back().permissions_;
652 StringMacros::getMapFromString<permissionLevel_t>(&line[si], lastPermissionsMap);
660 if(lastPermissionsMap.find(WebUsers::DEFAULT_USER_GROUP) == lastPermissionsMap.end())
662 __MCOUT_INFO__(
"User '" << Users_.back().username_ <<
"' is not a member of the default user group '"
663 << WebUsers::DEFAULT_USER_GROUP
664 <<
".' Assuming user account is inactive (permission "
666 << WebUsers::PERMISSION_LEVEL_INACTIVE <<
")." << __E__);
667 lastPermissionsMap[WebUsers::DEFAULT_USER_GROUP] = WebUsers::PERMISSION_LEVEL_INACTIVE;
670 if(Users_.back().username_ == DEFAULT_ADMIN_USERNAME)
674 std::map<std::string , WebUsers::permissionLevel_t> initPermissions = {
675 {WebUsers::DEFAULT_USER_GROUP, WebUsers::PERMISSION_LEVEL_ADMIN}};
677 Users_.back().permissions_ = initPermissions;
681 sscanf(&line[si],
"%ld", &Users_.back().lastLoginAttempt_);
683 sscanf(&line[si],
"%ld", &Users_.back().accountCreationTime_);
685 sscanf(&line[si],
"%hhu", &Users_.back().loginFailureCount_);
687 sscanf(&line[si],
"%ld", &Users_.back().accessModifierTime());
689 Users_.back().loadModifierUsername(&line[si]);
691 Users_.back().email_ = &line[si];
698 __COUT__ << Users_.size() <<
" Users found." << __E__;
699 for(
size_t ii = 0; ii < Users_.size(); ++ii)
702 "User [" << Users_[ii].userId_ <<
"] \tName: " << std::left << std::setfill(
' ') << std::setw(20) << Users_[ii].username_
703 <<
"\tDisplay Name: " << std::left << std::setfill(
' ') << std::setw(30) << Users_[ii].displayName_ <<
"\tEmail: " << std::left
704 << std::setfill(
' ') << std::setw(30) << Users_[ii].email_ <<
"\tNAC: " << std::left << std::setfill(
' ') << std::setw(5)
705 << Users_[ii].getNewAccountCode() <<
"\tFailedCount: " << (int)Users_[ii].loginFailureCount_
706 <<
"\tPermissions: " << StringMacros::mapToString(Users_[ii].permissions_) <<
723 void WebUsers::saveToDatabase(FILE* fp,
const std::string& field,
const std::string& value, uint8_t type,
bool addNewLine)
728 std::string newLine = addNewLine ?
"\n" :
"";
730 if(type == DB_SAVE_OPEN_AND_CLOSE)
731 fprintf(fp,
"<%s>%s</%s>%s", field.c_str(), value.c_str(), field.c_str(), newLine.c_str());
732 else if(type == DB_SAVE_OPEN)
733 fprintf(fp,
"<%s>%s%s", field.c_str(), value.c_str(), newLine.c_str());
734 else if(type == DB_SAVE_CLOSE)
735 fprintf(fp,
"</%s>%s", field.c_str(), newLine.c_str());
744 bool WebUsers::saveDatabaseToFile(uint8_t db)
748 std::string fn = (std::string)WEB_LOGIN_DB_PATH + ((db == DB_USERS) ? (std::string)USERS_DB_FILE : (std::string)HASHES_DB_FILE);
750 __COUT__ <<
"Save Database Filename: " << fn << __E__;
756 sprintf(dayAppend,
".%lu.bkup", time(0) / (3600 * 24));
757 std::string bkup_fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)WEB_LOGIN_BKUP_DB_PATH +
758 ((db == DB_USERS) ? (std::string)USERS_DB_FILE : (std::
string)HASHES_DB_FILE) + (std::
string)dayAppend;
760 __COUT__ << "Backup file: " << bkup_fn << __E__;
762 std::
string shell_command = "mv " + fn + " " + bkup_fn;
763 system(shell_command.c_str());
766 FILE* fp = fopen(fn.c_str(), "wb");
774 saveToDatabase(fp, USERS_DB_GLOBAL_STRING,
"", DB_SAVE_OPEN);
776 sprintf(fldStr,
"%lu", usersNextUserId_);
777 saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, fldStr, DB_SAVE_OPEN_AND_CLOSE);
779 __COUT__ <<
"Saving " << Users_.size() <<
" Users." << __E__;
781 for(uint64_t i = 0; i < Users_.size(); ++i)
785 saveToDatabase(fp, USERS_DB_ENTRY_STRING,
"", DB_SAVE_OPEN,
false);
787 for(
unsigned int f = 0; f < WebUsers::UsersDatabaseEntryFields_.size(); ++f)
791 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], Users_[i].username_, DB_SAVE_OPEN_AND_CLOSE,
false);
793 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], Users_[i].displayName_, DB_SAVE_OPEN_AND_CLOSE,
false);
795 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], Users_[i].salt_, DB_SAVE_OPEN_AND_CLOSE,
false);
798 sprintf(fldStr,
"%lu", Users_[i].userId_);
799 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], fldStr, DB_SAVE_OPEN_AND_CLOSE,
false);
803 WebUsers::UsersDatabaseEntryFields_[f],
804 StringMacros::mapToString(Users_[i].permissions_,
"," ,
":" ),
805 DB_SAVE_OPEN_AND_CLOSE,
809 sprintf(fldStr,
"%lu", Users_[i].lastLoginAttempt_);
810 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], fldStr, DB_SAVE_OPEN_AND_CLOSE,
false);
814 sprintf(fldStr,
"%lu", Users_[i].accountCreationTime_);
815 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], fldStr, DB_SAVE_OPEN_AND_CLOSE,
false);
819 sprintf(fldStr,
"%hhu", Users_[i].loginFailureCount_);
820 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], fldStr, DB_SAVE_OPEN_AND_CLOSE,
false);
824 sprintf(fldStr,
"%lu", Users_[i].getModifierTime());
825 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], fldStr, DB_SAVE_OPEN_AND_CLOSE,
false);
828 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], Users_[i].getModifierUsername(), DB_SAVE_OPEN_AND_CLOSE,
false);
830 saveToDatabase(fp, WebUsers::UsersDatabaseEntryFields_[f], Users_[i].email_, DB_SAVE_OPEN_AND_CLOSE,
false);
833 saveToDatabase(fp, USERS_DB_ENTRY_STRING,
"", DB_SAVE_CLOSE);
836 saveToDatabase(fp, USERS_DB_GLOBAL_STRING,
"", DB_SAVE_CLOSE);
840 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING,
"", DB_SAVE_OPEN);
842 __COUT__ <<
"Saving " << Hashes_.size() <<
" Hashes." << __E__;
843 for(uint64_t i = 0; i < Hashes_.size(); ++i)
845 __COUT__ <<
"Saving " << Hashes_[i].hash_ <<
" Hash." << __E__;
846 saveToDatabase(fp, HASHES_DB_ENTRY_STRING,
"", DB_SAVE_OPEN,
false);
847 for(
unsigned int f = 0; f < WebUsers::HashesDatabaseEntryFields_.size(); ++f)
850 saveToDatabase(fp, WebUsers::HashesDatabaseEntryFields_[f], Hashes_[i].hash_, DB_SAVE_OPEN_AND_CLOSE,
false);
853 sprintf(fldStr,
"%lu", Hashes_[i].accessTime_);
854 saveToDatabase(fp, WebUsers::HashesDatabaseEntryFields_[f], fldStr, DB_SAVE_OPEN_AND_CLOSE,
false);
857 saveToDatabase(fp, HASHES_DB_ENTRY_STRING,
"", DB_SAVE_CLOSE);
860 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING,
"", DB_SAVE_CLOSE);
875 void WebUsers::createNewAccount(
const std::string& username,
const std::string& displayName,
const std::string& email)
877 __COUT__ <<
"Creating account: " << username << __E__;
880 if((i = searchUsersDatabaseForUsername(username)) != NOT_FOUND_IN_DATABASE || username == WebUsers::DEFAULT_ITERATOR_USERNAME ||
881 username == WebUsers::DEFAULT_STATECHANGER_USERNAME)
884 __SS__ <<
"Username '" << username <<
"' already exists! Please choose a unique username." << __E__;
889 if((i = searchUsersDatabaseForDisplayName(displayName)) != NOT_FOUND_IN_DATABASE)
892 __SS__ <<
"Display Name '" << displayName <<
"' already exists! Please choose a unique display name." << __E__;
897 Users_.push_back(User());
899 Users_.back().username_ = username;
900 Users_.back().displayName_ = displayName;
901 Users_.back().email_ = email;
904 std::map<std::string , WebUsers::permissionLevel_t> initPermissions = {
905 {WebUsers::DEFAULT_USER_GROUP, (Users_.size() ? WebUsers::PERMISSION_LEVEL_NOVICE : WebUsers::PERMISSION_LEVEL_ADMIN)}};
907 Users_.back().permissions_ = initPermissions;
908 Users_.back().userId_ = usersNextUserId_++;
909 if(usersNextUserId_ >= ACCOUNT_ERROR_THRESHOLD)
911 __SS__ <<
"usersNextUserId_ wrap around!! Too many users??? Notify Admins." << __E__;
913 usersNextUserId_ = 1;
917 Users_.back().accountCreationTime_ = time(0);
919 if(!saveDatabaseToFile(DB_USERS))
921 __SS__ <<
"Failed to save User DB!" << __E__;
932 bool WebUsers::deleteAccount(
const std::string& username,
const std::string& displayName)
934 uint64_t i = searchUsersDatabaseForUsername(username);
935 if(i == NOT_FOUND_IN_DATABASE)
937 if(Users_[i].displayName_ != displayName)
942 Users_.erase(Users_.begin() + i);
945 return saveDatabaseToFile(DB_USERS);
949 unsigned int WebUsers::hexByteStrToInt(
const char* h)
952 char hs[3] = {h[0], h[1],
'\0'};
953 sscanf(hs,
"%X", &rv);
958 void WebUsers::intToHexStr(
unsigned char i,
char* h) { sprintf(h,
"%2.2X", i); }
970 uint64_t WebUsers::attemptActiveSession(
971 const std::string& uuid, std::string& jumbledUser,
const std::string& jumbledPw, std::string& newAccountCode,
const std::string& ip)
974 if(!checkIpAccess(ip))
976 __COUT_ERR__ <<
"rejected ip: " << ip << __E__;
977 return ACCOUNT_BLACKLISTED;
980 cleanupExpiredEntries();
982 if(!CareAboutCookieCodes_)
984 uint64_t uid = getAdminUserID();
985 jumbledUser = getUsersDisplayName(uid);
986 newAccountCode = genCookieCode();
993 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
995 __COUT_ERR__ <<
"uuid: " << uuid <<
" is not found" << __E__;
996 newAccountCode =
"1";
998 incrementIpBlacklistCount(ip);
1000 return NOT_FOUND_IN_DATABASE;
1002 ++LoginSessions_[i].loginAttempts_;
1004 std::string user = dejumble(jumbledUser, LoginSessions_[i].id_);
1006 std::string pw = dejumble(jumbledPw, LoginSessions_[i].id_);
1009 if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
1011 __COUT_ERR__ <<
"user: " << user <<
" is not found" << __E__;
1013 incrementIpBlacklistCount(ip);
1015 return NOT_FOUND_IN_DATABASE;
1018 ipBlacklistCounts_[ip] = 0;
1020 Users_[i].lastLoginAttempt_ = time(0);
1022 if(isInactiveForGroup(Users_[i].permissions_))
1024 __MCOUT_ERR__(
"User '" << user <<
"' account INACTIVE (could be due to failed logins)" << __E__);
1025 return ACCOUNT_INACTIVE;
1028 if(Users_[i].salt_ ==
"")
1030 __MCOUT__(
"First login attempt for user: " << user << __E__);
1032 if(newAccountCode != Users_[i].getNewAccountCode())
1034 __COUT__ <<
"New account code did not match: " << Users_[i].getNewAccountCode() <<
" != " << newAccountCode << __E__;
1035 saveDatabaseToFile(DB_USERS);
1036 return NOT_FOUND_IN_DATABASE;
1042 while(!addToHashesDatabase(sha512(user, pw, Users_[i].salt_)))
1047 Users_[i].salt_ =
"";
1050 __COUT__ <<
"\tHash added: " << Hashes_.back().hash_ << __E__;
1054 std::string salt = Users_[i].salt_;
1056 if(searchHashesDatabaseForHash(sha512(user, pw, salt)) == NOT_FOUND_IN_DATABASE)
1058 __COUT__ <<
"Failed login for " << user <<
" with permissions " << StringMacros::mapToString(Users_[i].permissions_) << __E__;
1061 if(++Users_[i].loginFailureCount_ != (
unsigned char)-1)
1062 ++Users_[i].loginFailureCount_;
1064 if(Users_[i].loginFailureCount_ >= USERS_MAX_LOGIN_FAILURES)
1065 Users_[i].permissions_[WebUsers::DEFAULT_USER_GROUP] = WebUsers::PERMISSION_LEVEL_INACTIVE;
1067 __COUT_INFO__ <<
"User/pw for user '" << user <<
"' was not correct (Failed Attempt #" << (int)Users_[i].loginFailureCount_ <<
" of "
1068 << (
int)USERS_MAX_LOGIN_FAILURES << " allowed)." << __E__;
1070 __COUTV__(isInactiveForGroup(Users_[i].permissions_));
1071 if(isInactiveForGroup(Users_[i].permissions_))
1072 __MCOUT_INFO__("Account '" << user
1073 << "' has been marked inactive due to too many failed "
1074 "login attempts (Failed Attempt
#"
1075 << (int)Users_[i].loginFailureCount_ <<
")! Note only admins can reactivate accounts." << __E__);
1077 saveDatabaseToFile(DB_USERS);
1078 return NOT_FOUND_IN_DATABASE;
1082 __MCOUT_INFO__(
"Login successful for: " << user << __E__);
1084 Users_[i].loginFailureCount_ = 0;
1087 for(
int h = 0; h < 2; ++h)
1089 std::string fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_LOGIN_HISTORY_PATH + (h ? USERS_GLOBAL_HISTORY_FILE : Users_[i].username_) +
"." +
1090 (std::string)USERS_LOGIN_HISTORY_FILETYPE;
1094 if(histXml.loadXmlDocument(fn))
1096 while(histXml.getChildrenCount() + 1 > (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE))
1097 histXml.removeDataElement();
1100 __COUT__ <<
"No previous login history found." << __E__;
1106 "Time=%lu Username=%s Permissions=%s UID=%lu",
1108 Users_[i].username_.c_str(),
1109 StringMacros::mapToString(Users_[i].permissions_).c_str(),
1113 "Time=%lu displayName=%s Permissions=%s UID=%lu",
1115 Users_[i].displayName_.c_str(),
1116 StringMacros::mapToString(Users_[i].permissions_).c_str(),
1118 histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr);
1121 histXml.saveXmlDocument(fn);
1125 saveDatabaseToFile(DB_USERS);
1126 jumbledUser = Users_[i].displayName_;
1127 newAccountCode = createNewActiveSession(Users_[i].userId_,
1130 if(ActiveSessions_.size() == 1)
1132 __COUT__ <<
"Attempting to auto-lock for first login user '" <<
1133 Users_[i].username_ <<
"'... " << __E__;
1134 setUserWithLock(Users_[i].userId_,
true , Users_[i].username_);
1137 return Users_[i].userId_;
1146 uint64_t WebUsers::attemptActiveSessionWithCert(
const std::string& uuid, std::string& email, std::string& cookieCode, std::string& user,
const std::string& ip)
1148 if(!checkIpAccess(ip))
1150 __COUT_ERR__ <<
"rejected ip: " << ip << __E__;
1151 return NOT_FOUND_IN_DATABASE;
1154 cleanupExpiredEntries();
1156 if(!CareAboutCookieCodes_)
1158 uint64_t uid = getAdminUserID();
1159 email = getUsersDisplayName(uid);
1160 cookieCode = genCookieCode();
1166 __COUT__ <<
"Rejecting cert logon with blank fingerprint" << __E__;
1168 incrementIpBlacklistCount(ip);
1170 return NOT_FOUND_IN_DATABASE;
1176 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
1178 __COUT__ <<
"uuid: " << uuid <<
" is not found" << __E__;
1181 incrementIpBlacklistCount(ip);
1183 return NOT_FOUND_IN_DATABASE;
1185 ++LoginSessions_[i].loginAttempts_;
1187 email = getUserEmailFromFingerprint(email);
1188 __COUT__ <<
"DejumbledEmail = " << email << __E__;
1191 __COUT__ <<
"Rejecting logon with unknown fingerprint" << __E__;
1193 incrementIpBlacklistCount(ip);
1195 return NOT_FOUND_IN_DATABASE;
1199 if((i = searchUsersDatabaseForUserEmail(email)) == NOT_FOUND_IN_DATABASE)
1201 __COUT__ <<
"email: " << email <<
" is not found" << __E__;
1203 incrementIpBlacklistCount(ip);
1205 return NOT_FOUND_IN_DATABASE;
1208 ipBlacklistCounts_[ip] = 0;
1210 user = getUsersUsername(i);
1212 Users_[i].lastLoginAttempt_ = time(0);
1213 if(isInactiveForGroup(Users_[i].permissions_))
1215 __MCOUT__(
"User '" << user <<
"' account INACTIVE (could be due to failed logins)." << __E__);
1216 return NOT_FOUND_IN_DATABASE;
1219 if(Users_[i].salt_ ==
"")
1221 return NOT_FOUND_IN_DATABASE;
1224 __MCOUT__(
"Login successful for: " << user << __E__);
1226 Users_[i].loginFailureCount_ = 0;
1229 for(
int h = 0; h < 2; ++h)
1231 std::string fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_LOGIN_HISTORY_PATH + (h ? USERS_GLOBAL_HISTORY_FILE : Users_[i].username_) +
"." +
1232 (std::string)USERS_LOGIN_HISTORY_FILETYPE;
1236 if(histXml.loadXmlDocument(fn))
1238 while(histXml.getChildrenCount() + 1 > (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE))
1239 histXml.removeDataElement();
1242 __COUT__ <<
"No previous login history found." << __E__;
1248 "Time=%lu Username=%s Permissions=%s UID=%lu",
1250 Users_[i].username_.c_str(),
1251 StringMacros::mapToString(Users_[i].permissions_).c_str(),
1255 "Time=%lu displayName=%s Permissions=%s UID=%lu",
1257 Users_[i].displayName_.c_str(),
1258 StringMacros::mapToString(Users_[i].permissions_).c_str(),
1260 histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr);
1263 histXml.saveXmlDocument(fn);
1267 saveDatabaseToFile(DB_USERS);
1268 email = Users_[i].displayName_;
1269 cookieCode = createNewActiveSession(Users_[i].userId_,
1271 return Users_[i].userId_;
1277 uint64_t WebUsers::searchActiveSessionDatabaseForCookie(
const std::string& cookieCode)
const
1280 for(; i < ActiveSessions_.size(); ++i)
1281 if(ActiveSessions_[i].cookieCode_ == cookieCode)
1283 return (i == ActiveSessions_.size()) ? NOT_FOUND_IN_DATABASE : i;
1289 bool WebUsers::isUsernameActive(
const std::string& username)
const
1292 if((u = searchUsersDatabaseForUsername(username)) == NOT_FOUND_IN_DATABASE)
1294 return isUserIdActive(Users_[u].userId_);
1300 bool WebUsers::isUserIdActive(uint64_t uid)
const
1303 for(; i < ActiveSessions_.size(); ++i)
1304 if(ActiveSessions_[i].userId_ == uid)
1312 uint64_t WebUsers::searchUsersDatabaseForUsername(
const std::string& username)
const
1315 for(; i < Users_.size(); ++i)
1316 if(Users_[i].username_ == username)
1318 return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
1324 uint64_t WebUsers::searchUsersDatabaseForDisplayName(
const std::string& displayName)
const
1327 for(; i < Users_.size(); ++i)
1328 if(Users_[i].displayName_ == displayName)
1330 return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
1336 uint64_t WebUsers::searchUsersDatabaseForUserEmail(
const std::string& useremail)
const
1339 for(; i < Users_.size(); ++i)
1340 if(Users_[i].email_ == useremail)
1342 return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
1348 uint64_t WebUsers::searchUsersDatabaseForUserId(uint64_t uid)
const
1351 for(; i < Users_.size(); ++i)
1352 if(Users_[i].userId_ == uid)
1354 return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
1360 uint64_t WebUsers::searchLoginSessionDatabaseForUUID(
const std::string& uuid)
const
1363 for(; i < LoginSessions_.size(); ++i)
1364 if(LoginSessions_[i].uuid_ == uuid)
1366 return (i == LoginSessions_.size()) ? NOT_FOUND_IN_DATABASE : i;
1372 uint64_t WebUsers::searchHashesDatabaseForHash(
const std::string& hash)
1376 for(; i < Hashes_.size(); ++i)
1377 if(Hashes_[i].hash_ == hash)
1382 if(i < Hashes_.size())
1383 Hashes_[i].accessTime_ = ((time(0) + (rand() % 2 ? 1 : -1) * (rand() % 30 * 24 * 60 * 60)) & 0x0FFFFFFFFFE000000);
1388 return (i == Hashes_.size()) ? NOT_FOUND_IN_DATABASE : i;
1395 bool WebUsers::addToHashesDatabase(
const std::string& hash)
1397 if(searchHashesDatabaseForHash(hash) != NOT_FOUND_IN_DATABASE)
1399 __COUT__ <<
"Hash collision: " << hash << __E__;
1402 Hashes_.push_back(Hash());
1403 Hashes_.back().hash_ = hash;
1404 Hashes_.back().accessTime_ = ((time(0) + (rand() % 2 ? 1 : -1) * (rand() % 30 * 24 * 60 * 60)) & 0x0FFFFFFFFFE000000);
1407 return saveDatabaseToFile(DB_HASHES);
1412 std::string WebUsers::genCookieCode()
1415 std::string cc =
"";
1416 for(uint32_t i = 0; i < COOKIE_CODE_LENGTH / 2; ++i)
1418 intToHexStr(rand(), hexStr);
1428 std::string WebUsers::createNewActiveSession(uint64_t uid,
const std::string& ip, uint64_t asIndex)
1431 ActiveSessions_.push_back(ActiveSession());
1432 ActiveSessions_.back().cookieCode_ = genCookieCode();
1433 ActiveSessions_.back().ip_ = ip;
1434 ActiveSessions_.back().userId_ = uid;
1435 ActiveSessions_.back().startTime_ = time(0);
1438 ActiveSessions_.back().sessionIndex_ = asIndex;
1443 for(uint64_t j = 0; j < ActiveSessions_.size(); ++j)
1444 if(ActiveSessions_[j].userId_ == uid && max < ActiveSessions_[j].sessionIndex_)
1445 max = ActiveSessions_[j].sessionIndex_;
1447 ActiveSessions_.back().sessionIndex_ = (max ? max + 1 : 1);
1450 return ActiveSessions_.back().cookieCode_;
1477 std::string WebUsers::refreshCookieCode(
unsigned int i,
bool enableRefresh)
1480 for(uint64_t j = ActiveSessions_.size() - 1; j != (uint64_t)-1; --j)
1481 if(ActiveSessions_[j].userId_ == ActiveSessions_[i].userId_ &&
1482 ActiveSessions_[j].sessionIndex_ == ActiveSessions_[i].sessionIndex_)
1487 if(enableRefresh && (time(0) - ActiveSessions_[j].startTime_ > ACTIVE_SESSION_EXPIRATION_TIME / 2))
1491 ActiveSessions_[j].startTime_ = time(0) - ACTIVE_SESSION_EXPIRATION_TIME + ACTIVE_SESSION_COOKIE_OVERLAP_TIME;
1497 return createNewActiveSession(ActiveSessions_[i].userId_, ActiveSessions_[i].ip_, ActiveSessions_[i].sessionIndex_);
1500 return ActiveSessions_[j].cookieCode_;
1511 uint64_t WebUsers::isCookieCodeActiveForLogin(
const std::string& uuid, std::string& cookieCode, std::string& username)
1513 if(!CareAboutCookieCodes_)
1514 return getAdminUserID();
1520 if(!ActiveSessions_.size())
1521 return NOT_FOUND_IN_DATABASE;
1526 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
1528 __COUT__ <<
"uuid not found: " << uuid << __E__;
1529 return NOT_FOUND_IN_DATABASE;
1532 username = dejumble(username, LoginSessions_[i].id_);
1535 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
1537 __COUT__ <<
"Cookie code not found" << __E__;
1538 return NOT_FOUND_IN_DATABASE;
1542 if((j = searchUsersDatabaseForUserId(ActiveSessions_[i].userId_)) == NOT_FOUND_IN_DATABASE)
1544 __COUT__ <<
"User ID not found" << __E__;
1545 return NOT_FOUND_IN_DATABASE;
1549 if(Users_[j].username_ != username)
1551 __COUT__ <<
"cookieCode: " << cookieCode <<
" was.." << __E__;
1552 __COUT__ <<
"username: " << username <<
" is not found" << __E__;
1553 return NOT_FOUND_IN_DATABASE;
1556 username = Users_[j].displayName_;
1557 cookieCode = refreshCookieCode(i);
1558 return Users_[j].userId_;
1564 uint64_t WebUsers::getActiveSessionCountForUser(uint64_t uid)
1567 std::vector<uint64_t> uniqueAsi;
1570 for(i = 0; i < ActiveSessions_.size(); ++i)
1571 if(ActiveSessions_[i].userId_ == uid)
1576 for(j = 0; j < uniqueAsi.size(); ++j)
1577 if(uniqueAsi[j] == ActiveSessions_[i].sessionIndex_)
1584 uniqueAsi.push_back(ActiveSessions_[i].sessionIndex_);
1587 __COUT__ <<
"Found " << uniqueAsi.size() <<
" active sessions for uid " << uid << __E__;
1589 return uniqueAsi.size();
1598 bool WebUsers::checkIpAccess(
const std::string& ip)
1603 FILE* fp = fopen((IP_ACCEPT_FILE).c_str(),
"r");
1609 while(fgets(line, 300, fp))
1613 if(len > 2 && line[len - 1] ==
'\n')
1614 line[len - 1] =
'\0';
1615 if(StringMacros::wildCardMatch(ip, line))
1622 fp = fopen((IP_REJECT_FILE).c_str(),
"r");
1625 while(fgets(line, 300, fp))
1629 if(len > 2 && line[len - 1] ==
'\n')
1630 line[len - 1] =
'\0';
1631 if(StringMacros::wildCardMatch(ip, line))
1638 fp = fopen((IP_BLACKLIST_FILE).c_str(),
"r");
1641 while(fgets(line, 300, fp))
1645 if(len > 2 && line[len - 1] ==
'\n')
1646 line[len - 1] =
'\0';
1647 if(StringMacros::wildCardMatch(ip, line))
1660 void WebUsers::incrementIpBlacklistCount(
const std::string& ip)
1663 auto it = ipBlacklistCounts_.find(ip);
1664 if(it == ipBlacklistCounts_.end())
1666 __COUT__ <<
"First error for ip '" << ip <<
"'" << __E__;
1667 ipBlacklistCounts_[ip] = 1;
1673 if(it->second >= IP_BLACKLIST_COUNT_THRESHOLD)
1675 __MCOUT__(
"Adding IP '" << ip <<
"' to blacklist!" << __E__);
1678 FILE* fp = fopen((IP_BLACKLIST_FILE).c_str(),
"a");
1681 __SS__ <<
"IP black list file '" << IP_BLACKLIST_FILE <<
"' could not be opened." << __E__;
1682 __MCOUT_ERR__(ss.str());
1685 fprintf(fp,
"%s\n", ip.c_str());
1693 std::string WebUsers::getUsersDisplayName(uint64_t uid)
1696 if((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE)
1698 return Users_[i].displayName_;
1703 std::string WebUsers::getUsersUsername(uint64_t uid)
1706 if((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE)
1708 return Users_[i].username_;
1722 uint64_t WebUsers::cookieCodeLogout(
const std::string& cookieCode,
bool logoutOtherUserSessions, uint64_t* userId,
const std::string& ip)
1727 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
1729 __COUT__ <<
"Cookie code not found" << __E__;
1731 incrementIpBlacklistCount(ip);
1733 return NOT_FOUND_IN_DATABASE;
1736 ipBlacklistCounts_[ip] = 0;
1739 if(ActiveSessions_[i].ip_ != ip)
1741 __COUT__ <<
"IP does not match active session" << __E__;
1742 return NOT_FOUND_IN_DATABASE;
1751 uint64_t asi = ActiveSessions_[i].sessionIndex_;
1752 uint64_t uid = ActiveSessions_[i].userId_;
1755 uint64_t logoutCount = 0;
1758 while(i < ActiveSessions_.size())
1760 if((logoutOtherUserSessions && ActiveSessions_[i].userId_ == uid && ActiveSessions_[i].sessionIndex_ != asi) ||
1761 (!logoutOtherUserSessions && ActiveSessions_[i].userId_ == uid && ActiveSessions_[i].sessionIndex_ == asi))
1763 __COUT__ <<
"Logging out of active session " << ActiveSessions_[i].userId_ <<
"-" << ActiveSessions_[i].sessionIndex_ << __E__;
1764 ActiveSessions_.erase(ActiveSessions_.begin() + i);
1771 __COUT__ <<
"Found and removed active session count = " << logoutCount << __E__;
1778 bool WebUsers::getUserInfoForCookie(std::string& cookieCode, std::string* userName, std::string* displayName, uint64_t* activeSessionIndex)
1785 if(!CareAboutCookieCodes_)
1787 uint64_t uid = getAdminUserID();
1789 *userName = getUsersUsername(uid);
1791 *displayName = getUsersDisplayName(uid);
1792 if(activeSessionIndex)
1793 *activeSessionIndex = -1;
1800 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
1802 __COUT__ <<
"cookieCode NOT_FOUND_IN_DATABASE" << __E__;
1807 if((j = searchUsersDatabaseForUserId(ActiveSessions_[i].userId_)) == NOT_FOUND_IN_DATABASE)
1809 __COUT__ <<
"ActiveSession UserId NOT_FOUND_IN_DATABASE" << __E__;
1814 *userName = Users_[j].username_;
1816 *displayName = Users_[j].displayName_;
1817 if(activeSessionIndex)
1818 *activeSessionIndex = ActiveSessions_[i].sessionIndex_;
1835 bool WebUsers::cookieCodeIsActiveForRequest(std::string& cookieCode,
1836 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>* userPermissions,
1838 const std::string& ip,
1840 std::string* userWithLock,
1841 uint64_t* activeUserSessionIndex)
1846 if(!checkIpAccess(ip))
1848 __COUT_ERR__ <<
"User IP rejected." << __E__;
1849 cookieCode = REQ_NO_LOGIN_RESPONSE;
1853 cleanupExpiredEntries();
1860 if(!CareAboutCookieCodes_)
1864 std::map<std::string , WebUsers::permissionLevel_t>({{WebUsers::DEFAULT_USER_GROUP, WebUsers::PERMISSION_LEVEL_ADMIN}});
1866 *uid = getAdminUserID();
1868 *userWithLock = usersUsernameWithLock_;
1869 if(activeUserSessionIndex)
1870 *activeUserSessionIndex = -1;
1872 if(cookieCode.size() != COOKIE_CODE_LENGTH)
1873 cookieCode = genCookieCode();
1880 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
1882 __COUT_ERR__ <<
"Cookie code not found" << __E__;
1883 cookieCode = REQ_NO_LOGIN_RESPONSE;
1885 incrementIpBlacklistCount(ip);
1890 ipBlacklistCounts_[ip] = 0;
1893 if(ip !=
"0" && ActiveSessions_[i].ip_ != ip)
1895 __COUTV__(ActiveSessions_[i].ip_);
1897 __COUT_ERR__ <<
"IP does not match active session." << __E__;
1898 cookieCode = REQ_NO_LOGIN_RESPONSE;
1903 if((j = searchUsersDatabaseForUserId(ActiveSessions_[i].userId_)) == NOT_FOUND_IN_DATABASE)
1905 __COUT_ERR__ <<
"User ID not found" << __E__;
1906 cookieCode = REQ_NO_LOGIN_RESPONSE;
1910 std::map<std::string , WebUsers::permissionLevel_t> tmpPerm = getPermissionsForUser(Users_[j].userId_);
1912 if(isInactiveForGroup(tmpPerm))
1914 cookieCode = REQ_NO_PERMISSION_RESPONSE;
1920 *userPermissions = tmpPerm;
1922 *uid = Users_[j].userId_;
1924 *userWithLock = usersUsernameWithLock_;
1925 if(activeUserSessionIndex)
1926 *activeUserSessionIndex = ActiveSessions_[i].sessionIndex_;
1928 cookieCode = refreshCookieCode(i, refresh);
1940 void WebUsers::cleanupExpiredEntries(std::vector<std::string>* loggedOutUsernames)
1945 if(loggedOutUsernames)
1947 for(i = 0; i < UsersLoggedOutUsernames_.size(); ++i)
1948 loggedOutUsernames->push_back(UsersLoggedOutUsernames_[i]);
1949 UsersLoggedOutUsernames_.clear();
1953 for(i = 0; i < LoginSessions_.size(); ++i)
1954 if(LoginSessions_[i].startTime_ + LOGIN_SESSION_EXPIRATION_TIME < time(0) ||
1955 LoginSessions_[i].loginAttempts_ > LOGIN_SESSION_ATTEMPTS_MAX)
1957 __COUT__ <<
"Found expired login sessions: " << i <<
" of " << LoginSessions_.size() << __E__;
1961 LoginSessions_.erase(LoginSessions_.begin() + i);
1974 for(i = 0; i < ActiveSessions_.size(); ++i)
1975 if(ActiveSessions_[i].startTime_ + ACTIVE_SESSION_EXPIRATION_TIME <= time(0))
1985 __COUT__ <<
"Found expired active sessions: " << i <<
" of " << ActiveSessions_.size() << __E__;
1987 tmpUid = ActiveSessions_[i].userId_;
1988 ActiveSessions_.erase(ActiveSessions_.begin() + i);
1990 if(!isUserIdActive(tmpUid))
1993 if(loggedOutUsernames)
1994 loggedOutUsernames->push_back(Users_[searchUsersDatabaseForUserId(tmpUid)].username_);
1996 UsersLoggedOutUsernames_.push_back(Users_[searchUsersDatabaseForUserId(tmpUid)].username_);
2016 if(CareAboutCookieCodes_ && !isUsernameActive(usersUsernameWithLock_))
2017 usersUsernameWithLock_ =
"";
2027 std::string WebUsers::createNewLoginSession(
const std::string& UUID,
const std::string& ip)
2033 for(; i < LoginSessions_.size(); ++i)
2034 if(LoginSessions_[i].uuid_ == UUID)
2037 if(i != LoginSessions_.size())
2039 __COUT_ERR__ <<
"UUID: " << UUID <<
" is not unique" << __E__;
2044 LoginSessions_.push_back(LoginSession());
2045 LoginSessions_.back().uuid_ = UUID;
2049 std::string sid =
"";
2050 for(i = 0; i < SESSION_ID_LENGTH / 2; ++i)
2052 intToHexStr(rand(), hexStr);
2055 LoginSessions_.back().id_ = sid;
2056 LoginSessions_.back().ip_ = ip;
2057 LoginSessions_.back().startTime_ = time(0);
2058 LoginSessions_.back().loginAttempts_ = 0;
2068 std::string WebUsers::sha512(
const std::string& user,
const std::string& password, std::string& salt)
2070 SHA512_CTX sha512_context;
2075 SHA512_Init(&sha512_context);
2077 for(
unsigned int i = 0; i < 8; ++i)
2078 sha512_context.h[i] += rand();
2080 for(
unsigned int i = 0; i <
sizeof(SHA512_CTX); ++i)
2082 intToHexStr((uint8_t)(((uint8_t*)(&sha512_context))[i]), hexStr);
2084 salt.append(hexStr);
2092 for(
unsigned int i = 0; i <
sizeof(SHA512_CTX); ++i)
2093 ((uint8_t*)(&sha512_context))[i] = hexByteStrToInt(&(salt.c_str()[i * 2]));
2096 std::string strToHash = salt + user + password;
2099 unsigned char hash[SHA512_DIGEST_LENGTH];
2101 char retHash[SHA512_DIGEST_LENGTH * 2 + 1];
2105 SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length());
2107 SHA512_Final(hash, &sha512_context);
2111 for(i = 0; i < SHA512_DIGEST_LENGTH; i++)
2112 sprintf(retHash + (i * 2),
"%02x", hash[i]);
2115 retHash[SHA512_DIGEST_LENGTH * 2] =
'\0';
2126 std::string WebUsers::dejumble(
const std::string& u,
const std::string& s)
2128 if(s.length() != SESSION_ID_LENGTH)
2131 const int ss = s.length() / 2;
2132 int p = hexByteStrToInt(&(s.c_str()[0])) % ss;
2133 int n = hexByteStrToInt(&(s.c_str()[p * 2])) % ss;
2134 int len = (hexByteStrToInt(&(u.c_str()[p * 2])) - p - n + ss * 3) % ss;
2136 std::vector<bool> x(ss);
2137 for(
int i = 0; i < ss; ++i)
2141 int c = hexByteStrToInt(&(u.c_str()[p * 2]));
2143 std::string user =
"";
2145 for(
int l = 0; l < len; ++l)
2147 p = (p + hexByteStrToInt(&(s.c_str()[p * 2]))) % ss;
2151 n = hexByteStrToInt(&(s.c_str()[p * 2]));
2152 user.append(1, (hexByteStrToInt(&(u.c_str()[p * 2])) - c - n + ss * 4) % ss);
2153 c = hexByteStrToInt(&(u.c_str()[p * 2]));
2162 std::map<std::string , WebUsers::permissionLevel_t> WebUsers::getPermissionsForUser(uint64_t uid)
2165 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2167 if(userIndex < Users_.size())
2168 return Users_[userIndex].permissions_;
2171 std::map<std::string , WebUsers::permissionLevel_t> retErrorMap;
2172 retErrorMap[WebUsers::DEFAULT_USER_GROUP] = WebUsers::PERMISSION_LEVEL_INACTIVE;
2179 WebUsers::permissionLevel_t WebUsers::getPermissionLevelForGroup(
const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
2180 const std::string& groupName)
2182 auto it = permissionMap.find(groupName);
2183 if(it == permissionMap.end())
2185 __COUT__ <<
"Group name '" << groupName <<
"' not found - assuming inactive user in this group." << __E__;
2186 return WebUsers::PERMISSION_LEVEL_INACTIVE;
2192 bool WebUsers::isInactiveForGroup(
const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
const std::string& groupName)
2194 return getPermissionLevelForGroup(permissionMap, groupName) == WebUsers::PERMISSION_LEVEL_INACTIVE;
2198 bool WebUsers::isAdminForGroup(
const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
const std::string& groupName)
2200 return getPermissionLevelForGroup(permissionMap, groupName) == WebUsers::PERMISSION_LEVEL_ADMIN;
2206 std::string WebUsers::getTooltipFilename(
const std::string& username,
const std::string& srcFile,
const std::string& srcFunc,
const std::string& srcId)
2208 std::string filename = (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH +
"/";
2212 mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755);
2213 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755);
2214 mkdir(filename.c_str(), 0755);
2216 for(
const char& c : username)
2218 (c >=
'a' && c <=
'z') || (c >=
'A' && c <= 'Z') || (c >=
'0' && c <=
'9'))
2223 mkdir(filename.c_str(), 0755);
2225 for(
const char& c : srcFile)
2227 (c >=
'a' && c <=
'z') || (c >=
'A' && c <= 'Z') || (c >=
'0' && c <=
'9'))
2230 for(
const char& c : srcFunc)
2232 (c >=
'a' && c <=
'z') || (c >=
'A' && c <= 'Z') || (c >=
'0' && c <=
'9'))
2235 for(
const char& c : srcId)
2237 (c >=
'a' && c <=
'z') || (c >=
'A' && c <= 'Z') || (c >=
'0' && c <=
'9'))
2244 std::string ots::WebUsers::getUserEmailFromFingerprint(
const std::string& fingerprint)
2246 __COUT__ <<
"Checking if user fingerprint " << fingerprint <<
" is in memory database" << __E__;
2247 if(certFingerprints_.count(fingerprint)) {
return certFingerprints_[fingerprint]; }
2249 __COUT__ <<
"Going to read credential database " << WEB_LOGIN_CERTDATA_PATH << __E__;
2250 std::ifstream f(WEB_LOGIN_CERTDATA_PATH);
2258 if(fp !=
"NOKEY" && fp !=
"") {
2259 __COUT__ <<
"Adding user " << email <<
" to list with fingerprint " << fp << __E__;
2260 certFingerprints_[fp] = email; }
2264 remove(WEB_LOGIN_CERTDATA_PATH.c_str());
2267 __COUT__ <<
"Checking again if fingerprint is in memory database" << __E__;
2268 if(certFingerprints_.count(fingerprint)) {
return certFingerprints_[fingerprint]; }
2270 __COUT__ <<
"Could not match fingerprint, returning null email" << __E__;
2277 void WebUsers::tooltipSetNeverShowForUsername(
const std::string& username,
2279 const std::string& srcFile,
2280 const std::string& srcFunc,
2281 const std::string& srcId,
2283 bool temporarySilence)
2285 __COUT__ <<
"Setting tooltip never show for user '" << username <<
"' to " << doNeverShow <<
" (temporarySilence=" << temporarySilence <<
")" << __E__;
2287 std::string filename = getTooltipFilename(username, srcFile, srcFunc, srcId);
2288 FILE* fp = fopen(filename.c_str(),
"w");
2291 if(temporarySilence)
2292 fprintf(fp,
"%ld", time(0) + 7 * 24 * 60 * 60);
2293 else if(doNeverShow && username == WebUsers::DEFAULT_ADMIN_USERNAME)
2296 fprintf(fp,
"%ld", time(0) + 30 * 24 * 60 * 60);
2298 __COUT__ <<
"User '" << username
2299 <<
"' may be a shared account, so max silence duration for tooltips "
2300 "is 30 days. Silencing now."
2304 fputc(doNeverShow ?
'1' :
'0', fp);
2308 __COUT_ERR__ <<
"Big problem with tooltips! File not accessible: " << filename << __E__;
2318 void WebUsers::tooltipCheckForUsername(
2319 const std::string& username,
HttpXmlDocument* xmldoc,
const std::string& srcFile,
const std::string& srcFunc,
const std::string& srcId)
2321 if(srcId ==
"ALWAYS")
2324 xmldoc->addTextElementToData(
"ShowTooltip",
"1");
2335 std::string silencefilename = getTooltipFilename(username, SILENCE_ALL_TOOLTIPS_FILENAME,
"",
"");
2337 FILE* silencefp = fopen(silencefilename.c_str(),
"r");
2338 if(silencefp != NULL)
2340 xmldoc->addTextElementToData(
"ShowTooltip",
"0");
2341 tooltipSetNeverShowForUsername(username, xmldoc, srcFile, srcFunc, srcId,
true,
true);
2345 std::string filename = getTooltipFilename(username, srcFile, srcFunc, srcId);
2346 FILE* fp = fopen(filename.c_str(),
"r");
2351 fgets(line, 100, fp);
2353 sscanf(line,
"%ld", &val);
2356 __COUT__ <<
"tooltip value read = " << val <<
" vs time(0)=" << time(0) << __E__;
2360 xmldoc->addTextElementToData(
"ShowTooltip", val == 1 ?
"0" : (time(0) > val ?
"1" :
"0"));
2364 xmldoc->addTextElementToData(
"ShowTooltip",
"1");
2371 void WebUsers::resetAllUserTooltips(
const std::string& userNeedle)
2373 std::system((
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH +
"/" + userNeedle).c_str());
2374 __COUT__ <<
"Successfully reset Tooltips for user " << userNeedle << __E__;
2380 void WebUsers::silenceAllUserTooltips(
const std::string& username)
2382 std::string silencefilename = getTooltipFilename(username, SILENCE_ALL_TOOLTIPS_FILENAME,
"",
"");
2383 FILE* silencefp = fopen(silencefilename.c_str(),
"w");
2384 if(silencefp != NULL)
2386 fputs(
"mute tool tips", silencefp);
2412 void WebUsers::insertSettingsForUser(uint64_t uid,
HttpXmlDocument* xmldoc,
bool includeAccounts)
2414 std::map<std::string , WebUsers::permissionLevel_t> permissionMap = getPermissionsForUser(uid);
2417 if(isInactiveForGroup(permissionMap))
2420 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2421 __COUT__ <<
"Gettings settings for user: " << Users_[userIndex].username_ << __E__;
2424 (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + Users_[userIndex].username_ +
"." + (std::string)USERS_PREFERENCES_FILETYPE;
2428 __COUT__ <<
"Preferences file: " << fn << __E__;
2430 if(!prefXml.loadXmlDocument(fn))
2432 __COUT__ <<
"Preferences are defaults." << __E__;
2434 xmldoc->addTextElementToData(PREF_XML_BGCOLOR_FIELD, PREF_XML_BGCOLOR_DEFAULT);
2435 xmldoc->addTextElementToData(PREF_XML_DBCOLOR_FIELD, PREF_XML_DBCOLOR_DEFAULT);
2436 xmldoc->addTextElementToData(PREF_XML_WINCOLOR_FIELD, PREF_XML_WINCOLOR_DEFAULT);
2437 xmldoc->addTextElementToData(PREF_XML_LAYOUT_FIELD, PREF_XML_LAYOUT_DEFAULT);
2441 __COUT__ <<
"Saved Preferences found." << __E__;
2442 xmldoc->copyDataChildren(prefXml);
2446 if(includeAccounts && isAdminForGroup(permissionMap))
2448 __COUT__ <<
"Admin on our hands" << __E__;
2450 xmldoc->addTextElementToData(PREF_XML_ACCOUNTS_FIELD,
"");
2452 if(Users_.size() == 0)
2454 __COUT__ <<
"Missing users? Attempting to load database" << __E__;
2459 for(uint64_t i = 0; i < Users_.size(); ++i)
2461 xmldoc->addTextElementToParent(
"username", Users_[i].username_, PREF_XML_ACCOUNTS_FIELD);
2462 xmldoc->addTextElementToParent(
"display_name", Users_[i].displayName_, PREF_XML_ACCOUNTS_FIELD);
2464 if(Users_[i].email_.size() > i)
2466 xmldoc->addTextElementToParent(
"useremail", Users_[i].email_, PREF_XML_ACCOUNTS_FIELD);
2470 xmldoc->addTextElementToParent(
"useremail",
"", PREF_XML_ACCOUNTS_FIELD);
2473 xmldoc->addTextElementToParent(
"permissions", StringMacros::mapToString(Users_[i].permissions_), PREF_XML_ACCOUNTS_FIELD);
2475 xmldoc->addTextElementToParent(
"nac", Users_[i].getNewAccountCode().c_str(), PREF_XML_ACCOUNTS_FIELD);
2480 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + (std::string)SYSTEM_PREFERENCES_PREFIX +
"." +
2481 (std::string)USERS_PREFERENCES_FILETYPE;
2482 if(!prefXml.loadXmlDocument(fn))
2484 __COUT__ <<
"System Preferences are defaults." << __E__;
2486 xmldoc->addTextElementToData(PREF_XML_SYSLAYOUT_FIELD, PREF_XML_SYSLAYOUT_DEFAULT);
2490 __COUT__ <<
"Saved System Preferences found." << __E__;
2491 xmldoc->copyDataChildren(prefXml);
2494 __COUTV__(StringMacros::mapToString(permissionMap));
2497 xmldoc->addTextElementToData(PREF_XML_PERMISSIONS_FIELD, StringMacros::mapToString(permissionMap));
2500 xmldoc->addTextElementToData(PREF_XML_USERLOCK_FIELD, usersUsernameWithLock_);
2503 xmldoc->addTextElementToData(PREF_XML_USERNAME_FIELD, getUsersUsername(uid));
2506 xmldoc->addTextElementToData(PREF_XML_OTS_OWNER_FIELD, WebUsers::OTS_OWNER);
2513 void WebUsers::setGenericPreference(uint64_t uid,
const std::string& preferenceName,
const std::string& preferenceValue)
2515 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2520 std::string safePreferenceName =
"";
2521 for(
const auto& c : preferenceName)
2522 if((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9') || (c >=
'-' || c <=
'_'))
2523 safePreferenceName += c;
2525 std::string dir = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
"generic_" + safePreferenceName +
"/";
2528 mkdir(dir.c_str(), 0755);
2530 std::string fn = Users_[userIndex].username_ +
"_" + safePreferenceName +
"." + (std::string)USERS_PREFERENCES_FILETYPE;
2532 __COUT__ <<
"Preferences file: " << (dir + fn) << __E__;
2534 FILE* fp = fopen((dir + fn).c_str(),
"w");
2537 fprintf(fp,
"%s", preferenceValue.c_str());
2541 __COUT_ERR__ <<
"Preferences file could not be opened for writing!" << __E__;
2548 std::string WebUsers::getGenericPreference(uint64_t uid,
const std::string& preferenceName,
HttpXmlDocument* xmldoc)
const
2550 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2555 std::string safePreferenceName =
"";
2556 for(
const auto& c : preferenceName)
2557 if((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9') || (c >=
'-' || c <=
'_'))
2558 safePreferenceName += c;
2560 std::string dir = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
"generic_" + safePreferenceName +
"/";
2562 std::string fn = Users_[userIndex].username_ +
"_" + safePreferenceName +
"." + (std::string)USERS_PREFERENCES_FILETYPE;
2564 __COUT__ <<
"Preferences file: " << (dir + fn) << __E__;
2567 FILE* fp = fopen((dir + fn).c_str(),
"rb");
2570 fseek(fp, 0, SEEK_END);
2571 long size = ftell(fp);
2573 line.reserve(size + 1);
2575 fgets(&line[0], size + 1, fp);
2578 __COUT__ <<
"Read value " << line << __E__;
2580 xmldoc->addTextElementToData(safePreferenceName, line);
2584 __COUT__ <<
"Using default value." << __E__;
2588 xmldoc->addTextElementToData(safePreferenceName,
"");
2594 void WebUsers::changeSettingsForUser(
2595 uint64_t uid,
const std::string& bgcolor,
const std::string& dbcolor,
const std::string& wincolor,
const std::string& layout,
const std::string& syslayout)
2597 std::map<std::string , WebUsers::permissionLevel_t> permissionMap = getPermissionsForUser(uid);
2598 if(isInactiveForGroup(permissionMap))
2601 uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2602 __COUT__ <<
"Changing settings for user: " << Users_[userIndex].username_ << __E__;
2605 (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + Users_[userIndex].username_ +
"." + (std::string)USERS_PREFERENCES_FILETYPE;
2607 __COUT__ <<
"Preferences file: " << fn << __E__;
2610 prefXml.addTextElementToData(PREF_XML_BGCOLOR_FIELD, bgcolor);
2611 prefXml.addTextElementToData(PREF_XML_DBCOLOR_FIELD, dbcolor);
2612 prefXml.addTextElementToData(PREF_XML_WINCOLOR_FIELD, wincolor);
2613 prefXml.addTextElementToData(PREF_XML_LAYOUT_FIELD, layout);
2615 prefXml.saveXmlDocument(fn);
2618 if(!isAdminForGroup(permissionMap))
2622 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + (std::string)SYSTEM_PREFERENCES_PREFIX +
"." +
2623 (std::string)USERS_PREFERENCES_FILETYPE;
2626 sysPrefXml.addTextElementToData(PREF_XML_SYSLAYOUT_FIELD, syslayout);
2628 sysPrefXml.saveXmlDocument(fn);
2636 bool WebUsers::setUserWithLock(uint64_t actingUid,
bool lock, const std::
string& username)
2638 std::map<std::string , WebUsers::permissionLevel_t> permissionMap = getPermissionsForUser(actingUid);
2640 std::string actingUser = getUsersUsername(actingUid);
2642 __COUTV__(actingUser);
2643 __COUT__ <<
"Permissions: " << StringMacros::mapToString(permissionMap) << __E__;
2644 __COUTV__(usersUsernameWithLock_);
2646 __COUTV__(username);
2647 __COUTV__(isUsernameActive(username));
2649 if(lock && (isUsernameActive(username) || !CareAboutCookieCodes_))
2651 if(!CareAboutCookieCodes_ && username != DEFAULT_ADMIN_USERNAME)
2653 __MCOUT_ERR__(
"User '" << actingUser <<
"' tried to lock for a user other than admin in wiz mode. Not allowed." << __E__);
2656 else if(!isAdminForGroup(permissionMap) && actingUser != username)
2658 __MCOUT_ERR__(
"A non-admin user '" << actingUser <<
"' tried to lock for a user other than self. Not allowed." << __E__);
2661 usersUsernameWithLock_ = username;
2663 else if(!lock && usersUsernameWithLock_ == username)
2664 usersUsernameWithLock_ =
"";
2667 if(!isUsernameActive(username))
2668 __MCOUT_ERR__(
"User '" << username <<
"' is inactive." << __E__);
2669 __MCOUT_ERR__(
"Failed to lock for user '" << username <<
".'" << __E__);
2673 __MCOUT_INFO__(
"User '" << username <<
"' has locked out the system!" << __E__);
2677 std::string securityFileName = USER_WITH_LOCK_FILE;
2678 FILE* fp = fopen(securityFileName.c_str(),
"w");
2681 __COUT_INFO__ <<
"USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE <<
" not found. Ignoring." << __E__;
2685 fprintf(fp,
"%s", usersUsernameWithLock_.c_str());
2694 void WebUsers::modifyAccountSettings(
2695 uint64_t actingUid, uint8_t cmd_type,
const std::string& username,
const std::string& displayname,
const std::string& email,
const std::string& permissions)
2697 std::map<std::string , WebUsers::permissionLevel_t> permissionMap = getPermissionsForUser(actingUid);
2698 if(!isAdminForGroup(permissionMap))
2701 __SS__ <<
"Only admins can modify user settings." << __E__;
2705 uint64_t i = searchUsersDatabaseForUserId(actingUid);
2706 uint64_t modi = searchUsersDatabaseForUsername(username);
2711 __COUT_INFO__ <<
"Admin password reset." << __E__;
2712 Users_[modi].setModifier(Users_[i].username_);
2713 Users_[modi].salt_ =
"";
2714 Users_[modi].loginFailureCount_ = 0;
2715 saveDatabaseToFile(DB_USERS);
2718 __SS__ <<
"Cannot modify first user" << __E__;
2722 if(username.length() < USERNAME_LENGTH)
2724 __SS__ <<
"Invalid Username, must be length " << USERNAME_LENGTH << __E__;
2727 if(displayname.length() < DISPLAY_NAME_LENGTH)
2729 __SS__ <<
"Invalid Display Name; must be length " << DISPLAY_NAME_LENGTH << __E__;
2733 __COUT__ <<
"Input Permissions: " << permissions << __E__;
2734 std::map<std::string , WebUsers::permissionLevel_t> newPermissionsMap;
2738 case MOD_TYPE_UPDATE:
2740 __COUT__ <<
"MOD_TYPE_UPDATE " << username <<
" := " << permissions << __E__;
2742 if(modi == NOT_FOUND_IN_DATABASE)
2744 __SS__ <<
"User not found!? Should not happen." << __E__;
2750 for(uint64_t i=0; i < Users_.size(); ++i)
2751 if(i == modi)
continue;
2752 else if(Users_[i].displayName_ == displayname)
2754 __SS__ <<
"Display Name '" << displayname <<
"' already exists! Please choose a unique display name." << __E__;
2759 Users_[modi].displayName_ = displayname;
2760 Users_[modi].email_ = email;
2763 StringMacros::getMapFromString(permissions, newPermissionsMap);
2764 bool wasInactive = isInactiveForGroup(Users_[modi].permissions_);
2767 if(newPermissionsMap.size() == 0)
2768 Users_[modi].permissions_[WebUsers::DEFAULT_USER_GROUP] = std::atoi(permissions.c_str());
2769 else if(newPermissionsMap.size() == 1 && newPermissionsMap.find(WebUsers::DEFAULT_USER_GROUP) == newPermissionsMap.end())
2771 if(newPermissionsMap.begin()->first ==
"")
2772 Users_[modi].permissions_[WebUsers::DEFAULT_USER_GROUP] = newPermissionsMap.begin()->second;
2775 newPermissionsMap[WebUsers::DEFAULT_USER_GROUP] = newPermissionsMap.begin()->second;
2776 Users_[modi].permissions_ = newPermissionsMap;
2780 Users_[modi].permissions_ = newPermissionsMap;
2785 !isInactiveForGroup(Users_[modi].permissions_))
2787 __COUT__ <<
"Reactivating " << username << __E__;
2788 Users_[modi].loginFailureCount_ = 0;
2789 Users_[modi].salt_ =
"";
2795 if(i == NOT_FOUND_IN_DATABASE)
2797 __SS__ <<
"Master User not found!? Should not happen." << __E__;
2800 Users_[modi].setModifier(Users_[i].username_);
2806 __COUT__ <<
"MOD_TYPE_ADD " << username <<
" - " << displayname << __E__;
2808 createNewAccount(username, displayname, email);
2811 if(i == NOT_FOUND_IN_DATABASE)
2813 __SS__ <<
"Master User not found!? Should not happen." << __E__;
2816 Users_.back().setModifier(Users_[i].username_);
2819 if(permissions.size())
2821 modifyAccountSettings(actingUid, MOD_TYPE_UPDATE, username, displayname, email, permissions);
2825 case MOD_TYPE_DELETE:
2826 __COUT__ <<
"MOD_TYPE_DELETE " << username <<
" - " << displayname << __E__;
2827 deleteAccount(username, displayname);
2830 __SS__ <<
"Undefined command - do nothing " << username << __E__;
2834 saveDatabaseToFile(DB_USERS);
2840 std::string WebUsers::getActiveUsersString()
2842 std::set<unsigned int> activeUserIndices;
2843 for(uint64_t i = 0; i < ActiveSessions_.size(); ++i)
2844 activeUserIndices.emplace(searchUsersDatabaseForUserId(ActiveSessions_[i].userId_));
2846 std::string ret =
"";
2847 bool addComma =
false;
2848 for(
const auto& i:activeUserIndices)
2850 if(i >= Users_.size())
continue;
2852 if(addComma) ret +=
",";
2853 else addComma =
true;
2855 ret += Users_[i].displayName_;
2864 uint64_t WebUsers::getAdminUserID()
2866 uint64_t uid = searchUsersDatabaseForUsername(DEFAULT_ADMIN_USERNAME);
2873 void WebUsers::loadUserWithLock()
2875 char username[300] =
"";
2877 std::string securityFileName = USER_WITH_LOCK_FILE;
2878 FILE* fp = fopen(securityFileName.c_str(),
"r");
2881 __COUT_INFO__ <<
"USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE <<
" not found. Defaulting to admin lock." << __E__;
2884 sprintf(username,
"%s", DEFAULT_ADMIN_USERNAME.c_str());
2888 fgets(username, 300, fp);
2889 username[299] =
'\0';
2894 __COUT__ <<
"Attempting to load username with lock: " << username << __E__;
2896 if(strlen(username) == 0)
2898 __COUT_INFO__ <<
"Loaded state for user-with-lock is unlocked." << __E__;
2902 uint64_t i = searchUsersDatabaseForUsername(username);
2903 if(i == NOT_FOUND_IN_DATABASE)
2905 __COUT_INFO__ <<
"username " << username <<
" not found in database. Ignoring." << __E__;
2908 __COUT__ <<
"Setting lock" << __E__;
2909 setUserWithLock(Users_[i].userId_,
true, username);
2916 void WebUsers::addSystemMessage(
const std::string& targetUsersCSV,
const std::string& message)
2918 addSystemMessage(targetUsersCSV,
"" ,message,
false );
2924 void WebUsers::addSystemMessage(
const std::string& targetUsersCSV,
const std::string& subject,
const std::string& message,
bool doEmail)
2926 std::vector<std::string> targetUsers;
2927 StringMacros::getVectorFromString(targetUsersCSV,targetUsers);
2928 addSystemMessage(targetUsers,subject,message,doEmail);
2934 void WebUsers::addSystemMessage(
const std::vector<std::string>& targetUsers,
2935 const std::string& subject,
const std::string& message,
bool doEmail)
2937 __COUT__ <<
"Before number of users with system messages: " << systemMessages_.size() << __E__;
2940 std::lock_guard<std::mutex> lock(systemMessageLock_);
2942 systemMessageCleanup();
2944 std::string fullMessage = StringMacros::encodeURIComponent((subject ==
""?
"":(subject +
": ")) +
2947 __COUTV__(fullMessage);
2948 __COUTV__(StringMacros::vectorToString(targetUsers));
2950 std::set<std::string> targetEmails;
2952 for(
const auto& targetUser : targetUsers)
2956 if(targetUser ==
"" || (targetUser !=
"*" && targetUser.size() < 3))
2958 __COUT__ <<
"Illegal username '" << targetUser <<
"'" << __E__;
2961 __COUTV__(targetUser);
2966 if(doEmail && targetUser ==
"*")
2969 for(
const auto& user : Users_)
2971 if(user.email_.size() > 5 &&
2972 user.email_.find(
'@') != std::string::npos &&
2973 user.email_.find(
'.') != std::string::npos)
2975 __COUT__ <<
"Adding " << user.displayName_ <<
" email: " << user.email_ << __E__;
2976 targetEmails.emplace(user.email_);
2981 else if(targetUser.find(
':') != std::string::npos)
2984 __COUT__ <<
"Treating as group email target: " << targetUser << __E__;
2986 std::map<std::string, WebUsers::permissionLevel_t> targetGroupMap;
2987 StringMacros::getMapFromString(
2991 __COUTV__(StringMacros::mapToString(targetGroupMap));
2993 if(targetGroupMap.size() == 1)
2998 for(
const auto& user : Users_)
3000 WebUsers::permissionLevel_t userLevel =
3001 getPermissionLevelForGroup(getPermissionsForUser(user.userId_),
3002 targetGroupMap.begin()->first);
3004 __COUTV__(StringMacros::mapToString(getPermissionsForUser(user.userId_)));
3005 __COUTV__((
int)userLevel);
3006 __COUTV__(targetGroupMap.begin()->first);
3008 if(userLevel != WebUsers::PERMISSION_LEVEL_INACTIVE &&
3009 userLevel >= targetGroupMap.begin()->second &&
3010 user.email_.size() > 5 &&
3011 user.email_.find(
'@') != std::string::npos &&
3012 user.email_.find(
'.') != std::string::npos)
3016 targetEmails.emplace(user.email_);
3017 __COUT__ <<
"Adding " << user.displayName_ <<
" email: " << user.email_ << __E__;
3019 addSystemMessageToMap(user.displayName_,fullMessage);
3024 __COUT__ <<
"target Group Map from '" << targetUser <<
"' is empty." << __E__;
3031 addSystemMessageToMap(targetUser,fullMessage);
3035 for(
const auto& user : Users_)
3037 if(user.displayName_ == targetUser)
3039 if(user.email_.size() > 5 &&
3040 user.email_.find(
'@') != std::string::npos &&
3041 user.email_.find(
'.') != std::string::npos)
3043 targetEmails.emplace(user.email_);
3044 __COUT__ <<
"Adding " << user.displayName_ <<
" email: " << user.email_ << __E__;
3053 __COUT__ <<
"After number of users with system messages: " << systemMessages_.size() << __E__;
3054 __COUTV__(targetEmails.size());
3056 if(doEmail && targetEmails.size())
3058 __COUTV__(StringMacros::setToString(targetEmails));
3060 std::string toList =
"";
3061 bool addComma =
false;
3062 for(
const auto& email : targetEmails)
3064 if(addComma) toList +=
", ";
3065 else addComma =
true;
3069 std::string filename = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH +
"/.tmp_email.txt";
3070 FILE* fp = fopen(filename.c_str(),
"w");
3073 __SS__ <<
"Could not open email file: " << filename << __E__;
3077 fprintf(fp,
"From: %s\n",(WebUsers::OTS_OWNER==
""?
"ots":(
3078 StringMacros::decodeURIComponent(WebUsers::OTS_OWNER) +
"_ots")).c_str());
3079 fprintf(fp,
"To: %s\n",toList.c_str());
3080 fprintf(fp,
"Subject: %s\n",subject.c_str());
3081 fprintf(fp,
"Content-Type: text/html\n");
3082 fprintf(fp,
"\n<html><pre>%s</pre></html>",message.c_str());
3085 StringMacros::exec((
"sendmail \"" + toList +
"\" < " + filename).c_str());
3088 __COUT_WARN__ <<
"Do email was attempted, but no target users had email addresses specified!" << __E__;
3096 void WebUsers::addSystemMessageToMap(
const std::string& targetUser,
const std::string& fullMessage)
3098 auto it = systemMessages_.find(targetUser);
3101 if(it != systemMessages_.end() &&
3102 it->second.size() &&
3103 it->second[it->second.size() - 1].message_ == fullMessage)
3106 if(it == systemMessages_.end())
3108 systemMessages_.emplace(
3109 std::pair<std::string ,std::vector<SystemMessage>>(
3111 std::vector<SystemMessage>({SystemMessage(fullMessage)})
3113 __COUT__ << targetUser <<
" Current System Messages count = " << 1 << __E__;
3118 it->second.push_back(SystemMessage(fullMessage));
3119 __COUT__ << it->first <<
" Current System Messages count = " << it->second.size() << __E__;
3130 std::string WebUsers::getSystemMessage(
const std::string& targetUser)
3135 std::lock_guard<std::mutex> lock(systemMessageLock_);
3140 std::string retStr =
"";
3145 auto it = systemMessages_.find(targetUser);
3152 for(uint64_t i = 0; it != systemMessages_.end() && i < it->second.size(); ++i)
3157 sprintf(tmp,
"%lu", it->second[i].creationTime_);
3158 retStr += std::string(tmp) +
"|" + it->second[i].message_;
3160 it->second[i].delivered_ =
true;
3163 it = systemMessages_.find(
"*");
3164 for(uint64_t i = 0; it != systemMessages_.end() && i < it->second.size(); ++i)
3169 sprintf(tmp,
"%lu", it->second[i].creationTime_);
3170 retStr += std::string(tmp) +
"|" + it->second[i].message_;
3176 systemMessageCleanup();
3185 void WebUsers::systemMessageCleanup()
3188 for(
auto& userMessagesPair : systemMessages_)
3193 for(uint64_t i = 0; i < userMessagesPair.second.size(); ++i)
3194 if((userMessagesPair.first !=
"*" && userMessagesPair.second[i].delivered_) ||
3195 userMessagesPair.second[i].creationTime_ + SYS_CLEANUP_WILDCARD_TIME < time(0))
3198 userMessagesPair.second.erase(userMessagesPair.second.begin() + i);
3210 std::string WebUsers::getSecurity() {
return securityType_; }
3213 void WebUsers::loadSecuritySelection()
3215 std::string securityFileName = SECURITY_FILE_NAME;
3216 FILE* fp = fopen(securityFileName.c_str(),
"r");
3217 char line[100] =
"";
3219 fgets(line, 100, fp);
3223 while(i < strlen(line) && line[i] >=
'A' && line[i] <=
'z')
3227 if(strcmp(line, SECURITY_TYPE_NONE.c_str()) == 0 || strcmp(line, SECURITY_TYPE_DIGEST_ACCESS.c_str()) == 0)
3228 securityType_ = line;
3230 securityType_ = SECURITY_TYPE_DEFAULT;
3232 __COUT__ <<
"The current security type is " << securityType_ << __E__;
3237 if(securityType_ == SECURITY_TYPE_NONE)
3238 CareAboutCookieCodes_ =
false;
3240 CareAboutCookieCodes_ =
true;
3242 __COUT__ <<
"CareAboutCookieCodes_: " << CareAboutCookieCodes_ << __E__;
3246 void WebUsers::NACDisplayThread(
const std::string& nac,
const std::string& user)
3257 std::this_thread::sleep_for(std::chrono::seconds(2));
3258 __COUT__ <<
"\n******************************************************************** " << __E__;
3259 __COUT__ <<
"\n******************************************************************** " << __E__;
3260 __COUT__ <<
"\n\nNew account code = " << nac <<
" for user: " << user <<
"\n" << __E__;
3261 __COUT__ <<
"\n******************************************************************** " << __E__;
3262 __COUT__ <<
"\n******************************************************************** " << __E__;
3267 void WebUsers::deleteUserData()
3269 __COUT__ <<
"$$$$$$$$$$$$$$ Deleting ALL service user data... $$$$$$$$$$$$" << __E__;
3272 std::system((
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH +
"/*").c_str());
3273 std::system((
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH +
"/*").c_str());
3274 std::system((
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH +
"/*").c_str());
3275 std::system((
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH +
"/*").c_str());
3276 std::system((
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH).c_str());
3278 std::string serviceDataPath = __ENV__(
"SERVICE_DATA_PATH");
3280 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/MacroData/").c_str());
3281 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/MacroHistory/").c_str());
3282 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/MacroExport/").c_str());
3285 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/ConsolePreferences/").c_str());
3288 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/CodeEditorData/").c_str());
3291 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/OtsWizardData/").c_str());
3294 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/ProgressBarData/").c_str());
3297 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/RunNumber/").c_str());
3298 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/RunControlData/").c_str());
3301 std::system((
"rm -rf " + std::string(serviceDataPath) +
"/VisualizerData/").c_str());
3308 std::system((
"rm -rf " + std::string(__ENV__(
"LOGBOOK_DATA_PATH")) +
"/").c_str());
3310 __COUT__ <<
"$$$$$$$$$$$$$$ Successfully deleted ALL service user data $$$$$$$$$$$$" << __E__;