otsdaq  v2_00_00
WebUsers.cc
1 #include "otsdaq-core/WebUsersUtilities/WebUsers.h"
2 #include "otsdaq-core/MessageFacility/MessageFacility.h"
3 #include "otsdaq-core/Macros/CoutHeaderMacros.h"
4 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h"
5 
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <iostream>
9 #include <openssl/sha.h>
10 #include <cstdlib>
11 #include <cstdio>
12 #include <cassert>
13 
14 #include <thread> // std::this_thread::sleep_for
15 #include <chrono> // std::chrono::seconds
16 #include "WebUsers.h"
17 
18 using namespace ots;
19 
20 
21 
22 #define WEB_LOGIN_BKUP_DB_PATH "bkup/"
23 
24 #define SECURITY_FILE_NAME std::string(getenv("SERVICE_DATA_PATH")) + "/OtsWizardData/security.dat"
25 
26 #define USERS_ACTIVE_SESSIONS_FILE USERS_DB_PATH + "/activeSessions.sv"
27 
28 #define HASHES_DB_FILE HASHES_DB_PATH + "/hashes.xml"
29 #define USERS_DB_FILE USERS_DB_PATH + "/users.xml"
30 #define USERS_GLOBAL_HISTORY_FILE "__global"
31 #define USERS_LOGIN_HISTORY_FILETYPE "hist"
32 #define USERS_PREFERENCES_FILETYPE "pref"
33 #define SYSTEM_PREFERENCES_PREFIX "system.preset"
34 #define USER_WITH_LOCK_FILE WEB_LOGIN_DB_PATH + "/user_with_lock.dat"
35 
36 #define HASHES_DB_GLOBAL_STRING "hashData"
37 #define HASHES_DB_ENTRY_STRING "hashEntry"
38 #define USERS_DB_GLOBAL_STRING "userData"
39 #define USERS_DB_ENTRY_STRING "userEntry"
40 #define USERS_DB_NEXT_UID_STRING "nextUserId"
41 
42 //defines for user preferences
43 #define PREF_XML_BGCOLOR_FIELD "pref_bgcolor" // -background color
44 #define PREF_XML_DBCOLOR_FIELD "pref_dbcolor" // -dashboard color
45 #define PREF_XML_WINCOLOR_FIELD "pref_wincolor" // -window color
46 #define PREF_XML_LAYOUT_FIELD "pref_layout" // -3 defaults window layouts(and current)
47 #define PREF_XML_SYSLAYOUT_FIELD "pref_syslayout" // -2 defaults window layouts
48 #define PREF_XML_PERMISSIONS_FIELD "desktop_user_permissions" // 0-255 permissions valud (255 is admin super user)
49 #define PREF_XML_USERLOCK_FIELD "username_with_lock" // user with lock (to lockout others)
50 #define PREF_XML_USERNAME_FIELD "pref_username" // user with lock (to lockout others)
51 
52 #define PREF_XML_BGCOLOR_DEFAULT "rgb(0,76,151)" // -background color
53 #define PREF_XML_DBCOLOR_DEFAULT "rgb(0,40,85)" // -dashboard color
54 #define PREF_XML_WINCOLOR_DEFAULT "rgba(196,229,255,0.9)" // -window color
55 #define PREF_XML_LAYOUT_DEFAULT "0;0;0;0" // 3 default window layouts(and current)
56 #define PREF_XML_SYSLAYOUT_DEFAULT "0;0" // 2 system default window layouts
57 
58 #define PREF_XML_ACCOUNTS_FIELD "users_accounts" // user accounts field for super users
59 #define PREF_XML_LOGIN_HISTORY_FIELD "login_entry" // login history field for user login history data
60 
61 const std::string WebUsers::DEFAULT_ADMIN_USERNAME = "admin";
62 const std::string WebUsers::DEFAULT_ADMIN_DISPLAY_NAME = "Administrator";
63 const std::string WebUsers::DEFAULT_ADMIN_EMAIL = "root@otsdaq.fnal.gov";
64 const std::string WebUsers::DEFAULT_ITERATOR_USERNAME = "iterator";
65 const std::string WebUsers::DEFAULT_STATECHANGER_USERNAME = "statechanger";
66 
67 const std::string WebUsers::REQ_NO_LOGIN_RESPONSE = "NoLogin";
68 const std::string WebUsers::REQ_NO_PERMISSION_RESPONSE = "NoPermission";
69 const std::string WebUsers::REQ_USER_LOCKOUT_RESPONSE = "UserLockout";
70 
71 const std::string WebUsers::SECURITY_TYPE_NONE = "NoSecurity";
72 const std::string WebUsers::SECURITY_TYPE_DIGEST_ACCESS = "DigestAccessAuthentication";
73 
74 #undef __MF_SUBJECT__
75 #define __MF_SUBJECT__ "WebUsers"
76 
77 
78 WebUsers::WebUsers()
79 {
80  //deleteUserData(); //for debugging to reset user data
81 
82  usersNextUserId_ = 0; //first UID, default to 0 but get from database
83  usersUsernameWithLock_ = ""; //init to no user with lock
84 
85  //define fields
86  HashesDatabaseEntryFields.push_back("hash");
87  HashesDatabaseEntryFields.push_back("lastAccessTime"); //last login month resolution, blurred by 1/2 month
88 
89  UsersDatabaseEntryFields.push_back("username");
90  UsersDatabaseEntryFields.push_back("displayName");
91  UsersDatabaseEntryFields.push_back("salt");
92  UsersDatabaseEntryFields.push_back("uid");
93  UsersDatabaseEntryFields.push_back("permissions");
94  UsersDatabaseEntryFields.push_back("lastLoginAttemptTime");
95  UsersDatabaseEntryFields.push_back("accountCreatedTime");
96  UsersDatabaseEntryFields.push_back("loginFailureCount");
97  UsersDatabaseEntryFields.push_back("lastModifiedTime");
98  UsersDatabaseEntryFields.push_back("lastModifierUsername");
99  UsersDatabaseEntryFields.push_back("useremail");
100 
101  //attempt to make directory structure (just in case)
102  mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755);
103  mkdir(((std::string)WEB_LOGIN_DB_PATH + "bkup/" + USERS_DB_PATH).c_str(), 0755);
104  mkdir(((std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH).c_str(), 0755);
105  mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755);
106  mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH).c_str(), 0755);
107  mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH).c_str(), 0755);
108 
109 
110  if (!loadDatabases())
111  __COUT__ << "FATAL USER DATABASE ERROR - failed to load!!!" << std::endl;
112 
113  loadSecuritySelection();
114 
115 
116  //print out admin new user code for ease of use
117  uint64_t i;
118  std::string user = DEFAULT_ADMIN_USERNAME;
119  if ((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
120  {
121  __SS__ << "user: " << user << " is not found" << std::endl;
122  __COUT_ERR__ << ss.str();
123  throw std::runtime_error(ss.str());
124  exit(0); //THIS CAN NOT HAPPEN?! There must be an admin user
125  }
126  else if (UsersSaltVector[i] == "" && //admin password not setup, so print out NAC to help out
127  securityType_ == SECURITY_TYPE_DIGEST_ACCESS)
128  {
129  char charTimeStr[10];
130  sprintf(charTimeStr, "%d", int(UsersAccountCreatedTimeVector[i] & 0xffff));
131  std::string tmpTimeStr = charTimeStr;
132 
134  //start thread for notifying the user about the admin new account code
135  // notify for 10 seconds (e.g.)
136  std::thread([](std::string nac, std::string user) { WebUsers::NACDisplayThread(nac, user); },
137  tmpTimeStr, user).detach();
138 
139  }
140 
141 
142  //attempt to load persistent user sessions
143  loadActiveSessions();
144 
145  //default user with lock to admin and/or try to load last user with lock
146  //Note: this must happen after getting persistent active sessions
147  loadUserWithLock();
148 
149  srand(time(0)); //seed random for hash salt generation
150 
151  __COUT__ << "Done with Web Users initialization!" << std::endl;
152 
153  // FIXME -- can delete this commented section eventually
154  // this is for debugging the registering and login functionality
155  //
156  // // deleteUserData();
157 
158  //std::string uuid = "0";
159  //std::string sid = createNewLoginSession(uuid);
160  //exit(0);
161 
162  // std::string newAccountCode = "60546";
163  // std::string pw = "testbeam";
164  //
165  // __COUT__ << "user: " << user << std::endl << std::endl;
166  //
167  //
168  // if(1) //test salt functionality
169  // {
170  // std::string salt = ""; //don't want to modify saved salt
171  // std::string hash1 = sha512(user,pw,salt);
172  // std::string hash2 = sha512(user,pw,salt);
173  // __COUT__ << hash1 << std::endl;
174  // __COUT__ << hash2 << std::endl;
175  //
176  // __COUT__ << "String comparison result: " << strcmp(hash1.c_str(),hash2.c_str()) << std::endl;
177  // }
178  //
179  // if(0) //login attempt
180  // {
181  // //search users for username
182  // if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
183  // {
184  // __COUT__ << "user: " << user << " is not found" << std::endl;
185  // return;// NOT_FOUND_IN_DATABASE;
186  // }
187  //
188  // __COUT__ << "user: " << user << std::endl << std::endl;
189  //
190  // std::string salt = UsersSaltVector[i]; //don't want to modify saved salt
191  // __COUT__ << salt<< " " << i << std::endl;
192  // if(searchHashesDatabaseForHash(sha512(user,pw,salt)) == NOT_FOUND_IN_DATABASE)
193  // {
194  // __COUT__ << "not found?" << std::endl;
195  // ++UsersLoginFailureCountVector[i];
196  // if(UsersLoginFailureCountVector[i] >= USERS_MAX_LOGIN_FAILURES)
197  // UsersPermissionsVector[i] = 0; //Lock account
198  //
199  // __COUT__ << "\tUser/pw for user: " << user << " was not correct Failed Attempt #" << (int)(UsersLoginFailureCountVector[i]) << std::endl;
200  // if(!UsersPermissionsVector[i])
201  // __COUT__ << "Account is locked!" << std::endl;
202  //
203  // saveDatabaseToFile(DB_USERS); //users db modified, so save
204  // return;//NOT_FOUND_IN_DATABASE;
205  // }
206  // }
207  //
208  //
209  // if(0) //first login
210  // {
211  // //search users for username
212  // if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
213  // {
214  // __COUT__ << "user: " << user << " is not found" << std::endl;
215  // return;// NOT_FOUND_IN_DATABASE;
216  // }
217  // __COUT__ << "user: " << user << std::endl << std::endl;
218  //
219  // UsersLastLoginAttemptVector[i] = time(0);
220  // if(!UsersPermissionsVector[i])
221  // {
222  // __COUT__ << "user: " << user << " account INACTIVE (could be due to failed logins)" << std::endl;
223  // return;// NOT_FOUND_IN_DATABASE;
224  // }
225  // __COUT__ << "user: " << user << std::endl << std::endl;
226  //
227  // if(UsersSaltVector[i] == "") //first login
228  // {
229  // __COUT__ << "First login attempt for user: " << user << std::endl;
230  //
231  // char charTimeStr[10];
232  // sprintf(charTimeStr,"%d",int(UsersAccountCreatedTimeVector[i] & 0xffff));
233  // std::string tmpTimeStr = charTimeStr;
234  // if(newAccountCode != tmpTimeStr)
235  // {
236  // __COUT__ << "New account code did not match: " << tmpTimeStr << " != " << newAccountCode << std::endl;
237  // saveDatabaseToFile(DB_USERS); //users db modified, so save
238  // return;// NOT_FOUND_IN_DATABASE;
239  // }
240  //
241  // __COUT__ << "First login attempt for user: " << user << std::endl;
242  // //initial user account setup
243  //
244  // //add until no collision (should 'never' be a collision)
245  // while(!addToHashesDatabase(sha512(user,pw,UsersSaltVector[i]))) //sha256 modifies UsersSaltVector[i]
246  // {
247  // //this should never happen, it would mean the user+pw+saltcontext was the same
248  // // but if it were to happen, try again...
249  // UsersSaltVector[i] = "";
250  // }
251  //
252  //
253  // __COUT__ << "\tHash added: " << HashesVector[0] << std::endl;
254  // }
255  //
256  // __COUT__ << "Login successful for: " << user << std::endl;
257  //
258  // UsersLoginFailureCountVector[i] = 0;
259  //
260  // saveDatabaseToFile(DB_USERS); //users db modified, so save
261  // }
262 
263 
264  // //starting
265  //
266  // SHA512_CTX sha512_context;
267  // char hexStr[3];
268  // SHA512_Init(&sha512_context);
269  //
270  //
271  // std::string password = "testbeam";
272  // std::string salt = "";
273  //
274  // {
275  // for(unsigned int i=0;i<sizeof(SHA512_CTX);++i)
276  // {
277  // intToHexStr((((uint8_t *)(&sha512_context))[i] + (i<32)?rand():0),hexStr);
278  // salt.append(hexStr);
279  // }
280  // __COUT__ << salt << std::endl;
281  //
282  // std::string strToHash = salt + user + password;
283  //
284  // __COUT__ << salt << std::endl;
285  // unsigned char hash[SHA512_DIGEST_LENGTH];
286  // __COUT__ << salt << std::endl;
287  // char retHash[SHA512_DIGEST_LENGTH*2+1];
288  // __COUT__ << strToHash.length() << " " << strToHash << std::endl;
289  // SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length());
290  // __COUT__ << salt << std::endl;
291  // SHA512_Final(hash, &sha512_context);
292  //
293  // __COUT__ << salt << std::endl;
294  // int i = 0;
295  // for(i = 0; i < SHA512_DIGEST_LENGTH; i++)
296  // sprintf(retHash + (i * 2), "%02x", hash[i]);
297  //
298  // __COUT__ << salt << std::endl;
299  // retHash[SHA512_DIGEST_LENGTH*2] = '\0';
300  //
301  // __COUT__ << "retHash: " << retHash << std::endl;
302  // }
303  //
304  // //check it
305  //
306  // {
307  // __COUT__ << salt << std::endl;
308  //
309  // for(unsigned int i=0;i<sizeof(SHA512_CTX);++i)
310  // ((uint8_t *)(&sha512_context))[i] = hexByteStrToInt(&(salt.c_str()[i*2]));
311  //
312  //
313  // std::string strToHash = salt + user + password;
314  //
315  // __COUT__ << salt << std::endl;
316  // unsigned char hash[SHA512_DIGEST_LENGTH];
317  // __COUT__ << salt << std::endl;
318  // char retHash[SHA512_DIGEST_LENGTH*2+1];
319  // __COUT__ << strToHash.length() << " " << strToHash << std::endl;
320  // SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length());
321  // __COUT__ << salt << std::endl;
322  // SHA512_Final(hash, &sha512_context);
323  //
324  // __COUT__ << salt << std::endl;
325  // int i = 0;
326  // for(i = 0; i < SHA512_DIGEST_LENGTH; i++)
327  // sprintf(retHash + (i * 2), "%02x", hash[i]);
328  //
329  // __COUT__ << salt << std::endl;
330  // retHash[SHA512_DIGEST_LENGTH*2] = '\0';
331  //
332  // __COUT__ << "retHash: " << retHash << std::endl;
333  // }
334 }
335 
336 //========================================================================================================================
337 //saveActiveSessions
338 // save active sessions structure so that they can survive restart
339 void WebUsers::saveActiveSessions()
340 {
341  std::string fn;
342 
343  fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE;
344  __COUT__ << fn << std::endl;
345 
346  FILE *fp = fopen(fn.c_str(), "w");
347  if (!fp)
348  {
349  __COUT_ERR__ << "Error! Persistent active sessions could not be saved." << std::endl;
350  return;
351  }
352 
353  int version = 0;
354  fprintf(fp, "%d\n", version);
355  for (unsigned int i = 0; i < ActiveSessionCookieCodeVector.size(); ++i)
356  {
357  // __COUT__ << "SAVE " << ActiveSessionCookieCodeVector[i] << std::endl;
358  // __COUT__ << "SAVE " << ActiveSessionIpVector[i] << std::endl;
359  // __COUT__ << "SAVE " << ActiveSessionUserIdVector[i] << std::endl;
360  // __COUT__ << "SAVE " << ActiveSessionIndex[i] << std::endl;
361  // __COUT__ << "SAVE " << ActiveSessionStartTimeVector[i] << std::endl;
362 
363  fprintf(fp, "%s\n", ActiveSessionCookieCodeVector[i].c_str());
364  fprintf(fp, "%s\n", ActiveSessionIpVector[i].c_str());
365  fprintf(fp, "%lu\n", ActiveSessionUserIdVector[i]);
366  fprintf(fp, "%lu\n", ActiveSessionIndex[i]);
367  fprintf(fp, "%ld\n", ActiveSessionStartTimeVector[i]);
368  }
369 
370  __COUT__ << "ActiveSessionCookieCodeVector saved with size " <<
371  ActiveSessionCookieCodeVector.size() << std::endl;
372 
373  fclose(fp);
374 }
375 
376 //====================================================================================================================
377 //loadActiveSessions
378 // load active sessions structure so that they can survive restart
379 void WebUsers::loadActiveSessions()
380 {
381  std::string fn;
382 
383  fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE;
384  __COUT__ << fn << std::endl;
385  FILE *fp = fopen(fn.c_str(), "r");
386  if (!fp)
387  {
388  __COUT_ERR__ << "Error! Persistent active sessions could not be saved." << std::endl;
389  return;
390  }
391 
392  int version;
393 
394  const int LINELEN = 1000;
395  char line[LINELEN];
396  fgets(line, LINELEN, fp);
397  sscanf(line, "%d", &version);
398  if (version == 0)
399  {
400  __COUT__ << "Extracting active sessions..." << std::endl;
401 
402  }
403  unsigned int i = 0;
404  while (fgets(line, LINELEN, fp))
405  {
406  if (strlen(line)) line[strlen(line) - 1] = '\0'; //remove new line
407  if (strlen(line) != COOKIE_CODE_LENGTH)
408  {
409  __COUT__ << "Illegal cookie code found: " << line << std::endl;
410 
411  fclose(fp);
412  return;
413  }
414  ActiveSessionCookieCodeVector.push_back(line);
415 
416  fgets(line, LINELEN, fp);
417  if (strlen(line)) line[strlen(line) - 1] = '\0'; //remove new line
418  ActiveSessionIpVector.push_back(line);
419 
420  fgets(line, LINELEN, fp);
421  ActiveSessionUserIdVector.push_back(uint64_t());
422  sscanf(line, "%lu", &(ActiveSessionUserIdVector[ActiveSessionUserIdVector.size() - 1]));
423 
424  fgets(line, LINELEN, fp);
425  ActiveSessionIndex.push_back(uint64_t());
426  sscanf(line, "%lu", &(ActiveSessionIndex[ActiveSessionIndex.size() - 1]));
427 
428  fgets(line, LINELEN, fp);
429  ActiveSessionStartTimeVector.push_back(time_t());
430  sscanf(line, "%ld", &(ActiveSessionStartTimeVector[ActiveSessionStartTimeVector.size() - 1]));
431 
432 
433  // __COUT__ << "LOAD " << ActiveSessionCookieCodeVector[i] << std::endl;
434  // __COUT__ << "LOAD " << ActiveSessionIpVector[i] << std::endl;
435  // __COUT__ << "LOAD " << ActiveSessionUserIdVector[i] << std::endl;
436  // __COUT__ << "LOAD " << ActiveSessionIndex[i] << std::endl;
437  // __COUT__ << "LOAD " << ActiveSessionStartTimeVector[i] << std::endl;
438  ++i;
439  }
440 
441  __COUT__ << "ActiveSessionCookieCodeVector loaded with size " <<
442  ActiveSessionCookieCodeVector.size() << std::endl;
443 
444 
445  fclose(fp);
446  //clear file after loading
447  fp = fopen(fn.c_str(), "w");
448  if (fp)fclose(fp);
449 
450 }
451 
452 //========================================================================================================================
453 //loadDatabaseFromFile
454 // load Hashes and Users from file
455 // create database if non-existent
456 bool WebUsers::loadDatabases()
457 {
458  std::string fn;
459  FILE *fp;
460  const unsigned int LINE_LEN = 1000;
461  char line[LINE_LEN];
462  unsigned int i, si, c, len, f;
463  uint64_t tmpInt64;
464 
465  //hashes
466  // File Organization:
467  // <hashData>
468  // <hashEntry><hash>hash0</hash><lastAccessTime>lastAccessTime0</lastAccessTime></hashEntry>
469  // <hashEntry><hash>hash1</hash><lastAccessTime>lastAccessTime1</lastAccessTime></hashEntry>
470  // ..
471  // </hashData>
472 
473  fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_FILE;
474  __COUT__ << fn << std::endl;
475  fp = fopen(fn.c_str(), "r");
476  if (!fp) //need to create file
477  {
478  mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str(), 0755);
479  __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str() << std::endl;
480  fp = fopen(fn.c_str(), "w");
481  if (!fp) return false;
482  __COUT__ << "Hashes database created: " << fn << std::endl;
483 
484  saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_OPEN);
485  saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE);
486  fclose(fp);
487  }
488  else //load structures if hashes exists
489  {
490  //for every HASHES_DB_ENTRY_STRING, extract to local vector
491  //trusting file construction, assuming fields based >'s and <'s
492  while (fgets(line, LINE_LEN, fp))
493  {
494  if (strlen(line) < SHA512_DIGEST_LENGTH) continue;
495 
496  c = 0;
497  len = strlen(line); //save len, strlen will change because of \0 manipulations
498  for (i = 0; i < len; ++i)
499  if (line[i] == '>')
500  {
501  ++c; //count >'s
502  if (c != 2 && c != 4) continue; //only proceed for field data
503 
504  si = ++i; //save start index
505  while (i < len && line[i] != '<') ++i;
506  if (i == len)
507  break;
508  line[i] = '\0'; //close std::string
509 
510  //__COUT__ << "Found Hashes field " << c/2 << " " << &line[si] << std::endl;
511 
512  f = c / 2 - 1;
513  if (f == 0) //hash
514  HashesVector.push_back(&line[si]);
515  else if (f == 1) //lastAccessTime
516  {
517  sscanf(&line[si], "%lu", &tmpInt64);
518  HashesAccessTimeVector.push_back(tmpInt64);
519  }
520  }
521  }
522  __COUT__ << HashesAccessTimeVector.size() << " Hashes found." << std::endl;
523 
524  fclose(fp);
525  }
526 
527  //users
528  // File Organization:
529  // <userData>
530  // <nextUserId>...</nextUserId>
531  // <userEntry>...</userEntry>
532  // <userEntry>...</userEntry>
533  // ..
534  // </userData>
535 
536  fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_FILE;
537  fp = fopen(fn.c_str(), "r");
538  if (!fp) //need to create file
539  {
540  mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str(), 0755);
541  __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str() << std::endl;
542  fp = fopen(fn.c_str(), "w");
543  if (!fp) return false;
544  __COUT__ << "Users database created: " << fn << std::endl;
545 
546  saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_OPEN);
547  char nidStr[100];
548  sprintf(nidStr, "%lu", usersNextUserId_);
549  saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, nidStr, DB_SAVE_OPEN_AND_CLOSE);
550  saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE);
551  fclose(fp);
552 
553  createNewAccount(DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_DISPLAY_NAME, DEFAULT_ADMIN_EMAIL); //account 0 is always admin
554  }
555  else //extract next user id and user entries if users exists
556  {
557  //for every USERS_DB_ENTRY_STRING, extract to local vector
558  //trusting file construction, assuming fields based >'s and <'s
559 
560  char salt[] = "nextUserId";
561  while (fgets(line, LINE_LEN, fp))
562  {
563  if (strlen(line) < strlen(salt) * 2) continue; //line size should indicate xml tags on same line
564 
565  for (i = 0; i < strlen(salt); ++i) //check for opening tag
566  if (line[i + 1] != salt[i]) break;
567 
568  if (i == strlen(salt)) //all salt matched, so found correct line! increment to get line index
569  {
570  i += 2;
571  si = i;
572  while (i < LINE_LEN && line[i] != '\0' && line[i] != '<') ++i; //find '<'
573  line[i] = '\0'; //close std::string
574  sscanf(&line[si], "%lu", &usersNextUserId_);
575  break; //done with next uid
576  }
577  }
578 
579  __COUT__ << "Found Users database next user Id: " << usersNextUserId_ << std::endl;
580 
581  //trusting file construction, assuming fields based >'s and <'s and each entry on one line
582  while (fgets(line, LINE_LEN, fp))
583  {
584  if (strlen(line) < 30) continue; //rule out header tags
585 
586  c = 0;
587  len = strlen(line); //save len, strlen will change because of \0 manipulations
588  if (len >= LINE_LEN)
589  {
590  __COUT__ << "Line buffer too small: " << len << std::endl;
591  break;
592  }
593 
594  //get fields from line
595  f = 0;
596  for (i = 0; i < len; ++i)
597  if (line[i] == '>')
598  {
599  ++c; //count >'s
600  if (c == 0 || c % 2 == 1) continue; //only proceed for field data (even
601 
602  si = ++i; //save start index
603  while (i < len && line[i] != '<') ++i;
604  if (i == len)
605  break;
606  line[i] = '\0'; //close std::string
607  f = c / 2 - 1;
608 
609  //__COUT__ << "Found Users field " << f << " " << &line[si] << std::endl;
610 
611  if (f == 0) //username
612  UsersUsernameVector.push_back(&line[si]);
613  else if (f == 1) //displayName
614  UsersDisplayNameVector.push_back(&line[si]);
615  else if (f == 2) //salt
616  UsersSaltVector.push_back(&line[si]);
617  else if (f == 3) //uid
618  {
619  sscanf(&line[si], "%lu", &tmpInt64);
620  UsersUserIdVector.push_back(tmpInt64);
621  }
622  else if (f == 4) //permissions
623  {
624  sscanf(&line[si], "%lu", &tmpInt64);
625  UsersPermissionsVector.push_back(tmpInt64);
626  }
627  else if (f == 5) //lastLoginAttemptTime
628  {
629  sscanf(&line[si], "%lu", &tmpInt64);
630  UsersLastLoginAttemptVector.push_back(tmpInt64);
631  }
632  else if (f == 6) //accountCreatedTime
633  {
634  sscanf(&line[si], "%lu", &tmpInt64);
635  UsersAccountCreatedTimeVector.push_back(tmpInt64);
636  }
637  else if (f == 7) //loginFailureCount
638  {
639  sscanf(&line[si], "%lu", &tmpInt64);
640  UsersLoginFailureCountVector.push_back(tmpInt64);
641  }
642  else if (f == 8) //lastModifierTime
643  {
644  sscanf(&line[si], "%lu", &tmpInt64);
645  UsersLastModifiedTimeVector.push_back(tmpInt64);
646  }
647  else if (f == 9) //lastModifierUsername
648  UsersLastModifierUsernameVector.push_back(&line[si]);
649  else if (f == 10) // user email
650  UsersUserEmailVector.push_back(&line[si]);
651  }
652 
653  //If user found in line, check if all fields found, else auto fill
654  //update in DB fields could cause inconsistencies!
655  if (f && f != UsersDatabaseEntryFields.size() - 1)
656  {
657  if (f != 7 && f != 9) //original database was size 8, so is ok to not match
658  {
659  __COUT__ << "FATAL ERROR - invalid database found with field number " << f << std::endl;
660  fclose(fp);
661  return false;
662  }
663 
664  if (f == 7)
665  {
666  //fix here if database size was 8
667  __COUT__ << "Update database to current version - adding fields: " <<
668  (UsersDatabaseEntryFields.size() - 1 - f) << std::endl;
669  //add db updates -- THIS IS FOR VERSION WITH UsersDatabaseEntryFields.size() == 10 !!
670  UsersLastModifiedTimeVector.push_back(0);
671  UsersLastModifierUsernameVector.push_back("");
672  }
673  else
674  {
675  UsersUserEmailVector.push_back("");
676  }
677  }
678  }
679  fclose(fp);
680  }
681 
682  __COUT__ << UsersLastModifiedTimeVector.size() << " Users found." << std::endl;
683  for (size_t ii = 0; ii < UsersLastModifiedTimeVector.size(); ++ii)
684  {
685  __COUT__ << "User " << UsersUserIdVector[ii] << ": Name: " << UsersUsernameVector[ii] << "\t\tDisplay Name: " << UsersDisplayNameVector[ii] << "\t\tEmail: " << UsersUserEmailVector[ii] << "\t\tPermissions: " << std::to_string(UsersPermissionsVector[ii]) << std::endl;
686  }
687  return true;
688 }
689 
690 //========================================================================================================================
691 //saveToDatabase
692 void WebUsers::saveToDatabase(FILE * fp, std::string field, std::string value, uint8_t type, bool addNewLine)
693 {
694  if (!fp) return;
695 
696  std::string newLine = addNewLine ? "\n" : "";
697 
698  if (type == DB_SAVE_OPEN_AND_CLOSE)
699  fprintf(fp, "<%s>%s</%s>%s", field.c_str(), value.c_str(), field.c_str(), newLine.c_str());
700  else if (type == DB_SAVE_OPEN)
701  fprintf(fp, "<%s>%s%s", field.c_str(), value.c_str(), newLine.c_str());
702  else if (type == DB_SAVE_CLOSE)
703  fprintf(fp, "</%s>%s", field.c_str(), newLine.c_str());
704 }
705 
706 //========================================================================================================================
707 //saveDatabaseToFile
708 // returns true if saved database successfully
709 // db: DB_USERS or DB_HASHES
710 // else false
711 
712 
713 
714 bool WebUsers::saveDatabaseToFile(uint8_t db)
715 {
716  __COUT__ << "Save Database: " << (int)db << std::endl;
717 
718  std::string fn = (std::string)WEB_LOGIN_DB_PATH +
719  ((db == DB_USERS) ? (std::string)USERS_DB_FILE : (std::string)HASHES_DB_FILE);
720 
721  __COUT__ << "Save Database Filename: " << fn << std::endl;
722 
723  //backup file organized by day
724  if (0)
725  {
726  char dayAppend[20];
727  sprintf(dayAppend, ".%lu.bkup", time(0) / (3600 * 24));
728  std::string bkup_fn = (std::string)WEB_LOGIN_DB_PATH +
729  (std::string)WEB_LOGIN_BKUP_DB_PATH +
730  ((db == DB_USERS) ? (std::string)USERS_DB_FILE : (std::string)HASHES_DB_FILE) +
731  (std::string)dayAppend;
732 
733  __COUT__ << "Backup file: " << bkup_fn << std::endl;
734 
735  std::string shell_command = "mv " + fn + " " + bkup_fn;
736  system(shell_command.c_str());
737  }
738 
739  FILE *fp = fopen(fn.c_str(), "wb"); //write in binary mode
740  if (!fp) return false;
741 
742  char fldStr[100];
743 
744  if (db == DB_USERS) //USERS
745  {
746  saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_OPEN);
747 
748  sprintf(fldStr, "%lu", usersNextUserId_);
749  saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, fldStr, DB_SAVE_OPEN_AND_CLOSE);
750 
751  __COUT__ << "Saving " << UsersUsernameVector.size() << " Users." << std::endl;
752 
753  for (uint64_t i = 0; i < UsersUsernameVector.size(); ++i)
754  {
755  //__COUT__ << "Saving User: " << UsersUsernameVector[i] << std::endl;
756 
757  saveToDatabase(fp, USERS_DB_ENTRY_STRING, "", DB_SAVE_OPEN, false);
758 
759  for (unsigned int f = 0; f < UsersDatabaseEntryFields.size(); ++f)
760  {
761  //__COUT__ << "Saving Field: " << f << std::endl;
762  if (f == 0) //username
763  saveToDatabase(fp, UsersDatabaseEntryFields[f], UsersUsernameVector[i], DB_SAVE_OPEN_AND_CLOSE, false);
764  else if (f == 1) //displayName
765  saveToDatabase(fp, UsersDatabaseEntryFields[f], UsersDisplayNameVector[i], DB_SAVE_OPEN_AND_CLOSE, false);
766  else if (f == 2) //salt
767  saveToDatabase(fp, UsersDatabaseEntryFields[f], UsersSaltVector[i], DB_SAVE_OPEN_AND_CLOSE, false);
768  else if (f == 3) //uid
769  {
770  sprintf(fldStr, "%lu", UsersUserIdVector[i]);
771  saveToDatabase(fp, UsersDatabaseEntryFields[f], fldStr, DB_SAVE_OPEN_AND_CLOSE, false);
772  }
773  else if (f == 4) //permissions
774  {
775  sprintf(fldStr, "%d", UsersPermissionsVector[i]);
776  saveToDatabase(fp, UsersDatabaseEntryFields[f], fldStr, DB_SAVE_OPEN_AND_CLOSE, false);
777  }
778  else if (f == 5) //lastLoginAttemptTime
779  {
780  sprintf(fldStr, "%lu", UsersLastLoginAttemptVector[i]);
781  saveToDatabase(fp, UsersDatabaseEntryFields[f], fldStr, DB_SAVE_OPEN_AND_CLOSE, false);
782  }
783  else if (f == 6) //accountCreatedTime
784  {
785  sprintf(fldStr, "%lu", UsersAccountCreatedTimeVector[i]);
786  saveToDatabase(fp, UsersDatabaseEntryFields[f], fldStr, DB_SAVE_OPEN_AND_CLOSE, false);
787  }
788  else if (f == 7) //loginFailureCount
789  {
790  sprintf(fldStr, "%d", UsersLoginFailureCountVector[i]);
791  saveToDatabase(fp, UsersDatabaseEntryFields[f], fldStr, DB_SAVE_OPEN_AND_CLOSE, false);
792  }
793  else if (f == 8) //lastModifierTime
794  {
795  sprintf(fldStr, "%lu", UsersLastModifiedTimeVector[i]);
796  saveToDatabase(fp, UsersDatabaseEntryFields[f], fldStr, DB_SAVE_OPEN_AND_CLOSE, false);
797  }
798  else if (f == 9) //lastModifierUsername
799  saveToDatabase(fp, UsersDatabaseEntryFields[f], UsersLastModifierUsernameVector[i], DB_SAVE_OPEN_AND_CLOSE, false);
800  else if (f == 10) // useremail
801  saveToDatabase(fp, UsersDatabaseEntryFields[f], UsersUserEmailVector[i], DB_SAVE_OPEN_AND_CLOSE, false);
802  }
803 
804  saveToDatabase(fp, USERS_DB_ENTRY_STRING, "", DB_SAVE_CLOSE);
805  }
806 
807  saveToDatabase(fp, USERS_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE);
808 
809  }
810  else //HASHES
811  {
812  saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_OPEN);
813 
814  __COUT__ << "Saving " << HashesVector.size() << " Hashes." << std::endl;
815  for (uint64_t i = 0; i < HashesVector.size(); ++i)
816  {
817  __COUT__ << "Saving " << HashesVector[i] << " Hashes." << std::endl;
818  saveToDatabase(fp, HASHES_DB_ENTRY_STRING, "", DB_SAVE_OPEN, false);
819  for (unsigned int f = 0; f < HashesDatabaseEntryFields.size(); ++f)
820  {
821  if (f == 0) //hash
822  saveToDatabase(fp, HashesDatabaseEntryFields[f], HashesVector[i], DB_SAVE_OPEN_AND_CLOSE, false);
823  else if (f == 1) //lastAccessTime
824  {
825  sprintf(fldStr, "%lu", HashesAccessTimeVector[i]);
826  saveToDatabase(fp, HashesDatabaseEntryFields[f], fldStr, DB_SAVE_OPEN_AND_CLOSE, false);
827  }
828  }
829  saveToDatabase(fp, HASHES_DB_ENTRY_STRING, "", DB_SAVE_CLOSE);
830  }
831 
832  saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, "", DB_SAVE_CLOSE);
833  }
834 
835 
836  fclose(fp);
837  return true;
838 }
839 
840 //========================================================================================================================
841 //createNewAccount
842 // adds a new valid user to database
843 // inputs: username and name to display
844 // initializes database entry with minimal permissions
845 // and salt starts as "" until password is set
846 // Special case if first user name!! max permissions given (super user made)
847 bool WebUsers::createNewAccount(std::string Username, std::string DisplayName, std::string Email)
848 {
849  __COUT__ << "Creating account: " << Username << std::endl;
850  //check if username already exists
851  uint64_t i;
852  if ((i = searchUsersDatabaseForUsername(Username)) != NOT_FOUND_IN_DATABASE ||
853  Username == WebUsers::DEFAULT_ITERATOR_USERNAME ||
854  Username == WebUsers::DEFAULT_STATECHANGER_USERNAME) //prevent reserved usernames from being created!
855  {
856  __COUT__ << "Username: " << Username << " already exists" << std::endl;
857  return false;
858  }
859 
860  //create Users database entry
861  UsersUsernameVector.push_back(Username);
862  UsersDisplayNameVector.push_back(DisplayName);
863  UsersUserEmailVector.push_back(Email);
864  UsersSaltVector.push_back("");
865  UsersPermissionsVector.push_back(UsersPermissionsVector.size() ? 1 : -1); //max permissions if first user
866  UsersUserIdVector.push_back(usersNextUserId_++);
867  if (usersNextUserId_ == (uint64_t)-1) //error wrap around case
868  {
869  __COUT__ << "usersNextUserId_ wrap around!! Too many users??? Notify Admins." << std::endl;
870  usersNextUserId_ = 1; //for safety to avoid wierd issues at -1 and 0 (if used for error indication)
871  }
872  UsersLastLoginAttemptVector.push_back(0);
873  UsersLoginFailureCountVector.push_back(0);
874  UsersAccountCreatedTimeVector.push_back(time(0));
875  UsersLastModifiedTimeVector.push_back(0);
876  UsersLastModifierUsernameVector.push_back("");
877 
878  return saveDatabaseToFile(DB_USERS);
879 }
880 
881 //========================================================================================================================
882 //deleteAccount
883 // private function, deletes user account
884 // inputs: username and name to display
885 // if username and display name match account found, then account is deleted and true returned
886 // else false
887 bool WebUsers::deleteAccount(std::string username, std::string displayName)
888 {
889  uint64_t i = searchUsersDatabaseForUsername(username);
890  if (i == NOT_FOUND_IN_DATABASE) return false;
891  if (UsersDisplayNameVector[i] != displayName) return false; //display name doesn't match
892 
893  //delete entry from all user database vectors
894 
895  UsersUsernameVector.erase(UsersUsernameVector.begin() + i);
896  UsersUserEmailVector.erase(UsersUserEmailVector.begin() + i);
897  UsersDisplayNameVector.erase(UsersDisplayNameVector.begin() + i);
898  UsersSaltVector.erase(UsersSaltVector.begin() + i);
899  UsersPermissionsVector.erase(UsersPermissionsVector.begin() + i);
900  UsersUserIdVector.erase(UsersUserIdVector.begin() + i);
901  UsersLastLoginAttemptVector.erase(UsersLastLoginAttemptVector.begin() + i);
902  UsersAccountCreatedTimeVector.erase(UsersAccountCreatedTimeVector.begin() + i);
903  UsersLoginFailureCountVector.erase(UsersLoginFailureCountVector.begin() + i);
904  UsersLastModifierUsernameVector.erase(UsersLastModifierUsernameVector.begin() + i);
905  UsersLastModifiedTimeVector.erase(UsersLastModifiedTimeVector.begin() + i);
906 
907  //save database
908  return saveDatabaseToFile(DB_USERS);
909 }
910 
911 //========================================================================================================================
912 unsigned int WebUsers::hexByteStrToInt(const char *h)
913 {
914  unsigned int rv;
915  char hs[3] = { h[0],h[1],'\0' };
916  sscanf(hs, "%X", &rv);
917  return rv;
918 }
919 
920 //========================================================================================================================
921 void WebUsers::intToHexStr(unsigned char i, char *h)
922 {
923  sprintf(h, "%2.2X", i);
924 }
925 
926 
927 
928 //========================================================================================================================
929 //WebUsers::attemptActiveSession ---
930 // If new login, then new account code must match account creation time and account is made with pw
931 //
932 // if old login, password is checked
933 // returns User Id, cookieCode in newAccountCode, and displayName in jumbledUser on success
934 // else returns -1 and cookieCode "0"
935 uint64_t WebUsers::attemptActiveSession(std::string uuid, std::string &jumbledUser,
936  std::string jumbledPw, std::string &newAccountCode)
937 {
938  cleanupExpiredEntries(); //remove expired active and login sessions
939 
940  if (!CareAboutCookieCodes_) //NO SECURITY
941  {
942  uint64_t uid = getAdminUserID();
943  jumbledUser = getUsersDisplayName(uid);
944  newAccountCode = genCookieCode(); //return "dummy" cookie code by reference
945  return uid;
946  }
947 
948  uint64_t i;
949 
950  //search login sessions for uuid
951  if ((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
952  {
953  __COUT__ << "uuid: " << uuid << " is not found" << std::endl;
954  newAccountCode = "1"; //to indicate uuid was not found
955  return NOT_FOUND_IN_DATABASE;
956  }
957  ++LoginSessionAttemptsVector[i];
958 
959  std::string user = dejumble(jumbledUser, LoginSessionIdVector[i]);
960  __COUT__ << "DejumbledUser = " << user << std::endl;
961  std::string pw = dejumble(jumbledPw, LoginSessionIdVector[i]);
962 
963  //search users for username
964  if ((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
965  {
966  __COUT__ << "user: " << user << " is not found" << std::endl;
967  return NOT_FOUND_IN_DATABASE;
968  }
969 
970  UsersLastLoginAttemptVector[i] = time(0);
971  if (!UsersPermissionsVector[i])
972  {
973  __COUT__ << "user: " << user << " account INACTIVE (could be due to failed logins)" << std::endl;
974  return NOT_FOUND_IN_DATABASE;
975  }
976 
977  if (UsersSaltVector[i] == "") //first login
978  {
979  __COUT__ << "First login attempt for user: " << user << std::endl;
980 
981  char charTimeStr[10];
982  sprintf(charTimeStr, "%d", int(UsersAccountCreatedTimeVector[i] & 0xffff));
983  std::string tmpTimeStr = charTimeStr;
984  if (newAccountCode != tmpTimeStr)
985  {
986  __COUT__ << "New account code did not match: " << tmpTimeStr << " != " << newAccountCode << std::endl;
987  saveDatabaseToFile(DB_USERS); //users db modified, so save
988  return NOT_FOUND_IN_DATABASE;
989  }
990 
991  //initial user account setup
992 
993  //add until no collision (should 'never' be a collision)
994  while (!addToHashesDatabase(sha512(user, pw, UsersSaltVector[i]))) //sha256 modifies UsersSaltVector[i]
995  {
996  //this should never happen, it would mean the user+pw+saltcontext was the same
997  // but if it were to happen, try again...
998  UsersSaltVector[i] = "";
999  }
1000 
1001 
1002  __COUT__ << "\tHash added: " << HashesVector[HashesVector.size() - 1] << std::endl;
1003  }
1004  else
1005  {
1006  std::string salt = UsersSaltVector[i]; //don't want to modify saved salt
1007  //__COUT__ << salt << " " << i << std::endl;
1008  if (searchHashesDatabaseForHash(sha512(user, pw, salt)) == NOT_FOUND_IN_DATABASE)
1009  {
1010  __COUT__ << "not found?" << std::endl;
1011  ++UsersLoginFailureCountVector[i];
1012  if (UsersLoginFailureCountVector[i] >= USERS_MAX_LOGIN_FAILURES)
1013  UsersPermissionsVector[i] = 0; //Lock account
1014 
1015  __COUT__ << "\tUser/pw for user: " << user << " was not correct Failed Attempt #" << (int)(UsersLoginFailureCountVector[i]) << std::endl;
1016  if (!UsersPermissionsVector[i])
1017  __COUT__ << "Account is locked!" << std::endl;
1018 
1019  saveDatabaseToFile(DB_USERS); //users db modified, so save
1020  return NOT_FOUND_IN_DATABASE;
1021  }
1022  }
1023 
1024  __COUT__ << "Login successful for: " << user << std::endl;
1025 
1026  UsersLoginFailureCountVector[i] = 0;
1027 
1028  //record to login history for user (h==0) and on global server level (h==1)
1029  for (int h = 0; h < 2; ++h)
1030  {
1031  std::string fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_LOGIN_HISTORY_PATH + (h ? USERS_GLOBAL_HISTORY_FILE : UsersUsernameVector[i])
1032  + "." + (std::string)USERS_LOGIN_HISTORY_FILETYPE;
1033 
1034  HttpXmlDocument histXml;
1035 
1036  if (histXml.loadXmlDocument(fn)) //not found
1037  {
1038  while (histXml.getChildrenCount() + 1 > (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE))
1039  histXml.removeDataElement();
1040  }
1041  else
1042  __COUT__ << "No previous login history found." << std::endl;
1043 
1044  //add new entry to history
1045  char entryStr[500];
1046  if (h)
1047  sprintf(entryStr, "Time=%lu Username=%s Permissions=%d UID=%lu",
1048  time(0), UsersUsernameVector[i].c_str(), UsersPermissionsVector[i], UsersUserIdVector[i]);
1049  else
1050  sprintf(entryStr, "Time=%lu DisplayName=%s Permissions=%d UID=%lu",
1051  time(0), UsersDisplayNameVector[i].c_str(), UsersPermissionsVector[i], UsersUserIdVector[i]);
1052  histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr);
1053 
1054  //save file
1055  histXml.saveXmlDocument(fn);
1056  }
1057 
1058  //SUCCESS!!
1059  saveDatabaseToFile(DB_USERS); //users db modified, so save
1060  jumbledUser = UsersDisplayNameVector[i]; //pass by reference displayName
1061  newAccountCode = createNewActiveSession(UsersUserIdVector[i]); //return cookie code by reference
1062  return UsersUserIdVector[i]; //return user Id
1063 }
1064 
1065 
1066 //========================================================================================================================
1067 //WebUsers::attemptActiveSessionWithCert ---
1068 // returns User Id, cookieCode, and displayName in jumbledEmail on success
1069 // else returns -1 and cookieCode "0"
1070 uint64_t WebUsers::attemptActiveSessionWithCert(std::string uuid, std::string &email,
1071  std::string &cookieCode, std::string& user)
1072 {
1073  cleanupExpiredEntries(); //remove expired active and login sessions
1074 
1075  if (!CareAboutCookieCodes_) //NO SECURITY
1076  {
1077  uint64_t uid = getAdminUserID();
1078  email = getUsersDisplayName(uid);
1079  cookieCode = genCookieCode(); //return "dummy" cookie code by reference
1080  return uid;
1081  }
1082 
1083  if (email == "")
1084  {
1085  __COUT__ << "Rejecting logon with blank fingerprint" << std::endl;
1086  return NOT_FOUND_IN_DATABASE;
1087  }
1088 
1089  uint64_t i;
1090 
1091  //search login sessions for uuid
1092  if ((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
1093  {
1094  __COUT__ << "uuid: " << uuid << " is not found" << std::endl;
1095  cookieCode = "1"; //to indicate uuid was not found
1096  return NOT_FOUND_IN_DATABASE;
1097  }
1098  ++LoginSessionAttemptsVector[i];
1099 
1100  email = getUserEmailFromFingerprint(email);
1101  __COUT__ << "DejumbledEmail = " << email << std::endl;
1102  if (email == "")
1103  {
1104  __COUT__ << "Rejecting logon with unknown fingerprint" << std::endl;
1105  return NOT_FOUND_IN_DATABASE;
1106  }
1107 
1108  //search users for username
1109  if ((i = searchUsersDatabaseForUserEmail(email)) == NOT_FOUND_IN_DATABASE)
1110  {
1111  __COUT__ << "email: " << email << " is not found" << std::endl;
1112  return NOT_FOUND_IN_DATABASE;
1113  }
1114 
1115  user = getUsersUsername(i);
1116 
1117  UsersLastLoginAttemptVector[i] = time(0);
1118  if (!UsersPermissionsVector[i])
1119  {
1120  __COUT__ << "user: " << user << " account INACTIVE (could be due to failed logins)" << std::endl;
1121  return NOT_FOUND_IN_DATABASE;
1122  }
1123 
1124  if (UsersSaltVector[i] == "") //Can't be first login
1125  {
1126  return NOT_FOUND_IN_DATABASE;
1127  }
1128 
1129  __COUT__ << "Login successful for: " << user << std::endl;
1130 
1131  UsersLoginFailureCountVector[i] = 0;
1132 
1133  //record to login history for user (h==0) and on global server level (h==1)
1134  for (int h = 0; h < 2; ++h)
1135  {
1136  std::string fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_LOGIN_HISTORY_PATH + (h ? USERS_GLOBAL_HISTORY_FILE : UsersUsernameVector[i])
1137  + "." + (std::string)USERS_LOGIN_HISTORY_FILETYPE;
1138 
1139  HttpXmlDocument histXml;
1140 
1141  if (histXml.loadXmlDocument(fn)) //not found
1142  {
1143  while (histXml.getChildrenCount() + 1 > (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE))
1144  histXml.removeDataElement();
1145  }
1146  else
1147  __COUT__ << "No previous login history found." << std::endl;
1148 
1149  //add new entry to history
1150  char entryStr[500];
1151  if (h)
1152  sprintf(entryStr, "Time=%lu Username=%s Permissions=%d UID=%lu",
1153  time(0), UsersUsernameVector[i].c_str(), UsersPermissionsVector[i], UsersUserIdVector[i]);
1154  else
1155  sprintf(entryStr, "Time=%lu DisplayName=%s Permissions=%d UID=%lu",
1156  time(0), UsersDisplayNameVector[i].c_str(), UsersPermissionsVector[i], UsersUserIdVector[i]);
1157  histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr);
1158 
1159  //save file
1160  histXml.saveXmlDocument(fn);
1161  }
1162 
1163  //SUCCESS!!
1164  saveDatabaseToFile(DB_USERS); //users db modified, so save
1165  email = UsersDisplayNameVector[i]; //pass by reference displayName
1166  cookieCode = createNewActiveSession(UsersUserIdVector[i]); //return cookie code by reference
1167  return UsersUserIdVector[i]; //return user Id
1168 }
1169 
1170 //========================================================================================================================
1171 //WebUsers::searchActiveSessionDatabaseForUID ---
1172 // returns index if found, else -1
1173 uint64_t WebUsers::searchActiveSessionDatabaseForCookie(std::string cookieCode) const
1174 {
1175  uint64_t i = 0;
1176  for (; i < ActiveSessionCookieCodeVector.size(); ++i)
1177  if (ActiveSessionCookieCodeVector[i] == cookieCode) break;
1178  return (i == ActiveSessionCookieCodeVector.size()) ? NOT_FOUND_IN_DATABASE : i;
1179 }
1180 
1181 
1182 //========================================================================================================================
1183 //WebUsers::isUsernameActive ---
1184 // returns true if found, else false
1185 bool WebUsers::isUsernameActive(std::string username) const
1186 {
1187  uint64_t u;
1188  if ((u = searchUsersDatabaseForUsername(username)) == NOT_FOUND_IN_DATABASE) return false;
1189  return isUserIdActive(UsersUserIdVector[u]);
1190 }
1191 
1192 
1193 //========================================================================================================================
1194 //WebUsers::isUserIdActive ---
1195 // returns true if found, else false
1196 bool WebUsers::isUserIdActive(uint64_t uid) const
1197 {
1198  uint64_t i = 0;
1199  for (; i < ActiveSessionUserIdVector.size(); ++i)
1200  if (ActiveSessionUserIdVector[i] == uid) return true;
1201  return false;
1202 }
1203 
1204 //========================================================================================================================
1205 //WebUsers::searchUsersDatabaseForUsername ---
1206 // returns index if found, else -1
1207 uint64_t WebUsers::searchUsersDatabaseForUsername(std::string username) const
1208 {
1209  uint64_t i = 0;
1210  for (; i < UsersUsernameVector.size(); ++i)
1211  if (UsersUsernameVector[i] == username) break;
1212  return (i == UsersUsernameVector.size()) ? NOT_FOUND_IN_DATABASE : i;
1213 }
1214 
1215 //========================================================================================================================
1216 //WebUsers::searchUsersDatabaseForUserEmail ---
1217 // returns index if found, else -1
1218 uint64_t WebUsers::searchUsersDatabaseForUserEmail(std::string useremail) const
1219 {
1220  uint64_t i = 0;
1221  for (; i < UsersUserEmailVector.size(); ++i)
1222  if (UsersUserEmailVector[i] == useremail) break;
1223  return (i == UsersUserEmailVector.size()) ? NOT_FOUND_IN_DATABASE : i;
1224 }
1225 
1226 //========================================================================================================================
1227 //WebUsers::searchUsersDatabaseForUserId ---
1228 // returns index if found, else -1
1229 uint64_t WebUsers::searchUsersDatabaseForUserId(uint64_t uid) const
1230 {
1231  uint64_t i = 0;
1232  for (; i < UsersUserIdVector.size(); ++i)
1233  if (UsersUserIdVector[i] == uid) break;
1234  return (i == UsersUserIdVector.size()) ? NOT_FOUND_IN_DATABASE : i;
1235 }
1236 
1237 //========================================================================================================================
1238 //WebUsers::searchLoginSessionDatabaseForUUID ---
1239 // returns index if found, else -1
1240 uint64_t WebUsers::searchLoginSessionDatabaseForUUID(std::string uuid) const
1241 {
1242  uint64_t i = 0;
1243  for (; i < LoginSessionUUIDVector.size(); ++i)
1244  if (LoginSessionUUIDVector[i] == uuid) break;
1245  return (i == LoginSessionUUIDVector.size()) ? NOT_FOUND_IN_DATABASE : i;
1246 }
1247 
1248 //========================================================================================================================
1249 //WebUsers::searchHashesDatabaseForHash ---
1250 // returns index if found, else -1
1251 uint64_t WebUsers::searchHashesDatabaseForHash(std::string hash)
1252 {
1253  uint64_t i = 0;
1254  //__COUT__ << i << " " << HashesVector.size() << " " << HashesAccessTimeVector.size() <<
1255  // hash << std::endl;
1256  for (; i < HashesVector.size(); ++i)
1257  if (HashesVector[i] == hash) break;
1258  //else
1259  // __COUT__ << HashesVector[i] << " ?????? " << std::endl;
1260  //__COUT__ << i << std::endl;
1261  if (i < HashesAccessTimeVector.size()) //if found, means login successful, so update access time
1262  HashesAccessTimeVector.push_back((time(0) + (rand() % 2 ? 1 : -1)*(rand() % 30 * 24 * 60 * 60)) & 0x0FFFFFFFFFE000000);
1263 
1264  //__COUT__ << i << std::endl;
1265  return (i == HashesVector.size()) ? NOT_FOUND_IN_DATABASE : i;
1266 }
1267 
1268 //========================================================================================================================
1269 //WebUsers::addToHashesDatabase ---
1270 // returns false if hash already exists
1271 // else true for success
1272 bool WebUsers::addToHashesDatabase(std::string hash)
1273 {
1274  if (searchHashesDatabaseForHash(hash) != NOT_FOUND_IN_DATABASE)
1275  {
1276  __COUT__ << "Hash collision: " << hash << std::endl;
1277  return false;
1278  }
1279  HashesVector.push_back(hash);
1280  HashesAccessTimeVector.push_back((time(0) + (rand() % 2 ? 1 : -1)*(rand() % 30 * 24 * 60 * 60)) & 0x0FFFFFFFFFE000000);
1281  //in seconds, blur by month and mask out changes on year time frame: 0xFFFFFFFF FE000000
1282  return saveDatabaseToFile(DB_HASHES);
1283 }
1284 
1285 //========================================================================================================================
1286 //WebUsers::genCookieCode ---
1287 std::string WebUsers::genCookieCode()
1288 {
1289  char hexStr[3];
1290  std::string cc = "";
1291  for (uint32_t i = 0; i < COOKIE_CODE_LENGTH / 2; ++i)
1292  {
1293  intToHexStr(rand(), hexStr);
1294  cc.append(hexStr);
1295  }
1296  return cc;
1297 }
1298 
1299 //========================================================================================================================
1300 //WebUsers::removeLoginSessionEntry ---
1301 void WebUsers::removeLoginSessionEntry(unsigned int i)
1302 {
1303  LoginSessionIdVector.erase(LoginSessionIdVector.begin() + i);
1304  LoginSessionUUIDVector.erase(LoginSessionUUIDVector.begin() + i);
1305  LoginSessionIpVector.erase(LoginSessionIpVector.begin() + i);
1306  LoginSessionStartTimeVector.erase(LoginSessionStartTimeVector.begin() + i);
1307  LoginSessionAttemptsVector.erase(LoginSessionAttemptsVector.begin() + i);
1308 }
1309 
1310 //========================================================================================================================
1311 //WebUsers::createNewActiveSession ---
1312 // if asIndex is not specified (0), new session receives max(ActiveSessionIndex) for user +1.. always skipping 0.
1313 // In this ActiveSessionIndex should link a thread of cookieCodes
1314 std::string WebUsers::createNewActiveSession(uint64_t uid, std::string ip, uint64_t asIndex)
1315 {
1316  ActiveSessionCookieCodeVector.push_back(genCookieCode());
1317  ActiveSessionIpVector.push_back(ip);
1318  ActiveSessionUserIdVector.push_back(uid);
1319  ActiveSessionStartTimeVector.push_back(time(0));
1320 
1321  if (asIndex) //this is a refresh of current active session
1322  ActiveSessionIndex.push_back(asIndex);
1323  else
1324  {
1325  //find max(ActiveSessionIndex)
1326  uint64_t max = 0;
1327  for (uint64_t j = 0; j < ActiveSessionIndex.size(); ++j)
1328  if (ActiveSessionUserIdVector[j] == uid && max < ActiveSessionIndex[j]) //new max
1329  max = ActiveSessionIndex[j];
1330 
1331  ActiveSessionIndex.push_back(max ? max + 1 : 1); //0 is illegal
1332  }
1333 
1334  return ActiveSessionCookieCodeVector[ActiveSessionCookieCodeVector.size() - 1];
1335 }
1336 
1337 //========================================================================================================================
1338 //WebUsers::removeActiveSession ---
1339 void WebUsers::removeActiveSessionEntry(unsigned int i)
1340 {
1341  ActiveSessionCookieCodeVector.erase(ActiveSessionCookieCodeVector.begin() + i);
1342  ActiveSessionIpVector.erase(ActiveSessionIpVector.begin() + i);
1343  ActiveSessionUserIdVector.erase(ActiveSessionUserIdVector.begin() + i);
1344  ActiveSessionStartTimeVector.erase(ActiveSessionStartTimeVector.begin() + i);
1345  ActiveSessionIndex.erase(ActiveSessionIndex.begin() + i);
1346 }
1347 
1348 //========================================================================================================================
1349 //WebUsers::refreshCookieCode ---
1350 // Basic idea is to return valid cookieCode to user for future commands
1351 // There are two issues that arise due to "same user - multiple location":
1352 // 1. Multiple Tabs Scenario (same browser cookie)
1353 // 2. Multiple Browser Scenario (separate login chain)
1354 // We want to allow both modes of operation.
1355 //
1356 // Solution to 1. : long expiration and overlap times
1357 // return most recent cookie for ActiveSessionIndex (should be deepest in vector always)
1358 // - If half of expiration time is up, a new cookie is generated as most recent
1359 // but previous is maintained and start time is changed to accommodate overlap time.
1360 // - Overlap time should be enough to allow other tabs to take an action and
1361 // receive the new cookie code.
1362 //
1363 // Solution to 2. : ActiveSessionIndex
1364 // return most recent cookie for ActiveSessionIndex (should be deepest in vector always)
1365 // - Independent browsers will have independent cookie chains for same user
1366 // based on ActiveSessionIndex.
1367 // - Can use ActiveSessionIndex to detect old logins and log them out.
1368 //
1369 // enableRefresh added for automatic actions that take place, that should still get
1370 // the most recent code, but should not generate new codes (set enableRefresh = false).
1371 std::string WebUsers::refreshCookieCode(unsigned int i, bool enableRefresh)
1372 {
1373  //find most recent cookie for ActiveSessionIndex (should be deepest in vector always)
1374  for (uint64_t j = ActiveSessionUserIdVector.size() - 1; j != (uint64_t)-1; --j) //reverse iterate vector
1375  if (ActiveSessionUserIdVector[j] == ActiveSessionUserIdVector[i] &&
1376  ActiveSessionIndex[j] == ActiveSessionIndex[i]) //if uid and asIndex match, found match
1377  {
1378  //found!
1379 
1380  //If half of expiration time is up, a new cookie is generated as most recent
1381  if (enableRefresh && (time(0) - ActiveSessionStartTimeVector[j] > ACTIVE_SESSION_EXPIRATION_TIME / 2))
1382  {
1383  //but previous is maintained and start time is changed to accommodate overlap time.
1384  ActiveSessionStartTimeVector[j] = time(0) - ACTIVE_SESSION_EXPIRATION_TIME +
1385  ACTIVE_SESSION_COOKIE_OVERLAP_TIME; //give time window for stale cookie commands before expiring
1386 
1387  //create new active cookieCode with same ActiveSessionIndex, will now be found as most recent
1388  return createNewActiveSession(ActiveSessionUserIdVector[i], ActiveSessionIpVector[i], ActiveSessionIndex[i]);
1389  }
1390 
1391  return ActiveSessionCookieCodeVector[j]; //cookieCode is unchanged
1392  }
1393 
1394  return "0"; //failure, should be impossible since i is already validated
1395 }
1396 
1397 //========================================================================================================================
1398 //WebUsers::IsCookieActive ---
1399 // returns User Id on success, returns by reference refreshed cookieCode and displayName if cookieCode/user combo is still active
1400 // displayName is returned in username std::string
1401 // else returns -1
1402 uint64_t WebUsers::isCookieCodeActiveForLogin(std::string uuid, std::string &cookieCode,
1403  std::string &username)
1404 {
1405  if (!CareAboutCookieCodes_)
1406  return getAdminUserID(); //always successful
1407 
1408  //else
1409  // __COUT__ << "I care about cookies?!?!?!*************************************************" << std::endl;
1410 
1411  if (!ActiveSessionStartTimeVector.size()) return NOT_FOUND_IN_DATABASE; //no active sessions, so do nothing
1412 
1413  uint64_t i, j; //used to iterate and search
1414 
1415  //find uuid in login session database else return "0"
1416  if ((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
1417  {
1418  __COUT__ << "uuid not found: " << uuid << std::endl;
1419  return NOT_FOUND_IN_DATABASE;
1420  }
1421 
1422  username = dejumble(username, LoginSessionIdVector[i]); //dejumble user for cookie check
1423 
1424  //search active users for cookie code
1425  if ((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
1426  {
1427  __COUT__ << "Cookie code not found" << std::endl;
1428  return NOT_FOUND_IN_DATABASE;
1429  }
1430 
1431  //search users for user id
1432  if ((j = searchUsersDatabaseForUserId(ActiveSessionUserIdVector[i])) == NOT_FOUND_IN_DATABASE)
1433  {
1434  __COUT__ << "User ID not found" << std::endl;
1435  return NOT_FOUND_IN_DATABASE;
1436  }
1437 
1438  //match username, with one found
1439  if (UsersUsernameVector[j] != username)
1440  {
1441 
1442  __COUT__ << "cookieCode: " << cookieCode << " was.." << std::endl;
1443  __COUT__ << "username: " << username << " is not found" << std::endl;
1444  return NOT_FOUND_IN_DATABASE;
1445  }
1446 
1447  username = UsersDisplayNameVector[j]; //return display name by reference
1448  cookieCode = refreshCookieCode(i); //refresh cookie by reference
1449  return UsersUserIdVector[j]; //return user ID
1450 }
1451 
1452 //========================================================================================================================
1453 //WebUsers::getActiveSessionCountForUser ---
1454 // Returns count of unique ActiveSessionIndex entries for user's uid
1455 uint64_t WebUsers::getActiveSessionCountForUser(uint64_t uid)
1456 {
1457  bool unique;
1458  std::vector<uint64_t> uniqueAsi; //maintain unique as indices for reference
1459 
1460  uint64_t i, j;
1461  for (i = 0; i < ActiveSessionUserIdVector.size(); ++i)
1462  if (ActiveSessionUserIdVector[i] == uid) //found active session for user
1463  {
1464  //check if ActiveSessionIndex is unique
1465  unique = true;
1466 
1467  for (j = 0; j < uniqueAsi.size(); ++j)
1468  if (uniqueAsi[j] == ActiveSessionIndex[i])
1469  {
1470  unique = false; break;
1471  }
1472 
1473  if (unique) //unique! so count and save
1474  uniqueAsi.push_back(ActiveSessionIndex[i]);
1475  }
1476 
1477  __COUT__ << "Found " << uniqueAsi.size() << " active sessions for uid " << uid << std::endl;
1478 
1479  return uniqueAsi.size();
1480 }
1481 
1482 //========================================================================================================================
1483 //WebUsers::getUsersDisplayName ---
1484 std::string WebUsers::getUsersDisplayName(uint64_t uid)
1485 {
1486  uint64_t i;
1487  if ((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE) return "";
1488  return UsersDisplayNameVector[i];
1489 }
1490 
1491 //========================================================================================================================
1492 //WebUsers::getUsersUsername ---
1493 std::string WebUsers::getUsersUsername(uint64_t uid)
1494 {
1495  uint64_t i;
1496  if ((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE) return "";
1497  return UsersUsernameVector[i];
1498 }
1499 
1500 //========================================================================================================================
1501 //WebUsers::cookieCodeLogout ---
1502 // Used to logout user based on cookieCode and ActiveSessionIndex
1503 // logoutOtherUserSessions true logs out all of user's other sessions by uid
1504 // Note: when true, user will remain logged in to current active session
1505 // logoutOtherUserSessions false logs out only this cookieCode/ActiveSessionIndex
1506 // Note: when false, user will remain logged in other locations based different ActiveSessionIndex
1507 //
1508 // on failure, returns -1
1509 // on success returns number of active sessions that were removed
1510 uint64_t WebUsers::cookieCodeLogout(std::string cookieCode, bool logoutOtherUserSessions, uint64_t *userId, std::string ip)
1511 {
1512  //TODO check ip black list and increment counter if cookie code not found
1513 
1514  uint64_t i;
1515 
1516  //search active users for cookie code
1517  if ((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
1518  {
1519  __COUT__ << "Cookie code not found" << std::endl;
1520  return NOT_FOUND_IN_DATABASE;
1521  }
1522 
1523  //check ip
1524  if (ActiveSessionIpVector[i] != ip)
1525  {
1526  __COUT__ << "IP does not match active session" << std::endl;
1527  return NOT_FOUND_IN_DATABASE;
1528  }
1529 
1530  //found valid active session i
1531  //if logoutOtherUserSessions
1532  // remove active sessions that match ActiveSessionUserIdVector[i] and ActiveSessionIndex[i]
1533  //else
1534  // remove active sessions that match ActiveSessionUserIdVector[i] but not ActiveSessionIndex[i]
1535 
1536  uint64_t asi = ActiveSessionIndex[i];
1537  uint64_t uid = ActiveSessionUserIdVector[i];
1538  if (userId) *userId = uid; //return uid if requested
1539  uint64_t logoutCount = 0;
1540 
1541  i = 0;
1542  while (i < ActiveSessionIndex.size())
1543  {
1544  if ((logoutOtherUserSessions && ActiveSessionUserIdVector[i] == uid &&
1545  ActiveSessionIndex[i] != asi) ||
1546  (!logoutOtherUserSessions && ActiveSessionUserIdVector[i] == uid &&
1547  ActiveSessionIndex[i] == asi))
1548  {
1549  __COUT__ << "Logging out of active session " << ActiveSessionUserIdVector[i]
1550  << "-" << ActiveSessionIndex[i] << std::endl;
1551  removeActiveSessionEntry(i);
1552  ++logoutCount;
1553  }
1554  else //only increment if no delete
1555  ++i;
1556  }
1557 
1558  __COUT__ << "Found and removed active session count = " << logoutCount << std::endl;
1559 
1560  return logoutCount;
1561 }
1562 
1563 //========================================================================================================================
1564 //WebUsers::getUserInfoForCookie ---
1565 bool WebUsers::getUserInfoForCookie(std::string &cookieCode,
1566  std::string *userName, std::string *displayName,
1567  uint64_t *activeSessionIndex)
1568 {
1569  if (userName) *userName = "";
1570  if (displayName) *displayName = "";
1571 
1572  if (!CareAboutCookieCodes_) //NO SECURITY, return admin
1573  {
1574  uint64_t uid = getAdminUserID();
1575  if (userName) *userName = getUsersUsername(uid);
1576  if (displayName) *displayName = getUsersDisplayName(uid);
1577  if (activeSessionIndex) *activeSessionIndex = -1;
1578  return true;
1579  }
1580 
1581  uint64_t i, j;
1582 
1583  //search active users for cookie code
1584  if ((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
1585  {
1586  __COUT__ << "cookieCode NOT_FOUND_IN_DATABASE" << std::endl;
1587  return false;
1588  }
1589 
1590  //get Users record
1591  if ((j = searchUsersDatabaseForUserId(ActiveSessionUserIdVector[i])) == NOT_FOUND_IN_DATABASE)
1592  {
1593  __COUT__ << "ActiveSessionUserIdVector NOT_FOUND_IN_DATABASE" << std::endl;
1594  return false;
1595  }
1596 
1597  if (userName) *userName = UsersUsernameVector[j];
1598  if (displayName) *displayName = UsersDisplayNameVector[j];
1599  if (activeSessionIndex) *activeSessionIndex = ActiveSessionIndex[i];
1600  return true;
1601 }
1602 
1603 //========================================================================================================================
1604 //WebUsers::isCookieCodeActiveForRequest ---
1605 // Used to verify cookie code for all general user requests
1606 // cookieCode/ip must be active to pass
1607 //
1608 // cookieCode is passed by reference. It is refreshed, if refresh=true on success and may be modified.
1609 // on success, if userPermissions and/or uid are not null, the permissions and uid are returned
1610 // on failure, cookieCode contains error message to return to client
1611 //
1612 // If do NOT care about cookie code, then choose uid 0 (admin)
1613 // and give full permissions
1614 bool WebUsers::cookieCodeIsActiveForRequest(std::string &cookieCode,
1615  uint8_t *userPermissions, uint64_t *uid, std::string ip,
1616  bool refresh, std::string *userWithLock)
1617 {
1618  //TODO check ip black list and increment counter if cookie code not found
1619  cleanupExpiredEntries(); //remove expired cookies
1620 
1621  uint64_t i, j;
1622 
1623  //__COUT__ << "I care about cookie codes: " << CareAboutCookieCodes_ << std::endl;
1624  //__COUT__ << "refresh cookie " << refresh << std::endl;
1625 
1626  if (!CareAboutCookieCodes_) //No Security, so grant admin
1627  {
1628  if (userPermissions) *userPermissions = -1;
1629  if (uid) *uid = getAdminUserID();
1630  if (userWithLock) *userWithLock = usersUsernameWithLock_;
1631 
1632  if(cookieCode.size() != COOKIE_CODE_LENGTH)
1633  cookieCode = genCookieCode(); //return "dummy" cookie code
1634 
1635  return true;
1636  }
1637  //else using security!
1638 
1639  //search active users for cookie code
1640  if ((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
1641  {
1642  __COUT__ << "Cookie code not found" << std::endl;
1643  cookieCode = REQ_NO_LOGIN_RESPONSE;
1644  return false;
1645  }
1646 
1647  //check ip
1648  if (ActiveSessionIpVector[i] != ip)
1649  {
1650  __COUT__ << "IP does not match active session" << std::endl;
1651  cookieCode = REQ_NO_LOGIN_RESPONSE;
1652  return false;
1653  }
1654 
1655  //get Users record
1656  if ((j = searchUsersDatabaseForUserId(ActiveSessionUserIdVector[i])) == NOT_FOUND_IN_DATABASE)
1657  {
1658  __COUT__ << "User ID not found" << std::endl;
1659  cookieCode = REQ_NO_LOGIN_RESPONSE;
1660  return false;
1661  }
1662 
1663  uint8_t tmpPerm = getPermissionsForUser(UsersUserIdVector[j]);
1664 
1665  if (!tmpPerm) //Permission of 0 is inactive for all requests!
1666  {
1667  cookieCode = REQ_NO_PERMISSION_RESPONSE;
1668  return false;
1669  }
1670 
1671  //success!
1672  if (userPermissions) *userPermissions = tmpPerm;
1673  if (uid) *uid = UsersUserIdVector[j];
1674  if (userWithLock) *userWithLock = usersUsernameWithLock_;
1675 
1676  cookieCode = refreshCookieCode(i, refresh); //refresh cookie by reference
1677 
1678  return true;
1679 }
1680 
1681 //========================================================================================================================
1682 //WebUsers::cleanupExpiredEntries ---
1683 // cleanup expired entries form Login Session and Active Session databases
1684 // check if usersUsernameWithLock_ is still active
1685 // return the vector of logged out user names if a parameter
1686 // if not a parameter, store logged out user names for next time called with parameter
1687 void WebUsers::cleanupExpiredEntries(std::vector<std::string> *loggedOutUsernames)
1688 {
1689  uint64_t i; //used to iterate and search
1690  uint64_t tmpUid;
1691 
1692  if (loggedOutUsernames) //return logged out users this time and clear storage vector
1693  {
1694  for (i = 0; i < UsersLoggedOutUsernames_.size(); ++i)
1695  loggedOutUsernames->push_back(UsersLoggedOutUsernames_[i]);
1696  UsersLoggedOutUsernames_.clear();
1697  }
1698 
1699 
1700 
1701  //remove expired entries from Login Session
1702  for (i = 0; i < LoginSessionStartTimeVector.size(); ++i)
1703  if (LoginSessionStartTimeVector[i] + LOGIN_SESSION_EXPIRATION_TIME < time(0) || //expired
1704  LoginSessionAttemptsVector[i] > LOGIN_SESSION_ATTEMPTS_MAX)
1705  {
1706  //__COUT__ << "Found expired userId: " << LoginSessionUUIDVector[i] <<
1707  // " at time " << LoginSessionStartTimeVector[i] << " with attempts " << LoginSessionAttemptsVector[i] << std::endl;
1708 
1709  removeLoginSessionEntry(i);
1710  --i; //rewind loop
1711  }
1712 
1713  //declare structures for ascii time
1714  // struct tm * timeinfo;
1715  // time_t tmpt;
1716  // char tstr[200];
1717  // timeinfo = localtime ( &(tmpt=time(0)) );
1718  // sprintf(tstr,"\"%s\"",asctime (timeinfo)); tstr[strlen(tstr)-2] = '\"';
1719  //__COUT__ << "Current time is: " << time(0) << " " << tstr << std::endl;
1720 
1721  //remove expired entries from Active Session
1722  for (i = 0; i < ActiveSessionStartTimeVector.size(); ++i)
1723  if (ActiveSessionStartTimeVector[i] + ACTIVE_SESSION_EXPIRATION_TIME <= time(0)) //expired
1724  {
1725  //timeinfo = localtime (&(tmpt=ActiveSessionStartTimeVector[i]));
1726  //sprintf(tstr,"\"%s\"",asctime (timeinfo)); tstr[strlen(tstr)-2] = '\"';
1727  //__COUT__ << "Found expired user: " << ActiveSessionUserIdVector[i] <<
1728  // " start time " << tstr << " i: " << i << " size: " << ActiveSessionStartTimeVector.size()
1729  // << std::endl;
1730  tmpUid = ActiveSessionUserIdVector[i];
1731  removeActiveSessionEntry(i);
1732 
1733 
1734 
1735  if (!isUserIdActive(tmpUid)) //if uid no longer active, then user was completely logged out
1736  {
1737  if (loggedOutUsernames) //return logged out users this time
1738  loggedOutUsernames->push_back(UsersUsernameVector[searchUsersDatabaseForUserId(tmpUid)]);
1739  else //store for next time requested as parameter
1740  UsersLoggedOutUsernames_.push_back(UsersUsernameVector[searchUsersDatabaseForUserId(tmpUid)]);
1741  }
1742 
1743  --i; //rewind loop
1744  }
1745  // else
1746  // {
1747  // timeinfo = localtime (&(tmpt=ActiveSessionStartTimeVector[i] + ACTIVE_SESSION_EXPIRATION_TIME));
1748  // sprintf(tstr,"\"%s\"",asctime (timeinfo)); tstr[strlen(tstr)-2] = '\"';
1749  //
1750  // //__COUT__ << "Found user: " << ActiveSessionUserIdVector[i] << "-" << ActiveSessionIndex[i] <<
1751  // // " expires " << tstr <<
1752  // // " sec left " << ActiveSessionStartTimeVector[i] + ACTIVE_SESSION_EXPIRATION_TIME - time(0) << std::endl;
1753  //
1754  // }
1755 
1756  //__COUT__ << "Found usersUsernameWithLock_: " << usersUsernameWithLock_ << " - " << userWithLockVerified << std::endl;
1757  if (CareAboutCookieCodes_ && !isUsernameActive(usersUsernameWithLock_)) //unlock if user no longer logged in
1758  usersUsernameWithLock_ = "";
1759 }
1760 
1761 //========================================================================================================================
1762 //createNewLoginSession
1763 // adds a new login session id to database
1764 // inputs: UUID
1765 // checks that UUID is unique
1766 // initializes database entry and returns sessionId std::string
1767 // return "" on failure
1768 std::string WebUsers::createNewLoginSession(std::string UUID, std::string ip)
1769 {
1770  __COUT__ << "UUID: " << UUID << std::endl << std::endl;
1771  uint64_t i = 0;
1772  for (; i < LoginSessionUUIDVector.size(); ++i)
1773  if (LoginSessionUUIDVector[i] == UUID) break;
1774 
1775  if (i != LoginSessionUUIDVector.size())
1776  {
1777  __COUT__ << "UUID: " << UUID << " is not unique" << std::endl;
1778  return "";
1779  }
1780  //else UUID is unique
1781 
1782  LoginSessionUUIDVector.push_back(UUID);
1783 
1784  //generate sessionId
1785  char hexStr[3];
1786  std::string sid = "";
1787  for (i = 0; i < SESSION_ID_LENGTH / 2; ++i)
1788  {
1789  intToHexStr(rand(), hexStr);
1790  sid.append(hexStr);
1791  }
1792  LoginSessionIdVector.push_back(sid);
1793  LoginSessionIpVector.push_back("");
1794  LoginSessionStartTimeVector.push_back(time(0));
1795  LoginSessionAttemptsVector.push_back(0);
1796 
1797  return sid;
1798 }
1799 
1800 
1801 
1802 //========================================================================================================================
1803 //WebUsers::sha512
1804 // performs SHA-512 encoding using openssl linux library crypto on context+user+password
1805 // if context is empty std::string "", context is generated and returned by reference
1806 // hashed result is returned
1807 std::string WebUsers::sha512(std::string user, std::string password, std::string &salt)
1808 {
1809  SHA512_CTX sha512_context;
1810  char hexStr[3];
1811 
1812  if (salt == "") //generate context
1813  {
1814  SHA512_Init(&sha512_context);
1815 
1816  for (unsigned int i = 0; i < 8; ++i)
1817  sha512_context.h[i] += rand();
1818 
1819  for (unsigned int i = 0; i < sizeof(SHA512_CTX); ++i)
1820  {
1821  intToHexStr((uint8_t)(((uint8_t *)(&sha512_context))[i]), hexStr);
1822 
1823  salt.append(hexStr);
1824  }
1825  //__COUT__ << salt << std::endl;
1826  }
1827  else //use existing context
1828  {
1829 
1830  //__COUT__ << salt << std::endl;
1831 
1832  for (unsigned int i = 0; i < sizeof(SHA512_CTX); ++i)
1833  ((uint8_t *)(&sha512_context))[i] = hexByteStrToInt(&(salt.c_str()[i * 2]));
1834  }
1835 
1836  std::string strToHash = salt + user + password;
1837 
1838  //__COUT__ << salt << std::endl;
1839  unsigned char hash[SHA512_DIGEST_LENGTH];
1840  //__COUT__ << salt << std::endl;
1841  char retHash[SHA512_DIGEST_LENGTH * 2 + 1];
1842  //__COUT__ << strToHash.length() << " " << strToHash << std::endl;
1843 
1844 
1845  //__COUT__ << "If crashing occurs here, may be an illegal salt context." << std::endl;
1846  SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length());
1847 
1848  SHA512_Final(hash, &sha512_context);
1849 
1850  //__COUT__ << salt << std::endl;
1851  int i = 0;
1852  for (i = 0; i < SHA512_DIGEST_LENGTH; i++)
1853  sprintf(retHash + (i * 2), "%02x", hash[i]);
1854 
1855  //__COUT__ << salt << std::endl;
1856  retHash[SHA512_DIGEST_LENGTH * 2] = '\0';
1857 
1858 
1859  //__COUT__ << salt << std::endl;
1860 
1861 
1862  return retHash;
1863 }
1864 
1865 
1866 //========================================================================================================================
1867 //WebUsers::dejumble
1868 // the client sends username and pw jumbled for http transmission
1869 // this function dejumbles
1870 std::string WebUsers::dejumble(std::string u, std::string s)
1871 {
1872 
1873  if (s.length() != SESSION_ID_LENGTH) return ""; //session std::string must be even
1874 
1875  const int ss = s.length() / 2;
1876  int p = hexByteStrToInt(&(s.c_str()[0])) % ss;
1877  int n = hexByteStrToInt(&(s.c_str()[p * 2])) % ss;
1878  int len = (hexByteStrToInt(&(u.c_str()[p * 2])) - p - n + ss * 3) % ss;
1879 
1880  std::vector<bool> x(ss);
1881  for (int i = 0; i < ss; ++i) x[i] = 0;
1882  x[p] = 1;
1883 
1884  int c = hexByteStrToInt(&(u.c_str()[p * 2]));
1885 
1886  std::string user = "";
1887 
1888  for (int l = 0; l < len; ++l)
1889  {
1890  p = (p + hexByteStrToInt(&(s.c_str()[p * 2]))) % ss;
1891  while (x[p]) p = (p + 1) % ss;
1892  x[p] = 1;
1893  n = hexByteStrToInt(&(s.c_str()[p * 2]));
1894  user.append(1, (hexByteStrToInt(&(u.c_str()[p * 2])) - c - n + ss * 4) % ss);
1895  c = hexByteStrToInt(&(u.c_str()[p * 2]));
1896  }
1897 
1898  return user;
1899 }
1900 
1901 //========================================================================================================================
1902 //WebUsers::getPermissionForUser
1903 // return 0 if invalid index
1904 uint8_t WebUsers::getPermissionsForUser(uint64_t uid)
1905 {
1906  uint64_t userIndex = searchUsersDatabaseForUserId(uid);
1907  return (userIndex < UsersPermissionsVector.size()) ? UsersPermissionsVector[userIndex] : 0;//255:0;//TODO FIX //UsersPermissionsVector[userIndex]:0;
1908 }
1909 
1910 
1911 //========================================================================================================================
1912 //WebUsers::getPermissionForUser
1913 // return 0 if invalid index
1914 std::string WebUsers::getTooltipFilename(
1915  const std::string& username, const std::string &srcFile,
1916  const std::string &srcFunc, const std::string &srcId)
1917 {
1918  std::string filename = (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH + "/";
1919 
1920  //make tooltip directory if not there
1921  // note: this is static so WebUsers constructor has not necessarily been called
1922  mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755);
1923  mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755);
1924  mkdir(filename.c_str(), 0755);
1925 
1926  for (const char& c : username)
1927  if ( //only keep alpha numeric
1928  (c >= 'a' && c <= 'z') ||
1929  (c >= 'A' && c <= 'Z') ||
1930  (c >= '0' && c <= '9'))
1931  filename += c;
1932  filename += "/";
1933 
1934  //make username tooltip directory if not there
1935  mkdir(filename.c_str(), 0755);
1936 
1937  for (const char& c : srcFile)
1938  if ( //only keep alpha numeric
1939  (c >= 'a' && c <= 'z') ||
1940  (c >= 'A' && c <= 'Z') ||
1941  (c >= '0' && c <= '9'))
1942  filename += c;
1943  filename += "_";
1944  for (const char& c : srcFunc)
1945  if ( //only keep alpha numeric
1946  (c >= 'a' && c <= 'z') ||
1947  (c >= 'A' && c <= 'Z') ||
1948  (c >= '0' && c <= '9'))
1949  filename += c;
1950  filename += "_";
1951  for (const char& c : srcId)
1952  if ( //only keep alpha numeric
1953  (c >= 'a' && c <= 'z') ||
1954  (c >= 'A' && c <= 'Z') ||
1955  (c >= '0' && c <= '9'))
1956  filename += c;
1957  filename += ".tip";
1958  __COUT__ << "filename " << filename << std::endl;
1959  return filename;
1960 }
1961 
1962 std::string ots::WebUsers::getUserEmailFromFingerprint(std::string fingerprint)
1963 {
1964  std::ifstream f(WEB_LOGIN_CERTDATA_PATH);
1965  if (f.is_open())
1966  {
1967  std::string email;
1968  std::string fp;
1969  getline(f, email);
1970  getline(f, fp);
1971  certFingerprints_[email] = fp;
1972  f.close();
1973  remove(WEB_LOGIN_CERTDATA_PATH.c_str());
1974  }
1975 
1976  for (auto fp : certFingerprints_)
1977  {
1978  if (fp.second == fingerprint) return fp.first;
1979  }
1980  return "";
1981 }
1982 
1983 //========================================================================================================================
1984 //WebUsers::tooltipSetNeverShowForUsername
1985 // temporarySilence has priority over the neverShow setting
1986 void WebUsers::tooltipSetNeverShowForUsername(const std::string& username,
1987  HttpXmlDocument *xmldoc,
1988  const std::string &srcFile, const std::string &srcFunc,
1989  const std::string &srcId, bool doNeverShow, bool temporarySilence)
1990 {
1991 
1992  __COUT__ << "Setting tooltip never show for user: " << username <<
1993  " to " << doNeverShow << " (temporarySilence=" <<
1994  temporarySilence << ")" << std::endl;
1995 
1996 
1997  std::string filename = getTooltipFilename(username, srcFile, srcFunc, srcId);
1998  FILE *fp = fopen(filename.c_str(), "w");
1999  if (fp)
2000  { //file exists, so do NOT show tooltip
2001  if (temporarySilence)
2002  fprintf(fp, "%ld", time(0) + 60 * 60); //mute for an hour
2003  else
2004  fputc(doNeverShow ? '1' : '0', fp);
2005  fclose(fp);
2006  }
2007  else //default to show tool tip
2008  __COUT_ERR__ << "Big problme with tooltips! File not accessible: " << filename << std::endl;
2009 }
2010 
2011 //========================================================================================================================
2012 //WebUsers::tooltipCheckForUsername
2013 // read file for tooltip
2014 // if not 1 then never show
2015 // if 0 then "always show"
2016 // if other then treat as temporary mute..
2017 // i.e. if time(0) > val show
2018 void WebUsers::tooltipCheckForUsername(const std::string& username,
2019  HttpXmlDocument *xmldoc,
2020  const std::string &srcFile, const std::string &srcFunc,
2021  const std::string &srcId)
2022 {
2023  if (srcId == "ALWAYS")
2024  {
2025  //ALWAYS shows tool tip
2026  xmldoc->addTextElementToData("ShowTooltip", "1");
2027  return;
2028  }
2029 
2030  // __COUT__ << "username " << username << std::endl;
2031  // __COUT__ << "srcFile " << srcFile << std::endl;
2032  // __COUT__ << "srcFunc " << srcFunc << std::endl;
2033  // __COUT__ << "srcId " << srcId << std::endl;
2034  __COUT__ << "Checking tooltip for user: " << username << std::endl;
2035 
2036 
2037 
2038  std::string filename = getTooltipFilename(username, srcFile, srcFunc, srcId);
2039  FILE *fp = fopen(filename.c_str(), "r");
2040  if (fp)
2041  { //file exists, so do NOT show tooltip
2042  time_t val;
2043  char line[100];
2044  fgets(line, 100, fp);
2045  //int val = fgetc(fp);
2046  sscanf(line, "%ld", &val);
2047  __COUT__ << "tooltip value read = " << val << std::endl;
2048  fclose(fp);
2049 
2050  //if first line in file is a 1 then do not show
2051  // else show if current time is greater than value
2052  xmldoc->addTextElementToData("ShowTooltip", val == 1 ? "0" :
2053  (time(0) > val ? "1" : "0"));
2054  }
2055  else //default to show tool tip
2056  xmldoc->addTextElementToData("ShowTooltip", "1");
2057 }
2058 
2059 //========================================================================================================================
2060 //WebUsers::resetAllUserTooltips
2061 void WebUsers::resetAllUserTooltips(const std::string &userNeedle)
2062 {
2063  std::system(("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH +
2064  "/" + userNeedle).c_str());
2065  __COUT__ << "Successfully reset Tooltips for user " << userNeedle << std::endl;
2066 }
2067 
2068 //========================================================================================================================
2069 //WebUsers::insertGetSettingsResponse
2070 // add settings to xml document
2071 // all active users have permissions of at least 1 so have web preferences:
2072 // -background color
2073 // -dashboard color
2074 // -window color
2075 // -3 user defaults for window layouts(and current), can set current as one of defaults
2076 // super users have account controls:
2077 // -list of user accounts to edit permissions, display name, or delete account
2078 // -add new account
2079 // ...and super users have system default window layout
2080 // -2 system defaults for window layouts
2081 //
2082 // layout settings explanation
2083 // 0 = no windows, never set, empty desktop
2084 // example 2 layouts set, 2 not,
2085 // [<win name>, <win subname>, <win url>, <x>, <y>, <w>, <h>]; [<win name>, <win subname>, <win url>, <x>, <y>, <w>, <h>]...];0;0
2086 void WebUsers::insertSettingsForUser(uint64_t uid, HttpXmlDocument *xmldoc, bool includeAccounts)
2087 {
2088  uint8_t p = getPermissionsForUser(uid);
2089  if (!p) return; //not an active user
2090 
2091  uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2092  __COUT__ << "Gettings settings for user: " << UsersUsernameVector[userIndex] << std::endl;
2093 
2094  std::string fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + UsersUsernameVector[userIndex] + "." + (std::string)USERS_PREFERENCES_FILETYPE;
2095 
2096  HttpXmlDocument prefXml;
2097 
2098  __COUT__ << "Preferences file: " << fn << std::endl;
2099 
2100  if (!prefXml.loadXmlDocument(fn))
2101  {
2102  __COUT__ << "Preferences are defaults." << std::endl;
2103  //insert defaults, no pref document found
2104  xmldoc->addTextElementToData(PREF_XML_BGCOLOR_FIELD, PREF_XML_BGCOLOR_DEFAULT);
2105  xmldoc->addTextElementToData(PREF_XML_DBCOLOR_FIELD, PREF_XML_DBCOLOR_DEFAULT);
2106  xmldoc->addTextElementToData(PREF_XML_WINCOLOR_FIELD, PREF_XML_WINCOLOR_DEFAULT);
2107  xmldoc->addTextElementToData(PREF_XML_LAYOUT_FIELD, PREF_XML_LAYOUT_DEFAULT);
2108  }
2109  else
2110  {
2111  __COUT__ << "Saved Preferences found." << std::endl;
2112  xmldoc->copyDataChildren(prefXml);
2113  }
2114 
2115  char permStr[10];
2116 
2117  //add settings if super user
2118  if (includeAccounts && p == (uint8_t)-1)
2119  {
2120 
2121  __COUT__ << "Super User on our hands" << std::endl;
2122 
2123  xmldoc->addTextElementToData(PREF_XML_ACCOUNTS_FIELD, "");
2124 
2125 
2126  //get all accounts
2127  for (uint64_t i = 0; i < UsersUsernameVector.size(); ++i)
2128  {
2129  xmldoc->addTextElementToParent("username", UsersUsernameVector[i], PREF_XML_ACCOUNTS_FIELD);
2130  xmldoc->addTextElementToParent("display_name", UsersDisplayNameVector[i], PREF_XML_ACCOUNTS_FIELD);
2131  if (UsersUserEmailVector.size() > i)
2132  {
2133  xmldoc->addTextElementToParent("useremail", UsersUserEmailVector[i], PREF_XML_ACCOUNTS_FIELD);
2134  }
2135  else
2136  {
2137  xmldoc->addTextElementToParent("useremail", "", PREF_XML_ACCOUNTS_FIELD);
2138 
2139  }
2140  sprintf(permStr, "%d", UsersPermissionsVector[i]);
2141  xmldoc->addTextElementToParent("permissions", permStr, PREF_XML_ACCOUNTS_FIELD);
2142  if (UsersSaltVector[i] == "") //only give nac if account has not been activated yet with password
2143  sprintf(permStr, "%d", int(UsersAccountCreatedTimeVector[i] & 0xffff));
2144  else
2145  permStr[0] = '\0';
2146  xmldoc->addTextElementToParent("nac", permStr, PREF_XML_ACCOUNTS_FIELD);
2147  }
2148  }
2149 
2150  //get system layout defaults
2151  fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
2152  (std::string)SYSTEM_PREFERENCES_PREFIX + "." + (std::string)USERS_PREFERENCES_FILETYPE;
2153  if (!prefXml.loadXmlDocument(fn))
2154  {
2155  __COUT__ << "System Preferences are defaults." << std::endl;
2156  //insert defaults, no pref document found
2157  xmldoc->addTextElementToData(PREF_XML_SYSLAYOUT_FIELD, PREF_XML_SYSLAYOUT_DEFAULT);
2158  }
2159  else
2160  {
2161  __COUT__ << "Saved System Preferences found." << std::endl;
2162  xmldoc->copyDataChildren(prefXml);
2163  }
2164 
2165  //add permissions value
2166  sprintf(permStr, "%d", p);
2167  xmldoc->addTextElementToData(PREF_XML_PERMISSIONS_FIELD, permStr);
2168 
2169  //add user with lock
2170  xmldoc->addTextElementToData(PREF_XML_USERLOCK_FIELD, usersUsernameWithLock_);
2171  //add user name
2172  xmldoc->addTextElementToData(PREF_XML_USERNAME_FIELD, getUsersUsername(uid));
2173 }
2174 
2175 //========================================================================================================================
2176 //WebUsers::setGenericPreference
2177 // each generic preference has its own directory, and each user has their own file
2178 void WebUsers::setGenericPreference(uint64_t uid, const std::string &preferenceName,
2179  const std::string &preferenceValue)
2180 {
2181  uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2182  //__COUT__ << "setGenericPreference for user: " << UsersUsernameVector[userIndex] << std::endl;
2183 
2184  //force alpha-numeric with dash/underscore
2185  std::string safePreferenceName = "";
2186  for (const auto &c : preferenceName)
2187  if ((c >= 'a' && c <= 'z') ||
2188  (c >= 'A' && c <= 'Z') ||
2189  (c >= '0' && c <= '9') ||
2190  (c >= '-' || c <= '_'))
2191  safePreferenceName += c;
2192 
2193  std::string dir = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
2194  "generic_" + safePreferenceName + "/";
2195 
2196  //attempt to make directory (just in case)
2197  mkdir(dir.c_str(), 0755);
2198 
2199  std::string fn = UsersUsernameVector[userIndex] + "_" + safePreferenceName +
2200  "." + (std::string)USERS_PREFERENCES_FILETYPE;
2201 
2202  __COUT__ << "Preferences file: " << (dir + fn) << std::endl;
2203 
2204  FILE *fp = fopen((dir + fn).c_str(), "w");
2205  if (fp)
2206  {
2207  fprintf(fp, "%s", preferenceValue.c_str());
2208  fclose(fp);
2209  }
2210  else
2211  __COUT_ERR__ << "Preferences file could not be opened for writing!" << std::endl;
2212 }
2213 
2214 //========================================================================================================================
2215 //WebUsers::getGenericPreference
2216 // each generic preference has its own directory, and each user has their own file
2217 // default preference is empty string.
2218 std::string WebUsers::getGenericPreference(uint64_t uid, const std::string &preferenceName,
2219  HttpXmlDocument *xmldoc) const
2220 {
2221  uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2222  //__COUT__ << "getGenericPreference for user: " << UsersUsernameVector[userIndex] << std::endl;
2223 
2224  //force alpha-numeric with dash/underscore
2225  std::string safePreferenceName = "";
2226  for (const auto &c : preferenceName)
2227  if ((c >= 'a' && c <= 'z') ||
2228  (c >= 'A' && c <= 'Z') ||
2229  (c >= '0' && c <= '9') ||
2230  (c >= '-' || c <= '_'))
2231  safePreferenceName += c;
2232 
2233  std::string dir = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
2234  "generic_" + safePreferenceName + "/";
2235 
2236  std::string fn = UsersUsernameVector[userIndex] + "_" + safePreferenceName +
2237  "." + (std::string)USERS_PREFERENCES_FILETYPE;
2238 
2239  __COUT__ << "Preferences file: " << (dir + fn) << std::endl;
2240 
2241  //read from preferences file
2242  FILE *fp = fopen((dir + fn).c_str(), "rb");
2243  if (fp)
2244  {
2245  fseek(fp, 0, SEEK_END);
2246  long size = ftell(fp);
2247  std::string line;
2248  line.reserve(size + 1);
2249  rewind(fp);
2250  fgets(&line[0], size + 1, fp);
2251  fclose(fp);
2252 
2253  __COUT__ << "Read value " << line << std::endl;
2254  if (xmldoc) xmldoc->addTextElementToData(safePreferenceName, line);
2255  return line;
2256  }
2257  else
2258  __COUT__ << "Using default value." << std::endl;
2259 
2260  //default preference is empty string
2261  if (xmldoc) xmldoc->addTextElementToData(safePreferenceName, "");
2262  return "";
2263 }
2264 
2265 //========================================================================================================================
2266 //WebUsers::changeSettingsForUser
2267 void WebUsers::changeSettingsForUser(uint64_t uid, const std::string &bgcolor, const std::string &dbcolor,
2268  const std::string &wincolor, const std::string &layout, const std::string &syslayout)
2269 {
2270  uint8_t p = getPermissionsForUser(uid);
2271  if (!p) return; //not an active user
2272 
2273  uint64_t userIndex = searchUsersDatabaseForUserId(uid);
2274  __COUT__ << "Changing settings for user: " << UsersUsernameVector[userIndex] << std::endl;
2275 
2276  std::string fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH + UsersUsernameVector[userIndex] + "." + (std::string)USERS_PREFERENCES_FILETYPE;
2277 
2278  __COUT__ << "Preferences file: " << fn << std::endl;
2279 
2280  HttpXmlDocument prefXml;
2281  prefXml.addTextElementToData(PREF_XML_BGCOLOR_FIELD, bgcolor);
2282  prefXml.addTextElementToData(PREF_XML_DBCOLOR_FIELD, dbcolor);
2283  prefXml.addTextElementToData(PREF_XML_WINCOLOR_FIELD, wincolor);
2284  prefXml.addTextElementToData(PREF_XML_LAYOUT_FIELD, layout);
2285 
2286  prefXml.saveXmlDocument(fn);
2287 
2288  //if admin privilieges set system default layouts
2289  if (p < 255) return; //not admin
2290 
2291  //set system layout defaults
2292  fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
2293  (std::string)SYSTEM_PREFERENCES_PREFIX + "." + (std::string)USERS_PREFERENCES_FILETYPE;
2294 
2295  HttpXmlDocument sysPrefXml;
2296  sysPrefXml.addTextElementToData(PREF_XML_SYSLAYOUT_FIELD, syslayout);
2297 
2298  sysPrefXml.saveXmlDocument(fn);
2299 
2300 }
2301 
2302 //========================================================================================================================
2303 //WebUsers::setUserWithLock
2304 // if lock is true, set lock user specified
2305 // if lock is false, attempt to unlock user specified
2306 // return true on success
2307 bool WebUsers::setUserWithLock(uint64_t uid_master, bool lock, std::string username)
2308 {
2309  uint8_t p = getPermissionsForUser(uid_master);
2310 
2311  __COUT__ << "permissions " << (int)p << std::endl;
2312  __COUT__ << "usersUsernameWithLock_ " << usersUsernameWithLock_ << std::endl;
2313  __COUT__ << "lock " << lock << std::endl;
2314  __COUT__ << "username " << username << std::endl;
2315  __COUT__ << "isUsernameActive(username) " << isUsernameActive(username) << std::endl;
2316 
2317  if (p != (uint8_t)-1) return false; //not a super user
2318 
2319 
2320  if (lock && (isUsernameActive(username) || !CareAboutCookieCodes_)) //lock and currently active
2321  {
2322  if (!CareAboutCookieCodes_ && username != DEFAULT_ADMIN_USERNAME)
2323  {
2324  __COUT__ << "User tried to lock for a user other than admin in wiz mode. Not allowed." << std::endl;
2325  return false;
2326  }
2327  usersUsernameWithLock_ = username;
2328  }
2329  else if (!lock && usersUsernameWithLock_ == username) //unlock
2330  usersUsernameWithLock_ = "";
2331  else
2332  {
2333  if (!isUsernameActive(username))
2334  __COUT__ << "User is inactive." << std::endl;
2335  __MOUT_WARN__ << "Failed to lock for user '" << username << ".'" << std::endl;
2336  return false;
2337  }
2338 
2339  __MOUT_INFO__ << "User '" << username << "' has locked out the system!" << std::endl;
2340 
2341  //save username with lock
2342  {
2343  std::string securityFileName = USER_WITH_LOCK_FILE;
2344  FILE *fp = fopen(securityFileName.c_str(), "w");
2345  if (!fp)
2346  {
2347  __COUT_INFO__ << "USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE <<
2348  " not found. Ignoring." << std::endl;
2349  }
2350  else
2351  {
2352  fprintf(fp, "%s", usersUsernameWithLock_.c_str());
2353  fclose(fp);
2354  }
2355  }
2356  return true;
2357 }
2358 
2359 //========================================================================================================================
2360 //WebUsers::modifyAccountSettings
2361 void WebUsers::modifyAccountSettings(uint64_t uid_master, uint8_t cmd_type, std::string username, std::string displayname, std::string email, std::string permissions)
2362 {
2363 
2364  uint8_t p = getPermissionsForUser(uid_master);
2365  if (p != (uint8_t)-1) return; //not a super user
2366 
2367  uint64_t modi = searchUsersDatabaseForUsername(username);
2368  if (modi == 0)
2369  {
2370  __COUT__ << "Cannot modify first user" << std::endl;
2371  return;
2372  }
2373 
2374  if (username.length() < USERNAME_LENGTH || displayname.length() < DISPLAY_NAME_LENGTH)
2375  {
2376  __COUT__ << "Invalid Username or Display Name must be length " << USERNAME_LENGTH << " or " << DISPLAY_NAME_LENGTH << std::endl;
2377  return;
2378  }
2379 
2380  int inputp;
2381  sscanf(permissions.c_str(), "%d", &inputp);
2382 
2383  __COUT__ << "Input Permissions " << inputp << std::endl;
2384 
2385  switch (cmd_type)
2386  {
2387  case MOD_TYPE_UPDATE:
2388  __COUT__ << "MOD_TYPE_UPDATE " << username << " := " << inputp << std::endl;
2389  if (modi == NOT_FOUND_IN_DATABASE)
2390  {
2391  __COUT__ << "User not found!? Should not happen." << std::endl;
2392  return;
2393  }
2394 
2395  UsersDisplayNameVector[modi] = displayname;
2396  UsersUserEmailVector[modi] = email;
2397 
2398  //account is currently inactive! if re-activiating, reset fail count and password,
2399  // this is account unlock mechanism.
2400  if (!UsersPermissionsVector[modi] && inputp)
2401  {
2402  UsersLoginFailureCountVector[modi] = 0;
2403  UsersSaltVector[modi] = "";
2404  }
2405  UsersPermissionsVector[modi] = inputp;
2406 
2407  //save information about modifier
2408  {
2409  uint64_t i = searchUsersDatabaseForUserId(uid_master);
2410  if (i == NOT_FOUND_IN_DATABASE)
2411  {
2412  __COUT__ << "Master User not found!? Should not happen." << std::endl;
2413  return;
2414  }
2415  UsersLastModifierUsernameVector[modi] = UsersUsernameVector[i];
2416  UsersLastModifiedTimeVector[modi] = time(0);
2417  }
2418  break;
2419  case MOD_TYPE_ADD:
2420  __COUT__ << "MOD_TYPE_ADD " << username << " - " << displayname << std::endl;
2421  createNewAccount(username, displayname, email);
2422  break;
2423  case MOD_TYPE_DELETE:
2424  __COUT__ << "MOD_TYPE_DELETE " << username << " - " << displayname << std::endl;
2425  deleteAccount(username, displayname);
2426  break;
2427  default:
2428  __COUT__ << "Undefined command - do nothing " << username << std::endl;
2429  }
2430 
2431  saveDatabaseToFile(DB_USERS);
2432 }
2433 //========================================================================================================================
2434 //WebUsers::getActiveUsersString
2435 // return comma separated list of active Display Names
2436 std::string WebUsers::getActiveUsersString()
2437 {
2438  std::string ret = "";
2439  uint64_t u;
2440  bool repeat;
2441  for (uint64_t i = 0; i < ActiveSessionUserIdVector.size(); ++i)
2442  {
2443  repeat = false;
2444  //check for no repeat
2445  for (uint64_t j = 0; j < i; ++j)
2446  if (ActiveSessionUserIdVector[i] == ActiveSessionUserIdVector[j])
2447  {
2448  repeat = true; break;
2449  } //found repeat!
2450 
2451  if (!repeat && (u = searchUsersDatabaseForUserId(ActiveSessionUserIdVector[i])) !=
2452  NOT_FOUND_IN_DATABASE) //if found, add displayName
2453  ret += UsersDisplayNameVector[u] + ",";
2454  }
2455  if (ret.length() > 1) ret.erase(ret.length() - 1); //get rid of last comma
2456  return ret;
2457 }
2458 //========================================================================================================================
2459 //WebUsers::getAdminUserID
2460 //
2461 uint64_t WebUsers::getAdminUserID()
2462 {
2463  uint64_t uid = searchUsersDatabaseForUsername(DEFAULT_ADMIN_USERNAME);
2464  return uid;
2465 }
2466 
2467 
2468 //========================================================================================================================
2469 //WebUsers::loadUserWithLock
2470 // //load username with lock from file
2471 void WebUsers::loadUserWithLock()
2472 {
2473  char username[300] = ""; //assume username is less than 300 chars
2474 
2475  std::string securityFileName = USER_WITH_LOCK_FILE;
2476  FILE *fp = fopen(securityFileName.c_str(), "r");
2477  if (!fp)
2478  {
2479  __COUT_INFO__ << "USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE <<
2480  " not found. Defaulting to admin lock." << std::endl;
2481 
2482  //default to admin lock if no file exists
2483  sprintf(username, "%s", DEFAULT_ADMIN_USERNAME.c_str());
2484  }
2485  else
2486  {
2487  fgets(username, 300, fp);
2488  username[299] = '\0'; //likely does nothing, but make sure there is closure on string
2489  fclose(fp);
2490  }
2491 
2492  //attempt to set lock
2493  __COUT__ << "Attempting to load username with lock: " << username << std::endl;
2494 
2495  if (strlen(username) == 0)
2496  {
2497  __COUT_INFO__ << "Loaded state for user-with-lock is unlocked." << std::endl;
2498  return;
2499  }
2500 
2501  uint64_t i = searchUsersDatabaseForUsername(username);
2502  if (i == NOT_FOUND_IN_DATABASE)
2503  {
2504  __COUT_INFO__ << "username " << username <<
2505  " not found in database. Ignoring." << std::endl;
2506  return;
2507  }
2508  __COUT__ << "Setting lock" << std::endl;
2509  setUserWithLock(UsersUserIdVector[i], true, username);
2510 }
2511 
2512 //========================================================================================================================
2513 //WebUsers::getSecurity
2514 //
2515 std::string WebUsers::getSecurity()
2516 {
2517  return securityType_;
2518 }
2519 //========================================================================================================================
2520 //WebUsers::loadSecuritySelection
2521 //
2522 void WebUsers::loadSecuritySelection()
2523 {
2524  std::string securityFileName = SECURITY_FILE_NAME;
2525  FILE *fp = fopen(securityFileName.c_str(), "r");
2526  char line[100] = "";
2527  if (fp) fgets(line, 100, fp);
2528  unsigned int i = 0;
2529 
2530  //find first character that is not alphabetic
2531  while (i < strlen(line) && line[i] >= 'A' && line[i] <= 'z') ++i;
2532  line[i] = '\0'; //end string at first illegal character
2533 
2534 
2535  if (strcmp(line, SECURITY_TYPE_NONE.c_str()) == 0 ||
2536  strcmp(line, SECURITY_TYPE_DIGEST_ACCESS.c_str()) == 0)
2537  securityType_ = line;
2538  else
2539  securityType_ = SECURITY_TYPE_DIGEST_ACCESS; // default, if illegal
2540 
2541  __COUT__ << "The current security type is " << securityType_ << std::endl;
2542 
2543  if (fp) fclose(fp);
2544 
2545 
2546  if (securityType_ == SECURITY_TYPE_NONE)
2547  CareAboutCookieCodes_ = false;
2548  else
2549  CareAboutCookieCodes_ = true;
2550 
2551  __COUT__ << "CareAboutCookieCodes_: " <<
2552  CareAboutCookieCodes_ << std::endl;
2553 
2554 }
2555 
2556 
2557 
2558 //========================================================================================================================
2559 void WebUsers::NACDisplayThread(std::string nac, std::string user)
2560 {
2561  INIT_MF("WebUsers_NAC");
2563  //thread notifying the user about the admin new account code
2564  // notify for 10 seconds (e.g.)
2565 
2566  // child thread
2567  int i = 0;
2568  for (; i < 5; ++i)
2569  {
2570  std::this_thread::sleep_for(std::chrono::seconds(2));
2571  __COUT__ << __COUT_HDR_P__ << "\n******************************************************************** " << std::endl;
2572  __COUT__ << __COUT_HDR_P__ << "\n******************************************************************** " << std::endl;
2573  __COUT__ << __COUT_HDR_P__ << "\n\nNew account code = " << nac << " for user: " << user << "\n" << std::endl;
2574  __COUT__ << __COUT_HDR_P__ << "\n******************************************************************** " << std::endl;
2575  __COUT__ << __COUT_HDR_P__ << "\n******************************************************************** " << std::endl;
2576  }
2577 }
2578 
2579 //========================================================================================================================
2580 void WebUsers::deleteUserData()
2581 {
2582  //delete Login data
2583  std::system(("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH + "/*").c_str());
2584  std::system(("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH + "/*").c_str());
2585  std::system(("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH + "/*").c_str());
2586  std::system(("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH + "/*").c_str());
2587  std::system(("rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH).c_str());
2588 
2589  std::string serviceDataPath = getenv("SERVICE_DATA_PATH");
2590  //delete macro maker folders
2591  std::system(("rm -rf " + std::string(serviceDataPath) + "/MacroData/").c_str());
2592  std::system(("rm -rf " + std::string(serviceDataPath) + "/MacroHistory/").c_str());
2593  std::system(("rm -rf " + std::string(serviceDataPath) + "/MacroExport/").c_str());
2594 
2595  //delete console folders
2596  std::system(("rm -rf " + std::string(serviceDataPath) + "/ConsolePreferences/").c_str());
2597 
2598  //delete wizard folders
2599  std::system(("rm -rf " + std::string(serviceDataPath) + "/OtsWizardData/").c_str());
2600 
2601  //delete progress bar folders
2602  std::system(("rm -rf " + std::string(serviceDataPath) + "/ProgressBarData/").c_str());
2603 
2604  //delete The Supervisor run folders
2605  std::system(("rm -rf " + std::string(serviceDataPath) + "/RunNumber/").c_str());
2606  std::system(("rm -rf " + std::string(serviceDataPath) + "/RunControlData/").c_str());
2607 
2608  //delete Visualizer folders
2609  std::system(("rm -rf " + std::string(serviceDataPath) + "/VisualizerData/").c_str());
2610 
2611  //delete active groups file
2612  std::system(("rm -rf " + std::string(serviceDataPath) + "/ActiveConfigurationGroups.cfg").c_str());
2613 
2614  //delete Logbook folders
2615  std::system(("rm -rf " + std::string(getenv("LOGBOOK_DATA_PATH")) + "/").c_str());
2616 
2617  std::cout << __COUT_HDR_FL__ << "$$$$$$$$$$$$$$ Successfully deleted ALL service user data $$$$$$$$$$$$" << std::endl;
2618 }
2619 
2620