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()
|