$treeview $search $mathjax $extrastylesheet
otsdaq
v2_03_00
$projectbrief
|
$projectbrief
|
$searchbox |
00001 #include "otsdaq-core/WebUsersUtilities/WebUsers.h" 00002 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h" 00003 00004 #include <openssl/sha.h> 00005 #include <sys/stat.h> 00006 #include <sys/types.h> 00007 #include <cassert> 00008 #include <cstdio> 00009 #include <cstdlib> 00010 #include <iostream> 00011 00012 #include <chrono> // std::chrono::seconds 00013 #include <thread> // std::this_thread::sleep_for 00014 00015 using namespace ots; 00016 00017 #define WEB_LOGIN_BKUP_DB_PATH "bkup/" 00018 00019 #define SECURITY_FILE_NAME \ 00020 std::string(getenv("SERVICE_DATA_PATH")) + "/OtsWizardData/security.dat" 00021 00022 #define USERS_ACTIVE_SESSIONS_FILE USERS_DB_PATH + "/activeSessions.sv" 00023 00024 #define HASHES_DB_FILE HASHES_DB_PATH + "/hashes.xml" 00025 #define USERS_DB_FILE USERS_DB_PATH + "/users.xml" 00026 #define USERS_GLOBAL_HISTORY_FILE "__global" 00027 #define USERS_LOGIN_HISTORY_FILETYPE "hist" 00028 #define USERS_PREFERENCES_FILETYPE "pref" 00029 #define SYSTEM_PREFERENCES_PREFIX "system.preset" 00030 #define USER_WITH_LOCK_FILE WEB_LOGIN_DB_PATH + "/user_with_lock.dat" 00031 #define IP_BLACKLIST_FILE WEB_LOGIN_DB_PATH + "/ip_generated_blacklist.dat" 00032 #define IP_REJECT_FILE WEB_LOGIN_DB_PATH + "/ip_reject.dat" 00033 #define IP_ACCEPT_FILE WEB_LOGIN_DB_PATH + "/ip_accept.dat" 00034 00035 #define HASHES_DB_GLOBAL_STRING "hashData" 00036 #define HASHES_DB_ENTRY_STRING "hashEntry" 00037 #define USERS_DB_GLOBAL_STRING "userData" 00038 #define USERS_DB_ENTRY_STRING "userEntry" 00039 #define USERS_DB_NEXT_UID_STRING "nextUserId" 00040 00041 // defines for user preferences 00042 #define PREF_XML_BGCOLOR_FIELD "pref_bgcolor" // -background color 00043 #define PREF_XML_DBCOLOR_FIELD "pref_dbcolor" // -dashboard color 00044 #define PREF_XML_WINCOLOR_FIELD "pref_wincolor" // -window color 00045 #define PREF_XML_LAYOUT_FIELD "pref_layout" // -3 defaults window layouts(and current) 00046 #define PREF_XML_SYSLAYOUT_FIELD "pref_syslayout" // -2 defaults window layouts 00047 #define PREF_XML_PERMISSIONS_FIELD \ 00048 "desktop_user_permissions" // 0-255 permissions value (255 is admin super user) 00049 #define PREF_XML_USERLOCK_FIELD \ 00050 "username_with_lock" // user with lock (to lockout others) 00051 #define PREF_XML_USERNAME_FIELD "pref_username" // user with lock (to lockout others) 00052 00053 #define PREF_XML_BGCOLOR_DEFAULT "rgb(0,76,151)" // -background color 00054 #define PREF_XML_DBCOLOR_DEFAULT "rgb(0,40,85)" // -dashboard color 00055 #define PREF_XML_WINCOLOR_DEFAULT "rgba(196,229,255,0.9)" // -window color 00056 #define PREF_XML_LAYOUT_DEFAULT "0;0;0;0" // 3 default window layouts(and current) 00057 #define PREF_XML_SYSLAYOUT_DEFAULT "0;0" // 2 system default window layouts 00058 00059 #define PREF_XML_ACCOUNTS_FIELD "users_accounts" // user accounts field for super users 00060 #define PREF_XML_LOGIN_HISTORY_FIELD \ 00061 "login_entry" // login history field for user login history data 00062 00063 const std::string WebUsers::DEFAULT_ADMIN_USERNAME = "admin"; 00064 const std::string WebUsers::DEFAULT_ADMIN_DISPLAY_NAME = "Administrator"; 00065 const std::string WebUsers::DEFAULT_ADMIN_EMAIL = "root@otsdaq.fnal.gov"; 00066 const std::string WebUsers::DEFAULT_ITERATOR_USERNAME = "iterator"; 00067 const std::string WebUsers::DEFAULT_STATECHANGER_USERNAME = "statechanger"; 00068 const std::string WebUsers::DEFAULT_USER_GROUP = "allUsers"; 00069 00070 const std::string WebUsers::REQ_NO_LOGIN_RESPONSE = "NoLogin"; 00071 const std::string WebUsers::REQ_NO_PERMISSION_RESPONSE = "NoPermission"; 00072 const std::string WebUsers::REQ_USER_LOCKOUT_RESPONSE = "UserLockout"; 00073 const std::string WebUsers::REQ_LOCK_REQUIRED_RESPONSE = "LockRequired"; 00074 const std::string WebUsers::REQ_ALLOW_NO_USER = "AllowNoUser"; 00075 00076 const std::string WebUsers::SECURITY_TYPE_NONE = "NoSecurity"; 00077 const std::string WebUsers::SECURITY_TYPE_DIGEST_ACCESS = "DigestAccessAuthentication"; 00078 00079 #undef __MF_SUBJECT__ 00080 #define __MF_SUBJECT__ "WebUsers" 00081 00082 WebUsers::WebUsers() 00083 { 00084 // deleteUserData(); //leave for debugging to reset user data 00085 00086 usersNextUserId_ = 0; // first UID, default to 0 but get from database 00087 usersUsernameWithLock_ = ""; // init to no user with lock 00088 00089 // define fields 00090 HashesDatabaseEntryFields.push_back("hash"); 00091 HashesDatabaseEntryFields.push_back( 00092 "lastAccessTime"); // last login month resolution, blurred by 1/2 month 00093 00094 UsersDatabaseEntryFields.push_back("username"); 00095 UsersDatabaseEntryFields.push_back("displayName"); 00096 UsersDatabaseEntryFields.push_back("salt"); 00097 UsersDatabaseEntryFields.push_back("uid"); 00098 UsersDatabaseEntryFields.push_back("permissions"); 00099 UsersDatabaseEntryFields.push_back("lastLoginAttemptTime"); 00100 UsersDatabaseEntryFields.push_back("accountCreatedTime"); 00101 UsersDatabaseEntryFields.push_back("loginFailureCount"); 00102 UsersDatabaseEntryFields.push_back("lastModifiedTime"); 00103 UsersDatabaseEntryFields.push_back("lastModifierUsername"); 00104 UsersDatabaseEntryFields.push_back("useremail"); 00105 00106 // attempt to make directory structure (just in case) 00107 mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755); 00108 mkdir(((std::string)WEB_LOGIN_DB_PATH + "bkup/" + USERS_DB_PATH).c_str(), 0755); 00109 mkdir(((std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH).c_str(), 0755); 00110 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755); 00111 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH).c_str(), 0755); 00112 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH).c_str(), 0755); 00113 00114 if(!loadDatabases()) 00115 __COUT__ << "FATAL USER DATABASE ERROR - failed to load!!!" << __E__; 00116 00117 loadSecuritySelection(); 00118 00119 // print out admin new user code for ease of use 00120 uint64_t i; 00121 std::string user = DEFAULT_ADMIN_USERNAME; 00122 if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE) 00123 { 00124 __SS__ << "user: " << user << " is not found" << __E__; 00125 __COUT_ERR__ << ss.str(); 00126 __SS_THROW__; 00127 exit(0); // THIS CAN NOT HAPPEN?! There must be an admin user 00128 } 00129 else if(UsersSaltVector[i] == 00130 "" && // admin password not setup, so print out NAC to help out 00131 securityType_ == SECURITY_TYPE_DIGEST_ACCESS) 00132 { 00133 char charTimeStr[10]; 00134 sprintf(charTimeStr, "%d", int(UsersAccountCreatedTimeVector[i] & 0xffff)); 00135 std::string tmpTimeStr = charTimeStr; 00136 00138 // start thread for notifying the user about the admin new account code 00139 // notify for 10 seconds (e.g.) 00140 std::thread( 00141 [](const std::string& nac, const std::string& user) { 00142 WebUsers::NACDisplayThread(nac, user); 00143 }, 00144 tmpTimeStr, 00145 user) 00146 .detach(); 00147 } 00148 00149 // attempt to load persistent user sessions 00150 loadActiveSessions(); 00151 00152 // default user with lock to admin and/or try to load last user with lock 00153 // Note: this must happen after getting persistent active sessions 00154 loadUserWithLock(); 00155 00156 srand(time(0)); // seed random for hash salt generation 00157 00158 __COUT__ << "Done with Web Users initialization!" << __E__; 00159 00160 // FIXME -- can delete this commented section eventually 00161 // this is for debugging the registering and login functionality 00162 // 00163 // // deleteUserData(); 00164 00165 // std::string uuid = "0"; 00166 // std::string sid = createNewLoginSession(uuid); 00167 // exit(0); 00168 00169 // std::string newAccountCode = "60546"; 00170 // std::string pw = "testbeam"; 00171 // 00172 // __COUT__ << "user: " << user << __E__ << __E__; 00173 // 00174 // 00175 // if(1) //test salt functionality 00176 // { 00177 // std::string salt = ""; //don't want to modify saved salt 00178 // std::string hash1 = sha512(user,pw,salt); 00179 // std::string hash2 = sha512(user,pw,salt); 00180 // __COUT__ << hash1 << __E__; 00181 // __COUT__ << hash2 << __E__; 00182 // 00183 // __COUT__ << "String comparison result: " << 00184 // strcmp(hash1.c_str(),hash2.c_str()) 00185 //<< __E__; 00186 // } 00187 // 00188 // if(0) //login attempt 00189 // { 00190 // //search users for username 00191 // if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE) 00192 // { 00193 // __COUT__ << "user: " << user << " is not found" << __E__; 00194 // return;// NOT_FOUND_IN_DATABASE; 00195 // } 00196 // 00197 // __COUT__ << "user: " << user << __E__ << __E__; 00198 // 00199 // std::string salt = UsersSaltVector[i]; //don't want to modify saved salt 00200 // __COUT__ << salt<< " " << i << __E__; 00201 // if(searchHashesDatabaseForHash(sha512(user,pw,salt)) == NOT_FOUND_IN_DATABASE) 00202 // { 00203 // __COUT__ << "not found?" << __E__; 00204 // ++UsersLoginFailureCountVector[i]; 00205 // if(UsersLoginFailureCountVector[i] >= USERS_MAX_LOGIN_FAILURES) 00206 // UsersPermissionsVector[i] = 0; //Lock account 00207 // 00208 // __COUT__ << "\tUser/pw for user: " << user << " was not correct Failed 00209 // Attempt 00210 //#" << (int)(UsersLoginFailureCountVector[i]) << __E__; 00211 // if(!UsersPermissionsVector[i]) 00212 // __COUT__ << "Account is locked!" << __E__; 00213 // 00214 // saveDatabaseToFile(DB_USERS); //users db modified, so save 00215 // return;//NOT_FOUND_IN_DATABASE; 00216 // } 00217 // } 00218 // 00219 // 00220 // if(0) //first login 00221 // { 00222 // //search users for username 00223 // if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE) 00224 // { 00225 // __COUT__ << "user: " << user << " is not found" << __E__; 00226 // return;// NOT_FOUND_IN_DATABASE; 00227 // } 00228 // __COUT__ << "user: " << user << __E__ << __E__; 00229 // 00230 // UsersLastLoginAttemptVector[i] = time(0); 00231 // if(!UsersPermissionsVector[i]) 00232 // { 00233 // __COUT__ << "user: " << user << " account INACTIVE (could be due to failed 00234 // logins)" << __E__; return;// NOT_FOUND_IN_DATABASE; 00235 // } 00236 // __COUT__ << "user: " << user << __E__ << __E__; 00237 // 00238 // if(UsersSaltVector[i] == "") //first login 00239 // { 00240 // __COUT__ << "First login attempt for user: " << user << __E__; 00241 // 00242 // char charTimeStr[10]; 00243 // sprintf(charTimeStr,"%d",int(UsersAccountCreatedTimeVector[i] & 0xffff)); 00244 // std::string tmpTimeStr = charTimeStr; 00245 // if(newAccountCode != tmpTimeStr) 00246 // { 00247 // __COUT__ << "New account code did not match: " << tmpTimeStr << " != " 00248 //<< newAccountCode << __E__; saveDatabaseToFile(DB_USERS); //users db 00249 // modified, so save return;// NOT_FOUND_IN_DATABASE; 00250 // } 00251 // 00252 // __COUT__ << "First login attempt for user: " << user << __E__; 00253 // //initial user account setup 00254 // 00255 // //add until no collision (should 'never' be a collision) 00256 // while(!addToHashesDatabase(sha512(user,pw,UsersSaltVector[i]))) //sha256 00257 // modifies UsersSaltVector[i] 00258 // { 00259 // //this should never happen, it would mean the user+pw+saltcontext was 00260 // the same 00261 // // but if it were to happen, try again... 00262 // UsersSaltVector[i] = ""; 00263 // } 00264 // 00265 // 00266 // __COUT__ << "\tHash added: " << HashesVector[0] << __E__; 00267 // } 00268 // 00269 // __COUT__ << "Login successful for: " << user << __E__; 00270 // 00271 // UsersLoginFailureCountVector[i] = 0; 00272 // 00273 // saveDatabaseToFile(DB_USERS); //users db modified, so save 00274 // } 00275 00276 // //starting 00277 // 00278 // SHA512_CTX sha512_context; 00279 // char hexStr[3]; 00280 // SHA512_Init(&sha512_context); 00281 // 00282 // 00283 // std::string password = "testbeam"; 00284 // std::string salt = ""; 00285 // 00286 // { 00287 // for(unsigned int i=0;i<sizeof(SHA512_CTX);++i) 00288 // { 00289 // intToHexStr((((uint8_t *)(&sha512_context))[i] + (i<32)?rand():0),hexStr); 00290 // salt.append(hexStr); 00291 // } 00292 // __COUT__ << salt << __E__; 00293 // 00294 // std::string strToHash = salt + user + password; 00295 // 00296 // __COUT__ << salt << __E__; 00297 // unsigned char hash[SHA512_DIGEST_LENGTH]; 00298 // __COUT__ << salt << __E__; 00299 // char retHash[SHA512_DIGEST_LENGTH*2+1]; 00300 // __COUT__ << strToHash.length() << " " << strToHash << __E__; 00301 // SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length()); 00302 // __COUT__ << salt << __E__; 00303 // SHA512_Final(hash, &sha512_context); 00304 // 00305 // __COUT__ << salt << __E__; 00306 // int i = 0; 00307 // for(i = 0; i < SHA512_DIGEST_LENGTH; i++) 00308 // sprintf(retHash + (i * 2), "%02x", hash[i]); 00309 // 00310 // __COUT__ << salt << __E__; 00311 // retHash[SHA512_DIGEST_LENGTH*2] = '\0'; 00312 // 00313 // __COUT__ << "retHash: " << retHash << __E__; 00314 // } 00315 // 00316 // //check it 00317 // 00318 // { 00319 // __COUT__ << salt << __E__; 00320 // 00321 // for(unsigned int i=0;i<sizeof(SHA512_CTX);++i) 00322 // ((uint8_t *)(&sha512_context))[i] = hexByteStrToInt(&(salt.c_str()[i*2])); 00323 // 00324 // 00325 // std::string strToHash = salt + user + password; 00326 // 00327 // __COUT__ << salt << __E__; 00328 // unsigned char hash[SHA512_DIGEST_LENGTH]; 00329 // __COUT__ << salt << __E__; 00330 // char retHash[SHA512_DIGEST_LENGTH*2+1]; 00331 // __COUT__ << strToHash.length() << " " << strToHash << __E__; 00332 // SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length()); 00333 // __COUT__ << salt << __E__; 00334 // SHA512_Final(hash, &sha512_context); 00335 // 00336 // __COUT__ << salt << __E__; 00337 // int i = 0; 00338 // for(i = 0; i < SHA512_DIGEST_LENGTH; i++) 00339 // sprintf(retHash + (i * 2), "%02x", hash[i]); 00340 // 00341 // __COUT__ << salt << __E__; 00342 // retHash[SHA512_DIGEST_LENGTH*2] = '\0'; 00343 // 00344 // __COUT__ << "retHash: " << retHash << __E__; 00345 // } 00346 } 00347 00348 //======================================================================================================================== 00349 // xmlRequestOnGateway 00350 // check the validity of an xml request at the server side, i.e. at the Gateway 00351 // supervisor, which is the owner of the web users instance. if false, gateway 00352 // request code should just return.. out is handled on false; on true, out is untouched 00353 bool WebUsers::xmlRequestOnGateway(cgicc::Cgicc& cgi, 00354 std::ostringstream* out, 00355 HttpXmlDocument* xmldoc, 00356 WebUsers::RequestUserInfo& userInfo) 00357 { 00358 // initialize user info parameters to failed results 00359 WebUsers::initializeRequestUserInfo(cgi, userInfo); 00360 00361 // tmpUserWithLock_ = ""; 00362 00363 if(!cookieCodeIsActiveForRequest(userInfo.cookieCode_, 00364 &userInfo.groupPermissionLevelMap_, 00365 &userInfo.uid_, 00366 userInfo.ip_, 00367 !userInfo.automatedCommand_ /*refresh cookie*/, 00368 &userInfo.usernameWithLock_, 00369 &userInfo.activeUserSessionIndex_)) 00370 { 00371 *out << userInfo.cookieCode_; 00372 goto HANDLE_ACCESS_FAILURE; // return false, access failed 00373 } 00374 00375 // setup userInfo.permissionLevel_ based on userInfo.groupPermissionLevelMap_ 00376 userInfo.getGroupPermissionLevel(); 00377 userInfo.username_ = UsersUsernameVector[userInfo.uid_]; 00378 userInfo.displayName_ = UsersDisplayNameVector[userInfo.uid_]; 00379 00380 if(!WebUsers::checkRequestAccess(cgi, out, xmldoc, userInfo)) 00381 goto HANDLE_ACCESS_FAILURE; // return false, access failed 00382 00383 return true; // access success! 00384 00385 HANDLE_ACCESS_FAILURE: 00386 // print out return string on failure 00387 if(!userInfo.automatedCommand_) 00388 __COUT_ERR__ << "Failed request (requestType = " << userInfo.requestType_ 00389 << "): " << out->str() << __E__; 00390 return false; // access failed 00391 00392 } // end xmlRequestOnGateway() 00393 00394 //======================================================================================================================== 00395 // initializeRequestUserInfo 00396 // initialize user info parameters to failed results 00397 void WebUsers::initializeRequestUserInfo(cgicc::Cgicc& cgi, 00398 WebUsers::RequestUserInfo& userInfo) 00399 { 00400 userInfo.ip_ = cgi.getEnvironment().getRemoteAddr(); 00401 00402 // note if related bools are false, members below may not be set 00403 userInfo.username_ = ""; 00404 userInfo.displayName_ = ""; 00405 userInfo.usernameWithLock_ = ""; 00406 userInfo.activeUserSessionIndex_ = -1; 00407 userInfo.setGroupPermissionLevels(""); // always init to inactive 00408 } 00409 00410 //======================================================================================================================== 00411 // checkRequestAccess 00412 // check user permission parameters based on cookie code, user permission level 00413 //(extracted previous from group membership) Note: assumes 00414 // userInfo.groupPermissionLevelMap_ and userInfo.permissionLevel_ are properly setup 00415 // by either calling userInfo.setGroupPermissionLevels() or 00416 // userInfo.getGroupPermissionLevel() 00417 bool WebUsers::checkRequestAccess(cgicc::Cgicc& cgi, 00418 std::ostringstream* out, 00419 HttpXmlDocument* xmldoc, 00420 WebUsers::RequestUserInfo& userInfo, 00421 bool isWizardMode) 00422 { 00423 // steps: 00424 // - check access based on cookieCode and permission level 00425 // - check user lock flags and status 00426 00427 if(!userInfo.automatedCommand_) 00428 { 00429 __COUT__ << "requestType ==========>>> " << userInfo.requestType_ << __E__; 00430 __COUTV__((unsigned int)userInfo.permissionLevel_); 00431 __COUTV__((unsigned int)userInfo.permissionsThreshold_); 00432 } 00433 00434 // second, start check access ------- 00435 if(!isWizardMode && !userInfo.allowNoUser_ && 00436 userInfo.cookieCode_.length() != WebUsers::COOKIE_CODE_LENGTH) 00437 { 00438 __COUT__ << "User (@" << userInfo.ip_ 00439 << ") has invalid cookie code: " << userInfo.cookieCode_ << std::endl; 00440 *out << WebUsers::REQ_NO_LOGIN_RESPONSE; 00441 return false; // invalid cookie and present sequence, but not correct sequence 00442 } 00443 00444 if(!userInfo.allowNoUser_ && 00445 (userInfo.permissionLevel_ == 0 || // reject inactive permission level 00446 userInfo.permissionLevel_ < userInfo.permissionsThreshold_)) 00447 { 00448 *out << WebUsers::REQ_NO_PERMISSION_RESPONSE; 00449 __COUT__ << "User (@" << userInfo.ip_ 00450 << ") has insufficient permissions for requestType '" 00451 << userInfo.requestType_ 00452 << "' : " << (unsigned int)userInfo.permissionLevel_ << "<" 00453 << (unsigned int)userInfo.permissionsThreshold_ << std::endl; 00454 return false; // invalid cookie and present sequence, but not correct sequence 00455 } 00456 // end check access ------- 00457 00458 if(isWizardMode) 00459 { 00460 userInfo.username_ = "admin"; 00461 userInfo.displayName_ = "Admin"; 00462 userInfo.usernameWithLock_ = "admin"; 00463 userInfo.activeUserSessionIndex_ = 0; 00464 return true; // done, wizard mode access granted 00465 } 00466 // else, normal gateway verify mode 00467 00468 if(xmldoc) // fill with cookie code tag 00469 { 00470 if(userInfo.allowNoUser_) 00471 xmldoc->setHeader(WebUsers::REQ_ALLOW_NO_USER); 00472 else 00473 xmldoc->setHeader(userInfo.cookieCode_); 00474 } 00475 00476 if(userInfo.allowNoUser_) 00477 { 00478 if(userInfo.automatedCommand_) 00479 __COUT__ << "Allowing anonymous access." << __E__; 00480 00481 return true; // ignore lock for allow-no-user case 00482 } 00483 00484 // if(!userInfo.automatedCommand_) 00485 // { 00486 // __COUTV__(userInfo.username_); 00487 // __COUTV__(userInfo.usernameWithLock_); 00488 // } 00489 00490 if((userInfo.checkLock_ || userInfo.requireLock_) && 00491 userInfo.usernameWithLock_ != "" && 00492 userInfo.usernameWithLock_ != userInfo.username_) 00493 { 00494 *out << WebUsers::REQ_USER_LOCKOUT_RESPONSE; 00495 __COUT__ << "User '" << userInfo.username_ << "' is locked out. '" 00496 << userInfo.usernameWithLock_ << "' has lock." << std::endl; 00497 return false; // failed due to another user having lock 00498 } 00499 00500 if(userInfo.requireLock_ && userInfo.usernameWithLock_ != userInfo.username_) 00501 { 00502 *out << WebUsers::REQ_LOCK_REQUIRED_RESPONSE; 00503 __COUT__ << "User '" << userInfo.username_ << "' must have lock to proceed. ('" 00504 << userInfo.usernameWithLock_ << "' has lock.)" << std::endl; 00505 return false; // failed due to lock being required, and this user does not have 00506 // it 00507 } 00508 00509 return true; // access success! 00510 00511 } // end checkRequestAccess() 00512 00513 //======================================================================================================================== 00514 // saveActiveSessions 00515 // save active sessions structure so that they can survive restart 00516 void WebUsers::saveActiveSessions() 00517 { 00518 std::string fn; 00519 00520 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE; 00521 __COUT__ << fn << __E__; 00522 00523 FILE* fp = fopen(fn.c_str(), "w"); 00524 if(!fp) 00525 { 00526 __COUT_ERR__ << "Error! Persistent active sessions could not be saved to file: " 00527 << fn << __E__; 00528 return; 00529 } 00530 00531 int version = 0; 00532 fprintf(fp, "%d\n", version); 00533 for(unsigned int i = 0; i < ActiveSessionCookieCodeVector.size(); ++i) 00534 { 00535 // __COUT__ << "SAVE " << ActiveSessionCookieCodeVector[i] << __E__; 00536 // __COUT__ << "SAVE " << ActiveSessionIpVector[i] << __E__; 00537 // __COUT__ << "SAVE " << ActiveSessionUserIdVector[i] << __E__; 00538 // __COUT__ << "SAVE " << ActiveSessionIndex[i] << __E__; 00539 // __COUT__ << "SAVE " << ActiveSessionStartTimeVector[i] << __E__; 00540 00541 fprintf(fp, "%s\n", ActiveSessionCookieCodeVector[i].c_str()); 00542 fprintf(fp, "%s\n", ActiveSessionIpVector[i].c_str()); 00543 fprintf(fp, "%lu\n", ActiveSessionUserIdVector[i]); 00544 fprintf(fp, "%lu\n", ActiveSessionIndex[i]); 00545 fprintf(fp, "%ld\n", ActiveSessionStartTimeVector[i]); 00546 } 00547 00548 __COUT__ << "ActiveSessionCookieCodeVector saved with size " 00549 << ActiveSessionCookieCodeVector.size() << __E__; 00550 00551 fclose(fp); 00552 } 00553 00554 //==================================================================================================================== 00555 // loadActiveSessions 00556 // load active sessions structure so that they can survive restart 00557 void WebUsers::loadActiveSessions() 00558 { 00559 std::string fn; 00560 00561 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE; 00562 __COUT__ << fn << __E__; 00563 FILE* fp = fopen(fn.c_str(), "r"); 00564 if(!fp) 00565 { 00566 __COUT_INFO__ 00567 << "Persistent active sessions were not found to be loaded at file: " << fn 00568 << __E__; 00569 return; 00570 } 00571 00572 int version; 00573 00574 const int LINELEN = 1000; 00575 char line[LINELEN]; 00576 fgets(line, LINELEN, fp); 00577 sscanf(line, "%d", &version); 00578 if(version == 0) 00579 { 00580 __COUT__ << "Extracting active sessions..." << __E__; 00581 } 00582 unsigned int i = 0; 00583 while(fgets(line, LINELEN, fp)) 00584 { 00585 if(strlen(line)) 00586 line[strlen(line) - 1] = '\0'; // remove new line 00587 if(strlen(line) != COOKIE_CODE_LENGTH) 00588 { 00589 __COUT__ << "Illegal cookie code found: " << line << __E__; 00590 00591 fclose(fp); 00592 return; 00593 } 00594 ActiveSessionCookieCodeVector.push_back(line); 00595 00596 fgets(line, LINELEN, fp); 00597 if(strlen(line)) 00598 line[strlen(line) - 1] = '\0'; // remove new line 00599 ActiveSessionIpVector.push_back(line); 00600 00601 fgets(line, LINELEN, fp); 00602 ActiveSessionUserIdVector.push_back(uint64_t()); 00603 sscanf(line, 00604 "%lu", 00605 &(ActiveSessionUserIdVector[ActiveSessionUserIdVector.size() - 1])); 00606 00607 fgets(line, LINELEN, fp); 00608 ActiveSessionIndex.push_back(uint64_t()); 00609 sscanf(line, "%lu", &(ActiveSessionIndex[ActiveSessionIndex.size() - 1])); 00610 00611 fgets(line, LINELEN, fp); 00612 ActiveSessionStartTimeVector.push_back(time_t()); 00613 sscanf(line, 00614 "%ld", 00615 &(ActiveSessionStartTimeVector[ActiveSessionStartTimeVector.size() - 1])); 00616 00617 // __COUT__ << "LOAD " << ActiveSessionCookieCodeVector[i] << __E__; 00618 // __COUT__ << "LOAD " << ActiveSessionIpVector[i] << __E__; 00619 // __COUT__ << "LOAD " << ActiveSessionUserIdVector[i] << __E__; 00620 // __COUT__ << "LOAD " << ActiveSessionIndex[i] << __E__; 00621 // __COUT__ << "LOAD " << ActiveSessionStartTimeVector[i] << __E__; 00622 ++i; 00623 } 00624 00625 __COUT__ << "ActiveSessionCookieCodeVector loaded with size " 00626 << ActiveSessionCookieCodeVector.size() << __E__; 00627 00628 fclose(fp); 00629 // clear file after loading 00630 fp = fopen(fn.c_str(), "w"); 00631 if(fp) 00632 fclose(fp); 00633 } 00634 00635 //======================================================================================================================== 00636 // loadDatabaseFromFile 00637 // load Hashes and Users from file 00638 // create database if non-existent 00639 bool WebUsers::loadDatabases() 00640 { 00641 std::string fn; 00642 00643 FILE* fp; 00644 const unsigned int LINE_LEN = 1000; 00645 char line[LINE_LEN]; 00646 unsigned int i, si, c, len, f; 00647 uint64_t tmpInt64; 00648 00649 // hashes 00650 // File Organization: 00651 // <hashData> 00652 // <hashEntry><hash>hash0</hash><lastAccessTime>lastAccessTime0</lastAccessTime></hashEntry> 00653 // <hashEntry><hash>hash1</hash><lastAccessTime>lastAccessTime1</lastAccessTime></hashEntry> 00654 // .. 00655 // </hashData> 00656 00657 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_FILE; 00658 __COUT__ << fn << __E__; 00659 fp = fopen(fn.c_str(), "r"); 00660 if(!fp) // need to create file 00661 { 00662 mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str(), 00663 0755); 00664 __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str() 00665 << __E__; 00666 fp = fopen(fn.c_str(), "w"); 00667 if(!fp) 00668 return false; 00669 __COUT__ << "Hashes database created: " << fn << __E__; 00670 00671 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_OPEN); 00672 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE); 00673 fclose(fp); 00674 } 00675 else // load structures if hashes exists 00676 { 00677 // for every HASHES_DB_ENTRY_STRING, extract to local vector 00678 // trusting file construction, assuming fields based >'s and <'s 00679 while(fgets(line, LINE_LEN, fp)) 00680 { 00681 if(strlen(line) < SHA512_DIGEST_LENGTH) 00682 continue; 00683 00684 c = 0; 00685 len = 00686 strlen(line); // save len, strlen will change because of \0 manipulations 00687 for(i = 0; i < len; ++i) 00688 if(line[i] == '>') 00689 { 00690 ++c; // count >'s 00691 if(c != 2 && c != 4) 00692 continue; // only proceed for field data 00693 00694 si = ++i; // save start index 00695 while(i < len && line[i] != '<') 00696 ++i; 00697 if(i == len) 00698 break; 00699 line[i] = '\0'; // close std::string 00700 00701 //__COUT__ << "Found Hashes field " << c/2 << " " << &line[si] << 00702 //__E__; 00703 00704 f = c / 2 - 1; 00705 if(f == 0) // hash 00706 HashesVector.push_back(&line[si]); 00707 else if(f == 1) // lastAccessTime 00708 { 00709 sscanf(&line[si], "%lu", &tmpInt64); 00710 HashesAccessTimeVector.push_back(tmpInt64); 00711 } 00712 } 00713 } 00714 __COUT__ << HashesAccessTimeVector.size() << " Hashes found." << __E__; 00715 00716 fclose(fp); 00717 } 00718 00719 // users 00720 // File Organization: 00721 // <userData> 00722 // <nextUserId>...</nextUserId> 00723 // <userEntry>...</userEntry> 00724 // <userEntry>...</userEntry> 00725 // .. 00726 // </userData> 00727 00728 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_FILE; 00729 fp = fopen(fn.c_str(), "r"); 00730 if(!fp) // need to create file 00731 { 00732 mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str(), 00733 0755); 00734 __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str() 00735 << __E__; 00736 fp = fopen(fn.c_str(), "w"); 00737 if(!fp) 00738 return false; 00739 __COUT__ << "Users database created: " << fn << __E__; 00740 00741 saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_OPEN); 00742 char nidStr[100]; 00743 sprintf(nidStr, "%lu", usersNextUserId_); 00744 saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, nidStr, DB_SAVE_OPEN_AND_CLOSE); 00745 saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE); 00746 fclose(fp); 00747 00748 createNewAccount(DEFAULT_ADMIN_USERNAME, 00749 DEFAULT_ADMIN_DISPLAY_NAME, 00750 DEFAULT_ADMIN_EMAIL); // account 0 is always admin 00751 } 00752 else // extract next user id and user entries if users exists 00753 { 00754 // for every USERS_DB_ENTRY_STRING, extract to local vector 00755 // trusting file construction, assuming fields based >'s and <'s 00756 00757 char salt[] = "nextUserId"; 00758 while(fgets(line, LINE_LEN, fp)) 00759 { 00760 if(strlen(line) < strlen(salt) * 2) 00761 continue; // line size should indicate xml tags on same line 00762 00763 for(i = 0; i < strlen(salt); ++i) // check for opening tag 00764 if(line[i + 1] != salt[i]) 00765 break; 00766 00767 if(i == strlen(salt)) // all salt matched, so found correct line! increment 00768 // to get line index 00769 { 00770 i += 2; 00771 si = i; 00772 while(i < LINE_LEN && line[i] != '\0' && line[i] != '<') 00773 ++i; // find '<' 00774 line[i] = '\0'; // close std::string 00775 sscanf(&line[si], "%lu", &usersNextUserId_); 00776 break; // done with next uid 00777 } 00778 } 00779 00780 __COUT__ << "Found Users database next user Id: " << usersNextUserId_ << __E__; 00781 00782 // trusting file construction, assuming fields based >'s and <'s and each entry on 00783 // one line 00784 while(fgets(line, LINE_LEN, fp)) 00785 { 00786 if(strlen(line) < 30) 00787 continue; // rule out header tags 00788 00789 c = 0; 00790 len = 00791 strlen(line); // save len, strlen will change because of \0 manipulations 00792 if(len >= LINE_LEN) 00793 { 00794 __COUT__ << "Line buffer too small: " << len << __E__; 00795 break; 00796 } 00797 00798 // get fields from line 00799 f = 0; 00800 for(i = 0; i < len; ++i) 00801 if(line[i] == '>') 00802 { 00803 ++c; // count >'s 00804 if(c == 0 || c % 2 == 1) 00805 continue; // only proceed for field data (even 00806 00807 si = ++i; // save start index 00808 while(i < len && line[i] != '<') 00809 ++i; 00810 if(i == len) 00811 break; 00812 line[i] = '\0'; // close std::string 00813 f = c / 2 - 1; 00814 00815 //__COUT__ << "Found Users field " << f << " " << &line[si] << __E__; 00816 00817 if(f == 0) // username 00818 UsersUsernameVector.push_back(&line[si]); 00819 else if(f == 1) // displayName 00820 UsersDisplayNameVector.push_back(&line[si]); 00821 else if(f == 2) // salt 00822 UsersSaltVector.push_back(&line[si]); 00823 else if(f == 3) // uid 00824 { 00825 sscanf(&line[si], "%lu", &tmpInt64); 00826 UsersUserIdVector.push_back(tmpInt64); 00827 } 00828 else if(f == 4) // permissions 00829 { 00830 UsersPermissionsVector.push_back( 00831 std::map<std::string, uint8_t>()); 00832 std::map<std::string, uint8_t>& lastPermissionsMap = 00833 UsersPermissionsVector.back(); 00834 StringMacros::getMapFromString<uint8_t>(&line[si], 00835 lastPermissionsMap); 00836 00837 //__COUT__ << "User permission levels:" << 00838 // StringMacros::mapToString(lastPermissionsMap) << __E__; 00839 00840 // verify 'allUsers' is there 00841 // if not, add it as a diabled user (i.e. 00842 // WebUsers::PERMISSION_LEVEL_INACTIVE) 00843 if(lastPermissionsMap.find(WebUsers::DEFAULT_USER_GROUP) == 00844 lastPermissionsMap.end()) 00845 { 00846 // try to accomplish backwards compatibility to 00847 // allow for the time before group permissions 00848 sscanf(&line[si], "%lu", &tmpInt64); 00849 tmpInt64 &= 0xFF; 00850 if(tmpInt64) // if not 0 00851 { 00852 lastPermissionsMap.clear(); 00853 __COUT_INFO__ 00854 << "User '" << UsersUsernameVector.back() 00855 << "' is not a member of the default user group '" 00856 << WebUsers::DEFAULT_USER_GROUP 00857 << ".' For backward compatibility, permission level " 00858 "assumed for default group (permission level := " 00859 << tmpInt64 << ")." << __E__; 00860 lastPermissionsMap[WebUsers::DEFAULT_USER_GROUP] = 00861 WebUsers::permissionLevel_t(tmpInt64); 00862 } 00863 else 00864 { 00865 __MCOUT_INFO__( 00866 "User '" 00867 << UsersUsernameVector.back() 00868 << "' is not a member of the default user group '" 00869 << WebUsers::DEFAULT_USER_GROUP 00870 << ".' Assuming user account is inactive (permission " 00871 "level := " 00872 << WebUsers::PERMISSION_LEVEL_INACTIVE << ")." 00873 << __E__); 00874 lastPermissionsMap[WebUsers::DEFAULT_USER_GROUP] = 00875 WebUsers::PERMISSION_LEVEL_INACTIVE; // mark inactive 00876 } 00877 } 00878 } 00879 else if(f == 5) // lastLoginAttemptTime 00880 { 00881 sscanf(&line[si], "%lu", &tmpInt64); 00882 UsersLastLoginAttemptVector.push_back(tmpInt64); 00883 } 00884 else if(f == 6) // accountCreatedTime 00885 { 00886 sscanf(&line[si], "%lu", &tmpInt64); 00887 UsersAccountCreatedTimeVector.push_back(tmpInt64); 00888 } 00889 else if(f == 7) // loginFailureCount 00890 { 00891 sscanf(&line[si], "%lu", &tmpInt64); 00892 UsersLoginFailureCountVector.push_back(tmpInt64); 00893 } 00894 else if(f == 8) // lastModifierTime 00895 { 00896 sscanf(&line[si], "%lu", &tmpInt64); 00897 UsersLastModifiedTimeVector.push_back(tmpInt64); 00898 } 00899 else if(f == 9) // lastModifierUsername 00900 UsersLastModifierUsernameVector.push_back(&line[si]); 00901 else if(f == 10) // user email 00902 UsersUserEmailVector.push_back(&line[si]); 00903 } 00904 00905 // If user found in line, check if all fields found, else auto fill 00906 // update in DB fields could cause inconsistencies! 00907 if(f && f != UsersDatabaseEntryFields.size() - 1) 00908 { 00909 if(f != 7 && 00910 f != 9) // original database was size 8, so is ok to not match 00911 { 00912 __SS__ 00913 << "FATAL ERROR - invalid user database found with field number " 00914 << f << __E__; 00915 fclose(fp); 00916 __SS_THROW__; 00917 return false; 00918 } 00919 00920 if(f == 7) 00921 { 00922 // fix here if database size was 8 00923 __COUT__ << "Update database to current version - adding fields: " 00924 << (UsersDatabaseEntryFields.size() - 1 - f) << __E__; 00925 // add db updates -- THIS IS FOR VERSION WITH 00926 // UsersDatabaseEntryFields.size() == 10 !! 00927 UsersLastModifiedTimeVector.push_back(0); 00928 UsersLastModifierUsernameVector.push_back(""); 00929 } 00930 else 00931 { 00932 UsersUserEmailVector.push_back(""); 00933 } 00934 } 00935 } 00936 fclose(fp); 00937 } 00938 00939 __COUT__ << UsersLastModifiedTimeVector.size() << " Users found." << __E__; 00940 for(size_t ii = 0; ii < UsersLastModifiedTimeVector.size(); ++ii) 00941 { 00942 __COUT__ << "User " << UsersUserIdVector[ii] 00943 << ": Name: " << UsersUsernameVector[ii] 00944 << "\t\tDisplay Name: " << UsersDisplayNameVector[ii] 00945 << "\t\tEmail: " << UsersUserEmailVector[ii] << "\t\tPermissions: " 00946 << StringMacros::mapToString(UsersPermissionsVector[ii]) << __E__; 00947 } 00948 return true; 00949 } 00950 00951 //======================================================================================================================== 00952 // saveToDatabase 00953 void WebUsers::saveToDatabase(FILE* fp, 00954 const std::string& field, 00955 const std::string& value, 00956 uint8_t type, 00957 bool addNewLine) 00958 { 00959 if(!fp) 00960 return; 00961 00962 std::string newLine = addNewLine ? "\n" : ""; 00963 00964 if(type == DB_SAVE_OPEN_AND_CLOSE) 00965 fprintf(fp, 00966 "<%s>%s</%s>%s", 00967 field.c_str(), 00968 value.c_str(), 00969 field.c_str(), 00970 newLine.c_str()); 00971 else if(type == DB_SAVE_OPEN) 00972 fprintf(fp, "<%s>%s%s", field.c_str(), value.c_str(), newLine.c_str()); 00973 else if(type == DB_SAVE_CLOSE) 00974 fprintf(fp, "</%s>%s", field.c_str(), newLine.c_str()); 00975 } 00976 00977 //======================================================================================================================== 00978 // saveDatabaseToFile 00979 // returns true if saved database successfully 00980 // db: DB_USERS or DB_HASHES 00981 // else false 00982 00983 bool WebUsers::saveDatabaseToFile(uint8_t db) 00984 { 00985 __COUT__ << "Save Database: " << (int)db << __E__; 00986 00987 std::string fn = 00988 (std::string)WEB_LOGIN_DB_PATH + 00989 ((db == DB_USERS) ? (std::string)USERS_DB_FILE : (std::string)HASHES_DB_FILE); 00990 00991 __COUT__ << "Save Database Filename: " << fn << __E__; 00992 00993 // backup file organized by day 00994 if(0) 00995 { 00996 char dayAppend[20]; 00997 sprintf(dayAppend, ".%lu.bkup", time(0) / (3600 * 24)); 00998 std::string bkup_fn = (std::string)WEB_LOGIN_DB_PATH + 00999 (std::string)WEB_LOGIN_BKUP_DB_PATH + 01000 ((db == DB_USERS) ? (std::string)USERS_DB_FILE 01001 : (std::string)HASHES_DB_FILE) + 01002 (std::string)dayAppend; 01003 01004 __COUT__ << "Backup file: " << bkup_fn << __E__; 01005 01006 std::string shell_command = "mv " + fn + " " + bkup_fn; 01007 system(shell_command.c_str()); 01008 } 01009 01010 FILE* fp = fopen(fn.c_str(), "wb"); // write in binary mode 01011 if(!fp) 01012 return false; 01013 01014 char fldStr[100]; 01015 01016 if(db == DB_USERS) // USERS 01017 { 01018 saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_OPEN); 01019 01020 sprintf(fldStr, "%lu", usersNextUserId_); 01021 saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, fldStr, DB_SAVE_OPEN_AND_CLOSE); 01022 01023 __COUT__ << "Saving " << UsersUsernameVector.size() << " Users." << __E__; 01024 01025 for(uint64_t i = 0; i < UsersUsernameVector.size(); ++i) 01026 { 01027 //__COUT__ << "Saving User: " << UsersUsernameVector[i] << __E__; 01028 01029 saveToDatabase(fp, USERS_DB_ENTRY_STRING, "", DB_SAVE_OPEN, false); 01030 01031 for(unsigned int f = 0; f < UsersDatabaseEntryFields.size(); ++f) 01032 { 01033 //__COUT__ << "Saving Field: " << f << __E__; 01034 if(f == 0) // username 01035 saveToDatabase(fp, 01036 UsersDatabaseEntryFields[f], 01037 UsersUsernameVector[i], 01038 DB_SAVE_OPEN_AND_CLOSE, 01039 false); 01040 else if(f == 1) // displayName 01041 saveToDatabase(fp, 01042 UsersDatabaseEntryFields[f], 01043 UsersDisplayNameVector[i], 01044 DB_SAVE_OPEN_AND_CLOSE, 01045 false); 01046 else if(f == 2) // salt 01047 saveToDatabase(fp, 01048 UsersDatabaseEntryFields[f], 01049 UsersSaltVector[i], 01050 DB_SAVE_OPEN_AND_CLOSE, 01051 false); 01052 else if(f == 3) // uid 01053 { 01054 sprintf(fldStr, "%lu", UsersUserIdVector[i]); 01055 saveToDatabase(fp, 01056 UsersDatabaseEntryFields[f], 01057 fldStr, 01058 DB_SAVE_OPEN_AND_CLOSE, 01059 false); 01060 } 01061 else if(f == 4) // permissions 01062 saveToDatabase(fp, 01063 UsersDatabaseEntryFields[f], 01064 StringMacros::mapToString(UsersPermissionsVector[i], 01065 "," /*primary delimeter*/, 01066 ":" /*secondary delimeter*/), 01067 DB_SAVE_OPEN_AND_CLOSE, 01068 false); 01069 else if(f == 5) // lastLoginAttemptTime 01070 { 01071 sprintf(fldStr, "%lu", UsersLastLoginAttemptVector[i]); 01072 saveToDatabase(fp, 01073 UsersDatabaseEntryFields[f], 01074 fldStr, 01075 DB_SAVE_OPEN_AND_CLOSE, 01076 false); 01077 } 01078 else if(f == 6) // accountCreatedTime 01079 { 01080 sprintf(fldStr, "%lu", UsersAccountCreatedTimeVector[i]); 01081 saveToDatabase(fp, 01082 UsersDatabaseEntryFields[f], 01083 fldStr, 01084 DB_SAVE_OPEN_AND_CLOSE, 01085 false); 01086 } 01087 else if(f == 7) // loginFailureCount 01088 { 01089 sprintf(fldStr, "%d", UsersLoginFailureCountVector[i]); 01090 saveToDatabase(fp, 01091 UsersDatabaseEntryFields[f], 01092 fldStr, 01093 DB_SAVE_OPEN_AND_CLOSE, 01094 false); 01095 } 01096 else if(f == 8) // lastModifierTime 01097 { 01098 sprintf(fldStr, "%lu", UsersLastModifiedTimeVector[i]); 01099 saveToDatabase(fp, 01100 UsersDatabaseEntryFields[f], 01101 fldStr, 01102 DB_SAVE_OPEN_AND_CLOSE, 01103 false); 01104 } 01105 else if(f == 9) // lastModifierUsername 01106 saveToDatabase(fp, 01107 UsersDatabaseEntryFields[f], 01108 UsersLastModifierUsernameVector[i], 01109 DB_SAVE_OPEN_AND_CLOSE, 01110 false); 01111 else if(f == 10) // useremail 01112 saveToDatabase(fp, 01113 UsersDatabaseEntryFields[f], 01114 UsersUserEmailVector[i], 01115 DB_SAVE_OPEN_AND_CLOSE, 01116 false); 01117 } 01118 01119 saveToDatabase(fp, USERS_DB_ENTRY_STRING, "", DB_SAVE_CLOSE); 01120 } 01121 01122 saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE); 01123 } 01124 else // HASHES 01125 { 01126 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_OPEN); 01127 01128 __COUT__ << "Saving " << HashesVector.size() << " Hashes." << __E__; 01129 for(uint64_t i = 0; i < HashesVector.size(); ++i) 01130 { 01131 __COUT__ << "Saving " << HashesVector[i] << " Hashes." << __E__; 01132 saveToDatabase(fp, HASHES_DB_ENTRY_STRING, "", DB_SAVE_OPEN, false); 01133 for(unsigned int f = 0; f < HashesDatabaseEntryFields.size(); ++f) 01134 { 01135 if(f == 0) // hash 01136 saveToDatabase(fp, 01137 HashesDatabaseEntryFields[f], 01138 HashesVector[i], 01139 DB_SAVE_OPEN_AND_CLOSE, 01140 false); 01141 else if(f == 1) // lastAccessTime 01142 { 01143 sprintf(fldStr, "%lu", HashesAccessTimeVector[i]); 01144 saveToDatabase(fp, 01145 HashesDatabaseEntryFields[f], 01146 fldStr, 01147 DB_SAVE_OPEN_AND_CLOSE, 01148 false); 01149 } 01150 } 01151 saveToDatabase(fp, HASHES_DB_ENTRY_STRING, "", DB_SAVE_CLOSE); 01152 } 01153 01154 saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE); 01155 } 01156 01157 fclose(fp); 01158 return true; 01159 } 01160 01161 //======================================================================================================================== 01162 // createNewAccount 01163 // adds a new valid user to database 01164 // inputs: username and name to display 01165 // initializes database entry with minimal permissions 01166 // and salt starts as "" until password is set 01167 // Special case if first user name!! max permissions given (super user made) 01168 bool WebUsers::createNewAccount(const std::string& username, 01169 const std::string& displayName, 01170 const std::string& email) 01171 { 01172 __COUT__ << "Creating account: " << username << __E__; 01173 // check if username already exists 01174 uint64_t i; 01175 if((i = searchUsersDatabaseForUsername(username)) != NOT_FOUND_IN_DATABASE || 01176 username == WebUsers::DEFAULT_ITERATOR_USERNAME || 01177 username == WebUsers::DEFAULT_STATECHANGER_USERNAME) // prevent reserved usernames 01178 // from being created! 01179 { 01180 __COUT_ERR__ << "Username '" << username << "' already exists" << __E__; 01181 return false; 01182 } 01183 01184 // create Users database entry 01185 UsersUsernameVector.push_back(username); 01186 UsersDisplayNameVector.push_back(displayName); 01187 UsersUserEmailVector.push_back(email); 01188 UsersSaltVector.push_back(""); 01189 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> initPermissions = { 01190 {WebUsers::DEFAULT_USER_GROUP, 01191 (UsersPermissionsVector.size() ? WebUsers::PERMISSION_LEVEL_NOVICE 01192 : WebUsers::PERMISSION_LEVEL_ADMIN)}}; 01193 UsersPermissionsVector.push_back(initPermissions); // max permissions if first user 01194 01195 UsersUserIdVector.push_back(usersNextUserId_++); 01196 if(usersNextUserId_ == (uint64_t)-1) // error wrap around case 01197 { 01198 __COUT__ << "usersNextUserId_ wrap around!! Too many users??? Notify Admins." 01199 << __E__; 01200 usersNextUserId_ = 1; // for safety to avoid wierd issues at -1 and 0 (if used 01201 // for error indication) 01202 } 01203 UsersLastLoginAttemptVector.push_back(0); 01204 UsersLoginFailureCountVector.push_back(0); 01205 UsersAccountCreatedTimeVector.push_back(time(0)); 01206 UsersLastModifiedTimeVector.push_back(0); 01207 UsersLastModifierUsernameVector.push_back(""); 01208 01209 return saveDatabaseToFile(DB_USERS); 01210 } 01211 01212 //======================================================================================================================== 01213 // deleteAccount 01214 // private function, deletes user account 01215 // inputs: username and name to display 01216 // if username and display name match account found, then account is deleted and true 01217 // returned else false 01218 bool WebUsers::deleteAccount(const std::string& username, const std::string& displayName) 01219 { 01220 uint64_t i = searchUsersDatabaseForUsername(username); 01221 if(i == NOT_FOUND_IN_DATABASE) 01222 return false; 01223 if(UsersDisplayNameVector[i] != displayName) 01224 return false; // display name does not match 01225 01226 // delete entry from all user database vectors 01227 01228 UsersUsernameVector.erase(UsersUsernameVector.begin() + i); 01229 UsersUserEmailVector.erase(UsersUserEmailVector.begin() + i); 01230 UsersDisplayNameVector.erase(UsersDisplayNameVector.begin() + i); 01231 UsersSaltVector.erase(UsersSaltVector.begin() + i); 01232 UsersPermissionsVector.erase(UsersPermissionsVector.begin() + i); 01233 UsersUserIdVector.erase(UsersUserIdVector.begin() + i); 01234 UsersLastLoginAttemptVector.erase(UsersLastLoginAttemptVector.begin() + i); 01235 UsersAccountCreatedTimeVector.erase(UsersAccountCreatedTimeVector.begin() + i); 01236 UsersLoginFailureCountVector.erase(UsersLoginFailureCountVector.begin() + i); 01237 UsersLastModifierUsernameVector.erase(UsersLastModifierUsernameVector.begin() + i); 01238 UsersLastModifiedTimeVector.erase(UsersLastModifiedTimeVector.begin() + i); 01239 01240 // save database 01241 return saveDatabaseToFile(DB_USERS); 01242 } 01243 01244 //======================================================================================================================== 01245 unsigned int WebUsers::hexByteStrToInt(const char* h) 01246 { 01247 unsigned int rv; 01248 char hs[3] = {h[0], h[1], '\0'}; 01249 sscanf(hs, "%X", &rv); 01250 return rv; 01251 } 01252 01253 //======================================================================================================================== 01254 void WebUsers::intToHexStr(unsigned char i, char* h) { sprintf(h, "%2.2X", i); } 01255 01256 //======================================================================================================================== 01257 // WebUsers::attemptActiveSession --- 01258 // Attempts login. 01259 // 01260 // If new login, then new account code must match account creation time and account is 01261 // made with pw 01262 // 01263 // if old login, password is checked 01264 // returns User Id, cookieCode in newAccountCode, and displayName in jumbledUser on 01265 // success else returns -1 and cookieCode "0" 01266 uint64_t WebUsers::attemptActiveSession(const std::string& uuid, 01267 std::string& jumbledUser, 01268 const std::string& jumbledPw, 01269 std::string& newAccountCode, 01270 const std::string& ip) 01271 { 01272 //__COUTV__(ip); 01273 if(!checkIpAccess(ip)) 01274 { 01275 __COUT_ERR__ << "rejected ip: " << ip << __E__; 01276 return NOT_FOUND_IN_DATABASE; 01277 } 01278 01279 cleanupExpiredEntries(); // remove expired active and login sessions 01280 01281 if(!CareAboutCookieCodes_) // NO SECURITY 01282 { 01283 uint64_t uid = getAdminUserID(); 01284 jumbledUser = getUsersDisplayName(uid); 01285 newAccountCode = genCookieCode(); // return "dummy" cookie code by reference 01286 return uid; 01287 } 01288 01289 uint64_t i; 01290 01291 // search login sessions for uuid 01292 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE) 01293 { 01294 __COUT_ERR__ << "uuid: " << uuid << " is not found" << __E__; 01295 newAccountCode = "1"; // to indicate uuid was not found 01296 01297 incrementIpBlacklistCount(ip); // increment ip blacklist counter 01298 01299 return NOT_FOUND_IN_DATABASE; 01300 } 01301 ++LoginSessionAttemptsVector[i]; 01302 01303 std::string user = dejumble(jumbledUser, LoginSessionIdVector[i]); 01304 __COUTV__(user); 01305 std::string pw = dejumble(jumbledPw, LoginSessionIdVector[i]); 01306 01307 // search users for username 01308 if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE) 01309 { 01310 __COUT_ERR__ << "user: " << user << " is not found" << __E__; 01311 01312 incrementIpBlacklistCount(ip); // increment ip blacklist counter 01313 01314 return NOT_FOUND_IN_DATABASE; 01315 } 01316 else 01317 ipBlacklistCounts_[ip] = 0; // clear blacklist count 01318 01319 UsersLastLoginAttemptVector[i] = time(0); 01320 01321 if(isInactiveForGroup(UsersPermissionsVector[i])) 01322 { 01323 __MCOUT_ERR__("User '" << user 01324 << "' account INACTIVE (could be due to failed logins)" 01325 << __E__); 01326 return NOT_FOUND_IN_DATABASE; 01327 } 01328 01329 if(UsersSaltVector[i] == "") // first login 01330 { 01331 __MCOUT__("First login attempt for user: " << user << __E__); 01332 01333 char charTimeStr[10]; 01334 sprintf(charTimeStr, "%d", int(UsersAccountCreatedTimeVector[i] & 0xffff)); 01335 std::string tmpTimeStr = charTimeStr; 01336 if(newAccountCode != tmpTimeStr) 01337 { 01338 __COUT__ << "New account code did not match: " << tmpTimeStr 01339 << " != " << newAccountCode << __E__; 01340 saveDatabaseToFile(DB_USERS); // users db modified, so save 01341 return NOT_FOUND_IN_DATABASE; 01342 } 01343 01344 // initial user account setup 01345 01346 // add until no collision (should 'never' be a collision) 01347 while(!addToHashesDatabase( 01348 sha512(user, pw, UsersSaltVector[i]))) // sha256 modifies UsersSaltVector[i] 01349 { 01350 // this should never happen, it would mean the user+pw+saltcontext was the 01351 // same 01352 // but if it were to happen, try again... 01353 UsersSaltVector[i] = ""; 01354 } 01355 01356 __COUT__ << "\tHash added: " << HashesVector[HashesVector.size() - 1] << __E__; 01357 } 01358 else 01359 { 01360 std::string salt = UsersSaltVector[i]; // don't want to modify saved salt 01361 //__COUT__ << salt << " " << i << __E__; 01362 if(searchHashesDatabaseForHash(sha512(user, pw, salt)) == NOT_FOUND_IN_DATABASE) 01363 { 01364 __COUT__ << "Failed login for " << user << " with permissions " 01365 << StringMacros::mapToString(UsersPermissionsVector[i]) << __E__; 01366 01367 ++UsersLoginFailureCountVector[i]; 01368 if(UsersLoginFailureCountVector[i] >= USERS_MAX_LOGIN_FAILURES) 01369 UsersPermissionsVector[i][WebUsers::DEFAULT_USER_GROUP] = 01370 WebUsers::PERMISSION_LEVEL_INACTIVE; // Lock account 01371 01372 __COUT_INFO__ << "User/pw for user '" << user 01373 << "' was not correct (Failed Attempt #" 01374 << (int)UsersLoginFailureCountVector[i] << " of " 01375 << (int)USERS_MAX_LOGIN_FAILURES << ")." << __E__; 01376 01377 __COUTV__(isInactiveForGroup(UsersPermissionsVector[i])); 01378 if(isInactiveForGroup(UsersPermissionsVector[i])) 01379 __MCOUT_INFO__("Account '" 01380 << user 01381 << "' has been marked inactive due to too many failed " 01382 "login attempts (Failed Attempt #" 01383 << (int)UsersLoginFailureCountVector[i] 01384 << ")! Note only admins can reactivate accounts." 01385 << __E__); 01386 01387 saveDatabaseToFile(DB_USERS); // users db modified, so save 01388 return NOT_FOUND_IN_DATABASE; 01389 } 01390 } 01391 01392 __MCOUT_INFO__("Login successful for: " << user << __E__); 01393 01394 UsersLoginFailureCountVector[i] = 0; 01395 01396 // record to login history for user (h==0) and on global server level (h==1) 01397 for(int h = 0; h < 2; ++h) 01398 { 01399 std::string fn = (std::string)WEB_LOGIN_DB_PATH + 01400 (std::string)USERS_LOGIN_HISTORY_PATH + 01401 (h ? USERS_GLOBAL_HISTORY_FILE : UsersUsernameVector[i]) + "." + 01402 (std::string)USERS_LOGIN_HISTORY_FILETYPE; 01403 01404 HttpXmlDocument histXml; 01405 01406 if(histXml.loadXmlDocument(fn)) // not found 01407 { 01408 while(histXml.getChildrenCount() + 1 > 01409 (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE)) 01410 histXml.removeDataElement(); 01411 } 01412 else 01413 __COUT__ << "No previous login history found." << __E__; 01414 01415 // add new entry to history 01416 char entryStr[500]; 01417 if(h) 01418 sprintf(entryStr, 01419 "Time=%lu Username=%s Permissions=%s UID=%lu", 01420 time(0), 01421 UsersUsernameVector[i].c_str(), 01422 StringMacros::mapToString(UsersPermissionsVector[i]).c_str(), 01423 UsersUserIdVector[i]); 01424 else 01425 sprintf(entryStr, 01426 "Time=%lu displayName=%s Permissions=%s UID=%lu", 01427 time(0), 01428 UsersDisplayNameVector[i].c_str(), 01429 StringMacros::mapToString(UsersPermissionsVector[i]).c_str(), 01430 UsersUserIdVector[i]); 01431 histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr); 01432 01433 // save file 01434 histXml.saveXmlDocument(fn); 01435 } 01436 01437 // SUCCESS!! 01438 saveDatabaseToFile(DB_USERS); // users db modified, so save 01439 jumbledUser = UsersDisplayNameVector[i]; // pass by reference displayName 01440 newAccountCode = createNewActiveSession(UsersUserIdVector[i], 01441 ip); // return cookie code by reference 01442 return UsersUserIdVector[i]; // return user Id 01443 } 01444 01445 //======================================================================================================================== 01446 // WebUsers::attemptActiveSessionWithCert --- 01447 // Attempts login using certificate. 01448 // 01449 // returns User Id, cookieCode, and displayName in jumbledEmail on success 01450 // else returns -1 and cookieCode "0" 01451 uint64_t WebUsers::attemptActiveSessionWithCert(const std::string& uuid, 01452 std::string& email, 01453 std::string& cookieCode, 01454 std::string& user, 01455 const std::string& ip) 01456 { 01457 if(!checkIpAccess(ip)) 01458 { 01459 __COUT_ERR__ << "rejected ip: " << ip << __E__; 01460 return NOT_FOUND_IN_DATABASE; 01461 } 01462 01463 cleanupExpiredEntries(); // remove expired active and login sessions 01464 01465 if(!CareAboutCookieCodes_) // NO SECURITY 01466 { 01467 uint64_t uid = getAdminUserID(); 01468 email = getUsersDisplayName(uid); 01469 cookieCode = genCookieCode(); // return "dummy" cookie code by reference 01470 return uid; 01471 } 01472 01473 if(email == "") 01474 { 01475 __COUT__ << "Rejecting logon with blank fingerprint" << __E__; 01476 01477 incrementIpBlacklistCount(ip); // increment ip blacklist counter 01478 01479 return NOT_FOUND_IN_DATABASE; 01480 } 01481 01482 uint64_t i; 01483 01484 // search login sessions for uuid 01485 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE) 01486 { 01487 __COUT__ << "uuid: " << uuid << " is not found" << __E__; 01488 cookieCode = "1"; // to indicate uuid was not found 01489 01490 incrementIpBlacklistCount(ip); // increment ip blacklist counter 01491 01492 return NOT_FOUND_IN_DATABASE; 01493 } 01494 ++LoginSessionAttemptsVector[i]; 01495 01496 email = getUserEmailFromFingerprint(email); 01497 __COUT__ << "DejumbledEmail = " << email << __E__; 01498 if(email == "") 01499 { 01500 __COUT__ << "Rejecting logon with unknown fingerprint" << __E__; 01501 01502 incrementIpBlacklistCount(ip); // increment ip blacklist counter 01503 01504 return NOT_FOUND_IN_DATABASE; 01505 } 01506 01507 // search users for username 01508 if((i = searchUsersDatabaseForUserEmail(email)) == NOT_FOUND_IN_DATABASE) 01509 { 01510 __COUT__ << "email: " << email << " is not found" << __E__; 01511 01512 incrementIpBlacklistCount(ip); // increment ip blacklist counter 01513 01514 return NOT_FOUND_IN_DATABASE; 01515 } 01516 else 01517 ipBlacklistCounts_[ip] = 0; // clear blacklist count 01518 01519 user = getUsersUsername(i); 01520 01521 UsersLastLoginAttemptVector[i] = time(0); 01522 if(isInactiveForGroup(UsersPermissionsVector[i])) 01523 { 01524 __MCOUT__("User '" << user 01525 << "' account INACTIVE (could be due to failed logins)." 01526 << __E__); 01527 return NOT_FOUND_IN_DATABASE; 01528 } 01529 01530 if(UsersSaltVector[i] == "") // Can't be first login 01531 { 01532 return NOT_FOUND_IN_DATABASE; 01533 } 01534 01535 __MCOUT__("Login successful for: " << user << __E__); 01536 01537 UsersLoginFailureCountVector[i] = 0; 01538 01539 // record to login history for user (h==0) and on global server level (h==1) 01540 for(int h = 0; h < 2; ++h) 01541 { 01542 std::string fn = (std::string)WEB_LOGIN_DB_PATH + 01543 (std::string)USERS_LOGIN_HISTORY_PATH + 01544 (h ? USERS_GLOBAL_HISTORY_FILE : UsersUsernameVector[i]) + "." + 01545 (std::string)USERS_LOGIN_HISTORY_FILETYPE; 01546 01547 HttpXmlDocument histXml; 01548 01549 if(histXml.loadXmlDocument(fn)) // not found 01550 { 01551 while(histXml.getChildrenCount() + 1 > 01552 (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE)) 01553 histXml.removeDataElement(); 01554 } 01555 else 01556 __COUT__ << "No previous login history found." << __E__; 01557 01558 // add new entry to history 01559 char entryStr[500]; 01560 if(h) 01561 sprintf(entryStr, 01562 "Time=%lu Username=%s Permissions=%s UID=%lu", 01563 time(0), 01564 UsersUsernameVector[i].c_str(), 01565 StringMacros::mapToString(UsersPermissionsVector[i]).c_str(), 01566 UsersUserIdVector[i]); 01567 else 01568 sprintf(entryStr, 01569 "Time=%lu displayName=%s Permissions=%s UID=%lu", 01570 time(0), 01571 UsersDisplayNameVector[i].c_str(), 01572 StringMacros::mapToString(UsersPermissionsVector[i]).c_str(), 01573 UsersUserIdVector[i]); 01574 histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr); 01575 01576 // save file 01577 histXml.saveXmlDocument(fn); 01578 } 01579 01580 // SUCCESS!! 01581 saveDatabaseToFile(DB_USERS); // users db modified, so save 01582 email = UsersDisplayNameVector[i]; // pass by reference displayName 01583 cookieCode = createNewActiveSession(UsersUserIdVector[i], 01584 ip); // return cookie code by reference 01585 return UsersUserIdVector[i]; // return user Id 01586 } 01587 01588 //======================================================================================================================== 01589 // WebUsers::searchActiveSessionDatabaseForUID --- 01590 // returns index if found, else -1 01591 uint64_t WebUsers::searchActiveSessionDatabaseForCookie( 01592 const std::string& cookieCode) const 01593 { 01594 uint64_t i = 0; 01595 for(; i < ActiveSessionCookieCodeVector.size(); ++i) 01596 if(ActiveSessionCookieCodeVector[i] == cookieCode) 01597 break; 01598 return (i == ActiveSessionCookieCodeVector.size()) ? NOT_FOUND_IN_DATABASE : i; 01599 } 01600 01601 //======================================================================================================================== 01602 // WebUsers::isUsernameActive --- 01603 // returns true if found, else false 01604 bool WebUsers::isUsernameActive(const std::string& username) const 01605 { 01606 uint64_t u; 01607 if((u = searchUsersDatabaseForUsername(username)) == NOT_FOUND_IN_DATABASE) 01608 return false; 01609 return isUserIdActive(UsersUserIdVector[u]); 01610 } 01611 01612 //======================================================================================================================== 01613 // WebUsers::isUserIdActive --- 01614 // returns true if found, else false 01615 bool WebUsers::isUserIdActive(uint64_t uid) const 01616 { 01617 uint64_t i = 0; 01618 for(; i < ActiveSessionUserIdVector.size(); ++i) 01619 if(ActiveSessionUserIdVector[i] == uid) 01620 return true; 01621 return false; 01622 } 01623 01624 //======================================================================================================================== 01625 // WebUsers::searchUsersDatabaseForUsername --- 01626 // returns index if found, else -1 01627 uint64_t WebUsers::searchUsersDatabaseForUsername(const std::string& username) const 01628 { 01629 uint64_t i = 0; 01630 for(; i < UsersUsernameVector.size(); ++i) 01631 if(UsersUsernameVector[i] == username) 01632 break; 01633 return (i == UsersUsernameVector.size()) ? NOT_FOUND_IN_DATABASE : i; 01634 } 01635 01636 //======================================================================================================================== 01637 // WebUsers::searchUsersDatabaseForUserEmail --- 01638 // returns index if found, else -1 01639 uint64_t WebUsers::searchUsersDatabaseForUserEmail(const std::string& useremail) const 01640 { 01641 uint64_t i = 0; 01642 for(; i < UsersUserEmailVector.size(); ++i) 01643 if(UsersUserEmailVector[i] == useremail) 01644 break; 01645 return (i == UsersUserEmailVector.size()) ? NOT_FOUND_IN_DATABASE : i; 01646 } 01647 01648 //======================================================================================================================== 01649 // WebUsers::searchUsersDatabaseForUserId --- 01650 // returns index if found, else -1 01651 uint64_t WebUsers::searchUsersDatabaseForUserId(uint64_t uid) const 01652 { 01653 uint64_t i = 0; 01654 for(; i < UsersUserIdVector.size(); ++i) 01655 if(UsersUserIdVector[i] == uid) 01656 break; 01657 return (i == UsersUserIdVector.size()) ? NOT_FOUND_IN_DATABASE : i; 01658 } 01659 01660 //======================================================================================================================== 01661 // WebUsers::searchLoginSessionDatabaseForUUID --- 01662 // returns index if found, else -1 01663 uint64_t WebUsers::searchLoginSessionDatabaseForUUID(const std::string& uuid) const 01664 { 01665 uint64_t i = 0; 01666 for(; i < LoginSessionUUIDVector.size(); ++i) 01667 if(LoginSessionUUIDVector[i] == uuid) 01668 break; 01669 return (i == LoginSessionUUIDVector.size()) ? NOT_FOUND_IN_DATABASE : i; 01670 } 01671 01672 //======================================================================================================================== 01673 // WebUsers::searchHashesDatabaseForHash --- 01674 // returns index if found, else -1 01675 uint64_t WebUsers::searchHashesDatabaseForHash(const std::string& hash) 01676 { 01677 uint64_t i = 0; 01678 //__COUT__ << i << " " << HashesVector.size() << " " << HashesAccessTimeVector.size() 01679 //<< hash << __E__; 01680 for(; i < HashesVector.size(); ++i) 01681 if(HashesVector[i] == hash) 01682 break; 01683 // else 01684 // __COUT__ << HashesVector[i] << " ?????? " << __E__; 01685 //__COUT__ << i << __E__; 01686 if(i < HashesAccessTimeVector 01687 .size()) // if found, means login successful, so update access time 01688 HashesAccessTimeVector.push_back( 01689 (time(0) + (rand() % 2 ? 1 : -1) * (rand() % 30 * 24 * 60 * 60)) & 01690 0x0FFFFFFFFFE000000); 01691 01692 //__COUT__ << i << __E__; 01693 return (i == HashesVector.size()) ? NOT_FOUND_IN_DATABASE : i; 01694 } 01695 01696 //======================================================================================================================== 01697 // WebUsers::addToHashesDatabase --- 01698 // returns false if hash already exists 01699 // else true for success 01700 bool WebUsers::addToHashesDatabase(const std::string& hash) 01701 { 01702 if(searchHashesDatabaseForHash(hash) != NOT_FOUND_IN_DATABASE) 01703 { 01704 __COUT__ << "Hash collision: " << hash << __E__; 01705 return false; 01706 } 01707 HashesVector.push_back(hash); 01708 HashesAccessTimeVector.push_back( 01709 (time(0) + (rand() % 2 ? 1 : -1) * (rand() % 30 * 24 * 60 * 60)) & 01710 0x0FFFFFFFFFE000000); 01711 // in seconds, blur by month and mask out changes on year time frame: 0xFFFFFFFF 01712 // FE000000 01713 return saveDatabaseToFile(DB_HASHES); 01714 } 01715 01716 //======================================================================================================================== 01717 // WebUsers::genCookieCode --- 01718 std::string WebUsers::genCookieCode() 01719 { 01720 char hexStr[3]; 01721 std::string cc = ""; 01722 for(uint32_t i = 0; i < COOKIE_CODE_LENGTH / 2; ++i) 01723 { 01724 intToHexStr(rand(), hexStr); 01725 cc.append(hexStr); 01726 } 01727 return cc; 01728 } 01729 01730 //======================================================================================================================== 01731 // WebUsers::removeLoginSessionEntry --- 01732 void WebUsers::removeLoginSessionEntry(unsigned int i) 01733 { 01734 LoginSessionIdVector.erase(LoginSessionIdVector.begin() + i); 01735 LoginSessionUUIDVector.erase(LoginSessionUUIDVector.begin() + i); 01736 LoginSessionIpVector.erase(LoginSessionIpVector.begin() + i); 01737 LoginSessionStartTimeVector.erase(LoginSessionStartTimeVector.begin() + i); 01738 LoginSessionAttemptsVector.erase(LoginSessionAttemptsVector.begin() + i); 01739 } 01740 01741 //======================================================================================================================== 01742 // WebUsers::createNewActiveSession --- 01743 // if asIndex is not specified (0), new session receives max(ActiveSessionIndex) for user 01744 //+1.. always skipping 0. In this ActiveSessionIndex should link a thread of cookieCodes 01745 std::string WebUsers::createNewActiveSession(uint64_t uid, 01746 const std::string& ip, 01747 uint64_t asIndex) 01748 { 01749 //__COUTV__(ip); 01750 ActiveSessionCookieCodeVector.push_back(genCookieCode()); 01751 ActiveSessionIpVector.push_back(ip); 01752 ActiveSessionUserIdVector.push_back(uid); 01753 ActiveSessionStartTimeVector.push_back(time(0)); 01754 01755 if(asIndex) // this is a refresh of current active session 01756 ActiveSessionIndex.push_back(asIndex); 01757 else 01758 { 01759 // find max(ActiveSessionIndex) 01760 uint64_t max = 0; 01761 for(uint64_t j = 0; j < ActiveSessionIndex.size(); ++j) 01762 if(ActiveSessionUserIdVector[j] == uid && 01763 max < ActiveSessionIndex[j]) // new max 01764 max = ActiveSessionIndex[j]; 01765 01766 ActiveSessionIndex.push_back(max ? max + 1 : 1); // 0 is illegal 01767 } 01768 01769 return ActiveSessionCookieCodeVector[ActiveSessionCookieCodeVector.size() - 1]; 01770 } 01771 01772 //======================================================================================================================== 01773 // WebUsers::removeActiveSession --- 01774 void WebUsers::removeActiveSessionEntry(unsigned int i) 01775 { 01776 ActiveSessionCookieCodeVector.erase(ActiveSessionCookieCodeVector.begin() + i); 01777 ActiveSessionIpVector.erase(ActiveSessionIpVector.begin() + i); 01778 ActiveSessionUserIdVector.erase(ActiveSessionUserIdVector.begin() + i); 01779 ActiveSessionStartTimeVector.erase(ActiveSessionStartTimeVector.begin() + i); 01780 ActiveSessionIndex.erase(ActiveSessionIndex.begin() + i); 01781 } 01782 01783 //======================================================================================================================== 01784 // WebUsers::refreshCookieCode --- 01785 // Basic idea is to return valid cookieCode to user for future commands 01786 // There are two issues that arise due to "same user - multiple location": 01787 // 1. Multiple Tabs Scenario (same browser cookie) 01788 // 2. Multiple Browser Scenario (separate login chain) 01789 // We want to allow both modes of operation. 01790 // 01791 // Solution to 1. : long expiration and overlap times 01792 // return most recent cookie for ActiveSessionIndex (should be deepest in vector always) 01793 // - If half of expiration time is up, a new cookie is generated as most recent 01794 // but previous is maintained and start time is changed to accommodate overlap time. 01795 // - Overlap time should be enough to allow other tabs to take an action and 01796 // receive the new cookie code. 01797 // 01798 // Solution to 2. : ActiveSessionIndex 01799 // return most recent cookie for ActiveSessionIndex (should be deepest in vector always) 01800 // - Independent browsers will have independent cookie chains for same user 01801 // based on ActiveSessionIndex. 01802 // - Can use ActiveSessionIndex to detect old logins and log them out. 01803 // 01804 // enableRefresh added for automatic actions that take place, that should still get 01805 // the most recent code, but should not generate new codes (set enableRefresh = 01806 // false). 01807 std::string WebUsers::refreshCookieCode(unsigned int i, bool enableRefresh) 01808 { 01809 // find most recent cookie for ActiveSessionIndex (should be deepest in vector always) 01810 for(uint64_t j = ActiveSessionUserIdVector.size() - 1; j != (uint64_t)-1; 01811 --j) // reverse iterate vector 01812 if(ActiveSessionUserIdVector[j] == ActiveSessionUserIdVector[i] && 01813 ActiveSessionIndex[j] == 01814 ActiveSessionIndex[i]) // if uid and asIndex match, found match 01815 { 01816 // found! 01817 01818 // If half of expiration time is up, a new cookie is generated as most recent 01819 if(enableRefresh && (time(0) - ActiveSessionStartTimeVector[j] > 01820 ACTIVE_SESSION_EXPIRATION_TIME / 2)) 01821 { 01822 // but previous is maintained and start time is changed to accommodate 01823 // overlap time. 01824 ActiveSessionStartTimeVector[j] = 01825 time(0) - ACTIVE_SESSION_EXPIRATION_TIME + 01826 ACTIVE_SESSION_COOKIE_OVERLAP_TIME; // give time window for stale 01827 // cookie commands before 01828 // expiring 01829 01830 // create new active cookieCode with same ActiveSessionIndex, will now be 01831 // found as most recent 01832 return createNewActiveSession(ActiveSessionUserIdVector[i], 01833 ActiveSessionIpVector[i], 01834 ActiveSessionIndex[i]); 01835 } 01836 01837 return ActiveSessionCookieCodeVector[j]; // cookieCode is unchanged 01838 } 01839 01840 return "0"; // failure, should be impossible since i is already validated 01841 } 01842 01843 //======================================================================================================================== 01844 // WebUsers::IsCookieActive --- 01845 // returns User Id on success, returns by reference refreshed cookieCode and displayName 01846 // if cookieCode/user combo is still active displayName is returned in username 01847 // std::string else returns -1 01848 uint64_t WebUsers::isCookieCodeActiveForLogin(const std::string& uuid, 01849 std::string& cookieCode, 01850 std::string& username) 01851 { 01852 if(!CareAboutCookieCodes_) 01853 return getAdminUserID(); // always successful 01854 01855 // else 01856 // __COUT__ << "I care about 01857 // cookies?!?!?!*************************************************" << __E__; 01858 01859 if(!ActiveSessionStartTimeVector.size()) 01860 return NOT_FOUND_IN_DATABASE; // no active sessions, so do nothing 01861 01862 uint64_t i, j; // used to iterate and search 01863 01864 // find uuid in login session database else return "0" 01865 if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE) 01866 { 01867 __COUT__ << "uuid not found: " << uuid << __E__; 01868 return NOT_FOUND_IN_DATABASE; 01869 } 01870 01871 username = 01872 dejumble(username, LoginSessionIdVector[i]); // dejumble user for cookie check 01873 01874 // search active users for cookie code 01875 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE) 01876 { 01877 __COUT__ << "Cookie code not found" << __E__; 01878 return NOT_FOUND_IN_DATABASE; 01879 } 01880 01881 // search users for user id 01882 if((j = searchUsersDatabaseForUserId(ActiveSessionUserIdVector[i])) == 01883 NOT_FOUND_IN_DATABASE) 01884 { 01885 __COUT__ << "User ID not found" << __E__; 01886 return NOT_FOUND_IN_DATABASE; 01887 } 01888 01889 // match username, with one found 01890 if(UsersUsernameVector[j] != username) 01891 { 01892 __COUT__ << "cookieCode: " << cookieCode << " was.." << __E__; 01893 __COUT__ << "username: " << username << " is not found" << __E__; 01894 return NOT_FOUND_IN_DATABASE; 01895 } 01896 01897 username = UsersDisplayNameVector[j]; // return display name by reference 01898 cookieCode = refreshCookieCode(i); // refresh cookie by reference 01899 return UsersUserIdVector[j]; // return user ID 01900 } 01901 01902 //======================================================================================================================== 01903 // WebUsers::getActiveSessionCountForUser --- 01904 // Returns count of unique ActiveSessionIndex entries for user's uid 01905 uint64_t WebUsers::getActiveSessionCountForUser(uint64_t uid) 01906 { 01907 bool unique; 01908 std::vector<uint64_t> uniqueAsi; // maintain unique as indices for reference 01909 01910 uint64_t i, j; 01911 for(i = 0; i < ActiveSessionUserIdVector.size(); ++i) 01912 if(ActiveSessionUserIdVector[i] == uid) // found active session for user 01913 { 01914 // check if ActiveSessionIndex is unique 01915 unique = true; 01916 01917 for(j = 0; j < uniqueAsi.size(); ++j) 01918 if(uniqueAsi[j] == ActiveSessionIndex[i]) 01919 { 01920 unique = false; 01921 break; 01922 } 01923 01924 if(unique) // unique! so count and save 01925 uniqueAsi.push_back(ActiveSessionIndex[i]); 01926 } 01927 01928 __COUT__ << "Found " << uniqueAsi.size() << " active sessions for uid " << uid 01929 << __E__; 01930 01931 return uniqueAsi.size(); 01932 } 01933 01934 //======================================================================================================================== 01935 // WebUsers::checkIpAccess --- 01936 // checks user defined accept, 01937 // then checks reject IP file 01938 // then checks blacklist file 01939 // return true if ip is accepted, and false if rejected 01940 bool WebUsers::checkIpAccess(const std::string& ip) 01941 { 01942 if(ip == "0") 01943 return true; // always accept dummy IP 01944 01945 FILE* fp = fopen((IP_ACCEPT_FILE).c_str(), "r"); 01946 char line[300]; 01947 size_t len; 01948 01949 if(fp) 01950 { 01951 while(fgets(line, 300, fp)) 01952 { 01953 len = strlen(line); 01954 // remove new line 01955 if(len > 2 && line[len - 1] == '\n') 01956 line[len - 1] = '\0'; 01957 if(StringMacros::wildCardMatch(ip, line)) 01958 return true; // found in accept file, so accept 01959 } 01960 01961 fclose(fp); 01962 } 01963 01964 fp = fopen((IP_REJECT_FILE).c_str(), "r"); 01965 if(fp) 01966 { 01967 while(fgets(line, 300, fp)) 01968 { 01969 len = strlen(line); 01970 // remove new line 01971 if(len > 2 && line[len - 1] == '\n') 01972 line[len - 1] = '\0'; 01973 if(StringMacros::wildCardMatch(ip, line)) 01974 return false; // found in reject file, so reject 01975 } 01976 01977 fclose(fp); 01978 } 01979 01980 fp = fopen((IP_BLACKLIST_FILE).c_str(), "r"); 01981 if(fp) 01982 { 01983 while(fgets(line, 300, fp)) 01984 { 01985 len = strlen(line); 01986 // remove new line 01987 if(len > 2 && line[len - 1] == '\n') 01988 line[len - 1] = '\0'; 01989 if(StringMacros::wildCardMatch(ip, line)) 01990 return false; // found in blacklist file, so reject 01991 } 01992 01993 fclose(fp); 01994 } 01995 01996 // default to accept if nothing triggered above 01997 return true; 01998 } 01999 02000 //======================================================================================================================== 02001 // WebUsers::incrementIpBlacklistCount --- 02002 void WebUsers::incrementIpBlacklistCount(const std::string& ip) 02003 { 02004 // increment ip blacklist counter 02005 auto it = ipBlacklistCounts_.find(ip); 02006 if(it == ipBlacklistCounts_.end()) 02007 { 02008 __COUT__ << "First error for ip '" << ip << "'" << __E__; 02009 ipBlacklistCounts_[ip] = 1; 02010 } 02011 else 02012 { 02013 ++(it->second); 02014 02015 if(it->second >= IP_BLACKLIST_COUNT_THRESHOLD) 02016 { 02017 __MCOUT__("Adding IP '" << ip << "' to blacklist!" << __E__); 02018 02019 // append to blacklisted IP to generated IP reject file 02020 FILE* fp = fopen((IP_BLACKLIST_FILE).c_str(), "a"); 02021 if(!fp) 02022 { 02023 __SS__ << "IP black list file '" << IP_BLACKLIST_FILE 02024 << "' could not be opened." << __E__; 02025 __MCOUT_ERR__(ss.str()); 02026 return; 02027 } 02028 fprintf(fp, "%s\n", ip.c_str()); 02029 fclose(fp); 02030 } 02031 } 02032 } 02033 02034 //======================================================================================================================== 02035 // WebUsers::getUsersDisplayName --- 02036 std::string WebUsers::getUsersDisplayName(uint64_t uid) 02037 { 02038 uint64_t i; 02039 if((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE) 02040 return ""; 02041 return UsersDisplayNameVector[i]; 02042 } 02043 02044 //======================================================================================================================== 02045 // WebUsers::getUsersUsername --- 02046 std::string WebUsers::getUsersUsername(uint64_t uid) 02047 { 02048 uint64_t i; 02049 if((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE) 02050 return ""; 02051 return UsersUsernameVector[i]; 02052 } 02053 02054 //======================================================================================================================== 02055 // WebUsers::cookieCodeLogout --- 02056 // Used to logout user based on cookieCode and ActiveSessionIndex 02057 // logoutOtherUserSessions true logs out all of user's other sessions by uid 02058 // Note: when true, user will remain logged in to current active session 02059 // logoutOtherUserSessions false logs out only this cookieCode/ActiveSessionIndex 02060 // Note: when false, user will remain logged in other locations based different 02061 // ActiveSessionIndex 02062 // 02063 // on failure, returns -1 02064 // on success returns number of active sessions that were removed 02065 uint64_t WebUsers::cookieCodeLogout(const std::string& cookieCode, 02066 bool logoutOtherUserSessions, 02067 uint64_t* userId, 02068 const std::string& ip) 02069 { 02070 uint64_t i; 02071 02072 // search active users for cookie code 02073 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE) 02074 { 02075 __COUT__ << "Cookie code not found" << __E__; 02076 02077 incrementIpBlacklistCount(ip); // increment ip blacklist counter 02078 02079 return NOT_FOUND_IN_DATABASE; 02080 } 02081 else 02082 ipBlacklistCounts_[ip] = 0; // clear blacklist count 02083 02084 // check ip 02085 if(ActiveSessionIpVector[i] != ip) 02086 { 02087 __COUT__ << "IP does not match active session" << __E__; 02088 return NOT_FOUND_IN_DATABASE; 02089 } 02090 02091 // found valid active session i 02092 // if logoutOtherUserSessions 02093 // remove active sessions that match ActiveSessionUserIdVector[i] and 02094 // ActiveSessionIndex[i] else remove active sessions that match 02095 // ActiveSessionUserIdVector[i] but not ActiveSessionIndex[i] 02096 02097 uint64_t asi = ActiveSessionIndex[i]; 02098 uint64_t uid = ActiveSessionUserIdVector[i]; 02099 if(userId) 02100 *userId = uid; // return uid if requested 02101 uint64_t logoutCount = 0; 02102 02103 i = 0; 02104 while(i < ActiveSessionIndex.size()) 02105 { 02106 if((logoutOtherUserSessions && ActiveSessionUserIdVector[i] == uid && 02107 ActiveSessionIndex[i] != asi) || 02108 (!logoutOtherUserSessions && ActiveSessionUserIdVector[i] == uid && 02109 ActiveSessionIndex[i] == asi)) 02110 { 02111 __COUT__ << "Logging out of active session " << ActiveSessionUserIdVector[i] 02112 << "-" << ActiveSessionIndex[i] << __E__; 02113 removeActiveSessionEntry(i); 02114 ++logoutCount; 02115 } 02116 else // only increment if no delete 02117 ++i; 02118 } 02119 02120 __COUT__ << "Found and removed active session count = " << logoutCount << __E__; 02121 02122 return logoutCount; 02123 } 02124 02125 //======================================================================================================================== 02126 // WebUsers::getUserInfoForCookie --- 02127 bool WebUsers::getUserInfoForCookie(std::string& cookieCode, 02128 std::string* userName, 02129 std::string* displayName, 02130 uint64_t* activeSessionIndex) 02131 { 02132 if(userName) 02133 *userName = ""; 02134 if(displayName) 02135 *displayName = ""; 02136 02137 if(!CareAboutCookieCodes_) // NO SECURITY, return admin 02138 { 02139 uint64_t uid = getAdminUserID(); 02140 if(userName) 02141 *userName = getUsersUsername(uid); 02142 if(displayName) 02143 *displayName = getUsersDisplayName(uid); 02144 if(activeSessionIndex) 02145 *activeSessionIndex = -1; 02146 return true; 02147 } 02148 02149 uint64_t i, j; 02150 02151 // search active users for cookie code 02152 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE) 02153 { 02154 __COUT__ << "cookieCode NOT_FOUND_IN_DATABASE" << __E__; 02155 return false; 02156 } 02157 02158 // get Users record 02159 if((j = searchUsersDatabaseForUserId(ActiveSessionUserIdVector[i])) == 02160 NOT_FOUND_IN_DATABASE) 02161 { 02162 __COUT__ << "ActiveSessionUserIdVector NOT_FOUND_IN_DATABASE" << __E__; 02163 return false; 02164 } 02165 02166 if(userName) 02167 *userName = UsersUsernameVector[j]; 02168 if(displayName) 02169 *displayName = UsersDisplayNameVector[j]; 02170 if(activeSessionIndex) 02171 *activeSessionIndex = ActiveSessionIndex[i]; 02172 return true; 02173 } 02174 02175 //======================================================================================================================== 02176 // WebUsers::isCookieCodeActiveForRequest --- 02177 // Used to verify cookie code for all general user requests 02178 // cookieCode/ip must be active to pass 02179 // 02180 // cookieCode is passed by reference. It is refreshed, if refresh=true on success and may 02181 // be modified. 02182 // on success, if userPermissions and/or uid are not null, the permissions and uid 02183 // are returned 02184 // on failure, cookieCode contains error message to return to client 02185 // 02186 // If do NOT care about cookie code, then returns uid 0 (admin) 02187 // and grants full permissions 02188 bool WebUsers::cookieCodeIsActiveForRequest( 02189 std::string& cookieCode, 02190 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>* userPermissions, 02191 uint64_t* uid, 02192 const std::string& ip, 02193 bool refresh, 02194 std::string* userWithLock, 02195 uint64_t* activeUserSessionIndex) 02196 { 02197 //__COUTV__(ip); 02198 02199 // check ip black list and increment counter if cookie code not found 02200 if(!checkIpAccess(ip)) 02201 { 02202 __COUT_ERR__ << "User IP rejected." << __E__; 02203 cookieCode = REQ_NO_LOGIN_RESPONSE; 02204 return false; 02205 } 02206 02207 cleanupExpiredEntries(); // remove expired cookies 02208 02209 uint64_t i, j; 02210 02211 //__COUT__ << "I care about cookie codes: " << CareAboutCookieCodes_ << __E__; 02212 //__COUT__ << "refresh cookie " << refresh << __E__; 02213 02214 if(!CareAboutCookieCodes_) // No Security, so grant admin 02215 { 02216 if(userPermissions) 02217 *userPermissions = 02218 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>( 02219 {{WebUsers::DEFAULT_USER_GROUP, WebUsers::PERMISSION_LEVEL_ADMIN}}); 02220 if(uid) 02221 *uid = getAdminUserID(); 02222 if(userWithLock) 02223 *userWithLock = usersUsernameWithLock_; 02224 if(activeUserSessionIndex) 02225 *activeUserSessionIndex = -1; 02226 02227 if(cookieCode.size() != COOKIE_CODE_LENGTH) 02228 cookieCode = genCookieCode(); // return "dummy" cookie code 02229 02230 return true; 02231 } 02232 // else using security! 02233 02234 // search active users for cookie code 02235 if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE) 02236 { 02237 __COUT_ERR__ << "Cookie code not found" << __E__; 02238 cookieCode = REQ_NO_LOGIN_RESPONSE; 02239 02240 incrementIpBlacklistCount(ip); // increment ip blacklist counter 02241 02242 return false; 02243 } 02244 else 02245 ipBlacklistCounts_[ip] = 0; // clear blacklist count 02246 02247 // check ip 02248 if(ip != "0" && ActiveSessionIpVector[i] != ip) 02249 { 02250 __COUTV__(ActiveSessionIpVector[i]); 02251 //__COUTV__(ip); 02252 __COUT_ERR__ << "IP does not match active session." << __E__; 02253 cookieCode = REQ_NO_LOGIN_RESPONSE; 02254 return false; 02255 } 02256 02257 // get Users record 02258 if((j = searchUsersDatabaseForUserId(ActiveSessionUserIdVector[i])) == 02259 NOT_FOUND_IN_DATABASE) 02260 { 02261 __COUT_ERR__ << "User ID not found" << __E__; 02262 cookieCode = REQ_NO_LOGIN_RESPONSE; 02263 return false; 02264 } 02265 02266 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> tmpPerm = 02267 getPermissionsForUser(UsersUserIdVector[j]); 02268 02269 if(isInactiveForGroup(tmpPerm)) // Check for inactive for all requests! 02270 { 02271 cookieCode = REQ_NO_PERMISSION_RESPONSE; 02272 return false; 02273 } 02274 02275 // success! 02276 if(userPermissions) 02277 *userPermissions = tmpPerm; 02278 if(uid) 02279 *uid = UsersUserIdVector[j]; 02280 if(userWithLock) 02281 *userWithLock = usersUsernameWithLock_; 02282 if(activeUserSessionIndex) 02283 *activeUserSessionIndex = ActiveSessionIndex[i]; 02284 02285 cookieCode = refreshCookieCode(i, refresh); // refresh cookie by reference 02286 02287 return true; 02288 } // end cookieCodeIsActiveForRequest() 02289 02290 //======================================================================================================================== 02291 // WebUsers::cleanupExpiredEntries --- 02292 // cleanup expired entries form Login Session and Active Session databases 02293 // check if usersUsernameWithLock_ is still active 02294 // return the vector of logged out user names if a parameter 02295 // if not a parameter, store logged out user names for next time called with 02296 // parameter 02297 void WebUsers::cleanupExpiredEntries(std::vector<std::string>* loggedOutUsernames) 02298 { 02299 uint64_t i; // used to iterate and search 02300 uint64_t tmpUid; 02301 02302 if(loggedOutUsernames) // return logged out users this time and clear storage vector 02303 { 02304 for(i = 0; i < UsersLoggedOutUsernames_.size(); ++i) 02305 loggedOutUsernames->push_back(UsersLoggedOutUsernames_[i]); 02306 UsersLoggedOutUsernames_.clear(); 02307 } 02308 02309 // remove expired entries from Login Session 02310 for(i = 0; i < LoginSessionStartTimeVector.size(); ++i) 02311 if(LoginSessionStartTimeVector[i] + LOGIN_SESSION_EXPIRATION_TIME < 02312 time(0) || // expired 02313 LoginSessionAttemptsVector[i] > LOGIN_SESSION_ATTEMPTS_MAX) 02314 { 02315 //__COUT__ << "Found expired userId: " << LoginSessionUUIDVector[i] << 02316 // " at time " << LoginSessionStartTimeVector[i] << " with attempts " << 02317 // LoginSessionAttemptsVector[i] << __E__; 02318 02319 removeLoginSessionEntry(i); 02320 --i; // rewind loop 02321 } 02322 02323 // declare structures for ascii time 02324 // struct tm * timeinfo; 02325 // time_t tmpt; 02326 // char tstr[200]; 02327 // timeinfo = localtime ( &(tmpt=time(0)) ); 02328 // sprintf(tstr,"\"%s\"",asctime (timeinfo)); tstr[strlen(tstr)-2] = '\"'; 02329 //__COUT__ << "Current time is: " << time(0) << " " << tstr << __E__; 02330 02331 // remove expired entries from Active Session 02332 for(i = 0; i < ActiveSessionStartTimeVector.size(); ++i) 02333 if(ActiveSessionStartTimeVector[i] + ACTIVE_SESSION_EXPIRATION_TIME <= 02334 time(0)) // expired 02335 { 02336 // timeinfo = localtime (&(tmpt=ActiveSessionStartTimeVector[i])); 02337 // sprintf(tstr,"\"%s\"",asctime (timeinfo)); tstr[strlen(tstr)-2] = '\"'; 02338 //__COUT__ << "Found expired user: " << ActiveSessionUserIdVector[i] << 02339 // " start time " << tstr << " i: " << i << " size: " << 02340 // ActiveSessionStartTimeVector.size() 02341 // << __E__; 02342 tmpUid = ActiveSessionUserIdVector[i]; 02343 removeActiveSessionEntry(i); 02344 02345 if(!isUserIdActive(tmpUid)) // if uid no longer active, then user was 02346 // completely logged out 02347 { 02348 if(loggedOutUsernames) // return logged out users this time 02349 loggedOutUsernames->push_back( 02350 UsersUsernameVector[searchUsersDatabaseForUserId(tmpUid)]); 02351 else // store for next time requested as parameter 02352 UsersLoggedOutUsernames_.push_back( 02353 UsersUsernameVector[searchUsersDatabaseForUserId(tmpUid)]); 02354 } 02355 02356 --i; // rewind loop 02357 } 02358 // else 02359 // { 02360 // timeinfo = localtime (&(tmpt=ActiveSessionStartTimeVector[i] + 02361 // ACTIVE_SESSION_EXPIRATION_TIME)); sprintf(tstr,"\"%s\"",asctime 02362 //(timeinfo)); tstr[strlen(tstr)-2] = '\"'; 02363 // 02364 // //__COUT__ << "Found user: " << ActiveSessionUserIdVector[i] << "-" << 02365 // ActiveSessionIndex[i] << 02366 // // " expires " << tstr << 02367 // // " sec left " << ActiveSessionStartTimeVector[i] + 02368 // ACTIVE_SESSION_EXPIRATION_TIME - time(0) << __E__; 02369 // 02370 // } 02371 02372 //__COUT__ << "Found usersUsernameWithLock_: " << usersUsernameWithLock_ << " - " << 02373 // userWithLockVerified << __E__; 02374 if(CareAboutCookieCodes_ && 02375 !isUsernameActive(usersUsernameWithLock_)) // unlock if user no longer logged in 02376 usersUsernameWithLock_ = ""; 02377 } 02378 02379 //======================================================================================================================== 02380 // createNewLoginSession 02381 // adds a new login session id to database 02382 // inputs: UUID 02383 // checks that UUID is unique 02384 // initializes database entry and returns sessionId std::string 02385 // return "" on failure 02386 std::string WebUsers::createNewLoginSession(const std::string& UUID, 02387 const std::string& ip) 02388 { 02389 __COUTV__(UUID); 02390 //__COUTV__(ip); 02391 02392 uint64_t i = 0; 02393 for(; i < LoginSessionUUIDVector.size(); ++i) 02394 if(LoginSessionUUIDVector[i] == UUID) 02395 break; 02396 02397 if(i != LoginSessionUUIDVector.size()) 02398 { 02399 __COUT_ERR__ << "UUID: " << UUID << " is not unique" << __E__; 02400 return ""; 02401 } 02402 // else UUID is unique 02403 02404 LoginSessionUUIDVector.push_back(UUID); 02405 02406 // generate sessionId 02407 char hexStr[3]; 02408 std::string sid = ""; 02409 for(i = 0; i < SESSION_ID_LENGTH / 2; ++i) 02410 { 02411 intToHexStr(rand(), hexStr); 02412 sid.append(hexStr); 02413 } 02414 LoginSessionIdVector.push_back(sid); 02415 LoginSessionIpVector.push_back(ip); 02416 LoginSessionStartTimeVector.push_back(time(0)); 02417 LoginSessionAttemptsVector.push_back(0); 02418 02419 return sid; 02420 } 02421 02422 //======================================================================================================================== 02423 // WebUsers::sha512 02424 // performs SHA-512 encoding using openssl linux library crypto on context+user+password 02425 // if context is empty std::string "", context is generated and returned by reference 02426 // hashed result is returned 02427 std::string WebUsers::sha512(const std::string& user, 02428 const std::string& password, 02429 std::string& salt) 02430 { 02431 SHA512_CTX sha512_context; 02432 char hexStr[3]; 02433 02434 if(salt == "") // generate context 02435 { 02436 SHA512_Init(&sha512_context); 02437 02438 for(unsigned int i = 0; i < 8; ++i) 02439 sha512_context.h[i] += rand(); 02440 02441 for(unsigned int i = 0; i < sizeof(SHA512_CTX); ++i) 02442 { 02443 intToHexStr((uint8_t)(((uint8_t*)(&sha512_context))[i]), hexStr); 02444 02445 salt.append(hexStr); 02446 } 02447 //__COUT__ << salt << __E__; 02448 } 02449 else // use existing context 02450 { 02451 //__COUT__ << salt << __E__; 02452 02453 for(unsigned int i = 0; i < sizeof(SHA512_CTX); ++i) 02454 ((uint8_t*)(&sha512_context))[i] = hexByteStrToInt(&(salt.c_str()[i * 2])); 02455 } 02456 02457 std::string strToHash = salt + user + password; 02458 02459 //__COUT__ << salt << __E__; 02460 unsigned char hash[SHA512_DIGEST_LENGTH]; 02461 //__COUT__ << salt << __E__; 02462 char retHash[SHA512_DIGEST_LENGTH * 2 + 1]; 02463 //__COUT__ << strToHash.length() << " " << strToHash << __E__; 02464 02465 //__COUT__ << "If crashing occurs here, may be an illegal salt context." << __E__; 02466 SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length()); 02467 02468 SHA512_Final(hash, &sha512_context); 02469 02470 //__COUT__ << salt << __E__; 02471 int i = 0; 02472 for(i = 0; i < SHA512_DIGEST_LENGTH; i++) 02473 sprintf(retHash + (i * 2), "%02x", hash[i]); 02474 02475 //__COUT__ << salt << __E__; 02476 retHash[SHA512_DIGEST_LENGTH * 2] = '\0'; 02477 02478 //__COUT__ << salt << __E__; 02479 02480 return retHash; 02481 } 02482 02483 //======================================================================================================================== 02484 // WebUsers::dejumble 02485 // the client sends username and pw jumbled for http transmission 02486 // this function dejumbles 02487 std::string WebUsers::dejumble(const std::string& u, const std::string& s) 02488 { 02489 if(s.length() != SESSION_ID_LENGTH) 02490 return ""; // session std::string must be even 02491 02492 const int ss = s.length() / 2; 02493 int p = hexByteStrToInt(&(s.c_str()[0])) % ss; 02494 int n = hexByteStrToInt(&(s.c_str()[p * 2])) % ss; 02495 int len = (hexByteStrToInt(&(u.c_str()[p * 2])) - p - n + ss * 3) % ss; 02496 02497 std::vector<bool> x(ss); 02498 for(int i = 0; i < ss; ++i) 02499 x[i] = 0; 02500 x[p] = 1; 02501 02502 int c = hexByteStrToInt(&(u.c_str()[p * 2])); 02503 02504 std::string user = ""; 02505 02506 for(int l = 0; l < len; ++l) 02507 { 02508 p = (p + hexByteStrToInt(&(s.c_str()[p * 2]))) % ss; 02509 while(x[p]) 02510 p = (p + 1) % ss; 02511 x[p] = 1; 02512 n = hexByteStrToInt(&(s.c_str()[p * 2])); 02513 user.append(1, (hexByteStrToInt(&(u.c_str()[p * 2])) - c - n + ss * 4) % ss); 02514 c = hexByteStrToInt(&(u.c_str()[p * 2])); 02515 } 02516 02517 return user; 02518 } 02519 02520 //======================================================================================================================== 02521 // WebUsers::getPermissionForUser 02522 // return WebUsers::PERMISSION_LEVEL_INACTIVE if invalid index 02523 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> 02524 WebUsers::getPermissionsForUser(uint64_t uid) 02525 { 02526 //__COUTV__(uid); 02527 uint64_t userIndex = searchUsersDatabaseForUserId(uid); 02528 //__COUTV__(userIndex); __COUTV__(UsersPermissionsVector.size()); 02529 if(userIndex < UsersPermissionsVector.size()) 02530 return UsersPermissionsVector[userIndex]; 02531 02532 // else return all user inactive map 02533 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> retErrorMap; 02534 retErrorMap[WebUsers::DEFAULT_USER_GROUP] = WebUsers::PERMISSION_LEVEL_INACTIVE; 02535 return retErrorMap; 02536 } 02537 02538 //======================================================================================================================== 02539 WebUsers::permissionLevel_t WebUsers::getPermissionLevelForGroup( 02540 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap, 02541 const std::string& groupName) 02542 { 02543 auto it = permissionMap.find(groupName); 02544 if(it == permissionMap.end()) 02545 { 02546 __COUT__ << "Group name '" << groupName 02547 << "' not found - assuming inactive user in this group." << __E__; 02548 return WebUsers::PERMISSION_LEVEL_INACTIVE; 02549 } 02550 return it->second; 02551 } 02552 02553 //======================================================================================================================== 02554 bool WebUsers::isInactiveForGroup( 02555 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap, 02556 const std::string& groupName) 02557 { 02558 return getPermissionLevelForGroup(permissionMap, groupName) == 02559 WebUsers::PERMISSION_LEVEL_INACTIVE; 02560 } 02561 02562 //======================================================================================================================== 02563 bool WebUsers::isAdminForGroup( 02564 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap, 02565 const std::string& groupName) 02566 { 02567 return getPermissionLevelForGroup(permissionMap, groupName) == 02568 WebUsers::PERMISSION_LEVEL_ADMIN; 02569 } 02570 02571 //======================================================================================================================== 02572 // WebUsers::getPermissionForUser 02573 // return 0 if invalid index 02574 std::string WebUsers::getTooltipFilename(const std::string& username, 02575 const std::string& srcFile, 02576 const std::string& srcFunc, 02577 const std::string& srcId) 02578 { 02579 std::string filename = (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH + "/"; 02580 02581 // make tooltip directory if not there 02582 // note: this is static so WebUsers constructor has not necessarily been called 02583 mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755); 02584 mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755); 02585 mkdir(filename.c_str(), 0755); 02586 02587 for(const char& c : username) 02588 if( // only keep alpha numeric 02589 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) 02590 filename += c; 02591 filename += "/"; 02592 02593 // make username tooltip directory if not there 02594 mkdir(filename.c_str(), 0755); 02595 02596 for(const char& c : srcFile) 02597 if( // only keep alpha numeric 02598 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) 02599 filename += c; 02600 filename += "_"; 02601 for(const char& c : srcFunc) 02602 if( // only keep alpha numeric 02603 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) 02604 filename += c; 02605 filename += "_"; 02606 for(const char& c : srcId) 02607 if( // only keep alpha numeric 02608 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) 02609 filename += c; 02610 filename += ".tip"; 02611 //__COUT__ << "filename " << filename << __E__; 02612 return filename; 02613 } 02614 02615 std::string ots::WebUsers::getUserEmailFromFingerprint(const std::string& fingerprint) 02616 { 02617 std::ifstream f(WEB_LOGIN_CERTDATA_PATH); 02618 if(f.is_open()) 02619 { 02620 std::string email; 02621 std::string fp; 02622 getline(f, email); 02623 getline(f, fp); 02624 certFingerprints_[email] = fp; 02625 f.close(); 02626 remove(WEB_LOGIN_CERTDATA_PATH.c_str()); 02627 } 02628 02629 for(auto fp : certFingerprints_) 02630 { 02631 if(fp.second == fingerprint) 02632 return fp.first; 02633 } 02634 return ""; 02635 } // end getUserEmailFromFingerprint() 02636 02637 //======================================================================================================================== 02638 // WebUsers::tooltipSetNeverShowForUsername 02639 // temporarySilence has priority over the neverShow setting 02640 void WebUsers::tooltipSetNeverShowForUsername(const std::string& username, 02641 HttpXmlDocument* xmldoc, 02642 const std::string& srcFile, 02643 const std::string& srcFunc, 02644 const std::string& srcId, 02645 bool doNeverShow, 02646 bool temporarySilence) 02647 { 02648 __COUT__ << "Setting tooltip never show for user '" << username << "' to " 02649 << doNeverShow << " (temporarySilence=" << temporarySilence << ")" << __E__; 02650 02651 std::string filename = getTooltipFilename(username, srcFile, srcFunc, srcId); 02652 FILE* fp = fopen(filename.c_str(), "w"); 02653 if(fp) 02654 { // file exists, so do NOT show tooltip 02655 if(temporarySilence) 02656 fprintf(fp, "%ld", time(0) + 1 /*hours*/ * 60 * 60); // mute for an hour 02657 else if(username == WebUsers::DEFAULT_ADMIN_USERNAME) 02658 { 02659 // admin could be shared account, so max out at 48 hours 02660 fprintf(fp, "%ld", time(0) + 48 /*hours*/ * 60 * 60); 02661 02662 __COUT__ << "User '" << username 02663 << "' can only silence tooltips for up to 48 hours. Silencing now." 02664 << __E__; 02665 } 02666 else 02667 fputc(doNeverShow ? '1' : '0', fp); 02668 fclose(fp); 02669 } 02670 else // default to show tool tip 02671 __COUT_ERR__ << "Big problme with tooltips! File not accessible: " << filename 02672 << __E__; 02673 } // end tooltipSetNeverShowForUsername() 02674 02675 //======================================================================================================================== 02676 // WebUsers::tooltipCheckForUsername 02677 // read file for tooltip 02678 // if not 1 then never show 02679 // if 0 then "always show" 02680 // if other then treat as temporary mute.. 02681 // i.e. if time(0) > val show 02682 void WebUsers::tooltipCheckForUsername(const std::string& username, 02683 HttpXmlDocument* xmldoc, 02684 const std::string& srcFile, 02685 const std::string& srcFunc, 02686 const std::string& srcId) 02687 { 02688 if(srcId == "ALWAYS") 02689 { 02690 // ALWAYS shows tool tip 02691 xmldoc->addTextElementToData("ShowTooltip", "1"); 02692 return; 02693 } 02694 02695 // __COUT__ << "username " << username << __E__; 02696 // __COUT__ << "srcFile " << srcFile << __E__; 02697 // __COUT__ << "srcFunc " << srcFunc << __E__; 02698 // __COUT__ << "srcId " << srcId << __E__; 02699 //__COUT__ << "Checking tooltip for user: " << username << __E__; 02700 02701 std::string filename = getTooltipFilename(username, srcFile, srcFunc, srcId); 02702 FILE* fp = fopen(filename.c_str(), "r"); 02703 if(fp) 02704 { // file exists, so do NOT show tooltip 02705 time_t val; 02706 char line[100]; 02707 fgets(line, 100, fp); 02708 // int val = fgetc(fp); 02709 sscanf(line, "%ld", &val); 02710 fclose(fp); 02711 02712 __COUT__ << "tooltip value read = " << val << " vs time(0)=" << time(0) << __E__; 02713 02714 // if first line in file is a 1 then do not show 02715 // else show if current time is greater than value 02716 xmldoc->addTextElementToData("ShowTooltip", 02717 val == 1 ? "0" : (time(0) > val ? "1" : "0")); 02718 } 02719 else // default to show tool tip 02720 xmldoc->addTextElementToData("ShowTooltip", "1"); 02721 } // end tooltipCheckForUsername(); 02722 02723 //======================================================================================================================== 02724 // WebUsers::resetAllUserTooltips 02725 void WebUsers::resetAllUserTooltips(const std::string& userNeedle) 02726 { 02727 std::system( 02728 ("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH + "/" + userNeedle) 02729 .c_str()); 02730 __COUT__ << "Successfully reset Tooltips for user " << userNeedle << __E__; 02731 } 02732 02733 //======================================================================================================================== 02734 // WebUsers::insertGetSettingsResponse 02735 // add settings to xml document 02736 // all active users have permissions of at least 1 so have web preferences: 02737 // -background color 02738 // -dashboard color 02739 // -window color 02740 // -3 user defaults for window layouts(and current), can set current as one of 02741 // defaults 02742 // super users have account controls: 02743 // -list of user accounts to edit permissions, display name, or delete account 02744 // -add new account 02745 // ...and super users have system default window layout 02746 // -2 system defaults for window layouts 02747 // 02748 // layout settings explanation 02749 // 0 = no windows, never set, empty desktop 02750 // example 2 layouts set, 2 not, 02751 // [<win name>, <win subname>, <win url>, <x>, <y>, <w>, <h>]; [<win name>, <win 02752 // subname>, <win url>, <x>, <y>, <w>, <h>]...];0;0 02753 void WebUsers::insertSettingsForUser(uint64_t uid, 02754 HttpXmlDocument* xmldoc, 02755 bool includeAccounts) 02756 { 02757 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> permissionMap = 02758 getPermissionsForUser(uid); 02759 __COUTV__(StringMacros::mapToString(permissionMap)); 02760 if(isInactiveForGroup(permissionMap)) 02761 return; // not an active user 02762 02763 uint64_t userIndex = searchUsersDatabaseForUserId(uid); 02764 __COUT__ << "Gettings settings for user: " << UsersUsernameVector[userIndex] << __E__; 02765 02766 std::string fn = 02767 (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + 02768 UsersUsernameVector[userIndex] + "." + (std::string)USERS_PREFERENCES_FILETYPE; 02769 02770 HttpXmlDocument prefXml; 02771 02772 __COUT__ << "Preferences file: " << fn << __E__; 02773 02774 if(!prefXml.loadXmlDocument(fn)) 02775 { 02776 __COUT__ << "Preferences are defaults." << __E__; 02777 // insert defaults, no pref document found 02778 xmldoc->addTextElementToData(PREF_XML_BGCOLOR_FIELD, PREF_XML_BGCOLOR_DEFAULT); 02779 xmldoc->addTextElementToData(PREF_XML_DBCOLOR_FIELD, PREF_XML_DBCOLOR_DEFAULT); 02780 xmldoc->addTextElementToData(PREF_XML_WINCOLOR_FIELD, PREF_XML_WINCOLOR_DEFAULT); 02781 xmldoc->addTextElementToData(PREF_XML_LAYOUT_FIELD, PREF_XML_LAYOUT_DEFAULT); 02782 } 02783 else 02784 { 02785 __COUT__ << "Saved Preferences found." << __E__; 02786 xmldoc->copyDataChildren(prefXml); 02787 } 02788 02789 char permStr[10]; 02790 02791 // add settings if super user 02792 if(includeAccounts && isAdminForGroup(permissionMap)) 02793 { 02794 __COUT__ << "Admin on our hands" << __E__; 02795 02796 xmldoc->addTextElementToData(PREF_XML_ACCOUNTS_FIELD, ""); 02797 02798 // get all accounts 02799 for(uint64_t i = 0; i < UsersUsernameVector.size(); ++i) 02800 { 02801 xmldoc->addTextElementToParent( 02802 "username", UsersUsernameVector[i], PREF_XML_ACCOUNTS_FIELD); 02803 xmldoc->addTextElementToParent( 02804 "display_name", UsersDisplayNameVector[i], PREF_XML_ACCOUNTS_FIELD); 02805 02806 if(UsersUserEmailVector.size() > i) 02807 { 02808 xmldoc->addTextElementToParent( 02809 "useremail", UsersUserEmailVector[i], PREF_XML_ACCOUNTS_FIELD); 02810 } 02811 else 02812 { 02813 xmldoc->addTextElementToParent("useremail", "", PREF_XML_ACCOUNTS_FIELD); 02814 } 02815 02816 sprintf(permStr, 02817 "%s", 02818 StringMacros::mapToString(UsersPermissionsVector[i]).c_str()); 02819 xmldoc->addTextElementToParent( 02820 "permissions", permStr, PREF_XML_ACCOUNTS_FIELD); 02821 if(UsersSaltVector[i] == 02822 "") // only give nac if account has not been activated yet with password 02823 sprintf(permStr, "%d", int(UsersAccountCreatedTimeVector[i] & 0xffff)); 02824 else 02825 permStr[0] = '\0'; 02826 xmldoc->addTextElementToParent("nac", permStr, PREF_XML_ACCOUNTS_FIELD); 02827 } 02828 } 02829 02830 // get system layout defaults 02831 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + 02832 (std::string)SYSTEM_PREFERENCES_PREFIX + "." + 02833 (std::string)USERS_PREFERENCES_FILETYPE; 02834 if(!prefXml.loadXmlDocument(fn)) 02835 { 02836 __COUT__ << "System Preferences are defaults." << __E__; 02837 // insert defaults, no pref document found 02838 xmldoc->addTextElementToData(PREF_XML_SYSLAYOUT_FIELD, 02839 PREF_XML_SYSLAYOUT_DEFAULT); 02840 } 02841 else 02842 { 02843 __COUT__ << "Saved System Preferences found." << __E__; 02844 xmldoc->copyDataChildren(prefXml); 02845 } 02846 02847 // add permissions value 02848 sprintf(permStr, "%s", StringMacros::mapToString(permissionMap).c_str()); 02849 xmldoc->addTextElementToData(PREF_XML_PERMISSIONS_FIELD, permStr); 02850 02851 // add user with lock 02852 xmldoc->addTextElementToData(PREF_XML_USERLOCK_FIELD, usersUsernameWithLock_); 02853 // add user name 02854 xmldoc->addTextElementToData(PREF_XML_USERNAME_FIELD, getUsersUsername(uid)); 02855 } 02856 02857 //======================================================================================================================== 02858 // WebUsers::setGenericPreference 02859 // each generic preference has its own directory, and each user has their own file 02860 void WebUsers::setGenericPreference(uint64_t uid, 02861 const std::string& preferenceName, 02862 const std::string& preferenceValue) 02863 { 02864 uint64_t userIndex = searchUsersDatabaseForUserId(uid); 02865 //__COUT__ << "setGenericPreference for user: " << UsersUsernameVector[userIndex] << 02866 //__E__; 02867 02868 // force alpha-numeric with dash/underscore 02869 std::string safePreferenceName = ""; 02870 for(const auto& c : preferenceName) 02871 if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || 02872 (c >= '-' || c <= '_')) 02873 safePreferenceName += c; 02874 02875 std::string dir = (std::string)WEB_LOGIN_DB_PATH + 02876 (std::string)USERS_PREFERENCES_PATH + "generic_" + 02877 safePreferenceName + "/"; 02878 02879 // attempt to make directory (just in case) 02880 mkdir(dir.c_str(), 0755); 02881 02882 std::string fn = UsersUsernameVector[userIndex] + "_" + safePreferenceName + "." + 02883 (std::string)USERS_PREFERENCES_FILETYPE; 02884 02885 __COUT__ << "Preferences file: " << (dir + fn) << __E__; 02886 02887 FILE* fp = fopen((dir + fn).c_str(), "w"); 02888 if(fp) 02889 { 02890 fprintf(fp, "%s", preferenceValue.c_str()); 02891 fclose(fp); 02892 } 02893 else 02894 __COUT_ERR__ << "Preferences file could not be opened for writing!" << __E__; 02895 } 02896 02897 //======================================================================================================================== 02898 // WebUsers::getGenericPreference 02899 // each generic preference has its own directory, and each user has their own file 02900 // default preference is empty string. 02901 std::string WebUsers::getGenericPreference(uint64_t uid, 02902 const std::string& preferenceName, 02903 HttpXmlDocument* xmldoc) const 02904 { 02905 uint64_t userIndex = searchUsersDatabaseForUserId(uid); 02906 //__COUT__ << "getGenericPreference for user: " << UsersUsernameVector[userIndex] << 02907 //__E__; 02908 02909 // force alpha-numeric with dash/underscore 02910 std::string safePreferenceName = ""; 02911 for(const auto& c : preferenceName) 02912 if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || 02913 (c >= '-' || c <= '_')) 02914 safePreferenceName += c; 02915 02916 std::string dir = (std::string)WEB_LOGIN_DB_PATH + 02917 (std::string)USERS_PREFERENCES_PATH + "generic_" + 02918 safePreferenceName + "/"; 02919 02920 std::string fn = UsersUsernameVector[userIndex] + "_" + safePreferenceName + "." + 02921 (std::string)USERS_PREFERENCES_FILETYPE; 02922 02923 __COUT__ << "Preferences file: " << (dir + fn) << __E__; 02924 02925 // read from preferences file 02926 FILE* fp = fopen((dir + fn).c_str(), "rb"); 02927 if(fp) 02928 { 02929 fseek(fp, 0, SEEK_END); 02930 long size = ftell(fp); 02931 std::string line; 02932 line.reserve(size + 1); 02933 rewind(fp); 02934 fgets(&line[0], size + 1, fp); 02935 fclose(fp); 02936 02937 __COUT__ << "Read value " << line << __E__; 02938 if(xmldoc) 02939 xmldoc->addTextElementToData(safePreferenceName, line); 02940 return line; 02941 } 02942 else 02943 __COUT__ << "Using default value." << __E__; 02944 02945 // default preference is empty string 02946 if(xmldoc) 02947 xmldoc->addTextElementToData(safePreferenceName, ""); 02948 return ""; 02949 } 02950 02951 //======================================================================================================================== 02952 // WebUsers::changeSettingsForUser 02953 void WebUsers::changeSettingsForUser(uint64_t uid, 02954 const std::string& bgcolor, 02955 const std::string& dbcolor, 02956 const std::string& wincolor, 02957 const std::string& layout, 02958 const std::string& syslayout) 02959 { 02960 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> permissionMap = 02961 getPermissionsForUser(uid); 02962 if(isInactiveForGroup(permissionMap)) 02963 return; // not an active user 02964 02965 uint64_t userIndex = searchUsersDatabaseForUserId(uid); 02966 __COUT__ << "Changing settings for user: " << UsersUsernameVector[userIndex] << __E__; 02967 02968 std::string fn = 02969 (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + 02970 UsersUsernameVector[userIndex] + "." + (std::string)USERS_PREFERENCES_FILETYPE; 02971 02972 __COUT__ << "Preferences file: " << fn << __E__; 02973 02974 HttpXmlDocument prefXml; 02975 prefXml.addTextElementToData(PREF_XML_BGCOLOR_FIELD, bgcolor); 02976 prefXml.addTextElementToData(PREF_XML_DBCOLOR_FIELD, dbcolor); 02977 prefXml.addTextElementToData(PREF_XML_WINCOLOR_FIELD, wincolor); 02978 prefXml.addTextElementToData(PREF_XML_LAYOUT_FIELD, layout); 02979 02980 prefXml.saveXmlDocument(fn); 02981 02982 // if admin privilieges set system default layouts 02983 if(!isAdminForGroup(permissionMap)) 02984 return; // not admin 02985 02986 // set system layout defaults 02987 fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + 02988 (std::string)SYSTEM_PREFERENCES_PREFIX + "." + 02989 (std::string)USERS_PREFERENCES_FILETYPE; 02990 02991 HttpXmlDocument sysPrefXml; 02992 sysPrefXml.addTextElementToData(PREF_XML_SYSLAYOUT_FIELD, syslayout); 02993 02994 sysPrefXml.saveXmlDocument(fn); 02995 } 02996 02997 //======================================================================================================================== 02998 // WebUsers::setUserWithLock 02999 // if lock is true, set lock user specified 03000 // if lock is false, attempt to unlock user specified 03001 // return true on success 03002 bool WebUsers::setUserWithLock(uint64_t actingUid, bool lock, const std::string& username) 03003 { 03004 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> permissionMap = 03005 getPermissionsForUser(actingUid); 03006 03007 std::string actingUser = getUsersUsername(actingUid); 03008 03009 __COUTV__(actingUser); 03010 __COUT__ << "Permissions: " << StringMacros::mapToString(permissionMap) << __E__; 03011 __COUTV__(usersUsernameWithLock_); 03012 __COUTV__(lock); 03013 __COUTV__(username); 03014 __COUTV__(isUsernameActive(username)); 03015 03016 if(lock && (isUsernameActive(username) || 03017 !CareAboutCookieCodes_)) // lock and currently active 03018 { 03019 if(!CareAboutCookieCodes_ && 03020 username != DEFAULT_ADMIN_USERNAME) // enforce wiz mode only use admin account 03021 { 03022 __MCOUT_ERR__( 03023 "User '" 03024 << actingUser 03025 << "' tried to lock for a user other than admin in wiz mode. Not allowed." 03026 << __E__); 03027 return false; 03028 } 03029 else if(!isAdminForGroup(permissionMap) && 03030 actingUser != username) // enforce normal mode admin privleges 03031 { 03032 __MCOUT_ERR__("A non-admin user '" 03033 << actingUser 03034 << "' tried to lock for a user other than self. Not allowed." 03035 << __E__); 03036 return false; 03037 } 03038 usersUsernameWithLock_ = username; 03039 } 03040 else if(!lock && usersUsernameWithLock_ == username) // unlock 03041 usersUsernameWithLock_ = ""; 03042 else 03043 { 03044 if(!isUsernameActive(username)) 03045 __MCOUT_ERR__("User '" << username << "' is inactive." << __E__); 03046 __MCOUT_ERR__("Failed to lock for user '" << username << ".'" << __E__); 03047 return false; 03048 } 03049 03050 __MCOUT_INFO__("User '" << username << "' has locked out the system!" << __E__); 03051 03052 // save username with lock 03053 { 03054 std::string securityFileName = USER_WITH_LOCK_FILE; 03055 FILE* fp = fopen(securityFileName.c_str(), "w"); 03056 if(!fp) 03057 { 03058 __COUT_INFO__ << "USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE 03059 << " not found. Ignoring." << __E__; 03060 } 03061 else 03062 { 03063 fprintf(fp, "%s", usersUsernameWithLock_.c_str()); 03064 fclose(fp); 03065 } 03066 } 03067 return true; 03068 } 03069 03070 //======================================================================================================================== 03071 // WebUsers::modifyAccountSettings 03072 void WebUsers::modifyAccountSettings(uint64_t actingUid, 03073 uint8_t cmd_type, 03074 const std::string& username, 03075 const std::string& displayname, 03076 const std::string& email, 03077 const std::string& permissions) 03078 { 03079 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> permissionMap = 03080 getPermissionsForUser(actingUid); 03081 if(!isAdminForGroup(permissionMap)) 03082 { 03083 __MCOUT_ERR__("Only admins can modify user settings." << __E__); 03084 return; // not an admin 03085 } 03086 03087 uint64_t modi = searchUsersDatabaseForUsername(username); 03088 if(modi == 0) 03089 { 03090 __MCOUT_ERR__("Cannot modify first user" << __E__); 03091 return; 03092 } 03093 03094 if(username.length() < USERNAME_LENGTH || displayname.length() < DISPLAY_NAME_LENGTH) 03095 { 03096 __MCOUT_ERR__("Invalid Username or Display Name must be length " 03097 << USERNAME_LENGTH << " or " << DISPLAY_NAME_LENGTH << __E__); 03098 return; 03099 } 03100 03101 __COUT__ << "Input Permissions: " << permissions << __E__; 03102 std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> newPermissionsMap; 03103 03104 switch(cmd_type) 03105 { 03106 case MOD_TYPE_UPDATE: 03107 03108 __COUT__ << "MOD_TYPE_UPDATE " << username << " := " << permissions << __E__; 03109 03110 if(modi == NOT_FOUND_IN_DATABASE) 03111 { 03112 __COUT__ << "User not found!? Should not happen." << __E__; 03113 return; 03114 } 03115 03116 UsersDisplayNameVector[modi] = displayname; 03117 UsersUserEmailVector[modi] = email; 03118 03119 StringMacros::getMapFromString(permissions, newPermissionsMap); 03120 03121 // If account is currently inactive and re-activating, then reset fail count and 03122 // password. Note: this is the account unlock mechanism. 03123 if(isInactiveForGroup(UsersPermissionsVector[modi]) && // curently inactive 03124 !isInactiveForGroup(newPermissionsMap)) // and re-activating 03125 { 03126 UsersLoginFailureCountVector[modi] = 0; 03127 UsersSaltVector[modi] = ""; 03128 } 03129 UsersPermissionsVector[modi] = newPermissionsMap; 03130 03131 // save information about modifier 03132 { 03133 uint64_t i = searchUsersDatabaseForUserId(actingUid); 03134 if(i == NOT_FOUND_IN_DATABASE) 03135 { 03136 __COUT__ << "Master User not found!? Should not happen." << __E__; 03137 return; 03138 } 03139 UsersLastModifierUsernameVector[modi] = UsersUsernameVector[i]; 03140 UsersLastModifiedTimeVector[modi] = time(0); 03141 } 03142 break; 03143 case MOD_TYPE_ADD: 03144 __COUT__ << "MOD_TYPE_ADD " << username << " - " << displayname << __E__; 03145 createNewAccount(username, displayname, email); 03146 break; 03147 case MOD_TYPE_DELETE: 03148 __COUT__ << "MOD_TYPE_DELETE " << username << " - " << displayname << __E__; 03149 deleteAccount(username, displayname); 03150 break; 03151 default: 03152 __COUT__ << "Undefined command - do nothing " << username << __E__; 03153 } 03154 03155 saveDatabaseToFile(DB_USERS); 03156 } 03157 //======================================================================================================================== 03158 // WebUsers::getActiveUsersString 03159 // return comma separated list of active Display Names 03160 std::string WebUsers::getActiveUsersString() 03161 { 03162 std::string ret = ""; 03163 uint64_t u; 03164 bool repeat; 03165 for(uint64_t i = 0; i < ActiveSessionUserIdVector.size(); ++i) 03166 { 03167 repeat = false; 03168 // check for no repeat 03169 for(uint64_t j = 0; j < i; ++j) 03170 if(ActiveSessionUserIdVector[i] == ActiveSessionUserIdVector[j]) 03171 { 03172 repeat = true; 03173 break; 03174 } // found repeat! 03175 03176 if(!repeat && (u = searchUsersDatabaseForUserId(ActiveSessionUserIdVector[i])) != 03177 NOT_FOUND_IN_DATABASE) // if found, add displayName 03178 ret += UsersDisplayNameVector[u] + ","; 03179 } 03180 if(ret.length() > 1) 03181 ret.erase(ret.length() - 1); // get rid of last comma 03182 return ret; 03183 } 03184 //======================================================================================================================== 03185 // WebUsers::getAdminUserID 03186 // 03187 uint64_t WebUsers::getAdminUserID() 03188 { 03189 uint64_t uid = searchUsersDatabaseForUsername(DEFAULT_ADMIN_USERNAME); 03190 return uid; 03191 } 03192 03193 //======================================================================================================================== 03194 // WebUsers::loadUserWithLock 03195 // //load username with lock from file 03196 void WebUsers::loadUserWithLock() 03197 { 03198 char username[300] = ""; // assume username is less than 300 chars 03199 03200 std::string securityFileName = USER_WITH_LOCK_FILE; 03201 FILE* fp = fopen(securityFileName.c_str(), "r"); 03202 if(!fp) 03203 { 03204 __COUT_INFO__ << "USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE 03205 << " not found. Defaulting to admin lock." << __E__; 03206 03207 // default to admin lock if no file exists 03208 sprintf(username, "%s", DEFAULT_ADMIN_USERNAME.c_str()); 03209 } 03210 else 03211 { 03212 fgets(username, 300, fp); 03213 username[299] = 03214 '\0'; // likely does nothing, but make sure there is closure on string 03215 fclose(fp); 03216 } 03217 03218 // attempt to set lock 03219 __COUT__ << "Attempting to load username with lock: " << username << __E__; 03220 03221 if(strlen(username) == 0) 03222 { 03223 __COUT_INFO__ << "Loaded state for user-with-lock is unlocked." << __E__; 03224 return; 03225 } 03226 03227 uint64_t i = searchUsersDatabaseForUsername(username); 03228 if(i == NOT_FOUND_IN_DATABASE) 03229 { 03230 __COUT_INFO__ << "username " << username << " not found in database. Ignoring." 03231 << __E__; 03232 return; 03233 } 03234 __COUT__ << "Setting lock" << __E__; 03235 setUserWithLock(UsersUserIdVector[i], true, username); 03236 } 03237 03238 //======================================================================================================================== 03239 // WebUsers::getSecurity 03240 // 03241 std::string WebUsers::getSecurity() { return securityType_; } 03242 //======================================================================================================================== 03243 // WebUsers::loadSecuritySelection 03244 // 03245 void WebUsers::loadSecuritySelection() 03246 { 03247 std::string securityFileName = SECURITY_FILE_NAME; 03248 FILE* fp = fopen(securityFileName.c_str(), "r"); 03249 char line[100] = ""; 03250 if(fp) 03251 fgets(line, 100, fp); 03252 unsigned int i = 0; 03253 03254 // find first character that is not alphabetic 03255 while(i < strlen(line) && line[i] >= 'A' && line[i] <= 'z') 03256 ++i; 03257 line[i] = '\0'; // end string at first illegal character 03258 03259 if(strcmp(line, SECURITY_TYPE_NONE.c_str()) == 0 || 03260 strcmp(line, SECURITY_TYPE_DIGEST_ACCESS.c_str()) == 0) 03261 securityType_ = line; 03262 else 03263 securityType_ = SECURITY_TYPE_NONE; // default to NO SECURITY 03264 03265 __COUT__ << "The current security type is " << securityType_ << __E__; 03266 03267 if(fp) 03268 fclose(fp); 03269 03270 if(securityType_ == SECURITY_TYPE_NONE) 03271 CareAboutCookieCodes_ = false; 03272 else 03273 CareAboutCookieCodes_ = true; 03274 03275 __COUT__ << "CareAboutCookieCodes_: " << CareAboutCookieCodes_ << __E__; 03276 } 03277 03278 //======================================================================================================================== 03279 void WebUsers::NACDisplayThread(const std::string& nac, const std::string& user) 03280 { 03281 INIT_MF("WebUsers_NAC"); 03283 // thread notifying the user about the admin new account code 03284 // notify for 10 seconds (e.g.) 03285 03286 // child thread 03287 int i = 0; 03288 for(; i < 5; ++i) 03289 { 03290 std::this_thread::sleep_for(std::chrono::seconds(2)); 03291 __COUT__ 03292 << "\n******************************************************************** " 03293 << __E__; 03294 __COUT__ 03295 << "\n******************************************************************** " 03296 << __E__; 03297 __COUT__ << "\n\nNew account code = " << nac << " for user: " << user << "\n" 03298 << __E__; 03299 __COUT__ 03300 << "\n******************************************************************** " 03301 << __E__; 03302 __COUT__ 03303 << "\n******************************************************************** " 03304 << __E__; 03305 } 03306 } 03307 03308 //======================================================================================================================== 03309 void WebUsers::deleteUserData() 03310 { 03311 // delete Login data 03312 std::system( 03313 ("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH + "/*").c_str()); 03314 std::system( 03315 ("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH + "/*").c_str()); 03316 std::system( 03317 ("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH + "/*") 03318 .c_str()); 03319 std::system( 03320 ("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH + "/*") 03321 .c_str()); 03322 std::system(("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH).c_str()); 03323 03324 std::string serviceDataPath = getenv("SERVICE_DATA_PATH"); 03325 // delete macro maker folders 03326 std::system(("rm -rf " + std::string(serviceDataPath) + "/MacroData/").c_str()); 03327 std::system(("rm -rf " + std::string(serviceDataPath) + "/MacroHistory/").c_str()); 03328 std::system(("rm -rf " + std::string(serviceDataPath) + "/MacroExport/").c_str()); 03329 03330 // delete console folders 03331 std::system( 03332 ("rm -rf " + std::string(serviceDataPath) + "/ConsolePreferences/").c_str()); 03333 03334 // delete code editor folders 03335 std::system(("rm -rf " + std::string(serviceDataPath) + "/CodeEditorData/").c_str()); 03336 03337 // delete wizard folders 03338 std::system(("rm -rf " + std::string(serviceDataPath) + "/OtsWizardData/").c_str()); 03339 03340 // delete progress bar folders 03341 std::system(("rm -rf " + std::string(serviceDataPath) + "/ProgressBarData/").c_str()); 03342 03343 // delete The Supervisor run folders 03344 std::system(("rm -rf " + std::string(serviceDataPath) + "/RunNumber/").c_str()); 03345 std::system(("rm -rf " + std::string(serviceDataPath) + "/RunControlData/").c_str()); 03346 03347 // delete Visualizer folders 03348 std::system(("rm -rf " + std::string(serviceDataPath) + "/VisualizerData/").c_str()); 03349 03350 // DO NOT delete active groups file (this messes with people's configuration world, 03351 // which is not expected when "resetting user info") std::system(("rm -rf " + 03352 // std::string(serviceDataPath) + "/ActiveTableGroups.cfg").c_str()); 03353 03354 // delete Logbook folders 03355 std::system(("rm -rf " + std::string(getenv("LOGBOOK_DATA_PATH")) + "/").c_str()); 03356 03357 std::cout << __COUT_HDR_FL__ 03358 << "$$$$$$$$$$$$$$ Successfully deleted ALL service user data $$$$$$$$$$$$" 03359 << __E__; 03360 }