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