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