bool ConsoleHandler::AutocompleteScriptHelper(HttpRequest& request, HttpResponse& response, const String& command, const String& session, bool sandboxed) { Log(LogInformation, "Console") << "Auto-completing expression: " << command; ApiScriptFrame& lsf = l_ApiScriptFrames[session]; lsf.Seen = Utility::GetTime(); if (!lsf.Locals) lsf.Locals = new Dictionary(); Array::Ptr results = new Array(); Dictionary::Ptr resultInfo = new Dictionary(); ScriptFrame frame; frame.Locals = lsf.Locals; frame.Self = lsf.Locals; frame.Sandboxed = sandboxed; resultInfo->Set("code", 200); resultInfo->Set("status", "Auto-completed successfully."); resultInfo->Set("suggestions", Array::FromVector(GetAutocompletionSuggestions(command, frame))); results->Add(resultInfo); Dictionary::Ptr result = new Dictionary(); result->Set("results", results); response.SetStatus(200, "OK"); HttpUtility::SendJsonBody(response, result); return true; }
void HttpUtility::SendJsonError(HttpResponse& response, const int code, const String& info, const String& diagnosticInformation) { Dictionary::Ptr result = new Dictionary(); response.SetStatus(code, HttpUtility::GetErrorNameByCode(code)); result->Set("error", code); if (!info.IsEmpty()) result->Set("status", info); if (!diagnosticInformation.IsEmpty()) result->Set("diagnostic information", diagnosticInformation); HttpUtility::SendJsonBody(response, result); }
bool InfoHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() > 2) return false; if (request.RequestMethod != "GET") return false; if (request.RequestUrl->GetPath().empty()) { response.SetStatus(302, "Found"); response.AddHeader("Location", "/v1"); return true; } if (request.RequestUrl->GetPath()[0] != "v1" || request.RequestUrl->GetPath().size() != 1) return false; response.SetStatus(200, "OK"); std::vector<String> permInfo; Array::Ptr permissions = user->GetPermissions(); if (permissions) { ObjectLock olock(permissions); BOOST_FOREACH(const Value& permission, permissions) { String name; bool hasFilter = false; if (permission.IsObjectType<Dictionary>()) { Dictionary::Ptr dpermission = permission; name = dpermission->Get("permission"); hasFilter = dpermission->Contains("filter"); } else name = permission; if (hasFilter) name += " (filtered)"; permInfo.push_back(name); } }
bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) { if (request.RequestMethod != "PUT") return false; if (request.RequestUrl->GetPath().size() < 3) return false; Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[1]); if (!type) return false; String name = request.RequestUrl->GetPath()[2]; Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); Array::Ptr templates = params->Get("templates"); Dictionary::Ptr attrs = params->Get("attrs"); Dictionary::Ptr result1 = new Dictionary(); int code; String status; Array::Ptr errors = new Array(); String config = ConfigObjectUtility::CreateObjectConfig(type, name, templates, attrs); if (!ConfigObjectUtility::CreateObject(type, name, config, errors)) { result1->Set("errors", errors); code = 500; status = "Object could not be created."; } else { code = 200; status = "Object was created."; } result1->Set("code", code); result1->Set("status", status); Array::Ptr results = new Array(); results->Add(result1); Dictionary::Ptr result = new Dictionary(); result->Set("results", results); response.SetStatus(code, status); HttpUtility::SendJsonBody(response, result); return true; }
bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) { if (request.RequestUrl->GetPath().size() != 3) return false; if (request.RequestMethod != "POST") return false; String actionName = request.RequestUrl->GetPath()[2]; ApiAction::Ptr action = ApiAction::GetByName(actionName); if (!action) { HttpUtility::SendJsonError(response, 404, "Action '" + actionName + "' does not exist."); return true; } QueryDescription qd; Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); const std::vector<String>& types = action->GetTypes(); std::vector<Value> objs; String permission = "actions/" + actionName; if (!types.empty()) { qd.Types = std::set<String>(types.begin(), types.end()); qd.Permission = permission; try { objs = FilterUtility::GetFilterTargets(qd, params, user); } catch (const std::exception& ex) { HttpUtility::SendJsonError(response, 400, "Type/Filter was required but not provided or was invalid.", HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : ""); return true; } } else { FilterUtility::CheckPermission(user, permission); objs.push_back(ConfigObject::Ptr()); } Array::Ptr results = new Array(); Log(LogNotice, "ApiActionHandler") << "Running action " << actionName; BOOST_FOREACH(const ConfigObject::Ptr& obj, objs) { try { results->Add(action->Invoke(obj, params)); } catch (const std::exception& ex) { Dictionary::Ptr fail = new Dictionary(); fail->Set("code", 500); fail->Set("status", "Action execution failed: '" + DiagnosticInformation(ex, false) + "'."); if (HttpUtility::GetLastParameter(params, "verboseErrors")) fail->Set("diagnostic information", DiagnosticInformation(ex)); results->Add(fail); } } Dictionary::Ptr result = new Dictionary(); result->Set("results", results); response.SetStatus(200, "OK"); HttpUtility::SendJsonBody(response, result); return true; }
bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) return false; if (request.RequestMethod != "POST") return false; Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, 400, "Invalid type specified."); return true; } QueryDescription qd; qd.Types.insert(type->GetName()); qd.Permission = "objects/modify/" + type->GetName(); params->Set("type", type->GetName()); if (request.RequestUrl->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); params->Set(attr, request.RequestUrl->GetPath()[3]); } std::vector<Value> objs; try { objs = FilterUtility::GetFilterTargets(qd, params, user); } catch (const std::exception& ex) { HttpUtility::SendJsonError(response, 404, "No objects found.", HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : ""); return true; } Dictionary::Ptr attrs = params->Get("attrs"); Array::Ptr results = new Array(); for (const ConfigObject::Ptr& obj : objs) { Dictionary::Ptr result1 = new Dictionary(); result1->Set("type", type->GetName()); result1->Set("name", obj->GetName()); String key; try { if (attrs) { ObjectLock olock(attrs); for (const Dictionary::Pair& kv : attrs) { key = kv.first; obj->ModifyAttribute(kv.first, kv.second); } } result1->Set("code", 200); result1->Set("status", "Attributes updated."); } catch (const std::exception& ex) { result1->Set("code", 500); result1->Set("status", "Attribute '" + key + "' could not be set: " + DiagnosticInformation(ex)); } results->Add(result1); } Dictionary::Ptr result = new Dictionary(); result->Set("results", results); response.SetStatus(200, "OK"); HttpUtility::SendJsonBody(response, result); return true; }
bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) { if (request.RequestUrl->GetPath().size() != 4) return false; if (request.RequestMethod != "PUT") return false; Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, 400, "Invalid type specified."); return true; } FilterUtility::CheckPermission(user, "objects/create/" + type->GetName()); String name = request.RequestUrl->GetPath()[3]; Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); Array::Ptr templates = params->Get("templates"); Dictionary::Ptr attrs = params->Get("attrs"); Dictionary::Ptr result1 = new Dictionary(); int code; String status; Array::Ptr errors = new Array(); bool ignoreOnError = false; if (params->Contains("ignore_on_error")) ignoreOnError = HttpUtility::GetLastParameter(params, "ignore_on_error"); String config = ConfigObjectUtility::CreateObjectConfig(type, name, ignoreOnError, templates, attrs); if (!ConfigObjectUtility::CreateObject(type, name, config, errors)) { result1->Set("errors", errors); HttpUtility::SendJsonError(response, 500, "Object could not be created."); return true; } ConfigType::Ptr dtype = ConfigType::GetByName(type->GetName()); ConfigObject::Ptr obj = dtype->GetObject(name); result1->Set("code", 200); if (obj) result1->Set("status", "Object was created"); else if (!obj && ignoreOnError) result1->Set("status", "Object was not created but 'ignore_on_error' was set to true"); Array::Ptr results = new Array(); results->Add(result1); Dictionary::Ptr result = new Dictionary(); result->Set("results", results); response.SetStatus(200, "OK"); HttpUtility::SendJsonBody(response, result); return true; }
bool ConsoleHandler::ExecuteScriptHelper(HttpRequest& request, HttpResponse& response, const String& command, const String& session, bool sandboxed) { Log(LogInformation, "Console") << "Executing expression: " << command; ApiScriptFrame& lsf = l_ApiScriptFrames[session]; lsf.Seen = Utility::GetTime(); if (!lsf.Locals) lsf.Locals = new Dictionary(); String fileName = "<" + Convert::ToString(lsf.NextLine) + ">"; lsf.NextLine++; lsf.Lines[fileName] = command; Array::Ptr results = new Array(); Dictionary::Ptr resultInfo = new Dictionary(); Expression *expr = NULL; Value exprResult; try { expr = ConfigCompiler::CompileText(fileName, command); ScriptFrame frame; frame.Locals = lsf.Locals; frame.Self = lsf.Locals; frame.Sandboxed = sandboxed; exprResult = expr->Evaluate(frame); resultInfo->Set("code", 200); resultInfo->Set("status", "Executed successfully."); resultInfo->Set("result", Serialize(exprResult, 0)); } catch (const ScriptError& ex) { DebugInfo di = ex.GetDebugInfo(); std::ostringstream msgbuf; msgbuf << di.Path << ": " << lsf.Lines[di.Path] << "\n" << String(di.Path.GetLength() + 2, ' ') << String(di.FirstColumn, ' ') << String(di.LastColumn - di.FirstColumn + 1, '^') << "\n" << ex.what() << "\n"; resultInfo->Set("code", 500); resultInfo->Set("status", String(msgbuf.str())); resultInfo->Set("incomplete_expression", ex.IsIncompleteExpression()); Dictionary::Ptr debugInfo = new Dictionary(); debugInfo->Set("path", di.Path); debugInfo->Set("first_line", di.FirstLine); debugInfo->Set("first_column", di.FirstColumn); debugInfo->Set("last_line", di.LastLine); debugInfo->Set("last_column", di.LastColumn); resultInfo->Set("debug_info", debugInfo); } catch (...) { delete expr; throw; } delete expr; results->Add(resultInfo); Dictionary::Ptr result = new Dictionary(); result->Set("results", results); response.SetStatus(200, "OK"); HttpUtility::SendJsonBody(response, result); return true; }
bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) return false; if (request.RequestMethod != "GET") return false; Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); return true; } QueryDescription qd; qd.Types.insert(type->GetName()); qd.Permission = "objects/query/" + type->GetName(); Array::Ptr uattrs, ujoins, umetas; try { uattrs = params->Get("attrs"); } catch (const std::exception&) { HttpUtility::SendJsonError(response, params, 400, "Invalid type for 'attrs' attribute specified. Array type is required."); return true; } try { ujoins = params->Get("joins"); } catch (const std::exception&) { HttpUtility::SendJsonError(response, params, 400, "Invalid type for 'joins' attribute specified. Array type is required."); return true; } try { umetas = params->Get("meta"); } catch (const std::exception&) { HttpUtility::SendJsonError(response, params, 400, "Invalid type for 'meta' attribute specified. Array type is required."); return true; } bool allJoins = HttpUtility::GetLastParameter(params, "all_joins"); params->Set("type", type->GetName()); if (request.RequestUrl->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); params->Set(attr, request.RequestUrl->GetPath()[3]); } std::vector<Value> objs; try { objs = FilterUtility::GetFilterTargets(qd, params, user); } catch (const std::exception& ex) { HttpUtility::SendJsonError(response, params, 404, "No objects found.", DiagnosticInformation(ex)); return true; } ArrayData results; results.reserve(objs.size()); std::set<String> joinAttrs; std::set<String> userJoinAttrs; if (ujoins) { ObjectLock olock(ujoins); for (const String& ujoin : ujoins) { userJoinAttrs.insert(ujoin.SubStr(0, ujoin.FindFirstOf("."))); } } for (int fid = 0; fid < type->GetFieldCount(); fid++) { Field field = type->GetFieldInfo(fid); if (!(field.Attributes & FANavigation)) continue; if (!allJoins && userJoinAttrs.find(field.NavigationName) == userJoinAttrs.end()) continue; joinAttrs.insert(field.Name); } for (const ConfigObject::Ptr& obj : objs) { DictionaryData result1{ { "name", obj->GetName() }, { "type", obj->GetReflectionType()->GetName() } }; DictionaryData metaAttrs; if (umetas) { ObjectLock olock(umetas); for (const String& meta : umetas) { if (meta == "used_by") { Array::Ptr used_by = new Array(); metaAttrs.emplace_back("used_by", used_by); for (const Object::Ptr& pobj : DependencyGraph::GetParents((obj))) { ConfigObject::Ptr configObj = dynamic_pointer_cast<ConfigObject>(pobj); if (!configObj) continue; used_by->Add(new Dictionary({ { "type", configObj->GetReflectionType()->GetName() }, { "name", configObj->GetName() } })); } } else if (meta == "location") { metaAttrs.emplace_back("location", obj->GetSourceLocation()); } else { HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for meta: " + meta); return true; } } } result1.emplace_back("meta", new Dictionary(std::move(metaAttrs))); try { result1.emplace_back("attrs", SerializeObjectAttrs(obj, String(), uattrs, false, false)); } catch (const ScriptError& ex) { HttpUtility::SendJsonError(response, params, 400, ex.what()); return true; } DictionaryData joins; for (const String& joinAttr : joinAttrs) { Object::Ptr joinedObj; int fid = type->GetFieldId(joinAttr); if (fid < 0) { HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for join: " + joinAttr); return true; } Field field = type->GetFieldInfo(fid); if (!(field.Attributes & FANavigation)) { HttpUtility::SendJsonError(response, params, 400, "Not a joinable field: " + joinAttr); return true; } joinedObj = obj->NavigateField(fid); if (!joinedObj) continue; String prefix = field.NavigationName; try { joins.emplace_back(prefix, SerializeObjectAttrs(joinedObj, prefix, ujoins, true, allJoins)); } catch (const ScriptError& ex) { HttpUtility::SendJsonError(response, params, 400, ex.what()); return true; } } result1.emplace_back("joins", new Dictionary(std::move(joins))); results.push_back(new Dictionary(std::move(result1))); } Dictionary::Ptr result = new Dictionary({ { "results", new Array(std::move(results)) } }); response.SetStatus(200, "OK"); HttpUtility::SendJsonBody(response, params, result); return true; }
bool EventsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() != 2) return false; if (request.RequestMethod != "POST") return false; if (request.ProtocolVersion == HttpVersion10) { HttpUtility::SendJsonError(response, 400, "HTTP/1.0 not supported for event streams."); return true; } Array::Ptr types = params->Get("types"); if (!types) { HttpUtility::SendJsonError(response, 400, "'types' query parameter is required."); return true; } { ObjectLock olock(types); for (const String& type : types) { FilterUtility::CheckPermission(user, "events/" + type); } } String queueName = HttpUtility::GetLastParameter(params, "queue"); if (queueName.IsEmpty()) { HttpUtility::SendJsonError(response, 400, "'queue' query parameter is required."); return true; } String filter = HttpUtility::GetLastParameter(params, "filter"); Expression *ufilter = NULL; if (!filter.IsEmpty()) ufilter = ConfigCompiler::CompileText("<API query>", filter); /* create a new queue or update an existing one */ EventQueue::Ptr queue = EventQueue::GetByName(queueName); if (!queue) { queue = new EventQueue(queueName); EventQueue::Register(queueName, queue); } queue->SetTypes(types->ToSet<String>()); queue->SetFilter(ufilter); queue->AddClient(&request); response.SetStatus(200, "OK"); response.AddHeader("Content-Type", "application/json"); for (;;) { Dictionary::Ptr result = queue->WaitForEvent(&request); if (!response.IsPeerConnected()) { queue->RemoveClient(&request); EventQueue::UnregisterIfUnused(queueName, queue); return true; } if (!result) continue; String body = JsonEncode(result); boost::algorithm::replace_all(body, "\n", ""); try { response.WriteBody(body.CStr(), body.GetLength()); response.WriteBody("\n", 1); } catch (const std::exception&) { queue->RemoveClient(&request); EventQueue::UnregisterIfUnused(queueName, queue); throw; } } }