otsdaq  3.10.00
RemoteWebUsers.cc
1 #include "otsdaq/WebUsersUtilities/RemoteWebUsers.h"
2 
3 #include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
4 #include "otsdaq/SOAPUtilities/SOAPCommand.h"
5 #include "otsdaq/SOAPUtilities/SOAPParameters.h" //must include in .h for static function
6 #include "otsdaq/SOAPUtilities/SOAPUtilities.h"
7 #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
8 
9 #include <cstdio>
10 #include <cstdlib>
11 #include <tuple>
12 #include <vector>
13 
14 #include "otsdaq/SupervisorInfo/AllSupervisorInfo.h"
15 
16 using namespace ots;
17 
18 #undef __MF_SUBJECT__
19 #define __MF_SUBJECT__ "RemoteWebUsers"
20 
21 //==============================================================================
29 //==============================================================================
31  xdaq::Application* application,
32  XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisorDescriptor)
33  : SOAPMessenger(application)
34  , gatewaySupervisorDescriptor_(gatewaySupervisorDescriptor)
35 {
36  ActiveUserLastUpdateTime_ = 0; // init to never
37  ActiveUserList_ = ""; // init to empty
38 } // end constructor()
39 
40 //==============================================================================
44 bool RemoteWebUsers::xmlRequestToGateway(cgicc::Cgicc& cgi,
45  std::ostringstream* out,
46  HttpXmlDocument* xmldoc,
47  const AllSupervisorInfo& allSupervisorInfo,
48  WebUsers::RequestUserInfo& userInfo)
49 {
50  //__COUT__ << std::endl;
51  // initialize user info parameters to failed results
53 
54  XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisor;
55 
56  SOAPParameters parameters;
57  xoap::MessageReference retMsg;
58 
59  //**** start LOGIN GATEWAY CODE ***//
60  // If TRUE, cookie code is good, and refreshed code is in cookieCode
61  // Else, error message is returned in cookieCode
62 
64  // if Wiz or Macormaker mode, use sequence instead of cookieCode
65  if(allSupervisorInfo.isWizardMode() || allSupervisorInfo.isMacroMakerMode())
66  {
67  // if missing CookieCode... check if in Wizard mode and using sequence
68  std::string sequence =
69  CgiDataUtilities::getOrPostData(cgi, "sequence"); // from GET or POST
70  //__COUT__ << "sequence=" << sequence << std::endl;
71  if(!sequence.length())
72  {
73  __COUT_ERR__ << "Invalid access attempt (@" << userInfo.ip_ << ")."
74  << std::endl;
75  *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
76  // invalid cookie and also invalid sequence
77  goto HANDLE_ACCESS_FAILURE; // return false, access failed
78  }
79 
80  // have sequence, try it out
81 
82  if(allSupervisorInfo.isWizardMode())
83  gatewaySupervisor = allSupervisorInfo.getWizardInfo().getDescriptor();
84  else //is MacroMaker mode
85  gatewaySupervisor = allSupervisorInfo.getAllMacroMakerTypeSupervisorInfo()
86  .begin()
87  ->second.getDescriptor();
88 
89  if(!gatewaySupervisor)
90  {
91  __COUT_ERR__ << "Missing gateway supervisor." << std::endl;
92  *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
93  // sequence code present, but no wizard supervisor
94  goto HANDLE_ACCESS_FAILURE; // return false, access failed
95  }
96 
97  parameters.addParameter("sequence", sequence);
98  parameters.addParameter("IPAddress", userInfo.ip_);
99  retMsg = SOAPMessenger::sendWithSOAPReply(
100  gatewaySupervisor, "SupervisorSequenceCheck", parameters);
101  parameters.clear();
102  parameters.addParameter("Permissions");
103  SOAPUtilities::receive(retMsg, parameters);
104 
105  userInfo.setGroupPermissionLevels(parameters.getValue("Permissions"));
106 
108  cgi, out, xmldoc, userInfo, true /*isWizardMode*/, sequence))
109  return true; //successful sequence login!
110  else
111  goto HANDLE_ACCESS_FAILURE; // return false, access failed
112  } //end Wiz or Macormaker mode
113 
114  // else proceed with inquiry to Gateway Supervisor
115 
116  gatewaySupervisor = allSupervisorInfo.getGatewayInfo().getDescriptor();
117 
118  if(!gatewaySupervisor)
119  {
120  __COUT_ERR__ << "Missing gateway supervisor." << std::endl;
121  *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
122  goto HANDLE_ACCESS_FAILURE; // return false, access failed
123  }
124 
125  // ---- Cookie Check Cache: attempt lookup ----
126  // Bypass cache when requireLock_ and no one holds lock --
127  // Gateway must auto-take lock (see GatewaySupervisor::supervisorCookieCheck).
128  // Safe: requireLock_ is forced false for automatedCommand_ requests
129  // (see CorePropertySupervisorBase::getRequestUserInfo), so high-freq polling always caches.
130  {
131  bool cacheHit = false;
132  {
133  std::lock_guard<std::mutex> cacheLock(cookieCheckCacheMutex_);
134  auto cacheIt = cookieCheckCache_.find(userInfo.cookieCode_);
135  if(cacheIt != cookieCheckCache_.end() &&
136  (time(0) - cacheIt->second.cacheTime) < COOKIE_CHECK_CACHE_TTL)
137  {
138  if(!(userInfo.requireLock_ && cacheIt->second.userWithLock.empty()))
139  {
140  userInfo.setGroupPermissionLevels(cacheIt->second.permissions);
141  userInfo.cookieCode_ = cacheIt->second.cookieCode;
142  userInfo.username_ = cacheIt->second.username;
143  userInfo.displayName_ = cacheIt->second.displayName;
144  userInfo.usernameWithLock_ = cacheIt->second.userWithLock;
145  cacheHit = true;
146  }
147  }
148  } // mutex released
149 
150  if(cacheHit)
151  {
152  if(!WebUsers::checkRequestAccess(cgi, out, xmldoc, userInfo))
153  goto HANDLE_ACCESS_FAILURE;
154  return true;
155  }
156  }
157  // ---- end Cookie Check Cache lookup ----
158 
159  // Save original cookie code before SOAP (Gateway may refresh it in the response)
160  {
161  const std::string originalCookieCode = userInfo.cookieCode_;
162 
163  parameters.clear();
164  parameters.addParameter("CookieCode", userInfo.cookieCode_);
165  parameters.addParameter("RefreshOption", userInfo.automatedCommand_ ? "0" : "1");
166  parameters.addParameter("IPAddress", userInfo.ip_);
167  parameters.addParameter("RequireLock", userInfo.requireLock_ ? "1" : "0");
168 
169  retMsg = SOAPMessenger::sendWithSOAPReply(
170  gatewaySupervisor, "SupervisorCookieCheck", parameters);
171 
172  parameters.clear();
173  parameters.addParameter("CookieCode");
174  parameters.addParameter("Permissions");
175  parameters.addParameter("UserGroups");
176  parameters.addParameter("UserWithLock");
177  parameters.addParameter("Username");
178  parameters.addParameter("DisplayName");
179  // parameters.addParameter("ActiveSessionIndex");
180  SOAPUtilities::receive(retMsg, parameters);
181 
182  // first extract a few things always from parameters
183  // like permissionLevel for this request... must consider allowed groups!!
184  userInfo.setGroupPermissionLevels(parameters.getValue("Permissions"));
185  userInfo.cookieCode_ = parameters.getValue("CookieCode");
186  userInfo.username_ = parameters.getValue("Username");
187  userInfo.displayName_ = parameters.getValue("DisplayName");
188  userInfo.usernameWithLock_ = parameters.getValue("UserWithLock");
189  // userInfo.activeUserSessionIndex_ = strtoul(parameters.getValue("ActiveSessionIndex").c_str(), 0, 0);
190 
191  // ---- Cookie Check Cache: store successful response ----
192  if(userInfo.cookieCode_.length() == WebUsers::COOKIE_CODE_LENGTH)
193  {
194  std::lock_guard<std::mutex> cacheLock(cookieCheckCacheMutex_);
195  cookieCheckCache_[originalCookieCode] = {time(0),
196  userInfo.cookieCode_,
197  parameters.getValue("Permissions"),
198  userInfo.username_,
199  userInfo.displayName_,
200  userInfo.usernameWithLock_};
201 
202  // Prune stale entries (bounded by active users, typically < 20)
203  if(cookieCheckCache_.size() > 10)
204  {
205  time_t now = time(0);
206  for(auto it = cookieCheckCache_.begin(); it != cookieCheckCache_.end();)
207  {
208  if(now - it->second.cacheTime > 2 * COOKIE_CHECK_CACHE_TTL)
209  it = cookieCheckCache_.erase(it);
210  else
211  ++it;
212  }
213  }
214  }
215  // ---- end Cookie Check Cache store ----
216  } // end originalCookieCode scope
217 
218  if(!WebUsers::checkRequestAccess(cgi, out, xmldoc, userInfo))
219  goto HANDLE_ACCESS_FAILURE; // return false, access failed
220  // else successful access request!
221 
222  return true; // request granted
223 
225 
226 HANDLE_ACCESS_FAILURE:
227 
228  // print out return string on failure
229  if(!userInfo.automatedCommand_)
230  __COUT_ERR__ << "Failed request (requestType = " << userInfo.requestType_
231  << "): " << out->str() << __E__;
232  return false; // access failed
233 } // end xmlRequestToGateway()
234 
235 //==============================================================================
240 {
241  if(time(0) - ActiveUserLastUpdateTime_ >
242  ACTIVE_USERS_UPDATE_THRESHOLD) // need to update
243  {
244  __COUTS__(2) << "Need to update active user list" << std::endl;
245 
246  xoap::MessageReference retMsg = ots::SOAPMessenger::sendWithSOAPReply(
247  gatewaySupervisorDescriptor_, "SupervisorGetActiveUsers");
248 
249  SOAPParameters retParameters("UserList");
250  SOAPUtilities::receive(retMsg, retParameters);
251 
252  ActiveUserLastUpdateTime_ = time(0);
253  return (ActiveUserList_ = retParameters.getValue("UserList"));
254  }
255  else
256  return ActiveUserList_;
257 } // end getActiveUserList()
258 
259 //==============================================================================
266  std::map<std::string /* group type */,
267  std::tuple<std::string /*group name*/,
269  std::string /* time string*/>>& theGroups)
270 {
271  xoap::MessageReference retMsg =
272  ots::SOAPMessenger::sendWithSOAPReply(gatewaySupervisorDescriptor_,
273  "SupervisorLastTableGroupRequest",
274  SOAPParameters("ActionOfLastGroup", "ALL"));
275 
276  SOAPParameters retParameters;
277  retParameters.addParameter("GroupName");
278  retParameters.addParameter("GroupKey");
279  retParameters.addParameter("GroupAction");
280  retParameters.addParameter("GroupActionTime");
281  SOAPUtilities::receive(retMsg, retParameters);
282 
283  //parse as CSV
284  std::vector<std::string> groupNames =
285  StringMacros::getVectorFromString(retParameters.getValue("GroupName"), {','});
286  std::vector<std::string> groupKeys =
287  StringMacros::getVectorFromString(retParameters.getValue("GroupKey"), {','});
288  std::vector<std::string> groupActions =
289  StringMacros::getVectorFromString(retParameters.getValue("GroupAction"), {','});
290  std::vector<std::string> groupTimes = StringMacros::getVectorFromString(
291  retParameters.getValue("GroupActionTime"), {','});
292 
293  if(groupNames.size() < 2)
294  {
295  //expecting something like 7?
296  __SS__ << "Failure in handling request for recent config group activity. "
297  "Response received was this: \n"
298  << SOAPUtilities::translate(retMsg) << __E__;
299  __SS_THROW__;
300  }
301 
302  if(groupNames.size() != groupKeys.size() ||
303  groupNames.size() != groupActions.size() || groupNames.size() != groupTimes.size())
304  {
305  __SS__ << "Illegal list size mismatch while retrieving recent config group info. "
306  "Should not be possible! Notify admins."
307  << __E__;
308  __SS_THROW__;
309  }
310 
311  for(size_t i = 0; i < groupNames.size(); ++i)
312  {
313  theGroups[groupActions[i]] = std::make_tuple(
314  groupNames[i], strtol(groupKeys[i].c_str(), 0, 0), groupTimes[i]);
315  }
316 
317  __COUTT__ << "Done with getLastTableGroups()" << __E__;
318 } // end getLastTableGroup()
319 
320 //==============================================================================
326 std::pair<std::string /*group name*/, TableGroupKey> RemoteWebUsers::getLastTableGroup(
327  const std::string& actionOfLastGroup, std::string& actionTimeString)
328 {
329  actionTimeString = "";
330  xoap::MessageReference retMsg = ots::SOAPMessenger::sendWithSOAPReply(
332  "SupervisorLastTableGroupRequest",
333  SOAPParameters("ActionOfLastGroup", actionOfLastGroup));
334 
335  SOAPParameters retParameters;
336  retParameters.addParameter("GroupName");
337  retParameters.addParameter("GroupKey");
338  retParameters.addParameter("GroupAction");
339  retParameters.addParameter("GroupActionTime");
340  SOAPUtilities::receive(retMsg, retParameters);
341 
342  std::pair<std::string /*group name*/, TableGroupKey> theGroup;
343  if(retParameters.getValue("GroupAction") !=
344  actionOfLastGroup) // if action doesn't match.. weird
345  {
346  __SS__ << "Returned group action '" << retParameters.getValue("GroupAction")
347  << "' does not match requested group action '" << actionOfLastGroup << ".'"
348  << std::endl;
349  __SS_THROW__;
350  }
351  // else we have an action match
352 
353  theGroup.first = retParameters.getValue("GroupName");
354  theGroup.second = strtol(retParameters.getValue("GroupKey").c_str(), 0, 0);
355  actionTimeString = retParameters.getValue("GroupActionTime");
356  return theGroup;
357 } // end getLastTableGroup()
358 
359 //==============================================================================
363 void RemoteWebUsers::sendSystemMessage(const std::string& toUser,
364  const std::string& message,
365  bool doEmail /*=false*/)
366 {
367  sendSystemMessage(toUser, "" /*subject*/, message, doEmail);
368 } // end sendSystemMessage)
369 
370 //==============================================================================
374 void RemoteWebUsers::sendSystemMessage(const std::string& toUser,
375  const std::string& subject,
376  const std::string& message,
377  bool doEmail /*=false*/)
378 {
379  SOAPParameters parameters;
380  parameters.addParameter("ToUser", toUser); // CSV list or *
381  parameters.addParameter("Subject", subject);
382  parameters.addParameter("Message", message);
383  parameters.addParameter("DoEmail", doEmail ? "1" : "0");
384 
385  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
386  gatewaySupervisorDescriptor_, "SupervisorSystemMessage", parameters);
387 
388  //__COUT__ << SOAPUtilities::translate(retMsg) << __E__;
389 } // end sendSystemMessage)
390 
391 //==============================================================================
394 void RemoteWebUsers::makeSystemLogEntry(const std::string& entryText)
395 {
396  SOAPParameters parameters;
397  parameters.addParameter("EntryText", entryText);
398 
399  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
400  gatewaySupervisorDescriptor_, "SupervisorSystemLogbookEntry", parameters);
401 
402  //__COUT__ << SOAPUtilities::translate(retMsg) << __E__;
403 } // end makeSystemLogEntry()
bool isWizardMode(void) const
BOOLs.
static std::string getOrPostData(cgicc::Cgicc &cgi, const std::string &needle)
XDAQ_CONST_CALL xdaq::ApplicationDescriptor * gatewaySupervisorDescriptor_
void getLastTableGroups(std::map< std::string, std::tuple< std::string, TableGroupKey, std::string >> &theGroups)
RemoteWebUsers(xdaq::Application *application, XDAQ_CONST_CALL xdaq::ApplicationDescriptor *gatewaySupervisorDescriptor)
std::pair< std::string, TableGroupKey > getLastTableGroup(const std::string &actionOfLastGroup, std::string &returnedActionTimeString)
actionOfLastGroup = "Configured" or "Started", for example
std::string getActiveUserList(void)
void sendSystemMessage(const std::string &toUser, const std::string &message, bool doEmail=false)
void makeSystemLogEntry(const std::string &entryText)
bool xmlRequestToGateway(cgicc::Cgicc &cgi, std::ostringstream *out, HttpXmlDocument *xmldoc, const AllSupervisorInfo &allSupervisorInfo, WebUsers::RequestUserInfo &userInfo)
XDAQ_CONST_CALL xdaq::ApplicationDescriptor * getDescriptor(void) const
Getters ----------------—.
static bool checkRequestAccess(cgicc::Cgicc &cgi, std::ostringstream *out, HttpXmlDocument *xmldoc, WebUsers::RequestUserInfo &userInfo, bool isWizardMode=false, const std::string &wizardModeSequence="")
Definition: WebUsers.cc:271
static void initializeRequestUserInfo(cgicc::Cgicc &cgi, WebUsers::RequestUserInfo &userInfo)
used by gateway and other supervisors to verify requests consistently
Definition: WebUsers.cc:250
defines used also by OtsConfigurationWizardSupervisor
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
bool setGroupPermissionLevels(const std::string &groupPermissionLevelsString)
end setGroupPermissionLevels()
Definition: WebUsers.h:263