//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann //////////////////////////////////////////////////////////////////////////////// #include "RestAdminExecuteHandler.h" #include "Actions/ActionFeature.h" #include "Actions/actions.h" #include "Basics/Exceptions.h" #include "Basics/StaticStrings.h" #include "Basics/StringUtils.h" #include "Basics/ScopeGuard.h" #include "Logger/Logger.h" #include "V8/JavaScriptSecurityContext.h" #include "V8/v8-globals.h" #include "V8/v8-vpack.h" #include "V8Server/v8-actions.h" #include "V8Server/V8Context.h" #include "V8Server/V8DealerFeature.h" #include #include #include using namespace arangodb; using namespace arangodb::rest; RestAdminExecuteHandler::RestAdminExecuteHandler(GeneralRequest* request, GeneralResponse* response) : RestVocbaseBaseHandler(request, response) {} RestStatus RestAdminExecuteHandler::execute() { if (!V8DealerFeature::DEALER) { generateError(rest::ResponseCode::BAD, TRI_ERROR_INTERNAL, "JavaScript operations are not available"); return RestStatus::DONE; } TRI_ASSERT(V8DealerFeature::DEALER->allowAdminExecute()); arangodb::velocypack::StringRef bodyStr = _request->rawPayload(); char const* body = bodyStr.data(); size_t bodySize = bodyStr.size(); if (bodySize == 0) { // nothing to execute. return an empty response VPackBuilder result; result.openObject(true); result.add(StaticStrings::Error, VPackValue(false)); result.add(StaticStrings::Code, VPackValue(static_cast(rest::ResponseCode::OK))); result.close(); generateResult(rest::ResponseCode::OK, result.slice()); return RestStatus::DONE; } try { LOG_TOPIC("c838e", DEBUG, Logger::SECURITY) << "about to execute: '" << Logger::CHARS(body, bodySize) << "'"; // get a V8 context bool const allowUseDatabase = ActionFeature::ACTION->allowUseDatabase(); JavaScriptSecurityContext securityContext = JavaScriptSecurityContext::createRestAdminScriptActionContext(allowUseDatabase); V8ContextGuard guard(&_vocbase, securityContext); { v8::Isolate* isolate = guard.isolate(); v8::HandleScope scope(isolate); v8::Handle current = isolate->GetCurrentContext()->Global(); v8::TryCatch tryCatch(isolate); // get built-in Function constructor (see ECMA-262 5th edition 15.3.2) v8::Local ctor = v8::Local::Cast( current->Get(TRI_V8_ASCII_STRING(isolate, "Function"))); v8::Handle args[1] = {TRI_V8_PAIR_STRING(isolate, body, bodySize)}; v8::Local function = ctor->NewInstance(TRI_IGETC, 1, args).FromMaybe(v8::Local()); v8::Handle action = v8::Local::Cast(function); v8::Handle rv; if (!action.IsEmpty()) { action->SetName(TRI_V8_ASCII_STRING(isolate, "source")); TRI_GET_GLOBALS(); TRI_fake_action_t adminExecuteAction("_admin/execute", 2); v8g->_currentRequest = TRI_RequestCppToV8(isolate, v8g, _request.get(), &adminExecuteAction); v8g->_currentResponse = v8::Object::New(isolate); auto guard = scopeGuard([&v8g, &isolate]() { v8g->_currentRequest = v8::Undefined(isolate); v8g->_currentResponse = v8::Undefined(isolate); }); v8::Handle args[] = {v8::Null(isolate)}; rv = action->Call(current, 0, args); } if (tryCatch.HasCaught()) { // got an error std::string errorMessage; auto stacktraceV8 = tryCatch.StackTrace(TRI_IGETC).FromMaybe(v8::Local()); v8::String::Utf8Value tryCatchStackTrace(isolate, stacktraceV8); if (*tryCatchStackTrace != nullptr) { errorMessage = *tryCatchStackTrace; } else if (!tryCatch.Message().IsEmpty()) { v8::String::Utf8Value tryCatchMessage(isolate, tryCatch.Message()->Get()); if (*tryCatchMessage != nullptr) { errorMessage = *tryCatchMessage; } } _response->setResponseCode(rest::ResponseCode::SERVER_ERROR); switch (_response->transportType()) { case Endpoint::TransportType::HTTP: { HttpResponse* httpResponse = dynamic_cast(_response.get()); if (httpResponse == nullptr) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unable to cast response object"); } _response->setContentType(rest::ContentType::TEXT); httpResponse->body().appendText(errorMessage.data(), errorMessage.size()); break; } case Endpoint::TransportType::VST: { VPackBuffer buffer; VPackBuilder builder(buffer); builder.add(VPackValuePair(reinterpret_cast(errorMessage.data()), errorMessage.size())); _response->setContentType(rest::ContentType::VPACK); _response->setPayload(std::move(buffer), true); break; } } } else { // all good! bool returnAsJSON = _request->parsedValue("returnAsJSON", false); if (returnAsJSON) { // if the result is one of the following type, we return it as is returnAsJSON &= (rv->IsString() || rv->IsStringObject() || rv->IsNumber() || rv->IsNumberObject() || rv->IsBoolean()); } VPackBuilder result; bool handled = false; int res = TRI_ERROR_FAILED; if (returnAsJSON) { result.openObject(true); result.add(StaticStrings::Error, VPackValue(false)); result.add(StaticStrings::Code, VPackValue(static_cast(rest::ResponseCode::OK))); if (rv->IsObject()) { res = TRI_V8ToVPack(isolate, result, rv, false); handled = true; } result.close(); } if (!handled) { result.clear(); VPackBuilder temp; res = TRI_V8ToVPack(isolate, temp, rv, false); result.add(temp.slice()); } if (res != TRI_ERROR_NO_ERROR) { THROW_ARANGO_EXCEPTION(res); } generateResult(rest::ResponseCode::OK, result.slice()); } } } catch (basics::Exception const& ex) { generateError(GeneralResponse::responseCode(ex.code()), ex.code(), ex.what()); } catch (std::exception const& ex) { generateError(rest::ResponseCode::SERVER_ERROR, TRI_ERROR_INTERNAL, ex.what()); } return RestStatus::DONE; }