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