mirror of https://gitee.com/bigwinds/arangodb
forward port branch feature-3.3/allow-jwt-in-arangosh (#5009)
This commit is contained in:
parent
167dfcdf7f
commit
8b9eb13925
|
@ -66,7 +66,8 @@ auth::TokenCache::~TokenCache() {
|
|||
|
||||
void auth::TokenCache::setJwtSecret(std::string const& jwtSecret) {
|
||||
WRITE_LOCKER(writeLocker, _jwtLock);
|
||||
LOG_TOPIC(DEBUG, Logger::AUTHENTICATION) << "Setting jwt secret " << jwtSecret;
|
||||
LOG_TOPIC(DEBUG, Logger::AUTHENTICATION)
|
||||
<< "Setting jwt secret " << jwtSecret;
|
||||
_jwtSecret = jwtSecret;
|
||||
_jwtCache.clear();
|
||||
generateJwtToken();
|
||||
|
@ -171,7 +172,7 @@ auth::TokenCache::Entry auth::TokenCache::checkAuthenticationJWT(
|
|||
_jwtCache.remove(jwt);
|
||||
} catch (std::range_error const&) {
|
||||
}
|
||||
LOG_TOPIC(TRACE, Logger::AUTHENTICATION) << "JWT Token expired";
|
||||
LOG_TOPIC(TRACE, Logger::AUTHENTICATION) << "JWT Token expired";
|
||||
return auth::TokenCache::Entry(); // unauthorized
|
||||
}
|
||||
// LDAP rights might need to be refreshed
|
||||
|
@ -180,11 +181,11 @@ auth::TokenCache::Entry auth::TokenCache::checkAuthenticationJWT(
|
|||
} catch (std::range_error const&) {
|
||||
// mop: not found
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> const parts = StringUtils::split(jwt, '.');
|
||||
if (parts.size() != 3) {
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "Secret contains "
|
||||
<< parts.size() << " parts";
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::FIXME)
|
||||
<< "Secret contains " << parts.size() << " parts";
|
||||
return auth::TokenCache::Entry();
|
||||
}
|
||||
|
||||
|
@ -208,8 +209,8 @@ auth::TokenCache::Entry auth::TokenCache::checkAuthenticationJWT(
|
|||
std::string const message = header + "." + body;
|
||||
if (!validateJwtHMAC256Signature(message, signature)) {
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::AUTHENTICATION)
|
||||
<< "Couldn't validate jwt signature " << signature
|
||||
<< " against " << _jwtSecret;
|
||||
<< "Couldn't validate jwt signature " << signature << " against "
|
||||
<< _jwtSecret;
|
||||
return auth::TokenCache::Entry();
|
||||
}
|
||||
|
||||
|
@ -226,11 +227,11 @@ std::shared_ptr<VPackBuilder> auth::TokenCache::parseJson(
|
|||
parser.parse(str);
|
||||
result = parser.steal();
|
||||
} catch (std::bad_alloc const&) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Out of memory parsing " << hint
|
||||
<< "!";
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "Out of memory parsing " << hint << "!";
|
||||
} catch (VPackException const& ex) {
|
||||
LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "Couldn't parse " << hint
|
||||
<< ": " << ex.what();
|
||||
LOG_TOPIC(DEBUG, arangodb::Logger::FIXME)
|
||||
<< "Couldn't parse " << hint << ": " << ex.what();
|
||||
} catch (...) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "Got unknown exception trying to parse " << hint;
|
||||
|
@ -292,14 +293,12 @@ auth::TokenCache::Entry auth::TokenCache::validateJwtBody(
|
|||
|
||||
VPackSlice const issSlice = bodySlice.get("iss");
|
||||
if (!issSlice.isString()) {
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::AUTHENTICATION)
|
||||
<< "missing iss value";
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::AUTHENTICATION) << "missing iss value";
|
||||
return authResult; // unauthenticated
|
||||
}
|
||||
|
||||
if (issSlice.copyString() != "arangodb") {
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::AUTHENTICATION)
|
||||
<< "invalid iss value";
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::AUTHENTICATION) << "invalid iss value";
|
||||
return authResult; // unauthenticated
|
||||
}
|
||||
|
||||
|
@ -314,7 +313,7 @@ auth::TokenCache::Entry auth::TokenCache::validateJwtBody(
|
|||
// authResult._username = "root";
|
||||
} else {
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::AUTHENTICATION)
|
||||
<< "Lacking preferred_username or server_id";
|
||||
<< "Lacking preferred_username or server_id";
|
||||
return authResult; // unauthenticated
|
||||
}
|
||||
|
||||
|
@ -363,7 +362,8 @@ std::string auth::TokenCache::generateRawJwt(VPackSlice const& body) const {
|
|||
std::string fullMessage(StringUtils::encodeBase64(headerBuilder.toJson()) +
|
||||
"." + StringUtils::encodeBase64(body.toJson()));
|
||||
if (_jwtSecret.empty()) {
|
||||
LOG_TOPIC(INFO, Logger::AUTHENTICATION) << "Using cluster without JWT Token";
|
||||
LOG_TOPIC(INFO, Logger::AUTHENTICATION)
|
||||
<< "Using cluster without JWT Token";
|
||||
}
|
||||
|
||||
std::string signature =
|
||||
|
|
|
@ -23,13 +23,13 @@
|
|||
#include "AuthenticationFeature.h"
|
||||
|
||||
#include "ApplicationFeatures/ApplicationServer.h"
|
||||
#include "Auth/Common.h"
|
||||
#include "Auth/Handler.h"
|
||||
#include "Cluster/ServerState.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "ProgramOptions/ProgramOptions.h"
|
||||
#include "Random/RandomGenerator.h"
|
||||
#include "RestServer/QueryRegistryFeature.h"
|
||||
#include "Auth/Common.h"
|
||||
|
||||
#if USE_ENTERPRISE
|
||||
#include "Enterprise/Ldap/LdapAuthenticationHandler.h"
|
||||
|
@ -87,13 +87,15 @@ void AuthenticationFeature::collectOptions(
|
|||
"enable or disable authentication for ALL client requests",
|
||||
new BooleanParameter(&_active));
|
||||
|
||||
options->addOption("--server.authentication-timeout",
|
||||
"timeout for the authentication cache in seconds (0 = indefinitely)",
|
||||
new DoubleParameter(&_authenticationTimeout));
|
||||
options->addOption(
|
||||
"--server.authentication-timeout",
|
||||
"timeout for the authentication cache in seconds (0 = indefinitely)",
|
||||
new DoubleParameter(&_authenticationTimeout));
|
||||
|
||||
options->addOption("--server.local-authentication",
|
||||
"enable or disable authentication using the local user database",
|
||||
new BooleanParameter(&_localAuthentication));
|
||||
options->addOption(
|
||||
"--server.local-authentication",
|
||||
"enable or disable authentication using the local user database",
|
||||
new BooleanParameter(&_localAuthentication));
|
||||
|
||||
options->addOption(
|
||||
"--server.authentication-system-only",
|
||||
|
@ -123,30 +125,33 @@ void AuthenticationFeature::validateOptions(std::shared_ptr<ProgramOptions>) {
|
|||
|
||||
void AuthenticationFeature::prepare() {
|
||||
TRI_ASSERT(isEnabled());
|
||||
TRI_ASSERT(_userManager == nullptr);
|
||||
TRI_ASSERT(_userManager == nullptr);
|
||||
#if USE_ENTERPRISE
|
||||
if (application_features::ApplicationServer::getFeature<LdapFeature>("Ldap")
|
||||
->isEnabled()) {
|
||||
_userManager = new auth::UserManager(std::make_unique<LdapAuthenticationHandler>());
|
||||
} else {
|
||||
_userManager = new auth::UserManager();
|
||||
}
|
||||
#else
|
||||
if (application_features::ApplicationServer::getFeature<LdapFeature>("Ldap")
|
||||
->isEnabled()) {
|
||||
_userManager =
|
||||
new auth::UserManager(std::make_unique<LdapAuthenticationHandler>());
|
||||
} else {
|
||||
_userManager = new auth::UserManager();
|
||||
}
|
||||
#else
|
||||
_userManager = new auth::UserManager();
|
||||
#endif
|
||||
TRI_ASSERT(_authCache == nullptr);
|
||||
TRI_ASSERT(_authCache == nullptr);
|
||||
_authCache = new auth::TokenCache(_userManager, _authenticationTimeout);
|
||||
|
||||
|
||||
std::string jwtSecret = _jwtSecretProgramOption;
|
||||
|
||||
if (jwtSecret.empty()) {
|
||||
LOG_TOPIC(INFO, Logger::AUTHENTICATION) << "Jwt secret not specified, generating...";
|
||||
LOG_TOPIC(INFO, Logger::AUTHENTICATION)
|
||||
<< "Jwt secret not specified, generating...";
|
||||
uint16_t m = 254;
|
||||
for (size_t i = 0; i < _maxSecretLength; i++) {
|
||||
jwtSecret += (1 + RandomGenerator::interval(m));
|
||||
}
|
||||
}
|
||||
_authCache->setJwtSecret(jwtSecret);
|
||||
|
||||
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
|
@ -158,8 +163,8 @@ void AuthenticationFeature::start() {
|
|||
out << "Authentication is turned " << (_active ? "on" : "off");
|
||||
|
||||
auto queryRegistryFeature =
|
||||
application_features::ApplicationServer::getFeature<
|
||||
QueryRegistryFeature>("QueryRegistry");
|
||||
application_features::ApplicationServer::getFeature<QueryRegistryFeature>(
|
||||
"QueryRegistry");
|
||||
_userManager->setQueryRegistry(queryRegistryFeature->queryRegistry());
|
||||
|
||||
if (_active && _authenticationSystemOnly) {
|
||||
|
@ -167,12 +172,11 @@ void AuthenticationFeature::start() {
|
|||
}
|
||||
|
||||
#ifdef ARANGODB_HAVE_DOMAIN_SOCKETS
|
||||
out << ", authentication for unix sockets is turned "
|
||||
<< (_authenticationUnixSockets ? "on" : "off");
|
||||
out << ", authentication for unix sockets is turned "
|
||||
<< (_authenticationUnixSockets ? "on" : "off");
|
||||
#endif
|
||||
|
||||
LOG_TOPIC(INFO, arangodb::Logger::AUTHENTICATION) << out.str();
|
||||
}
|
||||
|
||||
void AuthenticationFeature::unprepare() { INSTANCE = nullptr; }
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ int main(int argc, char* argv[]) {
|
|||
int ret;
|
||||
|
||||
server.addFeature(new BenchFeature(&server, &ret));
|
||||
server.addFeature(new ClientFeature(&server));
|
||||
server.addFeature(new ClientFeature(&server, false));
|
||||
server.addFeature(new ConfigFeature(&server, "arangobench"));
|
||||
server.addFeature(new LoggerFeature(&server, false));
|
||||
server.addFeature(new RandomFeature(&server));
|
||||
|
|
|
@ -58,7 +58,7 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
int ret;
|
||||
|
||||
server.addFeature(new ClientFeature(&server));
|
||||
server.addFeature(new ClientFeature(&server, false));
|
||||
server.addFeature(new ConfigFeature(&server, "arangodump"));
|
||||
server.addFeature(new DumpFeature(&server, ret));
|
||||
server.addFeature(new LoggerFeature(&server, false));
|
||||
|
|
|
@ -53,7 +53,7 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
int ret;
|
||||
|
||||
server.addFeature(new ClientFeature(&server));
|
||||
server.addFeature(new ClientFeature(&server, false));
|
||||
server.addFeature(new ConfigFeature(&server, "arangoexport"));
|
||||
server.addFeature(new ExportFeature(&server, &ret));
|
||||
server.addFeature(new LoggerFeature(&server, false));
|
||||
|
|
|
@ -53,7 +53,7 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
int ret;
|
||||
|
||||
server.addFeature(new ClientFeature(&server));
|
||||
server.addFeature(new ClientFeature(&server, false));
|
||||
server.addFeature(new ConfigFeature(&server, "arangoimport"));
|
||||
server.addFeature(new ImportFeature(&server, &ret));
|
||||
server.addFeature(new LoggerFeature(&server, false));
|
||||
|
|
|
@ -58,7 +58,7 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
int ret;
|
||||
|
||||
server.addFeature(new ClientFeature(&server));
|
||||
server.addFeature(new ClientFeature(&server, false));
|
||||
server.addFeature(new ConfigFeature(&server, "arangorestore"));
|
||||
server.addFeature(new LoggerFeature(&server, false));
|
||||
server.addFeature(new RandomFeature(&server));
|
||||
|
|
|
@ -42,21 +42,25 @@ using namespace arangodb::httpclient;
|
|||
using namespace arangodb::options;
|
||||
|
||||
ClientFeature::ClientFeature(application_features::ApplicationServer* server,
|
||||
double connectionTimeout, double requestTimeout)
|
||||
bool allowJwtSecret, double connectionTimeout,
|
||||
double requestTimeout)
|
||||
: ApplicationFeature(server, "Client"),
|
||||
_databaseName("_system"),
|
||||
_authentication(true),
|
||||
_askJwtSecret(false),
|
||||
_endpoint(Endpoint::defaultEndpoint(Endpoint::TransportType::HTTP)),
|
||||
_username("root"),
|
||||
_password(""),
|
||||
_jwtSecret(""),
|
||||
_connectionTimeout(connectionTimeout),
|
||||
_requestTimeout(requestTimeout),
|
||||
_maxPacketSize(128 * 1024 * 1024),
|
||||
_sslProtocol(TLS_V12),
|
||||
_allowJwtSecret(allowJwtSecret),
|
||||
_retries(DEFAULT_RETRIES),
|
||||
_warn(false),
|
||||
_warnConnect(true),
|
||||
_haveServerPassword(false){
|
||||
_haveServerPassword(false) {
|
||||
setOptional(true);
|
||||
requiresElevatedPrivileges(false);
|
||||
startsAfter("Logger");
|
||||
|
@ -70,11 +74,11 @@ void ClientFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
|
|||
new StringParameter(&_databaseName));
|
||||
|
||||
options->addOption("--server.authentication",
|
||||
"require authentication credentials when connecting (does not affect the server-side authentication settings)",
|
||||
"require authentication credentials when connecting (does "
|
||||
"not affect the server-side authentication settings)",
|
||||
new BooleanParameter(&_authentication));
|
||||
|
||||
options->addOption("--server.username",
|
||||
"username to use when connecting",
|
||||
options->addOption("--server.username", "username to use when connecting",
|
||||
new StringParameter(&_username));
|
||||
|
||||
options->addOption(
|
||||
|
@ -88,23 +92,37 @@ void ClientFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
|
|||
"for a password",
|
||||
new StringParameter(&_password));
|
||||
|
||||
if (_allowJwtSecret) {
|
||||
// currently the option is only present for arangosh, but none
|
||||
// of the other client tools
|
||||
options->addHiddenOption(
|
||||
"--server.ask-jwt-secret",
|
||||
"if this option is specified, the user will be prompted "
|
||||
"for a JWT secret. This option is not compatible with "
|
||||
"--server.username or --server.password. If specified, it will be used for all "
|
||||
"connections - even when a new connection to another server is "
|
||||
"created",
|
||||
new BooleanParameter(&_askJwtSecret));
|
||||
}
|
||||
|
||||
options->addOption("--server.connection-timeout",
|
||||
"connection timeout in seconds",
|
||||
new DoubleParameter(&_connectionTimeout));
|
||||
|
||||
options->addOption("--server.request-timeout",
|
||||
"request timeout in seconds",
|
||||
options->addOption("--server.request-timeout", "request timeout in seconds",
|
||||
new DoubleParameter(&_requestTimeout));
|
||||
|
||||
options->addHiddenOption("--server.max-packet-size",
|
||||
"maximum packet size (in bytes) for client/server communication",
|
||||
new UInt64Parameter(&_maxPacketSize));
|
||||
|
||||
options->addHiddenOption(
|
||||
"--server.max-packet-size",
|
||||
"maximum packet size (in bytes) for client/server communication",
|
||||
new UInt64Parameter(&_maxPacketSize));
|
||||
|
||||
std::unordered_set<uint64_t> sslProtocols = {1, 2, 3, 4, 5};
|
||||
|
||||
options->addSection("ssl", "Configure SSL communication");
|
||||
options->addOption("--ssl.protocol",
|
||||
"ssl protocol (1 = SSLv2, 2 = SSLv2 or SSLv3 (negotiated), 3 = SSLv3, 4 = "
|
||||
"ssl protocol (1 = SSLv2, 2 = SSLv2 or SSLv3 "
|
||||
"(negotiated), 3 = SSLv3, 4 = "
|
||||
"TLSv1, 5 = TLSV1.2)",
|
||||
new DiscreteValuesParameter<UInt64Parameter>(
|
||||
&_sslProtocol, sslProtocols));
|
||||
|
@ -116,6 +134,10 @@ void ClientFeature::validateOptions(std::shared_ptr<ProgramOptions> options) {
|
|||
_authentication = true;
|
||||
}
|
||||
|
||||
if (_askJwtSecret) {
|
||||
_authentication = false;
|
||||
}
|
||||
|
||||
// check timeouts
|
||||
if (_connectionTimeout < 0.0) {
|
||||
LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << "invalid value for --server.connect-timeout, must be >= 0";
|
||||
|
@ -144,30 +166,70 @@ void ClientFeature::validateOptions(std::shared_ptr<ProgramOptions> options) {
|
|||
|
||||
_haveServerPassword = !options->processingResult().touched("server.password");
|
||||
|
||||
if (_askJwtSecret && options->processingResult().touched("server.password")) {
|
||||
LOG_TOPIC(FATAL, arangodb::Logger::FIXME)
|
||||
<< "cannot specify both --server.password and --server.ask-jwt-token";
|
||||
FATAL_ERROR_EXIT();
|
||||
}
|
||||
|
||||
if (_askJwtSecret && options->processingResult().touched("server.username")) {
|
||||
LOG_TOPIC(FATAL, arangodb::Logger::FIXME)
|
||||
<< "cannot specify both --server.username and --server.ask-jwt-token";
|
||||
FATAL_ERROR_EXIT();
|
||||
}
|
||||
|
||||
SimpleHttpClientParams::setDefaultMaxPacketSize(_maxPacketSize);
|
||||
}
|
||||
|
||||
void ClientFeature::prepare() {
|
||||
// ask for a password
|
||||
if (_authentication &&
|
||||
isEnabled() &&
|
||||
_haveServerPassword) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10 * 1000));
|
||||
void ClientFeature::readPassword() {
|
||||
usleep(10 * 1000);
|
||||
|
||||
try {
|
||||
ConsoleFeature* console =
|
||||
ApplicationServer::getFeature<ConsoleFeature>("Console");
|
||||
try {
|
||||
ConsoleFeature* console =
|
||||
ApplicationServer::getFeature<ConsoleFeature>("Console");
|
||||
|
||||
if (console->isEnabled()) {
|
||||
_password = console->readPassword("Please specify a password: ");
|
||||
return;
|
||||
}
|
||||
} catch (...) {
|
||||
if (console->isEnabled()) {
|
||||
_password = console->readPassword("Please specify a password: ");
|
||||
return;
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
std::cout << "Please specify a password: " << std::flush;
|
||||
_password = ConsoleFeature::readPassword();
|
||||
std::cout << std::endl << std::flush;
|
||||
std::cout << "Please specify a password: " << std::flush;
|
||||
_password = ConsoleFeature::readPassword();
|
||||
std::cout << std::endl << std::flush;
|
||||
}
|
||||
|
||||
void ClientFeature::readJwtSecret() {
|
||||
usleep(10 * 1000);
|
||||
|
||||
try {
|
||||
ConsoleFeature* console =
|
||||
ApplicationServer::getFeature<ConsoleFeature>("Console");
|
||||
|
||||
if (console->isEnabled()) {
|
||||
_jwtSecret = console->readPassword("Please specify the JWT secret: ");
|
||||
return;
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
std::cout << "Please specify the JWT secret: " << std::flush;
|
||||
_jwtSecret = ConsoleFeature::readPassword();
|
||||
std::cout << std::endl << std::flush;
|
||||
}
|
||||
|
||||
void ClientFeature::prepare() {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_askJwtSecret) {
|
||||
// ask for a jwt secret
|
||||
readJwtSecret();
|
||||
} else if (_authentication && _haveServerPassword) {
|
||||
// ask for a password
|
||||
readPassword();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,7 +242,8 @@ std::unique_ptr<GeneralClientConnection> ClientFeature::createConnection(
|
|||
std::unique_ptr<Endpoint> endpoint(Endpoint::clientFactory(definition));
|
||||
|
||||
if (endpoint.get() == nullptr) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "invalid value for --server.endpoint ('" << definition << "')";
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "invalid value for --server.endpoint ('" << definition << "')";
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_BAD_PARAMETER);
|
||||
}
|
||||
|
||||
|
@ -196,24 +259,27 @@ std::unique_ptr<SimpleHttpClient> ClientFeature::createHttpClient() const {
|
|||
return createHttpClient(_endpoint);
|
||||
}
|
||||
|
||||
std::unique_ptr<SimpleHttpClient> ClientFeature::createHttpClient(std::string const& definition) const {
|
||||
return createHttpClient(definition, SimpleHttpClientParams(_requestTimeout, _warn));
|
||||
std::unique_ptr<SimpleHttpClient> ClientFeature::createHttpClient(
|
||||
std::string const& definition) const {
|
||||
return createHttpClient(definition,
|
||||
SimpleHttpClientParams(_requestTimeout, _warn));
|
||||
}
|
||||
|
||||
std::unique_ptr<httpclient::SimpleHttpClient> ClientFeature::createHttpClient(
|
||||
std::string const& definition,
|
||||
SimpleHttpClientParams const& params) const {
|
||||
std::string const& definition, SimpleHttpClientParams const& params) const {
|
||||
std::unique_ptr<Endpoint> endpoint(Endpoint::clientFactory(definition));
|
||||
|
||||
|
||||
if (endpoint.get() == nullptr) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "invalid value for --server.endpoint ('" << definition << "')";
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "invalid value for --server.endpoint ('" << definition << "')";
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_BAD_PARAMETER);
|
||||
}
|
||||
|
||||
std::unique_ptr<GeneralClientConnection> connection(GeneralClientConnection::factory(endpoint, _requestTimeout,
|
||||
_connectionTimeout, _retries,
|
||||
_sslProtocol));
|
||||
|
||||
|
||||
std::unique_ptr<GeneralClientConnection> connection(
|
||||
GeneralClientConnection::factory(endpoint, _requestTimeout,
|
||||
_connectionTimeout, _retries,
|
||||
_sslProtocol));
|
||||
|
||||
return std::make_unique<SimpleHttpClient>(connection, params);
|
||||
}
|
||||
|
||||
|
@ -224,21 +290,23 @@ std::vector<std::string> ClientFeature::httpEndpoints() {
|
|||
return {};
|
||||
}
|
||||
|
||||
return { http };
|
||||
return {http};
|
||||
}
|
||||
|
||||
int ClientFeature::runMain(int argc, char* argv[],
|
||||
std::function<int(int argc, char* argv[])> const& mainFunc) {
|
||||
|
||||
int ClientFeature::runMain(
|
||||
int argc, char* argv[],
|
||||
std::function<int(int argc, char* argv[])> const& mainFunc) {
|
||||
try {
|
||||
return mainFunc(argc, argv);
|
||||
} catch (std::exception const& ex) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< argv[0] << " terminated because of an unhandled exception: "
|
||||
<< ex.what();
|
||||
<< argv[0]
|
||||
<< " terminated because of an unhandled exception: " << ex.what();
|
||||
return EXIT_FAILURE;
|
||||
} catch (...) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< argv[0] << " terminated because of an unhandled exception of unknown type";
|
||||
<< argv[0]
|
||||
<< " terminated because of an unhandled exception of unknown type";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace httpclient {
|
|||
class GeneralClientConnection;
|
||||
class SimpleHttpClient;
|
||||
struct SimpleHttpClientParams;
|
||||
}
|
||||
} // namespace httpclient
|
||||
|
||||
class ClientFeature final : public application_features::ApplicationFeature,
|
||||
public HttpEndpointProvider {
|
||||
|
@ -45,6 +45,7 @@ class ClientFeature final : public application_features::ApplicationFeature,
|
|||
|
||||
public:
|
||||
ClientFeature(application_features::ApplicationServer* server,
|
||||
bool allowJwtSecret,
|
||||
double connectionTimeout = DEFAULT_CONNECTION_TIMEOUT,
|
||||
double requestTimeout = DEFAULT_REQUEST_TIMEOUT);
|
||||
|
||||
|
@ -62,6 +63,7 @@ class ClientFeature final : public application_features::ApplicationFeature,
|
|||
void setUsername(std::string const& value) { _username = value; }
|
||||
std::string const& password() const { return _password; }
|
||||
void setPassword(std::string const& value) { _password = value; }
|
||||
std::string const& jwtSecret() const { return _jwtSecret; }
|
||||
double connectionTimeout() const { return _connectionTimeout; }
|
||||
double requestTimeout() const { return _requestTimeout; }
|
||||
uint64_t maxPacketSize() const { return _maxPacketSize; }
|
||||
|
@ -75,7 +77,8 @@ class ClientFeature final : public application_features::ApplicationFeature,
|
|||
std::unique_ptr<httpclient::SimpleHttpClient> createHttpClient(
|
||||
std::string const& definition) const;
|
||||
std::unique_ptr<httpclient::SimpleHttpClient> createHttpClient(
|
||||
std::string const& definition, httpclient::SimpleHttpClientParams const&) const;
|
||||
std::string const& definition,
|
||||
httpclient::SimpleHttpClientParams const&) const;
|
||||
std::vector<std::string> httpEndpoints() override;
|
||||
|
||||
void setDatabaseName(std::string const& databaseName) {
|
||||
|
@ -92,26 +95,34 @@ class ClientFeature final : public application_features::ApplicationFeature,
|
|||
|
||||
bool getWarnConnect() { return _warnConnect; }
|
||||
|
||||
static int runMain(int argc, char* argv[],
|
||||
std::function<int(int argc, char* argv[])> const& mainFunc);
|
||||
static int runMain(
|
||||
int argc, char* argv[],
|
||||
std::function<int(int argc, char* argv[])> const& mainFunc);
|
||||
|
||||
private:
|
||||
void readPassword();
|
||||
void readJwtSecret();
|
||||
|
||||
private:
|
||||
std::string _databaseName;
|
||||
bool _authentication;
|
||||
bool _askJwtSecret;
|
||||
std::string _endpoint;
|
||||
std::string _username;
|
||||
std::string _password;
|
||||
std::string _jwtSecret;
|
||||
double _connectionTimeout;
|
||||
double _requestTimeout;
|
||||
uint64_t _maxPacketSize;
|
||||
uint64_t _sslProtocol;
|
||||
|
||||
private:
|
||||
bool _allowJwtSecret;
|
||||
size_t _retries;
|
||||
bool _warn;
|
||||
bool _warnConnect;
|
||||
bool _haveServerPassword;
|
||||
};
|
||||
}
|
||||
} // namespace arangodb
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
|
||||
#include "V8ClientConnection.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <v8.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "Basics/FileUtils.h"
|
||||
#include "Basics/StringUtils.h"
|
||||
|
@ -38,6 +38,7 @@
|
|||
#include "SimpleHttpClient/GeneralClientConnection.h"
|
||||
#include "SimpleHttpClient/SimpleHttpClient.h"
|
||||
#include "SimpleHttpClient/SimpleHttpResult.h"
|
||||
#include "Ssl/SslInterface.h"
|
||||
#include "V8/v8-conv.h"
|
||||
#include "V8/v8-json.h"
|
||||
#include "V8/v8-utils.h"
|
||||
|
@ -48,6 +49,35 @@ using namespace arangodb::basics;
|
|||
using namespace arangodb::httpclient;
|
||||
using namespace arangodb::import;
|
||||
|
||||
std::string V8ClientConnection::JWT_SECRET = "";
|
||||
|
||||
std::string V8ClientConnection::jwtToken(std::string const& secret) {
|
||||
VPackBuilder headerBuilder;
|
||||
{
|
||||
VPackObjectBuilder h(&headerBuilder);
|
||||
headerBuilder.add("alg", VPackValue("HS256"));
|
||||
headerBuilder.add("typ", VPackValue("JWT"));
|
||||
}
|
||||
|
||||
VPackBuilder bodyBuilder;
|
||||
{
|
||||
VPackObjectBuilder p(&bodyBuilder);
|
||||
bodyBuilder.add("server_id", VPackValue("arangosh"));
|
||||
bodyBuilder.add("iss", VPackValue("arangodb"));
|
||||
bodyBuilder.add("iat", VPackValue(TRI_microtime() / 1000));
|
||||
}
|
||||
|
||||
std::string fullMessage(StringUtils::encodeBase64(headerBuilder.toJson()) +
|
||||
"." +
|
||||
StringUtils::encodeBase64(bodyBuilder.toJson()));
|
||||
|
||||
std::string signature = sslHMAC(
|
||||
secret.c_str(), secret.length(), fullMessage.c_str(),
|
||||
fullMessage.length(), rest::SslInterface::Algorithm::ALGORITHM_SHA256);
|
||||
|
||||
return fullMessage + "." + StringUtils::encodeBase64U(signature);
|
||||
}
|
||||
|
||||
V8ClientConnection::V8ClientConnection(
|
||||
std::unique_ptr<GeneralClientConnection>& connection,
|
||||
std::string const& database, std::string const& username,
|
||||
|
@ -65,8 +95,9 @@ V8ClientConnection::V8ClientConnection(
|
|||
V8ClientConnection::~V8ClientConnection() {}
|
||||
|
||||
void V8ClientConnection::init(
|
||||
std::unique_ptr<GeneralClientConnection>& connection, std::string const& username,
|
||||
std::string const& password, std::string const& databaseName) {
|
||||
std::unique_ptr<GeneralClientConnection>& connection,
|
||||
std::string const& username, std::string const& password,
|
||||
std::string const& databaseName) {
|
||||
_username = username;
|
||||
_password = password;
|
||||
_databaseName = databaseName;
|
||||
|
@ -74,13 +105,18 @@ void V8ClientConnection::init(
|
|||
SimpleHttpClientParams params(_requestTimeout, false);
|
||||
params.setLocationRewriter(this, &rewriteLocation);
|
||||
params.setUserNamePassword("/", _username, _password);
|
||||
|
||||
if (!JWT_SECRET.empty()) {
|
||||
params.setJwt(jwtToken(JWT_SECRET));
|
||||
}
|
||||
|
||||
_client.reset(new SimpleHttpClient(connection, params));
|
||||
|
||||
// connect to server and get version number
|
||||
std::unordered_map<std::string, std::string> headerFields;
|
||||
std::unique_ptr<SimpleHttpResult> result(
|
||||
_client->request(rest::RequestType::GET,
|
||||
"/_api/version?details=true", nullptr, 0, headerFields));
|
||||
_client->request(rest::RequestType::GET, "/_api/version?details=true",
|
||||
nullptr, 0, headerFields));
|
||||
|
||||
if (result.get() == nullptr || !result->isComplete()) {
|
||||
// save error message
|
||||
|
@ -89,7 +125,8 @@ void V8ClientConnection::init(
|
|||
} else {
|
||||
_lastHttpReturnCode = result->getHttpReturnCode();
|
||||
|
||||
if (result->getHttpReturnCode() == static_cast<int>(rest::ResponseCode::OK)) {
|
||||
if (result->getHttpReturnCode() ==
|
||||
static_cast<int>(rest::ResponseCode::OK)) {
|
||||
try {
|
||||
std::shared_ptr<VPackBuilder> parsedBody = result->getBodyVelocyPack();
|
||||
VPackSlice const body = parsedBody->slice();
|
||||
|
@ -109,11 +146,13 @@ void V8ClientConnection::init(
|
|||
}
|
||||
std::string const versionString =
|
||||
VelocyPackHelper::getStringValue(body, "version", "");
|
||||
std::pair<int, int> version = rest::Version::parseVersionString(versionString);
|
||||
std::pair<int, int> version =
|
||||
rest::Version::parseVersionString(versionString);
|
||||
if (version.first < 3) {
|
||||
// major version of server is too low
|
||||
_client->disconnect();
|
||||
_lastErrorMessage = "Server version number ('" + versionString + "') is too low. Expecting 3.0 or higher";
|
||||
_lastErrorMessage = "Server version number ('" + versionString +
|
||||
"') is too low. Expecting 3.0 or higher";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +211,8 @@ void V8ClientConnection::reconnect(ClientFeature* client) {
|
|||
try {
|
||||
std::unique_ptr<GeneralClientConnection> connection =
|
||||
client->createConnection(client->endpoint());
|
||||
init(connection, client->username(), client->password(), client->databaseName());
|
||||
init(connection, client->username(), client->password(),
|
||||
client->databaseName());
|
||||
} catch (...) {
|
||||
std::string errorMessage = "error in '" + client->endpoint() + "'";
|
||||
throw errorMessage;
|
||||
|
@ -180,16 +220,17 @@ void V8ClientConnection::reconnect(ClientFeature* client) {
|
|||
|
||||
if (isConnected() &&
|
||||
_lastHttpReturnCode == static_cast<int>(rest::ResponseCode::OK)) {
|
||||
LOG_TOPIC(INFO, arangodb::Logger::FIXME) << "Connected to ArangoDB "
|
||||
<< "'" << endpointSpecification() << "', "
|
||||
<< "version " << _version << " [" << _mode << "], "
|
||||
<< "database '" << _databaseName << "', "
|
||||
<< "username: '" << _username << "'";
|
||||
LOG_TOPIC(INFO, arangodb::Logger::FIXME)
|
||||
<< "Connected to ArangoDB "
|
||||
<< "'" << endpointSpecification() << "', "
|
||||
<< "version " << _version << " [" << _mode << "], "
|
||||
<< "database '" << _databaseName << "', "
|
||||
<< "username: '" << _username << "'";
|
||||
} else {
|
||||
if (client->getWarnConnect()) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "Could not connect to endpoint '" << client->endpoint()
|
||||
<< "', username: '" << client->username() << "'";
|
||||
<< "Could not connect to endpoint '" << client->endpoint()
|
||||
<< "', username: '" << client->username() << "'";
|
||||
}
|
||||
|
||||
std::string errorMsg = "could not connect";
|
||||
|
@ -274,8 +315,7 @@ static V8ClientConnection* CreateV8ClientConnection(
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void ClientConnection_DestructorCallback(
|
||||
const v8::WeakCallbackInfo<v8::Persistent<v8::External>>&
|
||||
data) {
|
||||
const v8::WeakCallbackInfo<v8::Persistent<v8::External>>& data) {
|
||||
auto persistent = data.GetParameter();
|
||||
auto myConnection =
|
||||
v8::Local<v8::External>::New(data.GetIsolate(), *persistent);
|
||||
|
@ -335,14 +375,14 @@ static void ClientConnection_ConstructorCallback(
|
|||
CreateV8ClientConnection(connection, client));
|
||||
|
||||
if (v8connection->isConnected() &&
|
||||
v8connection->lastHttpReturnCode() ==
|
||||
(int)rest::ResponseCode::OK) {
|
||||
LOG_TOPIC(INFO, arangodb::Logger::FIXME) << "Connected to ArangoDB "
|
||||
<< "'" << v8connection->endpointSpecification() << "', "
|
||||
<< "version " << v8connection->version() << " ["
|
||||
<< v8connection->mode() << "], "
|
||||
<< "database '" << v8connection->databaseName() << "', "
|
||||
<< "username: '" << v8connection->username() << "'";
|
||||
v8connection->lastHttpReturnCode() == (int)rest::ResponseCode::OK) {
|
||||
LOG_TOPIC(INFO, arangodb::Logger::FIXME)
|
||||
<< "Connected to ArangoDB "
|
||||
<< "'" << v8connection->endpointSpecification() << "', "
|
||||
<< "version " << v8connection->version() << " [" << v8connection->mode()
|
||||
<< "], "
|
||||
<< "database '" << v8connection->databaseName() << "', "
|
||||
<< "username: '" << v8connection->username() << "'";
|
||||
|
||||
} else {
|
||||
std::string errorMessage =
|
||||
|
@ -394,7 +434,7 @@ static void ClientConnection_reconnect(
|
|||
std::string password;
|
||||
|
||||
if (args.Length() < 4) {
|
||||
ConsoleFeature* console =
|
||||
ConsoleFeature* console =
|
||||
ApplicationServer::getFeature<ConsoleFeature>("Console");
|
||||
|
||||
if (console->isEnabled()) {
|
||||
|
@ -428,9 +468,10 @@ static void ClientConnection_reconnect(
|
|||
TRI_V8_THROW_EXCEPTION_PARAMETER(errorMessage.c_str());
|
||||
}
|
||||
|
||||
TRI_ExecuteJavaScriptString(isolate, isolate->GetCurrentContext(),
|
||||
TRI_V8_STRING(isolate, "require('internal').db._flushCache();"),
|
||||
TRI_V8_ASCII_STRING(isolate, "reload db object"), false);
|
||||
TRI_ExecuteJavaScriptString(
|
||||
isolate, isolate->GetCurrentContext(),
|
||||
TRI_V8_STRING(isolate, "require('internal').db._flushCache();"),
|
||||
TRI_V8_ASCII_STRING(isolate, "reload db object"), false);
|
||||
|
||||
TRI_V8_RETURN_TRUE();
|
||||
TRI_V8_TRY_CATCH_END
|
||||
|
@ -956,7 +997,8 @@ static void ClientConnection_importCsv(
|
|||
}
|
||||
|
||||
// extract the options
|
||||
v8::Handle<v8::String> separatorKey = TRI_V8_ASCII_STRING(isolate, "separator");
|
||||
v8::Handle<v8::String> separatorKey =
|
||||
TRI_V8_ASCII_STRING(isolate, "separator");
|
||||
v8::Handle<v8::String> quoteKey = TRI_V8_ASCII_STRING(isolate, "quote");
|
||||
|
||||
std::string separator = ",";
|
||||
|
@ -1025,7 +1067,7 @@ static void ClientConnection_importCsv(
|
|||
for (std::string const& msg : ih.getErrorMessages()) {
|
||||
error.append(msg + ";\t");
|
||||
}
|
||||
|
||||
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FAILED, error.c_str());
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
@ -1089,7 +1131,7 @@ static void ClientConnection_importJson(
|
|||
|
||||
TRI_V8_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
std::string error = "error messages:";
|
||||
for (std::string const& msg : ih.getErrorMessages()) {
|
||||
error.append(msg + ";\t");
|
||||
|
@ -1174,7 +1216,7 @@ static void ClientConnection_isConnected(
|
|||
|
||||
if (v8connection->isConnected()) {
|
||||
TRI_V8_RETURN_TRUE();
|
||||
}
|
||||
}
|
||||
TRI_V8_RETURN_FALSE();
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
@ -1411,7 +1453,7 @@ v8::Handle<v8::Value> V8ClientConnection::requestDataRaw(
|
|||
_httpResult->setHttpReturnCode(500);
|
||||
_httpResult->setResultType(SimpleHttpResult::COULD_NOT_CONNECT);
|
||||
}
|
||||
|
||||
|
||||
v8::Handle<v8::Object> result = v8::Object::New(isolate);
|
||||
|
||||
TRI_ASSERT(_httpResult != nullptr);
|
||||
|
@ -1452,7 +1494,7 @@ v8::Handle<v8::Value> V8ClientConnection::requestDataRaw(
|
|||
}
|
||||
|
||||
result->ForceSet(TRI_V8_STD_STRING(isolate, StaticStrings::Error),
|
||||
v8::Boolean::New(isolate, true));
|
||||
v8::Boolean::New(isolate, true));
|
||||
result->ForceSet(TRI_V8_STD_STRING(isolate, StaticStrings::ErrorNum),
|
||||
v8::Integer::New(isolate, errorNumber));
|
||||
result->ForceSet(TRI_V8_STD_STRING(isolate, StaticStrings::ErrorMessage),
|
||||
|
@ -1473,14 +1515,14 @@ v8::Handle<v8::Value> V8ClientConnection::requestDataRaw(
|
|||
std::string returnMessage(_httpResult->getHttpReturnMessage());
|
||||
|
||||
result->ForceSet(TRI_V8_STD_STRING(isolate, StaticStrings::Error),
|
||||
v8::Boolean::New(isolate, true));
|
||||
v8::Boolean::New(isolate, true));
|
||||
result->ForceSet(TRI_V8_STD_STRING(isolate, StaticStrings::ErrorNum),
|
||||
v8::Integer::New(isolate, _lastHttpReturnCode));
|
||||
v8::Integer::New(isolate, _lastHttpReturnCode));
|
||||
result->ForceSet(TRI_V8_STD_STRING(isolate, StaticStrings::ErrorMessage),
|
||||
TRI_V8_STD_STRING(isolate, returnMessage));
|
||||
TRI_V8_STD_STRING(isolate, returnMessage));
|
||||
} else {
|
||||
result->ForceSet(TRI_V8_STD_STRING(isolate, StaticStrings::Error),
|
||||
v8::Boolean::New(isolate, false));
|
||||
v8::Boolean::New(isolate, false));
|
||||
}
|
||||
|
||||
// got a body, copy it into the result
|
||||
|
@ -1609,7 +1651,8 @@ void V8ClientConnection::initServer(v8::Isolate* isolate,
|
|||
v8::Local<v8::FunctionTemplate> connection_templ =
|
||||
v8::FunctionTemplate::New(isolate);
|
||||
|
||||
connection_templ->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoConnection"));
|
||||
connection_templ->SetClassName(
|
||||
TRI_V8_ASCII_STRING(isolate, "ArangoConnection"));
|
||||
|
||||
v8::Local<v8::ObjectTemplate> connection_proto =
|
||||
connection_templ->PrototypeTemplate();
|
||||
|
@ -1622,8 +1665,9 @@ void V8ClientConnection::initServer(v8::Isolate* isolate,
|
|||
isolate, "DELETE_RAW",
|
||||
v8::FunctionTemplate::New(isolate, ClientConnection_httpDeleteRaw));
|
||||
|
||||
connection_proto->Set(isolate, "GET", v8::FunctionTemplate::New(
|
||||
isolate, ClientConnection_httpGet));
|
||||
connection_proto->Set(
|
||||
isolate, "GET",
|
||||
v8::FunctionTemplate::New(isolate, ClientConnection_httpGet));
|
||||
|
||||
connection_proto->Set(
|
||||
isolate, "GET_RAW",
|
||||
|
@ -1661,8 +1705,9 @@ void V8ClientConnection::initServer(v8::Isolate* isolate,
|
|||
isolate, "POST_RAW",
|
||||
v8::FunctionTemplate::New(isolate, ClientConnection_httpPostRaw));
|
||||
|
||||
connection_proto->Set(isolate, "PUT", v8::FunctionTemplate::New(
|
||||
isolate, ClientConnection_httpPut));
|
||||
connection_proto->Set(
|
||||
isolate, "PUT",
|
||||
v8::FunctionTemplate::New(isolate, ClientConnection_httpPut));
|
||||
connection_proto->Set(
|
||||
isolate, "PUT_RAW",
|
||||
v8::FunctionTemplate::New(isolate, ClientConnection_httpPutRaw));
|
||||
|
@ -1690,10 +1735,10 @@ void V8ClientConnection::initServer(v8::Isolate* isolate,
|
|||
connection_proto->Set(
|
||||
isolate, "reconnect",
|
||||
v8::FunctionTemplate::New(isolate, ClientConnection_reconnect, v8client));
|
||||
|
||||
connection_proto->Set(
|
||||
isolate, "connectedUser",
|
||||
v8::FunctionTemplate::New(isolate, ClientConnection_connectedUser, v8client));
|
||||
|
||||
connection_proto->Set(isolate, "connectedUser",
|
||||
v8::FunctionTemplate::New(
|
||||
isolate, ClientConnection_connectedUser, v8client));
|
||||
|
||||
connection_proto->Set(
|
||||
isolate, "toString",
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace httpclient {
|
|||
class GeneralClientConnection;
|
||||
class SimpleHttpClient;
|
||||
class SimpleHttpResult;
|
||||
}
|
||||
} // namespace httpclient
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief class for http requests
|
||||
|
@ -48,6 +48,13 @@ class V8ClientConnection {
|
|||
V8ClientConnection(V8ClientConnection const&) = delete;
|
||||
V8ClientConnection& operator=(V8ClientConnection const&) = delete;
|
||||
|
||||
public:
|
||||
static void setJwtSecret(std::string const& jwtSecret) { JWT_SECRET = jwtSecret; }
|
||||
static std::string jwtToken(std::string const& secret);
|
||||
|
||||
private:
|
||||
static std::string JWT_SECRET;
|
||||
|
||||
public:
|
||||
V8ClientConnection(
|
||||
std::unique_ptr<httpclient::GeneralClientConnection>& connection,
|
||||
|
@ -104,7 +111,8 @@ class V8ClientConnection {
|
|||
static std::string rewriteLocation(void*, std::string const&);
|
||||
|
||||
private:
|
||||
void init(std::unique_ptr<httpclient::GeneralClientConnection>&, std::string const&, std::string const&, std::string const&);
|
||||
void init(std::unique_ptr<httpclient::GeneralClientConnection>&,
|
||||
std::string const&, std::string const&, std::string const&);
|
||||
|
||||
v8::Handle<v8::Value> requestData(
|
||||
v8::Isolate* isolate, rest::RequestType method,
|
||||
|
@ -132,6 +140,6 @@ class V8ClientConnection {
|
|||
std::string _version;
|
||||
std::string _mode;
|
||||
};
|
||||
}
|
||||
} // namespace arangodb
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
#include "Basics/ArangoGlobalContext.h"
|
||||
#include "Basics/FileUtils.h"
|
||||
#include "Basics/StringUtils.h"
|
||||
#include "Basics/terminal-utils.h"
|
||||
#include "Basics/Utf8Helper.h"
|
||||
#include "Basics/terminal-utils.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "ProgramOptions/ProgramOptions.h"
|
||||
#include "ProgramOptions/Section.h"
|
||||
|
@ -101,13 +101,13 @@ void V8ShellFeature::validateOptions(
|
|||
FATAL_ERROR_EXIT();
|
||||
}
|
||||
|
||||
LOG_TOPIC(DEBUG, Logger::V8) << "using Javascript startup files at '"
|
||||
<< _startupDirectory << "'";
|
||||
LOG_TOPIC(DEBUG, Logger::V8)
|
||||
<< "using Javascript startup files at '" << _startupDirectory << "'";
|
||||
|
||||
if (!_moduleDirectory.empty()) {
|
||||
LOG_TOPIC(DEBUG, Logger::V8) << "using Javascript modules at '"
|
||||
<< StringUtils::join(_moduleDirectory, ";")
|
||||
<< "'";
|
||||
LOG_TOPIC(DEBUG, Logger::V8)
|
||||
<< "using Javascript modules at '"
|
||||
<< StringUtils::join(_moduleDirectory, ";") << "'";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,6 +281,12 @@ V8ClientConnection* V8ShellFeature::setup(
|
|||
client = dynamic_cast<ClientFeature*>(server()->feature("Client"));
|
||||
|
||||
if (client != nullptr && client->isEnabled()) {
|
||||
auto jwtSecret = client->jwtSecret();
|
||||
|
||||
if (!jwtSecret.empty()) {
|
||||
V8ClientConnection::setJwtSecret(jwtSecret);
|
||||
}
|
||||
|
||||
auto connection = client->createConnection();
|
||||
v8connection = std::make_unique<V8ClientConnection>(
|
||||
connection, client->databaseName(), client->username(),
|
||||
|
@ -354,7 +360,8 @@ int V8ShellFeature::runShell(std::vector<std::string> const& positionals) {
|
|||
std::string input =
|
||||
v8LineEditor.prompt(prompt._colored, prompt._plain, eof);
|
||||
|
||||
if (eof == ShellBase::EOF_FORCE_ABORT || (eof == ShellBase::EOF_ABORT && lastEmpty)) {
|
||||
if (eof == ShellBase::EOF_FORCE_ABORT ||
|
||||
(eof == ShellBase::EOF_ABORT && lastEmpty)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -444,7 +451,7 @@ int V8ShellFeature::runShell(std::vector<std::string> const& positionals) {
|
|||
V8PlatformFeature::resetOutOfMemory(_isolate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!_console->quiet()) {
|
||||
_console->printLine("");
|
||||
_console->printByeBye();
|
||||
|
@ -473,7 +480,8 @@ bool V8ShellFeature::runScript(std::vector<std::string> const& files,
|
|||
|
||||
for (auto const& file : files) {
|
||||
if (!FileUtils::exists(file)) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "error: Javascript file not found: '" << file << "'";
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "error: Javascript file not found: '" << file << "'";
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -598,7 +606,8 @@ bool V8ShellFeature::jslint(std::vector<std::string> const& files) {
|
|||
uint32_t i = 0;
|
||||
for (auto& file : files) {
|
||||
if (!FileUtils::exists(file)) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "error: Javascript file not found: '" << file << "'";
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "error: Javascript file not found: '" << file << "'";
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -610,9 +619,8 @@ bool V8ShellFeature::jslint(std::vector<std::string> const& files) {
|
|||
context->Global()->Set(TRI_V8_ASCII_STRING(_isolate, "SYS_UNIT_TESTS"),
|
||||
sysTestFiles);
|
||||
|
||||
context->Global()->Set(
|
||||
TRI_V8_ASCII_STRING(_isolate, "SYS_UNIT_TESTS_RESULT"),
|
||||
v8::True(_isolate));
|
||||
context->Global()->Set(TRI_V8_ASCII_STRING(_isolate, "SYS_UNIT_TESTS_RESULT"),
|
||||
v8::True(_isolate));
|
||||
|
||||
// run tests
|
||||
auto input = TRI_V8_ASCII_STRING(
|
||||
|
@ -624,7 +632,8 @@ bool V8ShellFeature::jslint(std::vector<std::string> const& files) {
|
|||
TRI_ExecuteJavaScriptString(_isolate, context, input, name, true);
|
||||
|
||||
if (tryCatch.HasCaught()) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << TRI_StringifyV8Exception(_isolate, &tryCatch);
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< TRI_StringifyV8Exception(_isolate, &tryCatch);
|
||||
ok = false;
|
||||
} else {
|
||||
bool res = TRI_ObjectToBoolean(context->Global()->Get(
|
||||
|
@ -660,7 +669,8 @@ bool V8ShellFeature::runUnitTests(std::vector<std::string> const& files,
|
|||
|
||||
for (auto const& file : files) {
|
||||
if (!FileUtils::exists(file)) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "error: Javascript file not found: '" << file << "'";
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "error: Javascript file not found: '" << file << "'";
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -669,15 +679,13 @@ bool V8ShellFeature::runUnitTests(std::vector<std::string> const& files,
|
|||
++i;
|
||||
}
|
||||
|
||||
TRI_AddGlobalVariableVocbase(_isolate,
|
||||
TRI_V8_ASCII_STRING(_isolate, "SYS_UNIT_TESTS"),
|
||||
sysTestFiles);
|
||||
TRI_AddGlobalVariableVocbase(
|
||||
_isolate, TRI_V8_ASCII_STRING(_isolate, "SYS_UNIT_TESTS"), sysTestFiles);
|
||||
|
||||
// do not use TRI_AddGlobalVariableVocBase because it creates read-only
|
||||
// variables!!
|
||||
context->Global()->Set(
|
||||
TRI_V8_ASCII_STRING(_isolate, "SYS_UNIT_TESTS_RESULT"),
|
||||
v8::True(_isolate));
|
||||
context->Global()->Set(TRI_V8_ASCII_STRING(_isolate, "SYS_UNIT_TESTS_RESULT"),
|
||||
v8::True(_isolate));
|
||||
|
||||
// run tests
|
||||
auto input = TRI_V8_ASCII_STRING(
|
||||
|
@ -866,19 +874,19 @@ void V8ShellFeature::initGlobals() {
|
|||
v8::FunctionTemplate::New(_isolate, JS_CompareString)->GetFunction());
|
||||
|
||||
TRI_AddGlobalVariableVocbase(
|
||||
_isolate,
|
||||
TRI_V8_ASCII_STRING(_isolate, "ARANGODB_CLIENT_VERSION"),
|
||||
_isolate, TRI_V8_ASCII_STRING(_isolate, "ARANGODB_CLIENT_VERSION"),
|
||||
v8::FunctionTemplate::New(_isolate, JS_VersionClient)->GetFunction());
|
||||
|
||||
// is quite
|
||||
TRI_AddGlobalVariableVocbase(_isolate,
|
||||
TRI_AddGlobalVariableVocbase(_isolate,
|
||||
TRI_V8_ASCII_STRING(_isolate, "ARANGO_QUIET"),
|
||||
v8::Boolean::New(_isolate, _console->quiet()));
|
||||
|
||||
auto ctx = ArangoGlobalContext::CONTEXT;
|
||||
|
||||
if (ctx == nullptr) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "failed to get global context. ";
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "failed to get global context. ";
|
||||
FATAL_ERROR_EXIT();
|
||||
}
|
||||
|
||||
|
@ -1002,15 +1010,17 @@ void V8ShellFeature::loadModules(ShellFeature::RunMode runMode) {
|
|||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
switch (loader.loadScript(_isolate, context, files[i], nullptr)) {
|
||||
case JSLoader::eSuccess:
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "loaded JavaScript file '" << files[i] << "'";
|
||||
LOG_TOPIC(TRACE, arangodb::Logger::FIXME)
|
||||
<< "loaded JavaScript file '" << files[i] << "'";
|
||||
break;
|
||||
case JSLoader::eFailLoad:
|
||||
LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << "cannot load JavaScript file '" << files[i] << "'";
|
||||
LOG_TOPIC(FATAL, arangodb::Logger::FIXME)
|
||||
<< "cannot load JavaScript file '" << files[i] << "'";
|
||||
FATAL_ERROR_EXIT();
|
||||
break;
|
||||
case JSLoader::eFailExecute:
|
||||
LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << "error during execution of JavaScript file '" << files[i]
|
||||
<< "'";
|
||||
LOG_TOPIC(FATAL, arangodb::Logger::FIXME)
|
||||
<< "error during execution of JavaScript file '" << files[i] << "'";
|
||||
FATAL_ERROR_EXIT();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ int main(int argc, char* argv[]) {
|
|||
int ret = EXIT_SUCCESS;
|
||||
|
||||
try {
|
||||
server.addFeature(new ClientFeature(&server));
|
||||
server.addFeature(new ClientFeature(&server, true));
|
||||
server.addFeature(new ConfigFeature(&server, name));
|
||||
server.addFeature(new ConsoleFeature(&server));
|
||||
server.addFeature(new LanguageFeature(&server));
|
||||
|
|
|
@ -96,7 +96,7 @@ SimpleHttpClient::~SimpleHttpClient() {
|
|||
// -----------------------------------------------------------------------------
|
||||
// public methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
void SimpleHttpClient::setAborted(bool value) noexcept {
|
||||
_aborted.store(value, std::memory_order_release);
|
||||
setInterrupted(value);
|
||||
|
@ -160,7 +160,7 @@ SimpleHttpResult* SimpleHttpClient::retryRequest(
|
|||
std::unordered_map<std::string, std::string> const& headers) {
|
||||
SimpleHttpResult* result = nullptr;
|
||||
size_t tries = 0;
|
||||
|
||||
|
||||
while (true) {
|
||||
TRI_ASSERT(result == nullptr);
|
||||
|
||||
|
@ -174,18 +174,19 @@ SimpleHttpResult* SimpleHttpClient::retryRequest(
|
|||
result = nullptr;
|
||||
|
||||
if (tries++ >= _params._maxRetries) {
|
||||
LOG_TOPIC(WARN, arangodb::Logger::HTTPCLIENT) << "" << _params._retryMessage
|
||||
<< " - no retries left";
|
||||
LOG_TOPIC(WARN, arangodb::Logger::HTTPCLIENT)
|
||||
<< "" << _params._retryMessage << " - no retries left";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (isAborted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_params._retryMessage.empty() && (_params._maxRetries - tries) > 0) {
|
||||
LOG_TOPIC(WARN, arangodb::Logger::HTTPCLIENT) << "" << _params._retryMessage
|
||||
<< " - retries left: " << (_params._maxRetries - tries);
|
||||
LOG_TOPIC(WARN, arangodb::Logger::HTTPCLIENT)
|
||||
<< "" << _params._retryMessage
|
||||
<< " - retries left: " << (_params._maxRetries - tries);
|
||||
}
|
||||
|
||||
// 1 microsecond == 10^-6 seconds
|
||||
|
@ -352,7 +353,8 @@ SimpleHttpResult* SimpleHttpClient::doRequest(
|
|||
// progress is made (but without an error), this then means
|
||||
// that the server has closed the connection and we must
|
||||
// process the body one more time:
|
||||
_result->setContentLength(_readBuffer.length() - _readBufferOffset);
|
||||
_result->setContentLength(_readBuffer.length() -
|
||||
_readBufferOffset);
|
||||
}
|
||||
processBody();
|
||||
}
|
||||
|
@ -530,9 +532,11 @@ void SimpleHttpClient::setRequest(
|
|||
|
||||
// append hostname
|
||||
std::string hostname = _connection->getEndpoint()->host();
|
||||
|
||||
LOG_TOPIC(DEBUG, Logger::HTTPCLIENT) << "request to " << hostname << ": " << GeneralRequest::translateMethod(method) << ' ' << *l;
|
||||
|
||||
|
||||
LOG_TOPIC(DEBUG, Logger::HTTPCLIENT)
|
||||
<< "request to " << hostname << ": "
|
||||
<< GeneralRequest::translateMethod(method) << ' ' << *l;
|
||||
|
||||
_writeBuffer.appendText(TRI_CHAR_LENGTH_PAIR("Host: "));
|
||||
_writeBuffer.appendText(hostname);
|
||||
_writeBuffer.appendText(TRI_CHAR_LENGTH_PAIR("\r\n"));
|
||||
|
@ -554,15 +558,14 @@ void SimpleHttpClient::setRequest(
|
|||
}
|
||||
|
||||
// do basic authorization
|
||||
if (!_params._basicAuth.empty()) {
|
||||
_writeBuffer.appendText(TRI_CHAR_LENGTH_PAIR("Authorization: Basic "));
|
||||
_writeBuffer.appendText(_params._basicAuth);
|
||||
_writeBuffer.appendText(TRI_CHAR_LENGTH_PAIR("\r\n"));
|
||||
}
|
||||
if (!_params._jwt.empty()) {
|
||||
_writeBuffer.appendText(TRI_CHAR_LENGTH_PAIR("Authorization: bearer "));
|
||||
_writeBuffer.appendText(_params._jwt);
|
||||
_writeBuffer.appendText(TRI_CHAR_LENGTH_PAIR("\r\n"));
|
||||
} else if (!_params._basicAuth.empty()) {
|
||||
_writeBuffer.appendText(TRI_CHAR_LENGTH_PAIR("Authorization: Basic "));
|
||||
_writeBuffer.appendText(_params._basicAuth);
|
||||
_writeBuffer.appendText(TRI_CHAR_LENGTH_PAIR("\r\n"));
|
||||
}
|
||||
|
||||
for (auto const& header : headers) {
|
||||
|
@ -682,8 +685,11 @@ void SimpleHttpClient::processHeader() {
|
|||
// found content-length header in response
|
||||
else if (_result->hasContentLength() && _result->getContentLength() > 0) {
|
||||
if (_result->getContentLength() > _params._maxPacketSize) {
|
||||
std::string errorMessage("ignoring HTTP response with 'Content-Length' bigger than max packet size (");
|
||||
errorMessage += std::to_string(_result->getContentLength()) + " > " + std::to_string(_params._maxPacketSize) + ")";
|
||||
std::string errorMessage(
|
||||
"ignoring HTTP response with 'Content-Length' bigger than max "
|
||||
"packet size (");
|
||||
errorMessage += std::to_string(_result->getContentLength()) + " > " +
|
||||
std::to_string(_params._maxPacketSize) + ")";
|
||||
setErrorMessage(errorMessage, true);
|
||||
|
||||
// reset connection
|
||||
|
@ -725,7 +731,6 @@ void SimpleHttpClient::processHeader() {
|
|||
TRI_ASSERT(ptr == _readBuffer.c_str() + _readBufferOffset);
|
||||
TRI_ASSERT(remain == _readBuffer.length() - _readBufferOffset);
|
||||
pos = static_cast<char const*>(memchr(ptr, '\n', remain));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -830,8 +835,11 @@ void SimpleHttpClient::processChunkedHeader() {
|
|||
|
||||
// failed: too many bytes
|
||||
if (contentLength > _params._maxPacketSize) {
|
||||
std::string errorMessage("ignoring HTTP response with 'Content-Length' bigger than max packet size (");
|
||||
errorMessage += std::to_string(contentLength) + " > " + std::to_string(_params._maxPacketSize) + ")";
|
||||
std::string errorMessage(
|
||||
"ignoring HTTP response with 'Content-Length' bigger than max packet "
|
||||
"size (");
|
||||
errorMessage += std::to_string(contentLength) + " > " +
|
||||
std::to_string(_params._maxPacketSize) + ")";
|
||||
setErrorMessage(errorMessage, true);
|
||||
// reset connection
|
||||
this->close();
|
||||
|
@ -883,7 +891,7 @@ void SimpleHttpClient::processChunkedBody() {
|
|||
}
|
||||
|
||||
_readBufferOffset += (size_t)_nextChunkedSize + 2;
|
||||
|
||||
|
||||
_state = IN_READ_CHUNKED_HEADER;
|
||||
processChunkedHeader();
|
||||
}
|
||||
|
@ -987,5 +995,5 @@ std::string SimpleHttpClient::getServerVersion(int* errorCode) {
|
|||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace httpclient
|
||||
} // namespace arangodb
|
||||
|
|
|
@ -93,10 +93,7 @@ struct SimpleHttpClientParams {
|
|||
|
||||
void setJwt(std::string const& jwt) { _jwt = jwt; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief sets username and password
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// sets username and password
|
||||
void setUserNamePassword(char const* prefix,
|
||||
std::string const& username,
|
||||
std::string const& password) {
|
||||
|
|
Loading…
Reference in New Issue