otsdaq  v2_04_01
WebUsers.h
1 #ifndef _ots_Utilities_WebUsers_h_
2 #define _ots_Utilities_WebUsers_h_
3 
4 #include "otsdaq-core/Macros/CoutMacros.h"
5 #include "otsdaq-core/Macros/StringMacros.h"
6 #include "otsdaq-core/MessageFacility/MessageFacility.h"
7 #include "otsdaq-core/SOAPUtilities/SOAPMessenger.h"
8 #include "xgi/Method.h" //for cgicc::Cgicc
9 
10 #include <iostream>
11 #include <string>
12 #include <unordered_map>
13 #include <vector>
14 
15 #define WEB_LOGIN_DB_PATH std::string(__ENV__("SERVICE_DATA_PATH")) + "/LoginData/"
16 #define WEB_LOGIN_CERTDATA_PATH std::string(__ENV__("CERT_DATA_PATH"))
17 #define HASHES_DB_PATH "HashesData/"
18 #define USERS_DB_PATH "UsersData/"
19 #define USERS_LOGIN_HISTORY_PATH USERS_DB_PATH + "UserLoginHistoryData/"
20 #define USERS_PREFERENCES_PATH USERS_DB_PATH + "UserPreferencesData/"
21 #define TOOLTIP_DB_PATH USERS_DB_PATH + "/TooltipData/"
22 
23 namespace ots
24 {
25 class HttpXmlDocument;
26 
27 // WebUsers
28 // This class provides the functionality for managing all otsdaq user account preferences
29 // and permissions.
30 class WebUsers
31 {
32  public:
33  WebUsers();
34 
35  enum
36  {
37  SESSION_ID_LENGTH = 512,
38  COOKIE_CODE_LENGTH = 512,
39  NOT_FOUND_IN_DATABASE = uint64_t(-1),
40  USERNAME_LENGTH = 4,
41  DISPLAY_NAME_LENGTH = 4,
42  };
43 
44  using permissionLevel_t = uint8_t;
45  enum
46  {
47  PERMISSION_LEVEL_ADMIN =
48  WebUsers::permissionLevel_t(-1), // max permission level!
49  PERMISSION_LEVEL_EXPERT = 100,
50  PERMISSION_LEVEL_USER = 10,
51  PERMISSION_LEVEL_NOVICE = 1,
52  PERMISSION_LEVEL_INACTIVE = 0,
53  };
54 
55  static const std::string DEFAULT_ADMIN_USERNAME;
56  static const std::string DEFAULT_ADMIN_DISPLAY_NAME;
57  static const std::string DEFAULT_ADMIN_EMAIL;
58  static const std::string DEFAULT_ITERATOR_USERNAME;
59  static const std::string DEFAULT_STATECHANGER_USERNAME;
60  static const std::string DEFAULT_USER_GROUP;
61 
62  static const std::string REQ_NO_LOGIN_RESPONSE;
63  static const std::string REQ_NO_PERMISSION_RESPONSE;
64  static const std::string REQ_USER_LOCKOUT_RESPONSE;
65  static const std::string REQ_LOCK_REQUIRED_RESPONSE;
66  static const std::string REQ_ALLOW_NO_USER;
67 
68  static const std::string SECURITY_TYPE_NONE;
69  static const std::string SECURITY_TYPE_DIGEST_ACCESS;
70 
72  {
73  // WebUsers is a "Friend" class of RequestUserInfo so has access to private
74  // members.
75  friend class WebUsers;
76 
77  RequestUserInfo(const std::string& requestType, const std::string& cookieCode)
78  : requestType_(requestType)
79  , cookieCode_(cookieCode)
80  , uid_(-1) // init to invalid user, since only WebUser owner will have access
81  // to uid. RemoteWebUsers will see invalid uid.
82  {
83  }
84 
85  //------- setters --------//
86  //===========================================
87  // setGroupPermissionLevels
88  bool setGroupPermissionLevels(const std::string& groupPermissionLevelsString)
89  {
90  //__COUTV__(groupPermissionLevelsString);
91  permissionLevel_ = 0; // default to inactive, i.e. no access
92 
93  StringMacros::getMapFromString( // re-factor membership string to set
94  groupPermissionLevelsString,
95  groupPermissionLevelMap_);
96  getGroupPermissionLevel(); // setup permissionLevel_
97 
98  //__COUTV__((unsigned int)permissionLevel_);
99  return true; // was fully setup
100  } // end setGroupPermissionLevels()
101 
102  //------- getters --------//
103  const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>&
104  getGroupPermissionLevels() const
105  {
106  return groupPermissionLevelMap_;
107  }
108  //===========================================
109  // getGroupPermissionLevel
110  // sets up permissionLevel based on already prepared RequestUserInfo members
111  const WebUsers::permissionLevel_t& getGroupPermissionLevel()
112  {
113  permissionLevel_ = 0; // default to inactive, i.e. no access
114 
115  // check groups allowed
116  // i.e. if user is a member of one of the groups allowed
117  // then consider for highest permission level
118  bool matchedAcceptGroup = false;
119  for(const auto& userGroupPair : groupPermissionLevelMap_)
120  if(StringMacros::inWildCardSet( // if group is in allowed groups
121  userGroupPair.first,
122  groupsAllowed_) && // AND...
123  userGroupPair.second >
124  permissionLevel_) // if is a new high level, then...
125  {
126  permissionLevel_ =
127  userGroupPair.second; // take as new permission level
128  matchedAcceptGroup = true;
129  }
130 
131  // if no group match in groups allowed, then failed
132  if(!matchedAcceptGroup && groupsAllowed_.size())
133  {
134  __COUT_INFO__
135  << "User (@" << ip_
136  << ") has insufficient group permissions: user is in these groups... "
137  << StringMacros::mapToString(groupPermissionLevelMap_)
138  << " and the allowed groups are... "
139  << StringMacros::setToString(groupsAllowed_) << std::endl;
140  return permissionLevel_;
141  }
142 
143  // if no access groups specified, then check groups disallowed
144  if(!groupsAllowed_.size())
145  {
146  for(const auto& userGroupPair : groupPermissionLevelMap_)
147  if(StringMacros::inWildCardSet(userGroupPair.first,
148  groupsDisallowed_))
149  {
150  __COUT_INFO__
151  << "User (@" << ip_
152  << ") is in a disallowed group: user is in these groups... "
153  << StringMacros::mapToString(groupPermissionLevelMap_)
154  << " and the disallowed groups are... "
155  << StringMacros::setToString(groupsDisallowed_) << std::endl;
156  return permissionLevel_;
157  }
158  }
159 
160  // if no groups have been explicitly allowed nor disallowed
161  // then permission level should come from WebUsers::DEFAULT_USER_GROUP
162  auto findIt = groupPermissionLevelMap_.find(WebUsers::DEFAULT_USER_GROUP);
163  if(findIt != groupPermissionLevelMap_.end())
164  {
165  // found default group, take permission level
166  permissionLevel_ = findIt->second;
167  }
168 
169  return permissionLevel_;
170  } // end getGroupPermissionLevel()
171 
172  inline bool isInactive()
173  {
174  return permissionLevel_ == WebUsers::PERMISSION_LEVEL_INACTIVE;
175  }
176  inline bool isAdmin()
177  {
178  return permissionLevel_ == WebUsers::PERMISSION_LEVEL_ADMIN;
179  }
180 
181  // members extracted from supervisor properties on a per request type basis
182  const std::string& requestType_;
183  std::string cookieCode_;
184 
185  bool automatedCommand_, NonXMLRequestType_, NoXmlWhiteSpace_;
186  bool checkLock_, requireLock_, allowNoUser_;
187 
188  std::set<std::string> groupsAllowed_, groupsDisallowed_;
189 
190  WebUsers::permissionLevel_t permissionLevel_, permissionsThreshold_;
191  std::string ip_;
192  uint64_t uid_ /*only WebUser owner has access to uid, RemoteWebUsers do not*/;
193  std::string username_, displayName_, usernameWithLock_;
194  uint64_t activeUserSessionIndex_;
195 
196  private:
197  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>
198  groupPermissionLevelMap_;
199  };
200 
201  // for the gateway supervisor to check request access
202  // if false, gateway request handling code should just return.. out is handled on
203  // false; on true, out is untouched
204  bool xmlRequestOnGateway(cgicc::Cgicc& cgi,
205  std::ostringstream* out,
206  HttpXmlDocument* xmldoc,
207  WebUsers::RequestUserInfo& userInfo);
208 
209  public:
210  // used by gateway and other supervisors to verify requests consistently
211  static void initializeRequestUserInfo(cgicc::Cgicc& cgi,
212  WebUsers::RequestUserInfo& userInfo);
213  static bool checkRequestAccess(cgicc::Cgicc& cgi,
214  std::ostringstream* out,
215  HttpXmlDocument* xmldoc,
216  WebUsers::RequestUserInfo& userInfo,
217  bool isWizardMode = false);
218 
219  bool createNewAccount(const std::string& username,
220  const std::string& displayName,
221  const std::string& email);
222  void cleanupExpiredEntries(std::vector<std::string>* loggedOutUsernames = 0);
223  std::string createNewLoginSession(const std::string& uuid, const std::string& ip);
224 
225  uint64_t attemptActiveSession(const std::string& uuid,
226  std::string& jumbledUser,
227  const std::string& jumbledPw,
228  std::string& newAccountCode,
229  const std::string& ip);
230  uint64_t attemptActiveSessionWithCert(const std::string& uuid,
231  std::string& jumbledEmail,
232  std::string& cookieCode,
233  std::string& username,
234  const std::string& ip);
235  uint64_t isCookieCodeActiveForLogin(const std::string& uuid,
236  std::string& cookieCode,
237  std::string& username);
238  bool cookieCodeIsActiveForRequest(
239  std::string& cookieCode,
240  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>*
241  userPermissions = 0,
242  uint64_t* uid = 0,
243  const std::string& ip = "0",
244  bool refresh = true,
245  std::string* userWithLock = 0,
246  uint64_t* activeUserSessionIndex = 0);
247  uint64_t cookieCodeLogout(const std::string& cookieCode,
248  bool logoutOtherUserSessions,
249  uint64_t* uid = 0,
250  const std::string& ip = "0");
251  bool checkIpAccess(const std::string& ip);
252 
253  std::string getUsersDisplayName(uint64_t uid);
254  std::string getUsersUsername(uint64_t uid);
255  uint64_t getActiveSessionCountForUser(uint64_t uid);
256  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>
257  getPermissionsForUser(uint64_t uid);
258  void insertSettingsForUser(uint64_t uid,
259  HttpXmlDocument* xmldoc,
260  bool includeAccounts = false);
261  std::string getGenericPreference(uint64_t uid,
262  const std::string& preferenceName,
263  HttpXmlDocument* xmldoc = 0) const;
264 
265  void changeSettingsForUser(uint64_t uid,
266  const std::string& bgcolor,
267  const std::string& dbcolor,
268  const std::string& wincolor,
269  const std::string& layout,
270  const std::string& syslayout);
271  void setGenericPreference(uint64_t uid,
272  const std::string& preferenceName,
273  const std::string& preferenceValue);
274  static void tooltipCheckForUsername(const std::string& username,
275  HttpXmlDocument* xmldoc,
276  const std::string& srcFile,
277  const std::string& srcFunc,
278  const std::string& srcId);
279  static void tooltipSetNeverShowForUsername(const std::string& username,
280  HttpXmlDocument* xmldoc,
281  const std::string& srcFile,
282  const std::string& srcFunc,
283  const std::string& srcId,
284  bool doNeverShow,
285  bool temporarySilence);
286 
287  void modifyAccountSettings(uint64_t actingUid,
288  uint8_t cmd_type,
289  const std::string& username,
290  const std::string& displayname,
291  const std::string& email,
292  const std::string& permissions);
293  bool setUserWithLock(uint64_t actingUid, bool lock, const std::string& username);
294  std::string getUserWithLock(void) { return usersUsernameWithLock_; }
295 
296  std::string getActiveUsersString(void);
297 
298  bool getUserInfoForCookie(std::string& cookieCode,
299  std::string* userName,
300  std::string* displayName = 0,
301  uint64_t* activeSessionIndex = 0);
302 
303  bool isUsernameActive(const std::string& username) const;
304  bool isUserIdActive(uint64_t uid) const;
305  uint64_t getAdminUserID(void);
306  std::string getSecurity(void);
307 
308  static void deleteUserData(void);
309  static void resetAllUserTooltips(const std::string& userNeedle = "*");
310 
311  static void NACDisplayThread(const std::string& nac, const std::string& user);
312 
313  void saveActiveSessions(void);
314  void loadActiveSessions(void);
315 
316  private:
317  inline WebUsers::permissionLevel_t getPermissionLevelForGroup(
318  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
319  const std::string& groupName = WebUsers::DEFAULT_USER_GROUP);
320  inline bool isInactiveForGroup(
321  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
322  const std::string& groupName = WebUsers::DEFAULT_USER_GROUP);
323  inline bool isAdminForGroup(
324  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
325  const std::string& groupName = WebUsers::DEFAULT_USER_GROUP);
326 
327  void loadSecuritySelection(void);
328  void loadUserWithLock(void);
329  unsigned int hexByteStrToInt(const char* h);
330  void intToHexStr(uint8_t i, char* h);
331  std::string sha512(const std::string& user,
332  const std::string& password,
333  std::string& salt);
334  std::string dejumble(const std::string& jumbledUser, const std::string& sessionId);
335  std::string createNewActiveSession(uint64_t uid,
336  const std::string& ip = "0",
337  uint64_t asIndex = 0);
338  bool addToHashesDatabase(const std::string& hash);
339  std::string genCookieCode(void);
340  std::string refreshCookieCode(unsigned int i, bool enableRefresh = true);
341  void removeActiveSessionEntry(unsigned int i);
342  void removeLoginSessionEntry(unsigned int i);
343  bool deleteAccount(const std::string& username, const std::string& displayName);
344  void incrementIpBlacklistCount(const std::string& ip);
345 
346  void saveToDatabase(FILE* fp,
347  const std::string& field,
348  const std::string& value,
349  uint8_t type = DB_SAVE_OPEN_AND_CLOSE,
350  bool addNewLine = true);
351  bool saveDatabaseToFile(uint8_t db);
352  bool loadDatabases(void);
353 
354  uint64_t searchUsersDatabaseForUsername(const std::string& username) const;
355  uint64_t searchUsersDatabaseForUserEmail(const std::string& useremail) const;
356  uint64_t searchUsersDatabaseForUserId(uint64_t uid) const;
357  uint64_t searchLoginSessionDatabaseForUUID(const std::string& uuid) const;
358  uint64_t searchHashesDatabaseForHash(const std::string& hash);
359  uint64_t searchActiveSessionDatabaseForCookie(const std::string& cookieCode) const;
360 
361  static std::string getTooltipFilename(const std::string& username,
362  const std::string& srcFile,
363  const std::string& srcFunc,
364  const std::string& srcId);
365  std::string getUserEmailFromFingerprint(const std::string& fingerprint);
366 
367  enum
368  {
369  DB_USERS,
370  DB_HASHES
371  };
372 
373  enum
374  {
375  MOD_TYPE_UPDATE,
376  MOD_TYPE_ADD,
377  MOD_TYPE_DELETE
378  };
379 
380  enum
381  {
382  DB_SAVE_OPEN_AND_CLOSE,
383  DB_SAVE_OPEN,
384  DB_SAVE_CLOSE
385  };
386 
387  std::unordered_map<std::string, std::string> certFingerprints_;
388 
389  std::vector<std::string> UsersDatabaseEntryFields, HashesDatabaseEntryFields;
390  bool CareAboutCookieCodes_;
391  std::string securityType_;
392 
393  //"Login Session" database associations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
394  // Generate random sessionId when receive a unique user ID (UUID)
395  // reject UUID that have been used recently (e.g. last 5 minutes)
396  // Maintain list of active sessionIds and associated UUID
397  // remove from list if been idle after some time or login attempts (e.g. 5 minutes or
398  // 3 login attempts) maybe track IP address, to block multiple failed login attempts
399  // from same IP. Use sessionId to un-jumble login attempts, lookup using UUID
400  std::vector<std::string> LoginSessionIdVector, LoginSessionUUIDVector,
401  LoginSessionIpVector;
402  std::vector<time_t> LoginSessionStartTimeVector;
403  std::vector<uint8_t> LoginSessionAttemptsVector;
404  enum
405  {
406  LOGIN_SESSION_EXPIRATION_TIME = 5 * 60, // 5 minutes
407  LOGIN_SESSION_ATTEMPTS_MAX = 5, // 5 attempts on same session, forces new session
408  };
409 
410  //"Active Session" database associations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
411  // Maintain list of valid cookieCodes and associated user
412  // all request must come with a valid cookieCode, else server fails request
413  // On logout request, invalidate cookieCode
414  // cookieCode expires after some idle time (e.g. 5 minutes) and
415  // is renewed and possibly changed each request
416  //"single user - multiple locations" issue resolved using ActiveSessionIndex
417  // where each independent login starts a new thread of cookieCodes tagged with
418  // ActiveSessionIndex if cookieCode not refreshed, then return most recent cookie
419  // code
420  std::vector<std::string> ActiveSessionCookieCodeVector, ActiveSessionIpVector;
421  std::vector<uint64_t> ActiveSessionUserIdVector, ActiveSessionIndex;
422  std::vector<time_t> ActiveSessionStartTimeVector;
423  enum
424  {
425  ACTIVE_SESSION_EXPIRATION_TIME = 120 * 60, // 120 minutes, cookie is changed
426  // every half period of
427  // ACTIVE_SESSION_EXPIRATION_TIME
428  ACTIVE_SESSION_COOKIE_OVERLAP_TIME =
429  10 * 60, // 10 minutes of overlap when new cookie is generated
430  ACTIVE_SESSION_STALE_COOKIE_LIMIT =
431  10, // 10 stale cookies allowed for each active user
432  };
433 
434  //"Users" database associations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
435  // Maintain list of acceptable Usernames and associate:
436  // permissions
437  // map of group name to permission level (e.g. users, experts, masters) 0 to 255
438  // note: all users are at least in group WebUsers::DEFAULT_USER_GROUP
439  // 0 := account inactive, not allowed to login (e.g. could be due to too many
440  // failed login attempts) 1 := normal user 255 := admin for things in group
441  // permission level is determined by finding the highest permission level number (0 to
442  // 255) for an allowed group.. then that permission level is compared to the
443  // threshold
444  //
445  // Last Login attempt time, and last USERS_LOGIN_HISTORY_SIZE successful logins
446  // Name to display
447  // random salt, before first login salt is empty string ""
448  // Keep count of login attempt failures. Limit failures per unit time (e.g. 5 per
449  // hour) Preferences (e.g. color scheme, etc) Username appends to preferences file,
450  // and login history file UsersLastModifierUsernameVector - is username of last
451  // master user to modify something about account UsersLastModifierTimeVector - is
452  // time of last modify by a master user
453  std::vector<std::string> UsersUsernameVector, UsersUserEmailVector,
454  UsersDisplayNameVector, UsersSaltVector, UsersLastModifierUsernameVector;
455  std::vector<std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> >
456  UsersPermissionsVector;
457  std::vector<uint64_t> UsersUserIdVector;
458  std::vector<time_t> UsersLastLoginAttemptVector, UsersAccountCreatedTimeVector,
459  UsersLastModifiedTimeVector;
460  std::vector<uint8_t> UsersLoginFailureCountVector;
461  uint64_t usersNextUserId_;
462  enum
463  {
464  USERS_LOGIN_HISTORY_SIZE = 20,
465  USERS_GLOBAL_HISTORY_SIZE = 1000,
466  USERS_MAX_LOGIN_FAILURES = 20,
467  };
468  std::string usersUsernameWithLock_;
469 
470  std::vector<std::string> UsersLoggedOutUsernames_;
471 
472  //"Hashes" database associations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
473  // Maintain list of acceptable encoded (SHA-512) salt+user+pw's
474  std::vector<std::string> HashesVector;
475  std::vector<time_t> HashesAccessTimeVector;
476 
477  enum
478  {
479  IP_BLACKLIST_COUNT_THRESHOLD = 200,
480  };
481  std::map<std::string /*ip*/, uint32_t /*errorCount*/> ipBlacklistCounts_;
482 };
483 
484 } // namespace ots
485 
486 #endif