1 /*
2  * JsonRpc.cpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #include <core/json/JsonRpc.hpp>
17 
18 #include <sstream>
19 
20 #include <core/Log.hpp>
21 #include <core/system/Process.hpp>
22 #include <core/http/Response.hpp>
23 
24 namespace rstudio {
25 namespace core {
26 namespace json {
27 
28 const char * const kRpcResult = "result";
29 const char * const kRpcAsyncHandle = "asyncHandle";
30 const char * const kRpcError = "error";
31 const char * const kJsonContentType = "application/json";
32 
parseJsonRpcRequest(const std::string & input,JsonRpcRequest * pRequest)33 Error parseJsonRpcRequest(const std::string& input, JsonRpcRequest* pRequest)
34 {
35    try
36    {
37       // parse data and verify it contains an object
38       Value var;
39       if ( var.parse(input) || !var.isObject() )
40       {
41          return Error(json::errc::InvalidRequest, ERROR_LOCATION);
42       }
43 
44       // extract the fields
45       const Object& requestObject = var.getObject();
46       for (Object::Iterator it =
47             requestObject.begin(); it != requestObject.end(); ++it)
48       {
49          std::string fieldName = (*it).getName();
50          Value fieldValue = (*it).getValue();
51 
52          if ( fieldName == "method" )
53          {
54             if (!fieldValue.isString())
55                return Error(json::errc::InvalidRequest, ERROR_LOCATION);
56 
57             pRequest->method = fieldValue.getString();
58          }
59          else if ( fieldName == "params" )
60          {
61             if (!fieldValue.isArray())
62                return Error(json::errc::ParamTypeMismatch, ERROR_LOCATION);
63 
64             pRequest->params = fieldValue.getValue<json::Array>();
65          }
66          else if ( fieldName == "kwparams" )
67          {
68             if (!fieldValue.isObject())
69                return Error(json::errc::ParamTypeMismatch, ERROR_LOCATION);
70 
71             pRequest->kwparams = fieldValue.getValue<json::Object>();
72          }
73          else if (fieldName == "sourceWnd")
74          {
75             if (!fieldValue.isString())
76                return Error(json::errc::InvalidRequest, ERROR_LOCATION);
77 
78             pRequest->sourceWindow = fieldValue.getString();
79          }
80          else if (fieldName == "clientId" )
81          {
82             if (!fieldValue.isString())
83                return Error(json::errc::InvalidRequest, ERROR_LOCATION);
84 
85             pRequest->clientId = fieldValue.getString();
86          }
87          // legacy version field
88          else if (fieldName == "version" )
89          {
90             if (isType<double>(fieldValue))
91                pRequest->version = fieldValue.getDouble();
92             else
93                pRequest->version = 0;
94          }
95          // new version field
96          else if (fieldName == "clientVersion")
97          {
98             if (!fieldValue.isString())
99                pRequest->clientVersion = fieldValue.getString();
100             else
101                pRequest->clientVersion = std::string();
102          }
103       }
104 
105       // method is required
106       if (pRequest->method.empty() )
107          return Error(json::errc::InvalidRequest, ERROR_LOCATION);
108 
109       return Success();
110    }
111    catch(const std::exception& e)
112    {
113       Error error = Error(json::errc::ParseError, ERROR_LOCATION);
114       error.addProperty("exception", e.what());
115       return error;
116    }
117 }
118 
setErrorResponse(const core::Error & error,core::json::JsonRpcResponse * pResponse)119 void setErrorResponse(const core::Error& error, core::json::JsonRpcResponse* pResponse)
120 {
121    pResponse->setError(error, core::errorMessage(error));
122 }
123 
setProcessErrorResponse(const core::system::ProcessResult & result,const core::ErrorLocation & location,core::json::JsonRpcResponse * pResponse)124 void setProcessErrorResponse(const core::system::ProcessResult& result,
125                              const core::ErrorLocation& location,
126                              core::json::JsonRpcResponse* pResponse)
127 {
128    Error error = systemError(boost::system::errc::state_not_recoverable, result.stdErr, location);
129    pResponse->setError(error, json::Value(result.stdErr), true);
130 }
131 
parseJsonRpcRequestForMethod(const std::string & input,const std::string & method,JsonRpcRequest * pRequest,http::Response * pResponse)132 bool parseJsonRpcRequestForMethod(const std::string& input,
133                                   const std::string& method,
134                                   JsonRpcRequest* pRequest,
135                                   http::Response* pResponse)
136 {
137    // parse request
138    Error parseError = parseJsonRpcRequest(input, pRequest);
139    if (parseError)
140    {
141       LOG_ERROR(parseError);
142       setJsonRpcError(parseError, pResponse);
143       return false;
144    }
145 
146    // check for method
147    if (pRequest->method != method)
148    {
149       Error methodError = Error(json::errc::MethodNotFound, ERROR_LOCATION);
150       methodError.addProperty("method", pRequest->method);
151       LOG_ERROR(methodError);
152       setJsonRpcError(methodError, pResponse);
153       return false;
154    }
155 
156    // success
157    return true;
158 }
159 
160 namespace  {
161 
copyErrorCodeToJsonError(const boost::system::error_code & code,Object * pError)162 void copyErrorCodeToJsonError(const boost::system::error_code& code,
163                               Object* pError)
164 {
165    pError->operator[]("code") = code.value();
166    pError->operator[]("message") = code.message();
167 }
168 
setErrorProperties(Object & jsonError,const Error & error)169 void setErrorProperties(Object& jsonError,
170                         const Error& error)
171 {
172    if (error.getProperties().empty())
173       return;
174 
175    Object properties;
176    for (const std::pair<std::string, std::string>& property : error.getProperties())
177    {
178       properties[property.first] = property.second;
179    }
180 
181    jsonError["properties"] = properties;
182 }
183 
184 }
185 
setAfterResponse(const boost::function<void ()> & afterResponse)186 void JsonRpcResponse::setAfterResponse(
187                            const boost::function<void()>& afterResponse)
188 {
189    afterResponse_ = afterResponse;
190 }
191 
hasAfterResponse() const192 bool JsonRpcResponse::hasAfterResponse() const
193 {
194    return afterResponse_;
195 }
196 
197 
runAfterResponse()198 void JsonRpcResponse::runAfterResponse()
199 {
200    if (afterResponse_)
201       afterResponse_();
202 }
203 
getRawResponse()204 Object JsonRpcResponse::getRawResponse()
205 {
206    return response_;
207 }
208 
write(std::ostream & os) const209 void JsonRpcResponse::write(std::ostream& os) const
210 {
211    response_.write(os);
212 }
213 
setError(const Error & error,const Value & clientInfo,bool includeErrorProperties)214 void JsonRpcResponse::setError(const Error& error,
215                                const Value& clientInfo,
216                                bool includeErrorProperties)
217 {
218    // remove result
219    response_.erase(json::kRpcResult);
220    response_.erase(json::kRpcAsyncHandle);
221 
222    if (error.getName() == json::jsonRpcCategory().name())
223    {
224       setError(boost::system::error_code(error.getCode(), json::jsonRpcCategory()),
225                json::Value());
226    }
227    else
228    {
229       // execution error
230       Object jsonError;
231       copyErrorCodeToJsonError(json::errc::ExecutionError, &jsonError);
232 
233       // populate sub-error field with error details
234       Object executionError;
235       executionError["code"] = error.getCode();
236 
237       std::string errorCategoryName = error.getName();
238       executionError["category"] = errorCategoryName;
239 
240       std::string errorMessage = error.getMessage();
241       executionError["message"] = errorMessage;
242 
243       if (error.getLocation().hasLocation())
244       {
245          std::string errorLocation = error.getLocation().asString();
246          executionError["location"] = errorLocation;
247       }
248 
249       jsonError["error"] = executionError;
250       if (!clientInfo.isNull())
251       {
252          jsonError["client_info"] = clientInfo;
253       }
254 
255       // add error properties if requested
256       if (includeErrorProperties)
257          setErrorProperties(jsonError, error);
258 
259       // set error
260       setField(json::kRpcError, jsonError);
261    }
262 }
263 
setError(const Error & error,bool includeErrorProperties)264 void JsonRpcResponse::setError(const Error& error,
265                                bool includeErrorProperties)
266 {
267    setError(error, Value(), includeErrorProperties);
268 }
269 
setError(const Error & error,const char * message,bool includeErrorProperties)270 void JsonRpcResponse::setError(const Error& error,
271                                const char* message,
272                                bool includeErrorProperties)
273 {
274    setError(error, Value(message), includeErrorProperties);
275 }
276 
setError(const Error & error,const std::string & message,bool includeErrorProperties)277 void JsonRpcResponse::setError(const Error& error,
278                                const std::string& message,
279                                bool includeErrorProperties)
280 {
281    setError(error, Value(message), includeErrorProperties);
282 }
283 
284 
setError(const boost::system::error_code & ec,const Value & clientInfo)285 void JsonRpcResponse::setError(const boost::system::error_code& ec,
286                                const Value& clientInfo)
287 {
288    // remove result
289    response_.erase(json::kRpcResult);
290    response_.erase(json::kRpcAsyncHandle);
291 
292    // error from error code
293    Object error;
294    copyErrorCodeToJsonError(ec, &error);
295 
296    // client info if provided
297    if (!clientInfo.isNull())
298    {
299       error["client_info"] = clientInfo;
300    }
301 
302    // sub-error is null
303    error["error"] = Value();
304 
305    // set error
306    setField(json::kRpcError, error);
307 }
308 
setRedirectError(const Error & error,const std::string & redirectUrl)309 void JsonRpcResponse::setRedirectError(const Error& error,
310                                        const std::string& redirectUrl)
311 {
312    // set a standard error
313    setError(error);
314 
315    Object errorObj;
316    getField(json::kRpcError, &errorObj);
317 
318    // extend the error with redirect information
319    errorObj["redirect_url"] = redirectUrl;
320    setField(json::kRpcError, errorObj);
321 }
322 
setAsyncHandle(const std::string & handle)323 void JsonRpcResponse::setAsyncHandle(const std::string& handle)
324 {
325    response_.erase(json::kRpcResult);
326    response_.erase(json::kRpcError);
327 
328    setField(json::kRpcAsyncHandle, handle);
329 }
330 
setJsonRpcResponse(const JsonRpcResponse & jsonRpcResponse,core::http::Response * pResponse)331 void setJsonRpcResponse(const JsonRpcResponse& jsonRpcResponse,
332                         core::http::Response* pResponse)
333 {
334    // no cache!
335    pResponse->setNoCacheHeaders();
336 
337    // set content type if necessary (allows callers to override the
338    // default application/json content-type, which is necessary in some
339    // circumstances such as returning results to the GWT FileUpload widget
340    // (which expects text/html)
341    if (pResponse->contentType().empty())
342        pResponse->setContentType(json::kJsonContentType);
343 
344    // set body
345    std::stringstream responseStream;
346    jsonRpcResponse.write(responseStream);
347    Error error = pResponse->setBody(responseStream);
348 
349    // report error to client if one occurred
350    if (error)
351    {
352       LOG_ERROR(error);
353       pResponse->setError(http::status::InternalServerError,
354                           error.getMessage());
355    }
356 }
357 
parse(const std::string & input,JsonRpcResponse * pResponse)358 bool JsonRpcResponse::parse(const std::string& input,
359                             JsonRpcResponse* pResponse)
360 {
361    Value value;
362    if (value.parse(input))
363       return false;
364 
365    return parse(value, pResponse);
366 }
367 
parse(const Value & value,JsonRpcResponse * pResponse)368 bool JsonRpcResponse::parse(const Value& value,
369                             JsonRpcResponse* pResponse)
370 {
371    if (!value.isObject())
372       return false;
373 
374    pResponse->response_ = value.getValue<json::Object>();
375    return true;
376 }
377 
378 class JsonRpcErrorCategory : public boost::system::error_category
379 {
380 public:
381    virtual const char * name() const BOOST_NOEXCEPT;
382    virtual std::string message( int ev ) const;
383 };
384 
jsonRpcCategory()385 const boost::system::error_category& jsonRpcCategory()
386 {
387    static JsonRpcErrorCategory jsonRpcErrorCategoryConst;
388    return jsonRpcErrorCategoryConst;
389 }
390 
name() const391 const char * JsonRpcErrorCategory::name() const BOOST_NOEXCEPT
392 {
393    return "jsonrpc";
394 }
395 
message(int ev) const396 std::string JsonRpcErrorCategory::message( int ev ) const
397 {
398    switch(ev)
399    {
400       case errc::Success:
401          return "Method call succeeded";
402 
403       case errc::ConnectionError:
404          return "Unable to connect to service";
405 
406       case errc::Unavailable:
407          return "Service currently unavailable";
408 
409       case errc::Unauthorized:
410          return "Client unauthorized";
411 
412       case errc::InvalidClientId:
413          return "Invalid client id";
414 
415       case errc::ParseError:
416          return "Invalid json or unexpected error occurred while parsing";
417 
418       case errc::InvalidRequest:
419          return "Invalid json-rpc request";
420 
421       case errc::MethodNotFound:
422          return "Method not found";
423 
424       case errc::ParamMissing:
425          return "Parameter missing";
426 
427       case errc::ParamTypeMismatch:
428          return "Parameter type mismatch";
429 
430       case errc::ParamInvalid:
431          return "Parameter value invalid";
432 
433       case errc::MethodUnexpected:
434          return "Unexpected call to method";
435 
436       case errc::ExecutionError:
437          return "Error occurred while executing method";
438 
439       case errc::TransmissionError:
440          return "Error occurred during transmission";
441 
442       case errc::InvalidClientVersion:
443          return "Invalid client version";
444 
445       case errc::ServerOffline:
446          return "Server is offline";
447 
448       case errc::InvalidSession:
449          return "Invalid session";
450 
451       case errc::MaxSessionsReached:
452          return "The maximum amount of concurrent sessions for this license has been reached";
453 
454       case errc::MaxUsersReached:
455          return "The maximum amount of concurrent users for this license has been reached";
456 
457       case errc::LaunchParametersMissing:
458          return "Launch parameters for launcher session missing and should be resent";
459 
460       case errc::LimitSessionsReached:
461          return "The maximum amount of concurrent session allowed for the user profile has been reached";
462 
463       default:
464          BOOST_ASSERT(false);
465          return "Unknown error type";
466    }
467 }
468 
469 namespace {
470 
runSynchronousFunction(const JsonRpcFunction & func,const JsonRpcRequest & request,const JsonRpcFunctionContinuation & continuation)471 void runSynchronousFunction(const JsonRpcFunction& func,
472                             const JsonRpcRequest& request,
473                             const JsonRpcFunctionContinuation& continuation)
474 {
475    JsonRpcResponse response;
476    if (request.isBackgroundConnection)
477       response.setSuppressDetectChanges(true);
478    core::Error error = func(request, &response);
479    continuation(error, &response);
480 }
481 
482 } // anonymous namespace
483 
adaptToAsync(JsonRpcFunction synchronousFunction)484 JsonRpcAsyncFunction adaptToAsync(JsonRpcFunction synchronousFunction)
485 {
486    return boost::bind(runSynchronousFunction, synchronousFunction, _1, _2);
487 }
488 
adaptMethodToAsync(JsonRpcMethod synchronousMethod)489 JsonRpcAsyncMethod adaptMethodToAsync(JsonRpcMethod synchronousMethod)
490 {
491    return JsonRpcAsyncMethod(
492          synchronousMethod.first,
493          std::make_pair(true, adaptToAsync(synchronousMethod.second)));
494 }
495 
496 } // namespace json
497 } // namespace core
498 } // namespace rstudio
499 
500 
501 
502