LCOV - code coverage report
Current view: top level - otsdaq/otsdaq/WebUsersUtilities - RemoteWebUsers.cc (source / functions) Coverage Total Hit
Test: mu2edaq.info.cleaned Lines: 0.0 % 183 0
Test Date: 2026-09-10 01:53:42 Functions: 0.0 % 18 0

            Line data    Source code
       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              : //==============================================================================
      22              : /// User Notes:
      23              : ///     - use xmlRequestGateway to check security from outside the Supervisor and Wizard
      24              : ///
      25              : ///     Example usage: at void CoreSupervisorBase::requestWrapper(xgi::Input* in, xgi::Output* out)
      26              : ///
      27              : ///
      28              : ///
      29              : //==============================================================================
      30            0 : RemoteWebUsers::RemoteWebUsers(
      31              :     xdaq::Application* application,
      32            0 :     XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisorDescriptor)
      33              :     : SOAPMessenger(application)
      34            0 :     , gatewaySupervisorDescriptor_(gatewaySupervisorDescriptor)
      35              : {
      36            0 :         ActiveUserLastUpdateTime_ = 0;   // init to never
      37            0 :         ActiveUserList_           = "";  // init to empty
      38            0 : }  // end constructor()
      39              : 
      40              : //==============================================================================
      41              : /// xmlRequestGateway
      42              : ///     if false, user code should just return.. out is handled on false; on true, out is
      43              : /// untouched
      44            0 : 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
      52            0 :         WebUsers::initializeRequestUserInfo(cgi, userInfo);
      53              : 
      54              :         XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisor;
      55              : 
      56            0 :         SOAPParameters         parameters;
      57            0 :         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              : 
      63              :         /////////////////////////////////////////////////////
      64              :         // if Wiz or Macormaker mode, use sequence instead of cookieCode
      65            0 :         if(allSupervisorInfo.isWizardMode() || allSupervisorInfo.isMacroMakerMode())
      66              :         {
      67              :                 // if missing CookieCode... check if in Wizard mode and using sequence
      68              :                 std::string sequence =
      69            0 :                     CgiDataUtilities::getOrPostData(cgi, "sequence");  // from GET or POST
      70              :                 //__COUT__ << "sequence=" << sequence << std::endl;
      71            0 :                 if(!sequence.length())
      72              :                 {
      73            0 :                         __COUT_ERR__ << "Invalid access attempt (@" << userInfo.ip_ << ")."
      74            0 :                                      << std::endl;
      75            0 :                         *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
      76              :                         // invalid cookie and also invalid sequence
      77            0 :                         goto HANDLE_ACCESS_FAILURE;  // return false, access failed
      78              :                 }
      79              : 
      80              :                 // have sequence, try it out
      81              : 
      82            0 :                 if(allSupervisorInfo.isWizardMode())
      83            0 :                         gatewaySupervisor = allSupervisorInfo.getWizardInfo().getDescriptor();
      84              :                 else  //is MacroMaker mode
      85            0 :                         gatewaySupervisor = allSupervisorInfo.getAllMacroMakerTypeSupervisorInfo()
      86            0 :                                                 .begin()
      87            0 :                                                 ->second.getDescriptor();
      88              : 
      89            0 :                 if(!gatewaySupervisor)
      90              :                 {
      91            0 :                         __COUT_ERR__ << "Missing gateway supervisor." << std::endl;
      92            0 :                         *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
      93              :                         // sequence code present, but no wizard supervisor
      94            0 :                         goto HANDLE_ACCESS_FAILURE;  // return false, access failed
      95              :                 }
      96              : 
      97            0 :                 parameters.addParameter("sequence", sequence);
      98            0 :                 parameters.addParameter("IPAddress", userInfo.ip_);
      99            0 :                 retMsg = SOAPMessenger::sendWithSOAPReply(
     100            0 :                     gatewaySupervisor, "SupervisorSequenceCheck", parameters);
     101            0 :                 parameters.clear();
     102            0 :                 parameters.addParameter("Permissions");
     103            0 :                 SOAPUtilities::receive(retMsg, parameters);
     104              : 
     105            0 :                 userInfo.setGroupPermissionLevels(parameters.getValue("Permissions"));
     106              : 
     107            0 :                 if(WebUsers::checkRequestAccess(
     108              :                        cgi, out, xmldoc, userInfo, true /*isWizardMode*/, sequence))
     109            0 :                         return true;  //successful sequence login!
     110              :                 else
     111            0 :                         goto HANDLE_ACCESS_FAILURE;  // return false, access failed
     112            0 :         }                                    //end  Wiz or Macormaker mode
     113              : 
     114              :         // else proceed with inquiry to Gateway Supervisor
     115              : 
     116            0 :         gatewaySupervisor = allSupervisorInfo.getGatewayInfo().getDescriptor();
     117              : 
     118            0 :         if(!gatewaySupervisor)
     119              :         {
     120            0 :                 __COUT_ERR__ << "Missing gateway supervisor." << std::endl;
     121            0 :                 *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
     122            0 :                 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            0 :                 bool cacheHit = false;
     132              :                 {
     133            0 :                         std::lock_guard<std::mutex> cacheLock(cookieCheckCacheMutex_);
     134            0 :                         auto cacheIt = cookieCheckCache_.find(userInfo.cookieCode_);
     135            0 :                         if(cacheIt != cookieCheckCache_.end() &&
     136            0 :                            (time(0) - cacheIt->second.cacheTime) < COOKIE_CHECK_CACHE_TTL)
     137              :                         {
     138            0 :                                 if(!(userInfo.requireLock_ && cacheIt->second.userWithLock.empty()))
     139              :                                 {
     140            0 :                                         userInfo.setGroupPermissionLevels(cacheIt->second.permissions);
     141            0 :                                         userInfo.cookieCode_       = cacheIt->second.cookieCode;
     142            0 :                                         userInfo.username_         = cacheIt->second.username;
     143            0 :                                         userInfo.displayName_      = cacheIt->second.displayName;
     144            0 :                                         userInfo.usernameWithLock_ = cacheIt->second.userWithLock;
     145            0 :                                         cacheHit                   = true;
     146              :                                 }
     147              :                         }
     148            0 :                 }  // mutex released
     149              : 
     150            0 :                 if(cacheHit)
     151              :                 {
     152            0 :                         if(!WebUsers::checkRequestAccess(cgi, out, xmldoc, userInfo))
     153            0 :                                 goto HANDLE_ACCESS_FAILURE;
     154            0 :                         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            0 :                 const std::string originalCookieCode = userInfo.cookieCode_;
     162              : 
     163            0 :                 parameters.clear();
     164            0 :                 parameters.addParameter("CookieCode", userInfo.cookieCode_);
     165            0 :                 parameters.addParameter("RefreshOption", userInfo.automatedCommand_ ? "0" : "1");
     166            0 :                 parameters.addParameter("IPAddress", userInfo.ip_);
     167            0 :                 parameters.addParameter("RequireLock", userInfo.requireLock_ ? "1" : "0");
     168              : 
     169            0 :                 retMsg = SOAPMessenger::sendWithSOAPReply(
     170            0 :                     gatewaySupervisor, "SupervisorCookieCheck", parameters);
     171              : 
     172            0 :                 parameters.clear();
     173            0 :                 parameters.addParameter("CookieCode");
     174            0 :                 parameters.addParameter("Permissions");
     175            0 :                 parameters.addParameter("UserGroups");
     176            0 :                 parameters.addParameter("UserWithLock");
     177            0 :                 parameters.addParameter("Username");
     178            0 :                 parameters.addParameter("DisplayName");
     179              :                 // parameters.addParameter("ActiveSessionIndex");
     180            0 :                 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            0 :                 userInfo.setGroupPermissionLevels(parameters.getValue("Permissions"));
     185            0 :                 userInfo.cookieCode_       = parameters.getValue("CookieCode");
     186            0 :                 userInfo.username_         = parameters.getValue("Username");
     187            0 :                 userInfo.displayName_      = parameters.getValue("DisplayName");
     188            0 :                 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            0 :                 if(userInfo.cookieCode_.length() == WebUsers::COOKIE_CODE_LENGTH)
     193              :                 {
     194            0 :                         std::lock_guard<std::mutex> cacheLock(cookieCheckCacheMutex_);
     195            0 :                         cookieCheckCache_[originalCookieCode] = {time(0),
     196            0 :                                                                  userInfo.cookieCode_,
     197              :                                                                  parameters.getValue("Permissions"),
     198            0 :                                                                  userInfo.username_,
     199            0 :                                                                  userInfo.displayName_,
     200            0 :                                                                  userInfo.usernameWithLock_};
     201              : 
     202              :                         // Prune stale entries (bounded by active users, typically < 20)
     203            0 :                         if(cookieCheckCache_.size() > 10)
     204              :                         {
     205            0 :                                 time_t now = time(0);
     206            0 :                                 for(auto it = cookieCheckCache_.begin(); it != cookieCheckCache_.end();)
     207              :                                 {
     208            0 :                                         if(now - it->second.cacheTime > 2 * COOKIE_CHECK_CACHE_TTL)
     209            0 :                                                 it = cookieCheckCache_.erase(it);
     210              :                                         else
     211            0 :                                                 ++it;
     212              :                                 }
     213              :                         }
     214            0 :                 }
     215              :                 // ---- end Cookie Check Cache store ----
     216            0 :         }  // end originalCookieCode scope
     217              : 
     218            0 :         if(!WebUsers::checkRequestAccess(cgi, out, xmldoc, userInfo))
     219            0 :                 goto HANDLE_ACCESS_FAILURE;  // return false, access failed
     220              :         // else successful access request!
     221              : 
     222            0 :         return true;  // request granted
     223              : 
     224              :         /////////////////////////////////////////////////////
     225              : 
     226            0 : HANDLE_ACCESS_FAILURE:
     227              : 
     228              :         // print out return string on failure
     229            0 :         if(!userInfo.automatedCommand_)
     230            0 :                 __COUT_ERR__ << "Failed request (requestType = " << userInfo.requestType_
     231            0 :                              << "): " << out->str() << __E__;
     232            0 :         return false;  // access failed
     233            0 : }  // end xmlRequestToGateway()
     234              : 
     235              : //==============================================================================
     236              : /// getActiveUserList
     237              : ///     if lastUpdateTime is not too recent as spec'd by ACTIVE_USERS_UPDATE_THRESHOLD
     238              : ///     if server responds with
     239            0 : std::string RemoteWebUsers::getActiveUserList()
     240              : {
     241            0 :         if(time(0) - ActiveUserLastUpdateTime_ >
     242              :            ACTIVE_USERS_UPDATE_THRESHOLD)  // need to update
     243              :         {
     244            0 :                 __COUTS__(2) << "Need to update active user list" << std::endl;
     245              : 
     246              :                 xoap::MessageReference retMsg = ots::SOAPMessenger::sendWithSOAPReply(
     247            0 :                     gatewaySupervisorDescriptor_, "SupervisorGetActiveUsers");
     248              : 
     249            0 :                 SOAPParameters retParameters("UserList");
     250            0 :                 SOAPUtilities::receive(retMsg, retParameters);
     251              : 
     252            0 :                 ActiveUserLastUpdateTime_ = time(0);
     253            0 :                 return (ActiveUserList_ = retParameters.getValue("UserList"));
     254            0 :         }
     255              :         else
     256            0 :                 return ActiveUserList_;
     257              : }  // end getActiveUserList()
     258              : 
     259              : //==============================================================================
     260              : /// getLastTableGroups
     261              : ///     request last "Configured" or "Started" group, for example
     262              : ///     returns empty "" for actionTimeString on failure
     263              : ///     returns "Wed Dec 31 18:00:01 1969 CST" for actionTimeString (in CST) if action never
     264              : /// has occurred
     265            0 : void RemoteWebUsers::getLastTableGroups(
     266              :     std::map<std::string /* group type */,
     267              :              std::tuple<std::string /*group name*/,
     268              :                         TableGroupKey,
     269              :                         std::string /* time string*/>>& theGroups)
     270              : {
     271              :         xoap::MessageReference retMsg =
     272              :             ots::SOAPMessenger::sendWithSOAPReply(gatewaySupervisorDescriptor_,
     273              :                                                   "SupervisorLastTableGroupRequest",
     274            0 :                                                   SOAPParameters("ActionOfLastGroup", "ALL"));
     275              : 
     276            0 :         SOAPParameters retParameters;
     277            0 :         retParameters.addParameter("GroupName");
     278            0 :         retParameters.addParameter("GroupKey");
     279            0 :         retParameters.addParameter("GroupAction");
     280            0 :         retParameters.addParameter("GroupActionTime");
     281            0 :         SOAPUtilities::receive(retMsg, retParameters);
     282              : 
     283              :         //parse as CSV
     284              :         std::vector<std::string> groupNames =
     285            0 :             StringMacros::getVectorFromString(retParameters.getValue("GroupName"), {','});
     286              :         std::vector<std::string> groupKeys =
     287            0 :             StringMacros::getVectorFromString(retParameters.getValue("GroupKey"), {','});
     288              :         std::vector<std::string> groupActions =
     289            0 :             StringMacros::getVectorFromString(retParameters.getValue("GroupAction"), {','});
     290              :         std::vector<std::string> groupTimes = StringMacros::getVectorFromString(
     291            0 :             retParameters.getValue("GroupActionTime"), {','});
     292              : 
     293            0 :         if(groupNames.size() < 2)
     294              :         {
     295              :                 //expecting something like 7?
     296            0 :                 __SS__ << "Failure in handling request for recent config group activity. "
     297            0 :                           "Response received was this: \n"
     298            0 :                        << SOAPUtilities::translate(retMsg) << __E__;
     299            0 :                 __SS_THROW__;
     300            0 :         }
     301              : 
     302            0 :         if(groupNames.size() != groupKeys.size() ||
     303            0 :            groupNames.size() != groupActions.size() || groupNames.size() != groupTimes.size())
     304              :         {
     305            0 :                 __SS__ << "Illegal list size mismatch while retrieving recent config group info. "
     306            0 :                           "Should not be possible! Notify admins."
     307            0 :                        << __E__;
     308            0 :                 __SS_THROW__;
     309            0 :         }
     310              : 
     311            0 :         for(size_t i = 0; i < groupNames.size(); ++i)
     312              :         {
     313            0 :                 theGroups[groupActions[i]] = std::make_tuple(
     314            0 :                     groupNames[i], strtol(groupKeys[i].c_str(), 0, 0), groupTimes[i]);
     315              :         }
     316              : 
     317            0 :         __COUTT__ << "Done with getLastTableGroups()" << __E__;
     318            0 : }  // end getLastTableGroup()
     319              : 
     320              : //==============================================================================
     321              : /// getLastTableGroup
     322              : ///     request last "Configured" or "Started" group, for example
     323              : ///     returns empty "" for actionTimeString on failure
     324              : ///     returns "Wed Dec 31 18:00:01 1969 CST" for actionTimeString (in CST) if action never
     325              : /// has occurred
     326            0 : std::pair<std::string /*group name*/, TableGroupKey> RemoteWebUsers::getLastTableGroup(
     327              :     const std::string& actionOfLastGroup, std::string& actionTimeString)
     328              : {
     329            0 :         actionTimeString              = "";
     330              :         xoap::MessageReference retMsg = ots::SOAPMessenger::sendWithSOAPReply(
     331              :             gatewaySupervisorDescriptor_,
     332              :             "SupervisorLastTableGroupRequest",
     333            0 :             SOAPParameters("ActionOfLastGroup", actionOfLastGroup));
     334              : 
     335            0 :         SOAPParameters retParameters;
     336            0 :         retParameters.addParameter("GroupName");
     337            0 :         retParameters.addParameter("GroupKey");
     338            0 :         retParameters.addParameter("GroupAction");
     339            0 :         retParameters.addParameter("GroupActionTime");
     340            0 :         SOAPUtilities::receive(retMsg, retParameters);
     341              : 
     342            0 :         std::pair<std::string /*group name*/, TableGroupKey> theGroup;
     343            0 :         if(retParameters.getValue("GroupAction") !=
     344            0 :            actionOfLastGroup)  // if action doesn't match.. weird
     345              :         {
     346            0 :                 __SS__ << "Returned group action '" << retParameters.getValue("GroupAction")
     347            0 :                        << "' does not match requested group action '" << actionOfLastGroup << ".'"
     348            0 :                        << std::endl;
     349            0 :                 __SS_THROW__;
     350            0 :         }
     351              :         // else we have an action match
     352              : 
     353            0 :         theGroup.first   = retParameters.getValue("GroupName");
     354            0 :         theGroup.second  = strtol(retParameters.getValue("GroupKey").c_str(), 0, 0);
     355            0 :         actionTimeString = retParameters.getValue("GroupActionTime");
     356            0 :         return theGroup;
     357            0 : }  // end getLastTableGroup()
     358              : 
     359              : //==============================================================================
     360              : /// sendSystemMessage
     361              : ///     send system message to toUser through Supervisor
     362              : ///     toUser wild card * is to all users
     363            0 : void RemoteWebUsers::sendSystemMessage(const std::string& toUser,
     364              :                                        const std::string& message,
     365              :                                        bool               doEmail /*=false*/)
     366              : {
     367            0 :         sendSystemMessage(toUser, "" /*subject*/, message, doEmail);
     368            0 : }  // end sendSystemMessage)
     369              : 
     370              : //==============================================================================
     371              : /// sendSystemMessage
     372              : ///     send system message to toUser comma separate variable (CSV) list through Supervisor
     373              : ///     toUser wild card * is to all users
     374            0 : void RemoteWebUsers::sendSystemMessage(const std::string& toUser,
     375              :                                        const std::string& subject,
     376              :                                        const std::string& message,
     377              :                                        bool               doEmail /*=false*/)
     378              : {
     379            0 :         SOAPParameters parameters;
     380            0 :         parameters.addParameter("ToUser", toUser);  // CSV list or *
     381            0 :         parameters.addParameter("Subject", subject);
     382            0 :         parameters.addParameter("Message", message);
     383            0 :         parameters.addParameter("DoEmail", doEmail ? "1" : "0");
     384              : 
     385              :         xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
     386            0 :             gatewaySupervisorDescriptor_, "SupervisorSystemMessage", parameters);
     387              : 
     388              :         //__COUT__ << SOAPUtilities::translate(retMsg) << __E__;
     389            0 : }  // end sendSystemMessage)
     390              : 
     391              : //==============================================================================
     392              : /// makeSystemLogEntry
     393              : ///     make system logbook through Supervisor
     394            0 : void RemoteWebUsers::makeSystemLogEntry(const std::string& entryText)
     395              : {
     396            0 :         SOAPParameters parameters;
     397            0 :         parameters.addParameter("EntryText", entryText);
     398              : 
     399              :         xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
     400            0 :             gatewaySupervisorDescriptor_, "SupervisorSystemLogbookEntry", parameters);
     401              : 
     402              :         //__COUT__ << SOAPUtilities::translate(retMsg) << __E__;
     403            0 : }  // end makeSystemLogEntry()
        

Generated by: LCOV version 2.0-1