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