otsdaq  v2_01_00
RemoteWebUsers.cc
1 #include "otsdaq-core/WebUsersUtilities/RemoteWebUsers.h"
2 
3 #include "otsdaq-core/SOAPUtilities/SOAPParameters.h" //must include in .h for static function
4 #include "otsdaq-core/SOAPUtilities/SOAPUtilities.h"
5 #include "otsdaq-core/SOAPUtilities/SOAPCommand.h"
6 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h"
7 #include "otsdaq-core/CgiDataUtilities/CgiDataUtilities.h"
8 
9 #include <cstdlib>
10 #include <cstdio>
11 #include <vector>
12 
13 #include "otsdaq-core/SupervisorInfo/AllSupervisorInfo.h"
14 
15 
16 using namespace ots;
17 
18 #undef __MF_SUBJECT__
19 #define __MF_SUBJECT__ "RemoteWebUsers"
20 
21 //========================================================================================================================
22 //User Notes:
23 // - use xmlRequestGateway to check security from outside the Supervisor and Wizard
24 //
25 // Example usage:
26 //
27 //
28 //
29 // void exampleClass::exampleRequestHandler(xgi::Input * in, xgi::Output * out)
30 //
31 // {
32 // cgicc::Cgicc cgi(in);
33 //
34 // //...
35 //
36 // HttpXmlDocument xmldoc;
37 // std::string userWithLock, userName, displayName;
38 // uint64_t activeSessionIndex;
39 // uint8_t userPermissions;
40 //
41 // //**** start LOGIN GATEWAY CODE ***//
42 // //check cookieCode, sequence, userWithLock, and permissions access all in one shot!
43 // {
44 // bool automaticCommand = 0; //automatic commands should not refresh cookie code.. only user initiated commands should!
45 // bool checkLock = true;
46 // bool lockRequired = true;
47 //
48 // if(!theRemoteWebUsers_.xmlRequestToGateway(
49 // cgi,out,&xmldoc,theSupervisorsConfiguration_
50 // ,&userPermissions //acquire user's access level (optionally null pointer)//
51 // ,!automaticCommand //true/false refresh cookie code
52 // ,USER_PERMISSIONS_THRESHOLD //set access level requirement to pass gateway
53 // ,checkLock //true/false enable check that system is unlocked or this user has the lock
54 // ,lockRequired //true/false requires this user has the lock to proceed
55 // ,&userWithLock //acquire username with lock (optionally null pointer)
56 // ,&userName //acquire username of this user (optionally null pointer)
57 // ,0//,&displayName //acquire user's Display Name
58 // ,0//,&activeSessionIndex //acquire user's session index associated with the cookieCode
59 // ))
60 // { //failure
61 // //std::cout << out->str() << std::endl; //could print out return string on failure
62 // return;
63 // }
64 // }
65 // //done checking cookieCode, sequence, userWithLock, and permissions access all in one shot!
66 // //**** end LOGIN GATEWAY CODE ***//
67 //
68 // //Success! if here.
69 // //
70 // //... use acquired values below
71 // //...
72 //
73 // //add to xml document, for example:
74 // //DOMElement* parentEl;
75 // //parentEl = xmldoc.addTextElementToData("ExampleTag", "parent-data");
76 // //xmldoc.addTextElementToParent("ExampleChild", "child-data", parentEl);
77 //
78 // //return xml doc holding server response
79 // //xmldoc.outputXmlDocument((std::ostringstream*) out, true); //true to also print to std::cout
80 // }
81 //
82 //
83 //========================================================================================================================
84 
85 
86 
87 
88 RemoteWebUsers::RemoteWebUsers(xdaq::Application* application)
89 : SOAPMessenger (application)
90 {
91  ActiveUserLastUpdateTime_ = 0; //init to never
92  ActiveUserList_ = ""; //init to empty
93 }
94 
95 //========================================================================================================================
96 //xmlRequestGateway
97 // if false, user code should just return.. out is handled on false; on true, out is untouched
98 bool RemoteWebUsers::xmlRequestToGateway(
99  cgicc::Cgicc& cgi,
100  std::ostringstream* out,
101  HttpXmlDocument* xmldoc,
102  const AllSupervisorInfo& allSupervisorInfo,
103  WebUsers::RequestUserInfo& userInfo )
104 {
105 
106  //initialize user info parameters to failed results
107  WebUsers::initializeRequestUserInfo(cgi,userInfo);
108 
109  //const_cast away the const
110  // so that this line is compatible with slf6 and slf7 versions of xdaq
111  // where they changed to XDAQ_CONST_CALL xdaq::ApplicationDescriptor* in slf7
112  //
113  // XDAQ_CONST_CALL is defined in "otsdaq-core/Macros/CoutMacros.h"
114  XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisor;
115 
116  SOAPParameters parameters;
117  xoap::MessageReference retMsg;
118 
119  //**** start LOGIN GATEWAY CODE ***//
120  //If TRUE, cookie code is good, and refreshed code is in cookieCode
121  //Else, error message is returned in cookieCode
122  //tmpCookieCode_ = CgiDataUtilities::getOrPostData(cgi,"CookieCode"); //from GET or POST
123 
124  // __COUT__ << cookieCode.length() << std::endl;
125  // __COUT__ << "cookieCode=" << cookieCode << std::endl;
126  // __COUT__ << std::endl;
127 
129  //have CookieCode, try it out
130  if(allSupervisorInfo.isWizardMode())
131  {
132  //if missing CookieCode... check if in Wizard mode and using sequence
133  std::string sequence = CgiDataUtilities::getOrPostData(cgi,"sequence"); //from GET or POST
134  //__COUT__ << "sequence=" << sequence << std::endl;
135  if(!sequence.length())
136  {
137  __COUT__ << "Invalid attempt (@" << userInfo.ip_ << ")." << std::endl;
138  *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
139  //invalid cookie and also invalid sequence
140  goto HANDLE_ACCESS_FAILURE; //return false, access failed
141  }
142 
143  //have sequence, try it out
144 
145  gatewaySupervisor = allSupervisorInfo.getWizardInfo().getDescriptor();
146  if(!gatewaySupervisor)
147  {
148  __COUT_ERR__ << "Missing wizard supervisor." << std::endl;
149  *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
150  //sequence code present, but no wizard supervisor
151  goto HANDLE_ACCESS_FAILURE; //return false, access failed
152  }
153 
154  parameters.addParameter("sequence",sequence);
155  parameters.addParameter("IPAddress",userInfo.ip_);
156  retMsg = SOAPMessenger::sendWithSOAPReply(gatewaySupervisor,
157  "SupervisorSequenceCheck", parameters);
158  parameters.clear();
159  parameters.addParameter("Permissions");
160  receive(retMsg, parameters);
161 
162  userInfo.setGroupPermissionLevels(parameters.getValue("Permissions"));
163 
164  if(WebUsers::checkRequestAccess(cgi,out,xmldoc,userInfo,true /*isWizardMode*/))
165  return true;
166  else
167  goto HANDLE_ACCESS_FAILURE; //return false, access failed
168 
169 // if(userInfo.permissionLevel_ < userInfo.permissionsThreshold_)
170 // {
171 // *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
172 // __COUT__ << "User (@" << userInfo.ip_ << ") has insufficient permissions: " << userInfo.permissionLevel_ << "<" <<
173 // userInfo.permissionsThreshold_ << std::endl;
174 // return false; //invalid cookie and present sequence, but not correct sequence
175 // }
176 //
177 // userInfo.setUsername("admin");
178 // userInfo.setDisplayName("Admin");
179 // userInfo.setUsernameWithLock("admin");
180 // userInfo.setActiveUserSessionIndex(0);
181 // userInfo.setGroupMemebership("admin");
182 //
183 // return true; //successful sequence login!
184  }
185 
186  //else proceed with inquiry to Gateway Supervisor
187 
188  gatewaySupervisor = allSupervisorInfo.getGatewayInfo().getDescriptor();
189 
190  if(!gatewaySupervisor)
191  {
192  __COUT_ERR__ << "Missing gateway supervisor." << std::endl;
193  *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
194  goto HANDLE_ACCESS_FAILURE; //return false, access failed
195  }
196 
197  //__COUT__ << std::endl;
198 
199  parameters.clear();
200  parameters.addParameter("CookieCode",userInfo.cookieCode_);
201  parameters.addParameter("RefreshOption",userInfo.automatedCommand_?"0":"1");
202  parameters.addParameter("IPAddress",userInfo.ip_);
203 
204  retMsg = SOAPMessenger::sendWithSOAPReply(gatewaySupervisor,
205  "SupervisorCookieCheck", parameters);
206 
207  parameters.clear();
208  parameters.addParameter("CookieCode");
209  parameters.addParameter("Permissions");
210  parameters.addParameter("UserGroups");
211  parameters.addParameter("UserWithLock");
212  parameters.addParameter("Username");
213  parameters.addParameter("DisplayName");
214  parameters.addParameter("ActiveSessionIndex");
215  receive(retMsg, parameters);
216 
217  //first extract a few things always from parameters
218  // like permissionLevel for this request... must consider allowed groups!!
219  userInfo.setGroupPermissionLevels(parameters.getValue("Permissions"));
220  userInfo.cookieCode_ = parameters.getValue("CookieCode");
221  userInfo.username_ = parameters.getValue("Username");
222  userInfo.displayName_ = parameters.getValue("DisplayName");
223  userInfo.usernameWithLock_ = parameters.getValue("UserWithLock");
224  userInfo.activeUserSessionIndex_ = strtoul(parameters.getValue("ActiveSessionIndex").c_str(),0,0);
225 
226  if(!WebUsers::checkRequestAccess(cgi,out,xmldoc,userInfo))
227  goto HANDLE_ACCESS_FAILURE; //return false, access failed
228  //else successful access request!
229 
230  return true; //request granted
231 // if(!userInfo.checkLock_ && !userInfo.requireLock_)
232 // return true; //done, no need to get user info for this cookie code
233 //
234 
236  //get user info for cookie code and check lock (now that username is available)
237 
238 // parameters.clear();
239 // parameters.addParameter("CookieCode",userInfo.cookieCode_);
240 // retMsg = SOAPMessenger::sendWithSOAPReply(gatewaySupervisor,
241 // "SupervisorGetUserInfo", parameters);
242 //
243 // parameters.clear();
244 // parameters.addParameter("Username");
245 // parameters.addParameter("DisplayName");
246 // parameters.addParameter("ActiveSessionIndex");
247 // receive(retMsg, parameters);
248 
249 // if(WebUsers::finalizeRequestAccess(out,xmldoc,userInfo,
250 // parameters.getValue("Username"),
251 // parameters.getValue("DisplayName"),
252 // strtoul(parameters.getValue("ActiveSessionIndex").c_str(),0,0)))
253 // return true;
254 // else
255 // goto HANDLE_ACCESS_FAILURE; //return false, access failed
256 
257 HANDLE_ACCESS_FAILURE:
258 
259  //print out return string on failure
260  if(!userInfo.automatedCommand_)
261  __COUT_ERR__ << "Failed request (requestType = " << userInfo.requestType_ <<
262  "): " << out->str() << __E__;
263  return false; //access failed
264 
265 // tmpUserWithLock_ = parameters.getValue("UserWithLock");
266 // tmpUserGroups_ = parameters.getValue("UserGroups");
267 // sscanf(parameters.getValue("Permissions").c_str(),"%hhu",&userInfo.permissionLevel_); //unsigned char
268 // userInfo.setUsernameWithLock(parameters.getValue("UserWithLock"));
269 // userInfo.setGroupMemebership(parameters.getValue("UserGroups"));
270 // tmpCookieCode_ = parameters.getValue("CookieCode");
271 
272 
273 
274  //else access granted
275 
276  //__COUT__ << "cookieCode=" << cookieCode << std::endl;
277 //
278 // if(!allowNoUser && cookieCode.length() != WebUsers::COOKIE_CODE_LENGTH)
279 // {
280 // __COUT__ << "User (@" << ip << ") has invalid cookie code: " << cookieCode << std::endl;
281 // *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
282 // return false; //invalid cookie and present sequence, but not correct sequence
283 // }
284 //
285 // if(!allowNoUser && tmpUserPermissions_ < permissionsThreshold)
286 // {
287 // *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
288 // __COUT__ << "User (@" << ip << ") has insufficient permissions: " << tmpUserPermissions_ << "<" <<
289 // permissionsThreshold << std::endl;
290 // return false;
291 // }
292 
293  //check group membership
294 // if(!allowNoUser)
295 // {
296 // //check groups allowed
297 // // i.e. if user is a member of one of the groups allowed
298 // // then grant access
299 //
300 // std::set<std::string> userMembership;
301 // StringMacros::getSetFromString(
302 // tmpUserGroups_,
303 // userMembership);
304 //
305 // bool accept = false;
306 // for(const auto& userGroup:userMembership)
307 // if(StringMacros::inWildCardSet(
308 // userGroup,
309 // groupsAllowed))
310 // {
311 // accept = true;
312 // break;
313 // }
314 //
315 // if(!accept && userMembership.size())
316 // {
317 // *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
318 // std::stringstream ss;
319 // bool first = true;
320 // for(const auto& group:groupsAllowed)
321 // if(first && (first=false))
322 // ss << group;
323 // else
324 // ss << " | " << group;
325 //
326 // __COUT__ << "User (@" << ip << ") has insufficient group permissions: " << tmpUserGroups_ << " != " <<
327 // ss.str() << std::endl;
328 // return false;
329 // }
330 //
331 // //if no access groups specified, then check groups disallowed
332 // if(!userMembership.size())
333 // {
334 // for(const auto& userGroup:userMembership)
335 // if(StringMacros::inWildCardSet(
336 // userGroup,
337 // groupsDisallowed))
338 // {
339 // *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
340 // std::stringstream ss;
341 // bool first = true;
342 // for(const auto& group:groupsDisallowed)
343 // if(first && (first=false))
344 // ss << group;
345 // else
346 // ss << " | " << group;
347 //
348 // __COUT__ << "User (@" << ip << ") is in in a disallowed group permissions: " << tmpUserGroups_ << " == " <<
349 // ss.str() << std::endl;
350 // return false;
351 // }
352 // }
353 // } //end group membership check
354 //
355 // if(xmldoc) //fill with cookie code tag
356 // {
357 // if(!allowNoUser)
358 // xmldoc->setHeader(cookieCode);
359 // else
360 // xmldoc->setHeader(WebUsers::REQ_ALLOW_NO_USER);
361 // }
362 //
363 //
364 //
365 // if(!userName && !displayName && !activeSessionIndex && !checkLock && !lockRequired)
366 // return true; //done, no need to get user info for cookie
367 //
368 // //__COUT__ << "User with Lock: " << tmpUserWithLock_ << std::endl;
369 //
370 //
371 // /////////////////////////////////////////////////////
372 // //get user info
373 // parameters.clear();
374 // parameters.addParameter("CookieCode",cookieCode);
375 // retMsg = SOAPMessenger::sendWithSOAPReply(gatewaySupervisor,
376 // "SupervisorGetUserInfo", parameters);
377 //
378 // parameters.clear();
379 // parameters.addParameter("Username");
380 // parameters.addParameter("DisplayName");
381 // parameters.addParameter("ActiveSessionIndex");
382 // receive(retMsg, parameters);
383 //
384 //
385 //
386 //
387 // userInfo.setActiveUserSessionIndex(0);
388 //
389 //
390 // tmpUsername_ = parameters.getValue("Username");
391 // userInfo.setUsername(tmpUsername_);
392 // userInfo.setDisplayName(parameters.getValue("DisplayName"));
393 // userInfo.setActiveUserSessionIndex(
394 // strtoul(parameters.getValue("ActiveSessionIndex").c_str(),0,0));
395 //
396 // if(userInfo.checkLock_ && tmpUserWithLock_ != "" && tmpUserWithLock_ != tmpUsername_)
397 // {
398 // *out << WebUsers::REQ_USER_LOCKOUT_RESPONSE;
399 // __COUT__ << "User " << tmpUsername_ << " is locked out. " << tmpUserWithLock_ << " has lock." << std::endl;
400 // return false;
401 // }
402 //
403 // if(userInfo.lockRequired_ && tmpUserWithLock_ != tmpUsername_)
404 // {
405 // *out << WebUsers::REQ_LOCK_REQUIRED_RESPONSE;
406 // __COUT__ << "User " << tmpUsername_ << " must have lock to proceed. (" << tmpUserWithLock_ << " has lock.)" << std::endl;
407 // return false;
408 // }
409 //
410 // return true;
411 }
412 
413 //========================================================================================================================
414 //getActiveUserList
415 // if lastUpdateTime is not too recent as spec'd by ACTIVE_USERS_UPDATE_THRESHOLD
416 // if server responds with
417 std::string RemoteWebUsers::getActiveUserList(XDAQ_CONST_CALL xdaq::ApplicationDescriptor* supervisorDescriptor)
418 {
419 
420  if(1 || time(0) - ActiveUserLastUpdateTime_ > ACTIVE_USERS_UPDATE_THRESHOLD) //need to update
421  {
422 
423  __COUT__ << "Need to update " << std::endl;
424 
425  xoap::MessageReference retMsg = ots::SOAPMessenger::sendWithSOAPReply(supervisorDescriptor,"SupervisorGetActiveUsers");
426 
427 
428  SOAPParameters retParameters("UserList");
429  receive(retMsg, retParameters);
430 
431  ActiveUserLastUpdateTime_ = time(0);
432  return (ActiveUserList_ = retParameters.getValue("UserList"));
433  }
434  else
435  return ActiveUserList_;
436 }
437 
438 //========================================================================================================================
439 //getLastConfigGroup
440 // request last "Configured" or "Started" group, for example
441 // returns empty "" for actionTimeString on failure
442 // returns "Wed Dec 31 18:00:01 1969 CST" for actionTimeString (in CST) if action never has occurred
443 std::pair<std::string /*group name*/, ConfigurationGroupKey> RemoteWebUsers::getLastConfigGroup(
444  XDAQ_CONST_CALL xdaq::ApplicationDescriptor* supervisorDescriptor,
445  const std::string &actionOfLastGroup,
446  std::string &actionTimeString)
447 {
448  actionTimeString = "";
449  xoap::MessageReference retMsg = ots::SOAPMessenger::sendWithSOAPReply(
450  supervisorDescriptor,"SupervisorLastConfigGroupRequest",
451  SOAPParameters("ActionOfLastGroup",actionOfLastGroup));
452 
453 
454  SOAPParameters retParameters;
455  retParameters.addParameter("GroupName");
456  retParameters.addParameter("GroupKey");
457  retParameters.addParameter("GroupAction");
458  retParameters.addParameter("GroupActionTime");
459  receive(retMsg, retParameters);
460 
461  std::pair<std::string /*group name*/, ConfigurationGroupKey> theGroup;
462  if(retParameters.getValue("GroupAction") != actionOfLastGroup) //if action doesn't match.. weird
463  {
464  __COUT_WARN__ << "Returned group action '" << retParameters.getValue("GroupAction") <<
465  "' does not match requested group action '" << actionOfLastGroup << ".'" << std::endl;
466  return theGroup; //return empty and invalid
467  }
468  //else we have an action match
469 
470  theGroup.first = retParameters.getValue("GroupName");
471  theGroup.second = strtol(retParameters.getValue("GroupKey").c_str(),0,0);
472  actionTimeString = retParameters.getValue("GroupActionTime");
473  return theGroup;
474 }
475 
476 //========================================================================================================================
477 //getUserInfoForCookie
478 // get username and display name for user based on cookie code
479 // return true, if user info gotten successfully
480 // else false
481 bool RemoteWebUsers::getUserInfoForCookie(XDAQ_CONST_CALL xdaq::ApplicationDescriptor* supervisorDescriptor,
482  std::string &cookieCode, std::string *userName, std::string *displayName, uint64_t *activeSessionIndex)
483 {
484  __COUT__ << std::endl;
485  if(cookieCode.length() != WebUsers::COOKIE_CODE_LENGTH) return false; //return if invalid cookie code
486 
487  // SOAPParametersV parameters(1);
488  // parameters[0].setName("CookieCode"); parameters[0].setValue(cookieCode);
489  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(supervisorDescriptor, "SupervisorGetUserInfo", SOAPParameters("CookieCode",cookieCode));
490 
491  SOAPParameters retParameters;
492  retParameters.addParameter("Username");
493  retParameters.addParameter("DisplayName");
494  retParameters.addParameter("ActiveSessionIndex");
495  receive(retMsg, retParameters);
496  if(userName) *userName = retParameters.getValue("Username");
497  if(displayName) *displayName = retParameters.getValue("DisplayName");
498  if(activeSessionIndex) *activeSessionIndex = strtoul(retParameters.getValue("ActiveSessionIndex").c_str(),0,0);
499 
500  __COUT__ << "userName " << *userName << std::endl;
501 
502  return true;
503 }
504 
505 //========================================================================================================================
506 //cookieCodeIsActiveForRequest
507 // for external supervisors to check with Supervisor for login
508 bool RemoteWebUsers::cookieCodeIsActiveForRequest(XDAQ_CONST_CALL xdaq::ApplicationDescriptor* supervisorDescriptor,
509  std::string &cookieCode, uint8_t *userPermissions, std::string ip, bool refreshCookie, std::string *userWithLock)
510 {
511  //__COUT__ << "CookieCode: " << cookieCode << " " << cookieCode.length() << std::endl;
512  if(cookieCode.length() != WebUsers::COOKIE_CODE_LENGTH) return false; //return if invalid cookie code
513 
514  //
515 
516  SOAPParameters parameters;
517  parameters.addParameter("CookieCode",cookieCode);
518  parameters.addParameter("RefreshOption",refreshCookie?"1":"0");
519 
520  //__COUT__ << "CookieCode: " << cookieCode << std::endl;
521  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(supervisorDescriptor, "SupervisorCookieCheck", parameters);
522 
523 
524  SOAPParameters retParameters;
525  retParameters.addParameter("CookieCode");
526  retParameters.addParameter("Permissions");
527  retParameters.addParameter("UserWithLock");
528  receive(retMsg, retParameters);
529 
530 
531 
532  if(userWithLock) *userWithLock = retParameters.getValue("UserWithLock");
533  if(userPermissions) sscanf(retParameters.getValue("Permissions").c_str(),"%hhu",userPermissions); //unsigned char
534 
535  cookieCode = retParameters.getValue("CookieCode");
536 
537  return cookieCode.length() == WebUsers::COOKIE_CODE_LENGTH; //proper cookieCode has length WebUsers::COOKIE_CODE_LENGTH
538 }
539 //========================================================================================================================
540 //sendSystemMessage
541 // send system message to toUser through Supervisor
542 // toUser wild card * is to all users
543 void RemoteWebUsers::sendSystemMessage(XDAQ_CONST_CALL xdaq::ApplicationDescriptor* supervisorDescriptor, const std::string& toUser, const std::string& msg)
544 {
545  SOAPParameters parameters;
546  parameters.addParameter("ToUser" , toUser);
547  parameters.addParameter("Message", msg);
548 
549  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(supervisorDescriptor, "SupervisorSystemMessage",parameters);
550 }
551 
552 
553 //========================================================================================================================
554 //makeSystemLogbookEntry
555 // make system logbook through Supervisor
556 void RemoteWebUsers::makeSystemLogbookEntry(XDAQ_CONST_CALL xdaq::ApplicationDescriptor* supervisorDescriptor, const std::string& entryText)
557 {
558  SOAPParameters parameters;
559  parameters.addParameter("EntryText", entryText);
560 
561  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(supervisorDescriptor, "SupervisorSystemLogbookEntry",parameters);
562 }
563