2022-02-11 18:29:41 +08:00
|
|
|
// Copyright 2022 Memgraph Ltd.
|
2021-10-03 18:07:04 +08:00
|
|
|
//
|
|
|
|
// Use of this software is governed by the Business Source License
|
|
|
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
|
|
|
// License, and you may not use this file except in compliance with the Business Source License.
|
|
|
|
//
|
|
|
|
// As of the Change Date specified in that file, in accordance with
|
|
|
|
// the Business Source License, use of this software will be governed
|
|
|
|
// by the Apache License, Version 2.0, included in the file
|
|
|
|
// licenses/APL.txt.
|
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
#include <algorithm>
|
2021-03-10 02:55:58 +08:00
|
|
|
#include <atomic>
|
2018-04-20 20:58:49 +08:00
|
|
|
#include <chrono>
|
2020-02-24 21:06:21 +08:00
|
|
|
#include <csignal>
|
2018-04-20 20:58:49 +08:00
|
|
|
#include <cstdint>
|
|
|
|
#include <exception>
|
2020-02-24 21:06:21 +08:00
|
|
|
#include <filesystem>
|
2018-04-20 20:58:49 +08:00
|
|
|
#include <functional>
|
|
|
|
#include <limits>
|
2020-02-24 21:06:21 +08:00
|
|
|
#include <map>
|
|
|
|
#include <optional>
|
2020-02-17 21:41:46 +08:00
|
|
|
#include <regex>
|
2020-02-24 21:06:21 +08:00
|
|
|
#include <string>
|
2021-06-14 21:47:57 +08:00
|
|
|
#include <string_view>
|
2018-04-20 20:58:49 +08:00
|
|
|
#include <thread>
|
2016-08-10 16:39:02 +08:00
|
|
|
|
2021-01-21 22:47:56 +08:00
|
|
|
#include <fmt/format.h>
|
2017-07-06 19:53:39 +08:00
|
|
|
#include <gflags/gflags.h>
|
2021-01-21 22:47:56 +08:00
|
|
|
#include <spdlog/common.h>
|
|
|
|
#include <spdlog/sinks/daily_file_sink.h>
|
2022-01-04 19:13:48 +08:00
|
|
|
#include <spdlog/sinks/dist_sink.h>
|
2021-01-21 22:47:56 +08:00
|
|
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
2017-05-22 18:31:04 +08:00
|
|
|
|
2022-02-17 17:51:04 +08:00
|
|
|
#include "communication/bolt/v1/constants.hpp"
|
2022-02-17 17:35:48 +08:00
|
|
|
#include "communication/websocket/auth.hpp"
|
2022-01-04 19:13:48 +08:00
|
|
|
#include "communication/websocket/server.hpp"
|
2020-03-03 17:38:50 +08:00
|
|
|
#include "helpers.hpp"
|
2020-02-24 21:06:21 +08:00
|
|
|
#include "py/py.hpp"
|
2021-07-22 22:22:08 +08:00
|
|
|
#include "query/auth_checker.hpp"
|
2021-07-05 17:42:40 +08:00
|
|
|
#include "query/discard_value_stream.hpp"
|
Extract communication to static library
Summary:
Session specifics have been move out of the Bolt `executing` state, and
are accessed via pure virtual Session type. Our server is templated on
the session and we are setting the concrete type, so there should be no
virtual call overhead. Abstract Session is used to indicate the
interface, this could have also been templated, but the explicit
interface definition makes it clearer.
Specific session implementation for running Memgraph is now implemented
in memgraph_bolt, which instantiates the concrete session type. This may
not be 100% appropriate place, but Memgraph specific session isn't
needed anywhere else.
Bolt/communication tests now use a dummy session and depend only on
communication, which significantly improves test run times.
All these changes make the communication a library which doesn't depend
on storage nor the database. Only shared connection points, which aren't
part of the base communication library are:
* glue/conversion -- which converts between storage and bolt types, and
* communication/result_stream_faker -- templated, but used in tests and query/repl
Depends on D1453
Reviewers: mferencevic, buda, mtomic, msantl
Reviewed By: mferencevic, mtomic
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D1456
2018-07-10 22:18:19 +08:00
|
|
|
#include "query/exceptions.hpp"
|
2021-09-30 01:14:39 +08:00
|
|
|
#include "query/frontend/ast/ast.hpp"
|
2020-02-24 21:06:21 +08:00
|
|
|
#include "query/interpreter.hpp"
|
2021-03-18 18:03:42 +08:00
|
|
|
#include "query/plan/operator.hpp"
|
2020-01-15 20:58:41 +08:00
|
|
|
#include "query/procedure/module.hpp"
|
2020-02-24 21:06:21 +08:00
|
|
|
#include "query/procedure/py_module.hpp"
|
|
|
|
#include "requests/requests.hpp"
|
2021-06-14 21:47:57 +08:00
|
|
|
#include "storage/v2/isolation_level.hpp"
|
2020-02-05 22:05:18 +08:00
|
|
|
#include "storage/v2/storage.hpp"
|
2020-02-24 21:06:21 +08:00
|
|
|
#include "storage/v2/view.hpp"
|
2018-06-20 19:46:54 +08:00
|
|
|
#include "telemetry/telemetry.hpp"
|
2021-03-10 02:55:58 +08:00
|
|
|
#include "utils/event_counter.hpp"
|
Integrate loading openCypher module procedures
Summary:
All mgp_* symbols are exported from Memgraph executable, no other
symbols should be visible.
The primary C API header, mg_procedure.h, is now part of the
installation. Also, added a shippable query module example.
Directory `query_modules` is meant to contain sources of modules we
write and ship as part of the installation. Currently, there's only an
example module, but there may be potentially more. Some modules could
only be installed as part of the enterprise release.
For Memgraph to load custom procedures, it needs to be started with a
flag pointing to a directory with compiled shared libraries implementing
those procedures.
Reviewers: mferencevic, ipaljak, llugovic, dsantl, buda
Reviewed By: mferencevic
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2538
2019-10-31 23:36:34 +08:00
|
|
|
#include "utils/file.hpp"
|
2017-06-09 21:48:40 +08:00
|
|
|
#include "utils/flag_validation.hpp"
|
2021-09-30 01:14:39 +08:00
|
|
|
#include "utils/license.hpp"
|
2021-01-21 22:47:56 +08:00
|
|
|
#include "utils/logging.hpp"
|
2021-02-23 03:51:46 +08:00
|
|
|
#include "utils/memory_tracker.hpp"
|
2021-10-07 20:51:30 +08:00
|
|
|
#include "utils/message.hpp"
|
2021-03-05 17:24:08 +08:00
|
|
|
#include "utils/readable_size.hpp"
|
2021-07-22 22:22:08 +08:00
|
|
|
#include "utils/rw_lock.hpp"
|
2021-09-30 01:14:39 +08:00
|
|
|
#include "utils/settings.hpp"
|
2020-02-24 21:06:21 +08:00
|
|
|
#include "utils/signals.hpp"
|
2020-01-15 20:58:41 +08:00
|
|
|
#include "utils/string.hpp"
|
2021-07-22 22:22:08 +08:00
|
|
|
#include "utils/synchronized.hpp"
|
2020-02-24 21:06:21 +08:00
|
|
|
#include "utils/sysinfo/memory.hpp"
|
|
|
|
#include "utils/terminate_handler.hpp"
|
|
|
|
#include "version.hpp"
|
Integrate loading openCypher module procedures
Summary:
All mgp_* symbols are exported from Memgraph executable, no other
symbols should be visible.
The primary C API header, mg_procedure.h, is now part of the
installation. Also, added a shippable query module example.
Directory `query_modules` is meant to contain sources of modules we
write and ship as part of the installation. Currently, there's only an
example module, but there may be potentially more. Some modules could
only be installed as part of the enterprise release.
For Memgraph to load custom procedures, it needs to be started with a
flag pointing to a directory with compiled shared libraries implementing
those procedures.
Reviewers: mferencevic, ipaljak, llugovic, dsantl, buda
Reviewed By: mferencevic
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2538
2019-10-31 23:36:34 +08:00
|
|
|
|
2020-04-06 19:59:37 +08:00
|
|
|
// Communication libraries must be included after query libraries are included.
|
|
|
|
// This is to enable compilation of the binary when linking with old OpenSSL
|
|
|
|
// libraries (as on CentOS 7).
|
|
|
|
//
|
|
|
|
// The OpenSSL library available on CentOS 7 is v1.0.0, that version includes
|
|
|
|
// `libkrb5` in its public API headers (that we include in our communication
|
|
|
|
// stack). The `libkrb5` library has `#define`s for `TRUE` and `FALSE`. Those
|
|
|
|
// defines clash with Antlr's usage of `TRUE` and `FALSE` as enumeration keys.
|
|
|
|
// Because of that the definitions of `TRUE` and `FALSE` that are inherited
|
|
|
|
// from `libkrb5` must be included after the Antlr includes. Hence,
|
|
|
|
// communication headers must be included after query headers.
|
|
|
|
#include "communication/bolt/v1/exceptions.hpp"
|
|
|
|
#include "communication/bolt/v1/session.hpp"
|
|
|
|
#include "communication/init.hpp"
|
2022-04-27 16:13:16 +08:00
|
|
|
#include "communication/v2/server.hpp"
|
|
|
|
#include "communication/v2/session.hpp"
|
2020-04-06 19:59:37 +08:00
|
|
|
#include "glue/communication.hpp"
|
|
|
|
|
2020-02-24 21:06:21 +08:00
|
|
|
#include "auth/auth.hpp"
|
2020-02-05 22:05:18 +08:00
|
|
|
#include "glue/auth.hpp"
|
2021-09-30 01:14:39 +08:00
|
|
|
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
|
|
#include "audit/log.hpp"
|
2020-02-05 22:05:18 +08:00
|
|
|
#endif
|
|
|
|
|
2021-06-14 21:47:57 +08:00
|
|
|
namespace {
|
|
|
|
std::string GetAllowedEnumValuesString(const auto &mappings) {
|
|
|
|
std::vector<std::string> allowed_values;
|
|
|
|
allowed_values.reserve(mappings.size());
|
|
|
|
std::transform(mappings.begin(), mappings.end(), std::back_inserter(allowed_values),
|
|
|
|
[](const auto &mapping) { return std::string(mapping.first); });
|
2022-02-22 20:33:45 +08:00
|
|
|
return memgraph::utils::Join(allowed_values, ", ");
|
2021-06-14 21:47:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
enum class ValidationError : uint8_t { EmptyValue, InvalidValue };
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::BasicResult<ValidationError> IsValidEnumValueString(const auto &value, const auto &mappings) {
|
2021-06-14 21:47:57 +08:00
|
|
|
if (value.empty()) {
|
|
|
|
return ValidationError::EmptyValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::find_if(mappings.begin(), mappings.end(), [&](const auto &mapping) { return mapping.first == value; }) ==
|
|
|
|
mappings.cend()) {
|
|
|
|
return ValidationError::InvalidValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Enum>
|
|
|
|
std::optional<Enum> StringToEnum(const auto &value, const auto &mappings) {
|
|
|
|
const auto mapping_iter =
|
|
|
|
std::find_if(mappings.begin(), mappings.end(), [&](const auto &mapping) { return mapping.first == value; });
|
|
|
|
if (mapping_iter == mappings.cend()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mapping_iter->second;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2020-02-24 21:06:21 +08:00
|
|
|
// Bolt server flags.
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_string(bolt_address, "0.0.0.0", "IP address on which the Bolt server should listen.");
|
2022-02-17 17:27:24 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_string(monitoring_address, "0.0.0.0",
|
|
|
|
"IP address on which the websocket server for Memgraph monitoring should listen.");
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_VALIDATED_int32(bolt_port, 7687, "Port on which the Bolt server should listen.",
|
2018-01-15 21:03:07 +08:00
|
|
|
FLAG_IN_RANGE(0, std::numeric_limits<uint16_t>::max()));
|
2022-02-17 17:27:24 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_VALIDATED_int32(monitoring_port, 7444,
|
|
|
|
"Port on which the websocket server for Memgraph monitoring should listen.",
|
|
|
|
FLAG_IN_RANGE(0, std::numeric_limits<uint16_t>::max()));
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_VALIDATED_int32(bolt_num_workers, std::max(std::thread::hardware_concurrency(), 1U),
|
|
|
|
"Number of workers used by the Bolt server. By default, this will be the "
|
|
|
|
"number of processing units available on the machine.",
|
|
|
|
FLAG_IN_RANGE(1, INT32_MAX));
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_VALIDATED_int32(bolt_session_inactivity_timeout, 1800,
|
|
|
|
"Time in seconds after which inactive Bolt sessions will be "
|
|
|
|
"closed.",
|
|
|
|
FLAG_IN_RANGE(1, INT32_MAX));
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_string(bolt_cert_file, "", "Certificate file which should be used for the Bolt server.");
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_string(bolt_key_file, "", "Key file which should be used for the Bolt server.");
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2020-02-24 21:06:21 +08:00
|
|
|
DEFINE_string(bolt_server_name_for_init, "",
|
|
|
|
"Server name which the database should send to the client in the "
|
|
|
|
"Bolt INIT message.");
|
2018-08-22 21:00:16 +08:00
|
|
|
|
2019-10-09 22:00:02 +08:00
|
|
|
// General purpose flags.
|
2020-03-03 17:38:50 +08:00
|
|
|
// NOTE: The `data_directory` flag must be the same here and in
|
|
|
|
// `mg_import_csv`. If you change it, make sure to change it there as well.
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_string(data_directory, "mg_data", "Path to directory in which to save all permanent data.");
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_HIDDEN_string(log_link_basename, "", "Basename used for symlink creation to the last log file.");
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2020-02-24 21:06:21 +08:00
|
|
|
DEFINE_uint64(memory_warning_threshold, 1024,
|
|
|
|
"Memory warning threshold, in MB. If Memgraph detects there is "
|
|
|
|
"less available RAM it will log a warning. Set to 0 to "
|
|
|
|
"disable.");
|
2019-10-09 22:00:02 +08:00
|
|
|
|
2021-06-30 17:19:13 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_bool(allow_load_csv, true, "Controls whether LOAD CSV clause is allowed in queries.");
|
|
|
|
|
2019-10-09 22:00:02 +08:00
|
|
|
// Storage flags.
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_VALIDATED_uint64(storage_gc_cycle_sec, 30, "Storage garbage collector interval (in seconds).",
|
2019-10-09 22:00:02 +08:00
|
|
|
FLAG_IN_RANGE(1, 24 * 3600));
|
2020-03-03 17:38:50 +08:00
|
|
|
// NOTE: The `storage_properties_on_edges` flag must be the same here and in
|
|
|
|
// `mg_import_csv`. If you change it, make sure to change it there as well.
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_bool(storage_properties_on_edges, false, "Controls whether edges have properties.");
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_bool(storage_recover_on_startup, false, "Controls whether the storage recovers persisted data on startup.");
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2019-10-09 22:00:02 +08:00
|
|
|
DEFINE_VALIDATED_uint64(storage_snapshot_interval_sec, 0,
|
|
|
|
"Storage snapshot creation interval (in seconds). Set "
|
|
|
|
"to 0 to disable periodic snapshot creation.",
|
|
|
|
FLAG_IN_RANGE(0, 7 * 24 * 3600));
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2019-10-09 22:00:02 +08:00
|
|
|
DEFINE_bool(storage_wal_enabled, false,
|
|
|
|
"Controls whether the storage uses write-ahead-logging. To enable "
|
|
|
|
"WAL periodic snapshots must be enabled.");
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_VALIDATED_uint64(storage_snapshot_retention_count, 3, "The number of snapshots that should always be kept.",
|
2019-10-09 22:00:02 +08:00
|
|
|
FLAG_IN_RANGE(1, 1000000));
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2022-02-22 20:33:45 +08:00
|
|
|
DEFINE_VALIDATED_uint64(storage_wal_file_size_kib, memgraph::storage::Config::Durability().wal_file_size_kibibytes,
|
2022-03-01 20:01:59 +08:00
|
|
|
"Minimum file size of each WAL file.",
|
|
|
|
FLAG_IN_RANGE(1, static_cast<unsigned long>(1000) * 1024));
|
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2022-02-22 20:33:45 +08:00
|
|
|
DEFINE_VALIDATED_uint64(storage_wal_file_flush_every_n_tx,
|
|
|
|
memgraph::storage::Config::Durability().wal_file_flush_every_n_tx,
|
2021-02-18 22:32:43 +08:00
|
|
|
"Issue a 'fsync' call after this amount of transactions are written to the "
|
|
|
|
"WAL file. Set to 1 for fully synchronous operation.",
|
|
|
|
FLAG_IN_RANGE(1, 1000000));
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_bool(storage_snapshot_on_exit, false, "Controls whether the storage creates another snapshot on exit.");
|
2019-10-09 22:00:02 +08:00
|
|
|
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2018-06-20 19:46:54 +08:00
|
|
|
DEFINE_bool(telemetry_enabled, false,
|
|
|
|
"Set to true to enable telemetry. We collect information about the "
|
|
|
|
"running system (CPU and memory information) and information about "
|
|
|
|
"the database runtime (vertex and edge counts and resource usage) "
|
|
|
|
"to allow for easier improvement of the product.");
|
Extract communication to static library
Summary:
Session specifics have been move out of the Bolt `executing` state, and
are accessed via pure virtual Session type. Our server is templated on
the session and we are setting the concrete type, so there should be no
virtual call overhead. Abstract Session is used to indicate the
interface, this could have also been templated, but the explicit
interface definition makes it clearer.
Specific session implementation for running Memgraph is now implemented
in memgraph_bolt, which instantiates the concrete session type. This may
not be 100% appropriate place, but Memgraph specific session isn't
needed anywhere else.
Bolt/communication tests now use a dummy session and depend only on
communication, which significantly improves test run times.
All these changes make the communication a library which doesn't depend
on storage nor the database. Only shared connection points, which aren't
part of the base communication library are:
* glue/conversion -- which converts between storage and bolt types, and
* communication/result_stream_faker -- templated, but used in tests and query/repl
Depends on D1453
Reviewers: mferencevic, buda, mtomic, msantl
Reviewed By: mferencevic, mtomic
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D1456
2018-07-10 22:18:19 +08:00
|
|
|
|
2022-07-07 19:30:28 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_bool(storage_restore_replicas_on_startup, true,
|
|
|
|
"Controls replicas should be restored automatically."); // TODO(42jeremy) this must be removed once T0835
|
|
|
|
// is implemented.
|
|
|
|
|
2021-11-16 21:29:45 +08:00
|
|
|
// Streams flags
|
|
|
|
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_uint32(
|
|
|
|
stream_transaction_conflict_retries, 30,
|
|
|
|
"Number of times to retry when a stream transformation fails to commit because of conflicting transactions");
|
|
|
|
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_uint32(
|
|
|
|
stream_transaction_retry_interval, 500,
|
|
|
|
"Retry interval in milliseconds when a stream transformation fails to commit because of conflicting transactions");
|
2021-06-22 14:43:19 +08:00
|
|
|
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_string(kafka_bootstrap_servers, "",
|
2021-10-18 16:49:00 +08:00
|
|
|
"List of default Kafka brokers as a comma separated list of broker host or host:port.");
|
2021-06-22 14:43:19 +08:00
|
|
|
|
2021-11-15 18:12:07 +08:00
|
|
|
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_string(pulsar_service_url, "", "Default URL used while connecting to Pulsar brokers.");
|
|
|
|
|
2019-02-19 20:50:46 +08:00
|
|
|
// Audit logging flags.
|
2020-02-05 22:05:18 +08:00
|
|
|
#ifdef MG_ENTERPRISE
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2019-02-19 20:50:46 +08:00
|
|
|
DEFINE_bool(audit_enabled, false, "Set to true to enable audit logging.");
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2022-02-22 20:33:45 +08:00
|
|
|
DEFINE_VALIDATED_int32(audit_buffer_size, memgraph::audit::kBufferSizeDefault,
|
|
|
|
"Maximum number of items in the audit log buffer.", FLAG_IN_RANGE(1, INT32_MAX));
|
2022-03-01 20:01:59 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2022-02-22 20:33:45 +08:00
|
|
|
DEFINE_VALIDATED_int32(audit_buffer_flush_interval_ms, memgraph::audit::kBufferFlushIntervalMillisDefault,
|
2021-02-18 22:32:43 +08:00
|
|
|
"Interval (in milliseconds) used for flushing the audit log buffer.",
|
|
|
|
FLAG_IN_RANGE(10, INT32_MAX));
|
2020-02-05 22:05:18 +08:00
|
|
|
#endif
|
2019-02-19 20:50:46 +08:00
|
|
|
|
2019-11-25 22:08:16 +08:00
|
|
|
// Query flags.
|
2021-06-30 17:19:13 +08:00
|
|
|
|
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
2021-07-02 15:02:11 +08:00
|
|
|
DEFINE_double(query_execution_timeout_sec, 600,
|
2019-11-25 22:08:16 +08:00
|
|
|
"Maximum allowed query execution time. Queries exceeding this "
|
|
|
|
"limit will be aborted. Value of 0 means no limit.");
|
|
|
|
|
2022-05-21 10:29:17 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_uint64(replication_replica_check_frequency_sec, 1,
|
|
|
|
"The time duration between two replica checks/pings. If < 1, replicas will NOT be checked at all. NOTE: "
|
|
|
|
"The MAIN instance allocates a new thread for each REPLICA.");
|
|
|
|
|
2021-06-14 21:47:57 +08:00
|
|
|
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_uint64(
|
|
|
|
memory_limit, 0,
|
|
|
|
"Total memory limit in MiB. Set to 0 to use the default values which are 100\% of the phyisical memory if the swap "
|
|
|
|
"is enabled and 90\% of the physical memory otherwise.");
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
using namespace std::literals;
|
2022-03-31 19:52:43 +08:00
|
|
|
inline constexpr std::array isolation_level_mappings{
|
2022-02-22 20:33:45 +08:00
|
|
|
std::pair{"SNAPSHOT_ISOLATION"sv, memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION},
|
|
|
|
std::pair{"READ_COMMITTED"sv, memgraph::storage::IsolationLevel::READ_COMMITTED},
|
|
|
|
std::pair{"READ_UNCOMMITTED"sv, memgraph::storage::IsolationLevel::READ_UNCOMMITTED}};
|
2021-06-14 21:47:57 +08:00
|
|
|
|
|
|
|
const std::string isolation_level_help_string =
|
|
|
|
fmt::format("Default isolation level used for the transactions. Allowed values: {}",
|
|
|
|
GetAllowedEnumValuesString(isolation_level_mappings));
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_VALIDATED_string(isolation_level, "SNAPSHOT_ISOLATION", isolation_level_help_string.c_str(), {
|
|
|
|
if (const auto result = IsValidEnumValueString(value, isolation_level_mappings); result.HasError()) {
|
|
|
|
const auto error = result.GetError();
|
|
|
|
switch (error) {
|
|
|
|
case ValidationError::EmptyValue: {
|
|
|
|
std::cout << "Isolation level cannot be empty." << std::endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ValidationError::InvalidValue: {
|
|
|
|
std::cout << "Invalid value for isolation level. Allowed values: "
|
|
|
|
<< GetAllowedEnumValuesString(isolation_level_mappings) << std::endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
namespace {
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::storage::IsolationLevel ParseIsolationLevel() {
|
|
|
|
const auto isolation_level =
|
|
|
|
StringToEnum<memgraph::storage::IsolationLevel>(FLAGS_isolation_level, isolation_level_mappings);
|
2021-06-14 21:47:57 +08:00
|
|
|
MG_ASSERT(isolation_level, "Invalid isolation level");
|
|
|
|
return *isolation_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t GetMemoryLimit() {
|
|
|
|
if (FLAGS_memory_limit == 0) {
|
2022-02-22 20:33:45 +08:00
|
|
|
auto maybe_total_memory = memgraph::utils::sysinfo::TotalMemory();
|
2021-06-14 21:47:57 +08:00
|
|
|
MG_ASSERT(maybe_total_memory, "Failed to fetch the total physical memory");
|
2022-02-22 20:33:45 +08:00
|
|
|
const auto maybe_swap_memory = memgraph::utils::sysinfo::SwapTotalMemory();
|
2021-06-14 21:47:57 +08:00
|
|
|
MG_ASSERT(maybe_swap_memory, "Failed to fetch the total swap memory");
|
|
|
|
|
|
|
|
if (*maybe_swap_memory == 0) {
|
|
|
|
// take only 90% of the total memory
|
|
|
|
*maybe_total_memory *= 9;
|
|
|
|
*maybe_total_memory /= 10;
|
|
|
|
}
|
|
|
|
return *maybe_total_memory * 1024;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We parse the memory as MiB every time
|
|
|
|
return FLAGS_memory_limit * 1024 * 1024;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2021-03-10 19:18:09 +08:00
|
|
|
namespace {
|
|
|
|
std::vector<std::filesystem::path> query_modules_directories;
|
|
|
|
} // namespace
|
|
|
|
DEFINE_VALIDATED_string(query_modules_directory, "",
|
|
|
|
"Directory where modules with custom query procedures are stored. "
|
|
|
|
"NOTE: Multiple comma-separated directories can be defined.",
|
2021-02-18 22:32:43 +08:00
|
|
|
{
|
2021-03-10 19:18:09 +08:00
|
|
|
query_modules_directories.clear();
|
2021-02-18 22:32:43 +08:00
|
|
|
if (value.empty()) return true;
|
2022-02-22 20:33:45 +08:00
|
|
|
const auto directories = memgraph::utils::Split(value, ",");
|
2021-03-10 19:18:09 +08:00
|
|
|
for (const auto &dir : directories) {
|
2022-02-22 20:33:45 +08:00
|
|
|
if (!memgraph::utils::DirExists(dir)) {
|
2021-03-10 19:18:09 +08:00
|
|
|
std::cout << "Expected --" << flagname << " to point to directories." << std::endl;
|
|
|
|
std::cout << dir << " is not a directory." << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
query_modules_directories.reserve(directories.size());
|
|
|
|
std::transform(directories.begin(), directories.end(),
|
|
|
|
std::back_inserter(query_modules_directories),
|
|
|
|
[](const auto &dir) { return dir; });
|
|
|
|
return true;
|
2021-02-18 22:32:43 +08:00
|
|
|
});
|
Integrate loading openCypher module procedures
Summary:
All mgp_* symbols are exported from Memgraph executable, no other
symbols should be visible.
The primary C API header, mg_procedure.h, is now part of the
installation. Also, added a shippable query module example.
Directory `query_modules` is meant to contain sources of modules we
write and ship as part of the installation. Currently, there's only an
example module, but there may be potentially more. Some modules could
only be installed as part of the enterprise release.
For Memgraph to load custom procedures, it needs to be started with a
flag pointing to a directory with compiled shared libraries implementing
those procedures.
Reviewers: mferencevic, ipaljak, llugovic, dsantl, buda
Reviewed By: mferencevic
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2538
2019-10-31 23:36:34 +08:00
|
|
|
|
2021-01-21 22:47:56 +08:00
|
|
|
// Logging flags
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_bool(also_log_to_stderr, false, "Log messages go to stderr in addition to logfiles");
|
2021-01-21 22:47:56 +08:00
|
|
|
DEFINE_string(log_file, "", "Path to where the log should be stored.");
|
|
|
|
|
|
|
|
namespace {
|
2022-03-31 19:52:43 +08:00
|
|
|
inline constexpr std::array log_level_mappings{
|
2021-06-14 21:47:57 +08:00
|
|
|
std::pair{"TRACE"sv, spdlog::level::trace}, std::pair{"DEBUG"sv, spdlog::level::debug},
|
|
|
|
std::pair{"INFO"sv, spdlog::level::info}, std::pair{"WARNING"sv, spdlog::level::warn},
|
|
|
|
std::pair{"ERROR"sv, spdlog::level::err}, std::pair{"CRITICAL"sv, spdlog::level::critical}};
|
2021-01-21 22:47:56 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
const std::string log_level_help_string =
|
2021-06-14 21:47:57 +08:00
|
|
|
fmt::format("Minimum log level. Allowed values: {}", GetAllowedEnumValuesString(log_level_mappings));
|
2021-01-21 22:47:56 +08:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
DEFINE_VALIDATED_string(log_level, "WARNING", log_level_help_string.c_str(), {
|
2021-06-14 21:47:57 +08:00
|
|
|
if (const auto result = IsValidEnumValueString(value, log_level_mappings); result.HasError()) {
|
|
|
|
const auto error = result.GetError();
|
|
|
|
switch (error) {
|
|
|
|
case ValidationError::EmptyValue: {
|
|
|
|
std::cout << "Log level cannot be empty." << std::endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ValidationError::InvalidValue: {
|
|
|
|
std::cout << "Invalid value for log level. Allowed values: " << GetAllowedEnumValuesString(log_level_mappings)
|
|
|
|
<< std::endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-01-21 22:47:56 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
namespace {
|
2022-01-27 16:51:00 +08:00
|
|
|
spdlog::level::level_enum ParseLogLevel() {
|
2021-06-14 21:47:57 +08:00
|
|
|
const auto log_level = StringToEnum<spdlog::level::level_enum>(FLAGS_log_level, log_level_mappings);
|
|
|
|
MG_ASSERT(log_level, "Invalid log level");
|
2022-01-27 16:51:00 +08:00
|
|
|
return *log_level;
|
2021-01-21 22:47:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 5 weeks * 7 days
|
2022-03-31 19:52:43 +08:00
|
|
|
inline constexpr auto log_retention_count = 35;
|
2022-01-27 16:51:00 +08:00
|
|
|
void CreateLoggerFromSink(const auto &sinks, const auto log_level) {
|
|
|
|
auto logger = std::make_shared<spdlog::logger>("memgraph_log", sinks.begin(), sinks.end());
|
|
|
|
logger->set_level(log_level);
|
|
|
|
logger->flush_on(spdlog::level::trace);
|
|
|
|
spdlog::set_default_logger(std::move(logger));
|
|
|
|
}
|
2021-01-21 22:47:56 +08:00
|
|
|
|
2022-01-27 16:51:00 +08:00
|
|
|
void InitializeLogger() {
|
|
|
|
std::vector<spdlog::sink_ptr> sinks;
|
2021-01-21 22:47:56 +08:00
|
|
|
|
|
|
|
if (FLAGS_also_log_to_stderr) {
|
2022-01-27 16:51:00 +08:00
|
|
|
sinks.emplace_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
|
2021-01-21 22:47:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!FLAGS_log_file.empty()) {
|
|
|
|
// get local time
|
2022-01-27 16:51:00 +08:00
|
|
|
time_t current_time{0};
|
2021-01-21 22:47:56 +08:00
|
|
|
struct tm *local_time{nullptr};
|
|
|
|
|
|
|
|
time(¤t_time);
|
|
|
|
local_time = localtime(¤t_time);
|
|
|
|
|
2022-01-27 16:51:00 +08:00
|
|
|
sinks.emplace_back(std::make_shared<spdlog::sinks::daily_file_sink_mt>(
|
2021-02-18 22:32:43 +08:00
|
|
|
FLAGS_log_file, local_time->tm_hour, local_time->tm_min, false, log_retention_count));
|
2021-01-21 22:47:56 +08:00
|
|
|
}
|
2022-01-27 16:51:00 +08:00
|
|
|
CreateLoggerFromSink(sinks, ParseLogLevel());
|
|
|
|
}
|
2021-01-21 22:47:56 +08:00
|
|
|
|
2022-01-27 16:51:00 +08:00
|
|
|
void AddLoggerSink(spdlog::sink_ptr new_sink) {
|
|
|
|
auto default_logger = spdlog::default_logger();
|
|
|
|
auto sinks = default_logger->sinks();
|
|
|
|
sinks.push_back(new_sink);
|
|
|
|
CreateLoggerFromSink(sinks, default_logger->level());
|
2021-01-21 22:47:56 +08:00
|
|
|
}
|
2022-01-27 16:51:00 +08:00
|
|
|
|
2021-01-21 22:47:56 +08:00
|
|
|
} // namespace
|
|
|
|
|
2021-09-30 01:14:39 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_string(license_key, "", "License key for Memgraph Enterprise.");
|
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_string(organization_name, "", "Organization name.");
|
|
|
|
|
2020-02-24 21:06:21 +08:00
|
|
|
/// Encapsulates Dbms and Interpreter that are passed through the network server
|
|
|
|
/// and worker to the session.
|
|
|
|
struct SessionData {
|
|
|
|
// Explicit constructor here to ensure that pointers to all objects are
|
|
|
|
// supplied.
|
2021-09-30 01:14:39 +08:00
|
|
|
#if MG_ENTERPRISE
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
SessionData(memgraph::storage::Storage *db, memgraph::query::InterpreterContext *interpreter_context,
|
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth,
|
|
|
|
memgraph::audit::Log *audit_log)
|
2021-02-18 22:32:43 +08:00
|
|
|
: db(db), interpreter_context(interpreter_context), auth(auth), audit_log(audit_log) {}
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::storage::Storage *db;
|
|
|
|
memgraph::query::InterpreterContext *interpreter_context;
|
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
|
|
|
|
memgraph::audit::Log *audit_log;
|
2021-09-30 01:14:39 +08:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
SessionData(memgraph::storage::Storage *db, memgraph::query::InterpreterContext *interpreter_context,
|
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth)
|
2021-09-30 01:14:39 +08:00
|
|
|
: db(db), interpreter_context(interpreter_context), auth(auth) {}
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::storage::Storage *db;
|
|
|
|
memgraph::query::InterpreterContext *interpreter_context;
|
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
|
2021-09-30 01:14:39 +08:00
|
|
|
|
|
|
|
#endif
|
2020-02-24 21:06:21 +08:00
|
|
|
};
|
|
|
|
|
2022-03-31 19:52:43 +08:00
|
|
|
inline constexpr std::string_view default_user_role_regex = "[a-zA-Z0-9_.+-@]+";
|
2021-09-30 01:14:39 +08:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
|
|
DEFINE_string(auth_user_or_role_name_regex, default_user_role_regex.data(),
|
2021-02-18 22:32:43 +08:00
|
|
|
"Set to the regular expression that each user or role name must fulfill.");
|
2020-02-17 21:41:46 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
|
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
|
2021-09-30 01:14:39 +08:00
|
|
|
std::string name_regex_string_;
|
2020-02-17 21:41:46 +08:00
|
|
|
std::regex name_regex_;
|
2020-01-15 20:58:41 +08:00
|
|
|
|
|
|
|
public:
|
2022-02-22 20:33:45 +08:00
|
|
|
AuthQueryHandler(memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth,
|
|
|
|
std::string name_regex_string)
|
2021-09-30 01:14:39 +08:00
|
|
|
: auth_(auth), name_regex_string_(std::move(name_regex_string)), name_regex_(name_regex_string_) {}
|
2020-01-15 20:58:41 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
bool CreateUser(const std::string &username, const std::optional<std::string> &password) override {
|
2021-09-30 01:14:39 +08:00
|
|
|
if (name_regex_string_ != default_user_role_regex) {
|
|
|
|
if (const auto license_check_result =
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::license::global_license_checker.IsValidLicense(memgraph::utils::global_settings);
|
2021-09-30 01:14:39 +08:00
|
|
|
license_check_result.HasError()) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::auth::AuthException(
|
2021-09-30 01:14:39 +08:00
|
|
|
"Custom user/role regex is a Memgraph Enterprise feature. Please set the config "
|
|
|
|
"(\"--auth-user-or-role-name-regex\") to its default value (\"{}\") or remove the flag.\n{}",
|
|
|
|
default_user_role_regex,
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "user/role regex"));
|
2021-09-30 01:14:39 +08:00
|
|
|
}
|
|
|
|
}
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(username, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-09-30 01:14:39 +08:00
|
|
|
const auto [first_user, user_added] = std::invoke([&, this] {
|
|
|
|
auto locked_auth = auth_->Lock();
|
|
|
|
const auto first_user = !locked_auth->HasUsers();
|
|
|
|
const auto user_added = locked_auth->AddUser(username, password).has_value();
|
|
|
|
return std::make_pair(first_user, user_added);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (first_user) {
|
|
|
|
spdlog::info("{} is first created user. Granting all privileges.", username);
|
2022-02-22 20:33:45 +08:00
|
|
|
GrantPrivilege(username, memgraph::query::kPrivilegesAll);
|
2021-09-30 01:14:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return user_added;
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DropUser(const std::string &username) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(username, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->Lock();
|
|
|
|
auto user = locked_auth->GetUser(username);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!user) return false;
|
2021-07-22 22:22:08 +08:00
|
|
|
return locked_auth->RemoveUser(username);
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
void SetPassword(const std::string &username, const std::optional<std::string> &password) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(username, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->Lock();
|
|
|
|
auto user = locked_auth->GetUser(username);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!user) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist.", username);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
user->UpdatePassword(password);
|
2021-07-22 22:22:08 +08:00
|
|
|
locked_auth->SaveUser(*user);
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CreateRole(const std::string &rolename) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(rolename, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid role name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->Lock();
|
|
|
|
return locked_auth->AddRole(rolename).has_value();
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DropRole(const std::string &rolename) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(rolename, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid role name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->Lock();
|
|
|
|
auto role = locked_auth->GetRole(rolename);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!role) return false;
|
2021-07-22 22:22:08 +08:00
|
|
|
return locked_auth->RemoveRole(rolename);
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<memgraph::query::TypedValue> GetUsernames() override {
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->ReadLock();
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<memgraph::query::TypedValue> usernames;
|
2021-07-22 22:22:08 +08:00
|
|
|
const auto &users = locked_auth->AllUsers();
|
2020-01-15 20:58:41 +08:00
|
|
|
usernames.reserve(users.size());
|
|
|
|
for (const auto &user : users) {
|
|
|
|
usernames.emplace_back(user.username());
|
|
|
|
}
|
|
|
|
return usernames;
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<memgraph::query::TypedValue> GetRolenames() override {
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->ReadLock();
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<memgraph::query::TypedValue> rolenames;
|
2021-07-22 22:22:08 +08:00
|
|
|
const auto &roles = locked_auth->AllRoles();
|
2020-01-15 20:58:41 +08:00
|
|
|
rolenames.reserve(roles.size());
|
|
|
|
for (const auto &role : roles) {
|
|
|
|
rolenames.emplace_back(role.rolename());
|
|
|
|
}
|
|
|
|
return rolenames;
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
std::optional<std::string> GetRolenameForUser(const std::string &username) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(username, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->ReadLock();
|
|
|
|
auto user = locked_auth->GetUser(username);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!user) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
2021-07-22 22:22:08 +08:00
|
|
|
|
|
|
|
if (const auto *role = user->role(); role != nullptr) {
|
|
|
|
return role->rolename();
|
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
return std::nullopt;
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<memgraph::query::TypedValue> GetUsernamesForRole(const std::string &rolename) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(rolename, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid role name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->ReadLock();
|
|
|
|
auto role = locked_auth->GetRole(rolename);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!role) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Role '{}' doesn't exist.", rolename);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<memgraph::query::TypedValue> usernames;
|
2021-07-22 22:22:08 +08:00
|
|
|
const auto &users = locked_auth->AllUsersForRole(rolename);
|
2020-01-15 20:58:41 +08:00
|
|
|
usernames.reserve(users.size());
|
|
|
|
for (const auto &user : users) {
|
|
|
|
usernames.emplace_back(user.username());
|
|
|
|
}
|
|
|
|
return usernames;
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
void SetRole(const std::string &username, const std::string &rolename) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(username, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
|
|
|
if (!std::regex_match(rolename, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid role name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->Lock();
|
|
|
|
auto user = locked_auth->GetUser(username);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!user) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
2021-07-22 22:22:08 +08:00
|
|
|
auto role = locked_auth->GetRole(rolename);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!role) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Role '{}' doesn't exist .", rolename);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
2021-07-22 22:22:08 +08:00
|
|
|
if (const auto *current_role = user->role(); current_role != nullptr) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("User '{}' is already a member of role '{}'.", username,
|
|
|
|
current_role->rolename());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
user->SetRole(*role);
|
2021-07-22 22:22:08 +08:00
|
|
|
locked_auth->SaveUser(*user);
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClearRole(const std::string &username) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(username, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->Lock();
|
|
|
|
auto user = locked_auth->GetUser(username);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!user) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
user->ClearRole();
|
2021-07-22 22:22:08 +08:00
|
|
|
locked_auth->SaveUser(*user);
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<std::vector<memgraph::query::TypedValue>> GetPrivileges(const std::string &user_or_role) override {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(user_or_role, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid user or role name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2021-07-22 22:22:08 +08:00
|
|
|
auto locked_auth = auth_->ReadLock();
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<std::vector<memgraph::query::TypedValue>> grants;
|
2021-07-22 22:22:08 +08:00
|
|
|
auto user = locked_auth->GetUser(user_or_role);
|
|
|
|
auto role = locked_auth->GetRole(user_or_role);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!user && !role) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("User or role '{}' doesn't exist.", user_or_role);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
if (user) {
|
|
|
|
const auto &permissions = user->GetPermissions();
|
2022-02-22 20:33:45 +08:00
|
|
|
for (const auto &privilege : memgraph::query::kPrivilegesAll) {
|
|
|
|
auto permission = memgraph::glue::PrivilegeToPermission(privilege);
|
2020-01-15 20:58:41 +08:00
|
|
|
auto effective = permissions.Has(permission);
|
2022-02-22 20:33:45 +08:00
|
|
|
if (permissions.Has(permission) != memgraph::auth::PermissionLevel::NEUTRAL) {
|
2020-01-15 20:58:41 +08:00
|
|
|
std::vector<std::string> description;
|
|
|
|
auto user_level = user->permissions().Has(permission);
|
2022-02-22 20:33:45 +08:00
|
|
|
if (user_level == memgraph::auth::PermissionLevel::GRANT) {
|
2020-01-15 20:58:41 +08:00
|
|
|
description.emplace_back("GRANTED TO USER");
|
2022-02-22 20:33:45 +08:00
|
|
|
} else if (user_level == memgraph::auth::PermissionLevel::DENY) {
|
2020-01-15 20:58:41 +08:00
|
|
|
description.emplace_back("DENIED TO USER");
|
|
|
|
}
|
2021-07-22 22:22:08 +08:00
|
|
|
if (const auto *role = user->role(); role != nullptr) {
|
|
|
|
auto role_level = role->permissions().Has(permission);
|
2022-02-22 20:33:45 +08:00
|
|
|
if (role_level == memgraph::auth::PermissionLevel::GRANT) {
|
2020-01-15 20:58:41 +08:00
|
|
|
description.emplace_back("GRANTED TO ROLE");
|
2022-02-22 20:33:45 +08:00
|
|
|
} else if (role_level == memgraph::auth::PermissionLevel::DENY) {
|
2020-01-15 20:58:41 +08:00
|
|
|
description.emplace_back("DENIED TO ROLE");
|
|
|
|
}
|
|
|
|
}
|
2022-02-22 20:33:45 +08:00
|
|
|
grants.push_back({memgraph::query::TypedValue(memgraph::auth::PermissionToString(permission)),
|
|
|
|
memgraph::query::TypedValue(memgraph::auth::PermissionLevelToString(effective)),
|
|
|
|
memgraph::query::TypedValue(memgraph::utils::Join(description, ", "))});
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const auto &permissions = role->permissions();
|
2022-02-22 20:33:45 +08:00
|
|
|
for (const auto &privilege : memgraph::query::kPrivilegesAll) {
|
|
|
|
auto permission = memgraph::glue::PrivilegeToPermission(privilege);
|
2020-01-15 20:58:41 +08:00
|
|
|
auto effective = permissions.Has(permission);
|
2022-02-22 20:33:45 +08:00
|
|
|
if (effective != memgraph::auth::PermissionLevel::NEUTRAL) {
|
2020-01-15 20:58:41 +08:00
|
|
|
std::string description;
|
2022-02-22 20:33:45 +08:00
|
|
|
if (effective == memgraph::auth::PermissionLevel::GRANT) {
|
2020-01-15 20:58:41 +08:00
|
|
|
description = "GRANTED TO ROLE";
|
2022-02-22 20:33:45 +08:00
|
|
|
} else if (effective == memgraph::auth::PermissionLevel::DENY) {
|
2020-01-15 20:58:41 +08:00
|
|
|
description = "DENIED TO ROLE";
|
|
|
|
}
|
2022-02-22 20:33:45 +08:00
|
|
|
grants.push_back({memgraph::query::TypedValue(memgraph::auth::PermissionToString(permission)),
|
|
|
|
memgraph::query::TypedValue(memgraph::auth::PermissionLevelToString(effective)),
|
|
|
|
memgraph::query::TypedValue(description)});
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return grants;
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
void GrantPrivilege(const std::string &user_or_role,
|
2022-02-22 20:33:45 +08:00
|
|
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) override {
|
2021-02-18 22:32:43 +08:00
|
|
|
EditPermissions(user_or_role, privileges, [](auto *permissions, const auto &permission) {
|
|
|
|
// TODO (mferencevic): should we first check that the
|
|
|
|
// privilege is granted/denied/revoked before
|
|
|
|
// unconditionally granting/denying/revoking it?
|
|
|
|
permissions->Grant(permission);
|
|
|
|
});
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
void DenyPrivilege(const std::string &user_or_role,
|
2022-02-22 20:33:45 +08:00
|
|
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) override {
|
2021-02-18 22:32:43 +08:00
|
|
|
EditPermissions(user_or_role, privileges, [](auto *permissions, const auto &permission) {
|
|
|
|
// TODO (mferencevic): should we first check that the
|
|
|
|
// privilege is granted/denied/revoked before
|
|
|
|
// unconditionally granting/denying/revoking it?
|
|
|
|
permissions->Deny(permission);
|
|
|
|
});
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
void RevokePrivilege(const std::string &user_or_role,
|
2022-02-22 20:33:45 +08:00
|
|
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) override {
|
2021-02-18 22:32:43 +08:00
|
|
|
EditPermissions(user_or_role, privileges, [](auto *permissions, const auto &permission) {
|
|
|
|
// TODO (mferencevic): should we first check that the
|
|
|
|
// privilege is granted/denied/revoked before
|
|
|
|
// unconditionally granting/denying/revoking it?
|
|
|
|
permissions->Revoke(permission);
|
|
|
|
});
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <class TEditFun>
|
2022-02-22 20:33:45 +08:00
|
|
|
void EditPermissions(const std::string &user_or_role,
|
|
|
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges, const TEditFun &edit_fun) {
|
2020-02-17 21:41:46 +08:00
|
|
|
if (!std::regex_match(user_or_role, name_regex_)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("Invalid user or role name.");
|
2020-02-17 21:41:46 +08:00
|
|
|
}
|
2020-01-15 20:58:41 +08:00
|
|
|
try {
|
2022-02-22 20:33:45 +08:00
|
|
|
std::vector<memgraph::auth::Permission> permissions;
|
2020-01-15 20:58:41 +08:00
|
|
|
permissions.reserve(privileges.size());
|
|
|
|
for (const auto &privilege : privileges) {
|
2022-02-22 20:33:45 +08:00
|
|
|
permissions.push_back(memgraph::glue::PrivilegeToPermission(privilege));
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
2021-09-30 01:14:39 +08:00
|
|
|
auto locked_auth = auth_->Lock();
|
2021-07-22 22:22:08 +08:00
|
|
|
auto user = locked_auth->GetUser(user_or_role);
|
|
|
|
auto role = locked_auth->GetRole(user_or_role);
|
2020-01-15 20:58:41 +08:00
|
|
|
if (!user && !role) {
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::query::QueryRuntimeException("User or role '{}' doesn't exist.", user_or_role);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
if (user) {
|
|
|
|
for (const auto &permission : permissions) {
|
|
|
|
edit_fun(&user->permissions(), permission);
|
|
|
|
}
|
2021-07-22 22:22:08 +08:00
|
|
|
locked_auth->SaveUser(*user);
|
2020-01-15 20:58:41 +08:00
|
|
|
} else {
|
|
|
|
for (const auto &permission : permissions) {
|
|
|
|
edit_fun(&role->permissions(), permission);
|
|
|
|
}
|
2021-07-22 22:22:08 +08:00
|
|
|
locked_auth->SaveRole(*role);
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::auth::AuthException &e) {
|
|
|
|
throw memgraph::query::QueryRuntimeException(e.what());
|
2020-01-15 20:58:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2021-07-22 22:22:08 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
class AuthChecker final : public memgraph::query::AuthChecker {
|
2021-07-22 22:22:08 +08:00
|
|
|
public:
|
2022-02-22 20:33:45 +08:00
|
|
|
explicit AuthChecker(
|
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth)
|
|
|
|
: auth_{auth} {}
|
2021-07-22 22:22:08 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
static bool IsUserAuthorized(const memgraph::auth::User &user,
|
|
|
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) {
|
2021-07-22 22:22:08 +08:00
|
|
|
const auto user_permissions = user.GetPermissions();
|
|
|
|
return std::all_of(privileges.begin(), privileges.end(), [&user_permissions](const auto privilege) {
|
2022-02-22 20:33:45 +08:00
|
|
|
return user_permissions.Has(memgraph::glue::PrivilegeToPermission(privilege)) ==
|
|
|
|
memgraph::auth::PermissionLevel::GRANT;
|
2021-07-22 22:22:08 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsUserAuthorized(const std::optional<std::string> &username,
|
2022-02-22 20:33:45 +08:00
|
|
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) const final {
|
|
|
|
std::optional<memgraph::auth::User> maybe_user;
|
2021-07-22 22:22:08 +08:00
|
|
|
{
|
|
|
|
auto locked_auth = auth_->ReadLock();
|
|
|
|
if (!locked_auth->HasUsers()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (username.has_value()) {
|
|
|
|
maybe_user = locked_auth->GetUser(*username);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return maybe_user.has_value() && IsUserAuthorized(*maybe_user, privileges);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
|
2021-07-22 22:22:08 +08:00
|
|
|
};
|
|
|
|
|
2022-04-27 16:13:16 +08:00
|
|
|
class BoltSession final : public memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
|
|
|
|
memgraph::communication::v2::OutputStream> {
|
2021-07-22 22:22:08 +08:00
|
|
|
public:
|
2022-04-27 16:13:16 +08:00
|
|
|
BoltSession(SessionData *data, const memgraph::communication::v2::ServerEndpoint &endpoint,
|
|
|
|
memgraph::communication::v2::InputStream *input_stream,
|
|
|
|
memgraph::communication::v2::OutputStream *output_stream)
|
|
|
|
: memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
|
|
|
|
memgraph::communication::v2::OutputStream>(input_stream, output_stream),
|
2021-07-22 22:22:08 +08:00
|
|
|
db_(data->db),
|
|
|
|
interpreter_(data->interpreter_context),
|
|
|
|
auth_(data->auth),
|
2021-09-30 01:14:39 +08:00
|
|
|
#if MG_ENTERPRISE
|
2021-07-22 22:22:08 +08:00
|
|
|
audit_log_(data->audit_log),
|
|
|
|
#endif
|
|
|
|
endpoint_(endpoint) {
|
|
|
|
}
|
|
|
|
|
2022-04-27 16:13:16 +08:00
|
|
|
using memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
|
|
|
|
memgraph::communication::v2::OutputStream>::TEncoder;
|
2021-07-22 22:22:08 +08:00
|
|
|
|
|
|
|
void BeginTransaction() override { interpreter_.BeginTransaction(); }
|
|
|
|
|
|
|
|
void CommitTransaction() override { interpreter_.CommitTransaction(); }
|
|
|
|
|
|
|
|
void RollbackTransaction() override { interpreter_.RollbackTransaction(); }
|
|
|
|
|
|
|
|
std::pair<std::vector<std::string>, std::optional<int>> Interpret(
|
2022-02-22 20:33:45 +08:00
|
|
|
const std::string &query, const std::map<std::string, memgraph::communication::bolt::Value> ¶ms) override {
|
|
|
|
std::map<std::string, memgraph::storage::PropertyValue> params_pv;
|
|
|
|
for (const auto &kv : params) params_pv.emplace(kv.first, memgraph::glue::ToPropertyValue(kv.second));
|
2021-07-22 22:22:08 +08:00
|
|
|
const std::string *username{nullptr};
|
|
|
|
if (user_) {
|
|
|
|
username = &user_->username();
|
|
|
|
}
|
2021-09-30 01:14:39 +08:00
|
|
|
#ifdef MG_ENTERPRISE
|
2022-02-22 20:33:45 +08:00
|
|
|
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
2022-04-27 16:13:16 +08:00
|
|
|
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query,
|
|
|
|
memgraph::storage::PropertyValue(params_pv));
|
2021-09-30 01:14:39 +08:00
|
|
|
}
|
2020-02-05 22:05:18 +08:00
|
|
|
#endif
|
2021-07-22 22:22:08 +08:00
|
|
|
try {
|
|
|
|
auto result = interpreter_.Prepare(query, params_pv, username);
|
|
|
|
if (user_ && !AuthChecker::IsUserAuthorized(*user_, result.privileges)) {
|
|
|
|
interpreter_.Abort();
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::communication::bolt::ClientError(
|
2021-07-22 22:22:08 +08:00
|
|
|
"You are not authorized to execute this query! Please contact "
|
|
|
|
"your database administrator.");
|
|
|
|
}
|
|
|
|
return {result.headers, result.qid};
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::query::QueryException &e) {
|
2021-07-22 22:22:08 +08:00
|
|
|
// Wrap QueryException into ClientError, because we want to allow the
|
|
|
|
// client to fix their query.
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::communication::bolt::ClientError(e.what());
|
2021-07-22 22:22:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
std::map<std::string, memgraph::communication::bolt::Value> Pull(TEncoder *encoder, std::optional<int> n,
|
|
|
|
std::optional<int> qid) override {
|
2021-07-22 22:22:08 +08:00
|
|
|
TypedValueResultStream stream(encoder, db_);
|
|
|
|
return PullResults(stream, n, qid);
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
std::map<std::string, memgraph::communication::bolt::Value> Discard(std::optional<int> n,
|
|
|
|
std::optional<int> qid) override {
|
|
|
|
memgraph::query::DiscardValueResultStream stream;
|
2021-07-22 22:22:08 +08:00
|
|
|
return PullResults(stream, n, qid);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Abort() override { interpreter_.Abort(); }
|
|
|
|
|
|
|
|
bool Authenticate(const std::string &username, const std::string &password) override {
|
|
|
|
auto locked_auth = auth_->Lock();
|
|
|
|
if (!locked_auth->HasUsers()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
user_ = locked_auth->Authenticate(username, password);
|
|
|
|
return user_.has_value();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::string> GetServerNameForInit() override {
|
|
|
|
if (FLAGS_bolt_server_name_for_init.empty()) return std::nullopt;
|
|
|
|
return FLAGS_bolt_server_name_for_init;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename TStream>
|
2022-02-22 20:33:45 +08:00
|
|
|
std::map<std::string, memgraph::communication::bolt::Value> PullResults(TStream &stream, std::optional<int> n,
|
|
|
|
std::optional<int> qid) {
|
2021-07-22 22:22:08 +08:00
|
|
|
try {
|
|
|
|
const auto &summary = interpreter_.Pull(&stream, n, qid);
|
2022-02-22 20:33:45 +08:00
|
|
|
std::map<std::string, memgraph::communication::bolt::Value> decoded_summary;
|
2021-07-22 22:22:08 +08:00
|
|
|
for (const auto &kv : summary) {
|
2022-02-22 20:33:45 +08:00
|
|
|
auto maybe_value = memgraph::glue::ToBoltValue(kv.second, *db_, memgraph::storage::View::NEW);
|
2021-07-22 22:22:08 +08:00
|
|
|
if (maybe_value.HasError()) {
|
|
|
|
switch (maybe_value.GetError()) {
|
2022-02-22 20:33:45 +08:00
|
|
|
case memgraph::storage::Error::DELETED_OBJECT:
|
|
|
|
case memgraph::storage::Error::SERIALIZATION_ERROR:
|
|
|
|
case memgraph::storage::Error::VERTEX_HAS_EDGES:
|
|
|
|
case memgraph::storage::Error::PROPERTIES_DISABLED:
|
|
|
|
case memgraph::storage::Error::NONEXISTENT_OBJECT:
|
|
|
|
throw memgraph::communication::bolt::ClientError("Unexpected storage error when streaming summary.");
|
2021-07-22 22:22:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
decoded_summary.emplace(kv.first, std::move(*maybe_value));
|
|
|
|
}
|
|
|
|
return decoded_summary;
|
2022-02-22 20:33:45 +08:00
|
|
|
} catch (const memgraph::query::QueryException &e) {
|
2021-07-22 22:22:08 +08:00
|
|
|
// Wrap QueryException into ClientError, because we want to allow the
|
|
|
|
// client to fix their query.
|
2022-02-22 20:33:45 +08:00
|
|
|
throw memgraph::communication::bolt::ClientError(e.what());
|
2021-07-22 22:22:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Wrapper around TEncoder which converts TypedValue to Value
|
|
|
|
/// before forwarding the calls to original TEncoder.
|
|
|
|
class TypedValueResultStream {
|
|
|
|
public:
|
2022-02-22 20:33:45 +08:00
|
|
|
TypedValueResultStream(TEncoder *encoder, const memgraph::storage::Storage *db) : encoder_(encoder), db_(db) {}
|
2021-07-22 22:22:08 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
void Result(const std::vector<memgraph::query::TypedValue> &values) {
|
|
|
|
std::vector<memgraph::communication::bolt::Value> decoded_values;
|
2021-07-22 22:22:08 +08:00
|
|
|
decoded_values.reserve(values.size());
|
|
|
|
for (const auto &v : values) {
|
2022-02-22 20:33:45 +08:00
|
|
|
auto maybe_value = memgraph::glue::ToBoltValue(v, *db_, memgraph::storage::View::NEW);
|
2021-07-22 22:22:08 +08:00
|
|
|
if (maybe_value.HasError()) {
|
|
|
|
switch (maybe_value.GetError()) {
|
2022-02-22 20:33:45 +08:00
|
|
|
case memgraph::storage::Error::DELETED_OBJECT:
|
|
|
|
throw memgraph::communication::bolt::ClientError("Returning a deleted object as a result.");
|
|
|
|
case memgraph::storage::Error::NONEXISTENT_OBJECT:
|
|
|
|
throw memgraph::communication::bolt::ClientError("Returning a nonexistent object as a result.");
|
|
|
|
case memgraph::storage::Error::VERTEX_HAS_EDGES:
|
|
|
|
case memgraph::storage::Error::SERIALIZATION_ERROR:
|
|
|
|
case memgraph::storage::Error::PROPERTIES_DISABLED:
|
|
|
|
throw memgraph::communication::bolt::ClientError("Unexpected storage error when streaming results.");
|
2021-07-22 22:22:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
decoded_values.emplace_back(std::move(*maybe_value));
|
|
|
|
}
|
|
|
|
encoder_->MessageRecord(decoded_values);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
TEncoder *encoder_;
|
|
|
|
// NOTE: Needed only for ToBoltValue conversions
|
2022-02-22 20:33:45 +08:00
|
|
|
const memgraph::storage::Storage *db_;
|
2021-07-22 22:22:08 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// NOTE: Needed only for ToBoltValue conversions
|
2022-02-22 20:33:45 +08:00
|
|
|
const memgraph::storage::Storage *db_;
|
|
|
|
memgraph::query::Interpreter interpreter_;
|
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
|
|
|
|
std::optional<memgraph::auth::User> user_;
|
2021-09-30 01:14:39 +08:00
|
|
|
#ifdef MG_ENTERPRISE
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::audit::Log *audit_log_;
|
2021-07-22 22:22:08 +08:00
|
|
|
#endif
|
2022-04-27 16:13:16 +08:00
|
|
|
memgraph::communication::v2::ServerEndpoint endpoint_;
|
2021-07-22 22:22:08 +08:00
|
|
|
};
|
|
|
|
|
2022-04-27 16:13:16 +08:00
|
|
|
using ServerT = memgraph::communication::v2::Server<BoltSession, SessionData>;
|
2022-02-22 20:33:45 +08:00
|
|
|
using memgraph::communication::ServerContext;
|
2020-01-15 20:58:41 +08:00
|
|
|
|
2020-02-24 21:06:21 +08:00
|
|
|
// Needed to correctly handle memgraph destruction from a signal handler.
|
|
|
|
// Without having some sort of a flag, it is possible that a signal is handled
|
|
|
|
// when we are exiting main, inside destructors of database::GraphDb and
|
|
|
|
// similar. The signal handler may then initiate another shutdown on memgraph
|
|
|
|
// which is in half destructed state, causing invalid memory access and crash.
|
|
|
|
volatile sig_atomic_t is_shutting_down = 0;
|
|
|
|
|
|
|
|
void InitSignalHandlers(const std::function<void()> &shutdown_fun) {
|
|
|
|
// Prevent handling shutdown inside a shutdown. For example, SIGINT handler
|
|
|
|
// being interrupted by SIGTERM before is_shutting_down is set, thus causing
|
|
|
|
// double shutdown.
|
|
|
|
sigset_t block_shutdown_signals;
|
|
|
|
sigemptyset(&block_shutdown_signals);
|
|
|
|
sigaddset(&block_shutdown_signals, SIGTERM);
|
|
|
|
sigaddset(&block_shutdown_signals, SIGINT);
|
|
|
|
|
|
|
|
// Wrap the shutdown function in a safe way to prevent recursive shutdown.
|
|
|
|
auto shutdown = [shutdown_fun]() {
|
|
|
|
if (is_shutting_down) return;
|
|
|
|
is_shutting_down = 1;
|
|
|
|
shutdown_fun();
|
|
|
|
};
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
MG_ASSERT(memgraph::utils::SignalHandler::RegisterHandler(memgraph::utils::Signal::Terminate, shutdown,
|
|
|
|
block_shutdown_signals),
|
2021-01-21 22:47:56 +08:00
|
|
|
"Unable to register SIGTERM handler!");
|
2022-02-22 20:33:45 +08:00
|
|
|
MG_ASSERT(memgraph::utils::SignalHandler::RegisterHandler(memgraph::utils::Signal::Interupt, shutdown,
|
|
|
|
block_shutdown_signals),
|
2021-01-21 22:47:56 +08:00
|
|
|
"Unable to register SIGINT handler!");
|
2020-02-24 21:06:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
google::SetUsageMessage("Memgraph database server");
|
|
|
|
gflags::SetVersionString(version_string);
|
|
|
|
|
|
|
|
// Load config before parsing arguments, so that flags from the command line
|
|
|
|
// overwrite the config.
|
2020-03-03 17:38:50 +08:00
|
|
|
LoadConfig("memgraph");
|
2020-02-24 21:06:21 +08:00
|
|
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
|
|
|
|
2022-01-27 16:51:00 +08:00
|
|
|
InitializeLogger();
|
2020-02-24 21:06:21 +08:00
|
|
|
|
|
|
|
// Unhandled exception handler init.
|
2022-02-22 20:33:45 +08:00
|
|
|
std::set_terminate(&memgraph::utils::TerminateHandler);
|
2020-02-24 21:06:21 +08:00
|
|
|
|
|
|
|
// Initialize Python
|
|
|
|
auto *program_name = Py_DecodeLocale(argv[0], nullptr);
|
2021-01-21 22:47:56 +08:00
|
|
|
MG_ASSERT(program_name);
|
2020-02-24 21:06:21 +08:00
|
|
|
// Set program name, so Python can find its way to runtime libraries relative
|
|
|
|
// to executable.
|
|
|
|
Py_SetProgramName(program_name);
|
2022-02-22 20:33:45 +08:00
|
|
|
PyImport_AppendInittab("_mgp", &memgraph::query::procedure::PyInitMgpModule);
|
2020-02-24 21:06:21 +08:00
|
|
|
Py_InitializeEx(0 /* = initsigs */);
|
|
|
|
PyEval_InitThreads();
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
|
|
|
2020-03-09 18:06:01 +08:00
|
|
|
// Add our Python modules to sys.path
|
|
|
|
try {
|
2022-02-22 20:33:45 +08:00
|
|
|
auto exe_path = memgraph::utils::GetExecutablePath();
|
2020-03-09 18:06:01 +08:00
|
|
|
auto py_support_dir = exe_path.parent_path() / "python_support";
|
|
|
|
if (std::filesystem::is_directory(py_support_dir)) {
|
2022-02-22 20:33:45 +08:00
|
|
|
auto gil = memgraph::py::EnsureGIL();
|
|
|
|
auto maybe_exc = memgraph::py::AppendToSysPath(py_support_dir.c_str());
|
2020-03-09 18:06:01 +08:00
|
|
|
if (maybe_exc) {
|
2022-02-22 20:33:45 +08:00
|
|
|
spdlog::error(memgraph::utils::MessageWithLink("Unable to load support for embedded Python: {}.", *maybe_exc,
|
|
|
|
"https://memgr.ph/python"));
|
2022-05-13 17:43:10 +08:00
|
|
|
} else {
|
|
|
|
// Change how we load dynamic libraries on Python by using RTLD_NOW and
|
|
|
|
// RTLD_DEEPBIND flags. This solves an issue with using the wrong version of
|
|
|
|
// libstd.
|
|
|
|
auto gil = memgraph::py::EnsureGIL();
|
|
|
|
// NOLINTNEXTLINE(hicpp-signed-bitwise)
|
|
|
|
auto *flag = PyLong_FromLong(RTLD_NOW | RTLD_DEEPBIND);
|
|
|
|
auto *setdl = PySys_GetObject("setdlopenflags");
|
|
|
|
MG_ASSERT(setdl);
|
|
|
|
auto *arg = PyTuple_New(1);
|
|
|
|
MG_ASSERT(arg);
|
|
|
|
MG_ASSERT(PyTuple_SetItem(arg, 0, flag) == 0);
|
|
|
|
PyObject_CallObject(setdl, arg);
|
|
|
|
Py_DECREF(flag);
|
|
|
|
Py_DECREF(setdl);
|
|
|
|
Py_DECREF(arg);
|
2020-03-09 18:06:01 +08:00
|
|
|
}
|
|
|
|
} else {
|
2022-02-22 20:33:45 +08:00
|
|
|
spdlog::error(
|
|
|
|
memgraph::utils::MessageWithLink("Unable to load support for embedded Python: missing directory {}.",
|
2021-10-07 20:51:30 +08:00
|
|
|
py_support_dir, "https://memgr.ph/python"));
|
2020-03-09 18:06:01 +08:00
|
|
|
}
|
|
|
|
} catch (const std::filesystem::filesystem_error &e) {
|
2022-02-22 20:33:45 +08:00
|
|
|
spdlog::error(memgraph::utils::MessageWithLink("Unable to load support for embedded Python: {}.", e.what(),
|
|
|
|
"https://memgr.ph/python"));
|
2020-03-09 18:06:01 +08:00
|
|
|
}
|
|
|
|
|
2020-02-24 21:06:21 +08:00
|
|
|
// Initialize the communication library.
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::communication::SSLInit sslInit;
|
2020-02-24 21:06:21 +08:00
|
|
|
|
|
|
|
// Initialize the requests library.
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::requests::Init();
|
2020-02-24 21:06:21 +08:00
|
|
|
|
|
|
|
// Start memory warning logger.
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::Scheduler mem_log_scheduler;
|
2020-02-24 21:06:21 +08:00
|
|
|
if (FLAGS_memory_warning_threshold > 0) {
|
2022-02-22 20:33:45 +08:00
|
|
|
auto free_ram = memgraph::utils::sysinfo::AvailableMemory();
|
2020-02-24 21:06:21 +08:00
|
|
|
if (free_ram) {
|
|
|
|
mem_log_scheduler.Run("Memory warning", std::chrono::seconds(3), [] {
|
2022-02-22 20:33:45 +08:00
|
|
|
auto free_ram = memgraph::utils::sysinfo::AvailableMemory();
|
2020-02-24 21:06:21 +08:00
|
|
|
if (free_ram && *free_ram / 1024 < FLAGS_memory_warning_threshold)
|
2022-02-22 20:33:45 +08:00
|
|
|
spdlog::warn(memgraph::utils::MessageWithLink("Running out of available RAM, only {} MB left.",
|
|
|
|
*free_ram / 1024, "https://memgr.ph/ram"));
|
2020-02-24 21:06:21 +08:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Kernel version for the `MemAvailable` value is from: man procfs
|
2021-01-21 22:47:56 +08:00
|
|
|
spdlog::warn(
|
|
|
|
"You have an older kernel version (<3.14) or the /proc "
|
|
|
|
"filesystem isn't available so remaining memory warnings "
|
|
|
|
"won't be available.");
|
2020-02-24 21:06:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
std::cout << "You are running Memgraph v" << gflags::VersionString() << std::endl;
|
2021-10-07 21:56:06 +08:00
|
|
|
std::cout << "To get started with Memgraph, visit https://memgr.ph/start" << std::endl;
|
2020-01-17 20:02:50 +08:00
|
|
|
|
2020-02-05 22:05:18 +08:00
|
|
|
auto data_directory = std::filesystem::path(FLAGS_data_directory);
|
|
|
|
|
2021-09-30 01:14:39 +08:00
|
|
|
const auto memory_limit = GetMemoryLimit();
|
|
|
|
// NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
|
2022-02-22 20:33:45 +08:00
|
|
|
spdlog::info("Memory limit in config is set to {}", memgraph::utils::GetReadableSize(memory_limit));
|
|
|
|
memgraph::utils::total_memory_tracker.SetMaximumHardLimit(memory_limit);
|
|
|
|
memgraph::utils::total_memory_tracker.SetHardLimit(memory_limit);
|
2021-09-30 01:14:39 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::global_settings.Initialize(data_directory / "settings");
|
|
|
|
memgraph::utils::OnScopeExit settings_finalizer([&] { memgraph::utils::global_settings.Finalize(); });
|
2021-09-30 01:14:39 +08:00
|
|
|
|
|
|
|
// register all runtime settings
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::license::RegisterLicenseSettings(memgraph::utils::license::global_license_checker,
|
|
|
|
memgraph::utils::global_settings);
|
2021-09-30 01:14:39 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::license::global_license_checker.CheckEnvLicense();
|
2021-09-30 01:14:39 +08:00
|
|
|
if (!FLAGS_organization_name.empty() && !FLAGS_license_key.empty()) {
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::license::global_license_checker.SetLicenseInfoOverride(FLAGS_license_key, FLAGS_organization_name);
|
2021-09-30 01:14:39 +08:00
|
|
|
}
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::license::global_license_checker.StartBackgroundLicenseChecker(memgraph::utils::global_settings);
|
2021-09-30 01:14:39 +08:00
|
|
|
|
2019-02-19 20:50:46 +08:00
|
|
|
// All enterprise features should be constructed before the main database
|
|
|
|
// storage. This will cause them to be destructed *after* the main database
|
|
|
|
// storage. That way any errors that happen during enterprise features
|
|
|
|
// destruction won't have an impact on the storage engine.
|
|
|
|
// Example: When the main storage is destructed it makes a snapshot. When
|
|
|
|
// audit logging is destructed it syncs all pending data to disk and that can
|
|
|
|
// fail. That is why it must be destructed *after* the main database storage
|
|
|
|
// to minimise the impact of their failure on the main storage.
|
|
|
|
|
|
|
|
// Begin enterprise features initialization
|
|
|
|
|
|
|
|
// Auth
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> auth{data_directory /
|
|
|
|
"auth"};
|
2019-02-19 20:50:46 +08:00
|
|
|
|
2021-09-30 01:14:39 +08:00
|
|
|
#ifdef MG_ENTERPRISE
|
2019-02-19 20:50:46 +08:00
|
|
|
// Audit log
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::audit::Log audit_log{data_directory / "audit", FLAGS_audit_buffer_size,
|
|
|
|
FLAGS_audit_buffer_flush_interval_ms};
|
2019-02-19 20:50:46 +08:00
|
|
|
// Start the log if enabled.
|
|
|
|
if (FLAGS_audit_enabled) {
|
|
|
|
audit_log.Start();
|
|
|
|
}
|
|
|
|
// Setup SIGUSR2 to be used for reopening audit log files, when e.g. logrotate
|
|
|
|
// rotates our audit logs.
|
2022-02-22 20:33:45 +08:00
|
|
|
MG_ASSERT(memgraph::utils::SignalHandler::RegisterHandler(memgraph::utils::Signal::User2,
|
|
|
|
[&audit_log]() { audit_log.ReopenLog(); }),
|
2021-02-18 22:32:43 +08:00
|
|
|
"Unable to register SIGUSR2 handler!");
|
2019-02-19 20:50:46 +08:00
|
|
|
|
|
|
|
// End enterprise features initialization
|
2020-02-05 22:05:18 +08:00
|
|
|
#endif
|
2019-02-19 20:50:46 +08:00
|
|
|
|
2021-03-04 19:20:11 +08:00
|
|
|
// Main storage and execution engines initialization
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::storage::Config db_config{
|
|
|
|
.gc = {.type = memgraph::storage::Config::Gc::Type::PERIODIC,
|
|
|
|
.interval = std::chrono::seconds(FLAGS_storage_gc_cycle_sec)},
|
2019-10-09 22:00:02 +08:00
|
|
|
.items = {.properties_on_edges = FLAGS_storage_properties_on_edges},
|
2021-02-18 22:32:43 +08:00
|
|
|
.durability = {.storage_directory = FLAGS_data_directory,
|
|
|
|
.recover_on_startup = FLAGS_storage_recover_on_startup,
|
|
|
|
.snapshot_retention_count = FLAGS_storage_snapshot_retention_count,
|
|
|
|
.wal_file_size_kibibytes = FLAGS_storage_wal_file_size_kib,
|
|
|
|
.wal_file_flush_every_n_tx = FLAGS_storage_wal_file_flush_every_n_tx,
|
2022-07-07 19:30:28 +08:00
|
|
|
.snapshot_on_exit = FLAGS_storage_snapshot_on_exit,
|
|
|
|
.restore_replicas_on_startup = FLAGS_storage_restore_replicas_on_startup},
|
2021-06-14 21:47:57 +08:00
|
|
|
.transaction = {.isolation_level = ParseIsolationLevel()}};
|
2019-10-09 22:00:02 +08:00
|
|
|
if (FLAGS_storage_snapshot_interval_sec == 0) {
|
2021-01-21 22:47:56 +08:00
|
|
|
if (FLAGS_storage_wal_enabled) {
|
|
|
|
LOG_FATAL(
|
|
|
|
"In order to use write-ahead-logging you must enable "
|
|
|
|
"periodic snapshots by setting the snapshot interval to a "
|
|
|
|
"value larger than 0!");
|
2022-02-22 20:33:45 +08:00
|
|
|
db_config.durability.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::DISABLED;
|
2021-01-21 22:47:56 +08:00
|
|
|
}
|
2019-10-09 22:00:02 +08:00
|
|
|
} else {
|
|
|
|
if (FLAGS_storage_wal_enabled) {
|
2022-02-22 20:33:45 +08:00
|
|
|
db_config.durability.snapshot_wal_mode =
|
|
|
|
memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL;
|
2019-10-09 22:00:02 +08:00
|
|
|
} else {
|
2022-02-22 20:33:45 +08:00
|
|
|
db_config.durability.snapshot_wal_mode =
|
|
|
|
memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT;
|
2019-10-09 22:00:02 +08:00
|
|
|
}
|
2021-02-18 22:32:43 +08:00
|
|
|
db_config.durability.snapshot_interval = std::chrono::seconds(FLAGS_storage_snapshot_interval_sec);
|
2019-10-09 22:00:02 +08:00
|
|
|
}
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::storage::Storage db(db_config);
|
2021-09-30 01:14:39 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::query::InterpreterContext interpreter_context{
|
2021-11-16 21:29:45 +08:00
|
|
|
&db,
|
|
|
|
{.query = {.allow_load_csv = FLAGS_allow_load_csv},
|
|
|
|
.execution_timeout_sec = FLAGS_query_execution_timeout_sec,
|
2022-05-21 10:29:17 +08:00
|
|
|
.replication_replica_check_frequency = std::chrono::seconds(FLAGS_replication_replica_check_frequency_sec),
|
2021-11-16 21:29:45 +08:00
|
|
|
.default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers,
|
|
|
|
.default_pulsar_service_url = FLAGS_pulsar_service_url,
|
|
|
|
.stream_transaction_conflict_retries = FLAGS_stream_transaction_conflict_retries,
|
|
|
|
.stream_transaction_retry_interval = std::chrono::milliseconds(FLAGS_stream_transaction_retry_interval)},
|
|
|
|
FLAGS_data_directory};
|
2020-02-05 22:05:18 +08:00
|
|
|
#ifdef MG_ENTERPRISE
|
2019-10-07 23:31:25 +08:00
|
|
|
SessionData session_data{&db, &interpreter_context, &auth, &audit_log};
|
2020-02-05 22:05:18 +08:00
|
|
|
#else
|
2021-09-30 01:14:39 +08:00
|
|
|
SessionData session_data{&db, &interpreter_context, &auth};
|
2020-02-05 22:05:18 +08:00
|
|
|
#endif
|
2018-06-20 23:44:47 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::query::procedure::gModuleRegistry.SetModulesDirectory(query_modules_directories, FLAGS_data_directory);
|
|
|
|
memgraph::query::procedure::gModuleRegistry.UnloadAndLoadModulesFromDirectories();
|
2020-02-24 21:06:21 +08:00
|
|
|
|
2021-09-30 01:14:39 +08:00
|
|
|
AuthQueryHandler auth_handler(&auth, FLAGS_auth_user_or_role_name_regex);
|
2021-07-22 22:22:08 +08:00
|
|
|
AuthChecker auth_checker{&auth};
|
2020-01-15 20:58:41 +08:00
|
|
|
interpreter_context.auth = &auth_handler;
|
2021-07-22 22:22:08 +08:00
|
|
|
interpreter_context.auth_checker = &auth_checker;
|
|
|
|
|
|
|
|
{
|
|
|
|
// Triggers can execute query procedures, so we need to reload the modules first and then
|
|
|
|
// the triggers
|
|
|
|
auto storage_accessor = interpreter_context.db->Access();
|
2022-02-22 20:33:45 +08:00
|
|
|
auto dba = memgraph::query::DbAccessor{&storage_accessor};
|
2021-07-22 22:22:08 +08:00
|
|
|
interpreter_context.trigger_store.RestoreTriggers(&interpreter_context.ast_cache, &dba,
|
|
|
|
&interpreter_context.antlr_lock, interpreter_context.config.query,
|
|
|
|
interpreter_context.auth_checker);
|
|
|
|
}
|
2018-07-06 15:28:05 +08:00
|
|
|
|
2021-11-11 19:17:05 +08:00
|
|
|
// As the Stream transformations are using modules, they have to be restored after the query modules are loaded.
|
|
|
|
interpreter_context.streams.RestoreStreams();
|
|
|
|
|
2018-06-20 23:44:47 +08:00
|
|
|
ServerContext context;
|
|
|
|
std::string service_name = "Bolt";
|
2019-12-08 18:42:59 +08:00
|
|
|
if (!FLAGS_bolt_key_file.empty() && !FLAGS_bolt_cert_file.empty()) {
|
|
|
|
context = ServerContext(FLAGS_bolt_key_file, FLAGS_bolt_cert_file);
|
2018-06-20 23:44:47 +08:00
|
|
|
service_name = "BoltS";
|
2021-01-21 22:47:56 +08:00
|
|
|
spdlog::info("Using secure Bolt connection (with SSL)");
|
2020-10-20 21:49:10 +08:00
|
|
|
} else {
|
2022-02-22 20:33:45 +08:00
|
|
|
spdlog::warn(
|
|
|
|
memgraph::utils::MessageWithLink("Using non-secure Bolt connection (without SSL).", "https://memgr.ph/ssl"));
|
2018-06-20 23:44:47 +08:00
|
|
|
}
|
|
|
|
|
2022-04-27 16:13:16 +08:00
|
|
|
auto server_endpoint = memgraph::communication::v2::ServerEndpoint{
|
|
|
|
boost::asio::ip::address::from_string(FLAGS_bolt_address), static_cast<uint16_t>(FLAGS_bolt_port)};
|
|
|
|
ServerT server(server_endpoint, &session_data, &context, FLAGS_bolt_session_inactivity_timeout, service_name,
|
|
|
|
FLAGS_bolt_num_workers);
|
2017-12-19 19:40:30 +08:00
|
|
|
|
2018-06-20 19:46:54 +08:00
|
|
|
// Setup telemetry
|
2022-02-22 20:33:45 +08:00
|
|
|
std::optional<memgraph::telemetry::Telemetry> telemetry;
|
2018-06-20 19:46:54 +08:00
|
|
|
if (FLAGS_telemetry_enabled) {
|
2021-02-18 22:32:43 +08:00
|
|
|
telemetry.emplace("https://telemetry.memgraph.com/88b5e7e8-746a-11e8-9f85-538a9e9690cc/",
|
|
|
|
data_directory / "telemetry", std::chrono::minutes(10));
|
2021-03-10 02:55:58 +08:00
|
|
|
telemetry->AddCollector("storage", [&db]() -> nlohmann::json {
|
2019-12-11 22:45:34 +08:00
|
|
|
auto info = db.GetInfo();
|
|
|
|
return {{"vertices", info.vertex_count}, {"edges", info.edge_count}};
|
|
|
|
});
|
2021-03-10 02:55:58 +08:00
|
|
|
telemetry->AddCollector("event_counters", []() -> nlohmann::json {
|
|
|
|
nlohmann::json ret;
|
|
|
|
for (size_t i = 0; i < EventCounter::End(); ++i) {
|
|
|
|
ret[EventCounter::GetName(i)] = EventCounter::global_counters[i].load(std::memory_order_relaxed);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
});
|
2022-02-22 20:33:45 +08:00
|
|
|
telemetry->AddCollector("query_module_counters", []() -> nlohmann::json {
|
|
|
|
return memgraph::query::plan::CallProcedure::GetAndResetCounters();
|
|
|
|
});
|
2019-12-11 22:45:34 +08:00
|
|
|
}
|
2018-06-20 19:46:54 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::communication::websocket::SafeAuth websocket_auth{&auth};
|
|
|
|
memgraph::communication::websocket::Server websocket_server{
|
2022-02-17 17:27:24 +08:00
|
|
|
{FLAGS_monitoring_address, static_cast<uint16_t>(FLAGS_monitoring_port)}, &context, websocket_auth};
|
2022-01-27 16:51:00 +08:00
|
|
|
AddLoggerSink(websocket_server.GetLoggingSink());
|
2022-01-04 19:13:48 +08:00
|
|
|
|
2017-12-19 19:40:30 +08:00
|
|
|
// Handler for regular termination signals
|
2022-01-04 19:13:48 +08:00
|
|
|
auto shutdown = [&websocket_server, &server, &interpreter_context] {
|
2021-01-21 22:47:56 +08:00
|
|
|
// Server needs to be shutdown first and then the database. This prevents
|
|
|
|
// a race condition when a transaction is accepted during server shutdown.
|
2017-12-19 19:40:30 +08:00
|
|
|
server.Shutdown();
|
2021-01-21 22:47:56 +08:00
|
|
|
// After the server is notified to stop accepting and processing
|
|
|
|
// connections we tell the execution engine to stop processing all pending
|
|
|
|
// queries.
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::query::Shutdown(&interpreter_context);
|
2022-01-04 19:13:48 +08:00
|
|
|
websocket_server.Shutdown();
|
2017-12-19 19:40:30 +08:00
|
|
|
};
|
2022-01-04 19:13:48 +08:00
|
|
|
|
2017-12-19 19:40:30 +08:00
|
|
|
InitSignalHandlers(shutdown);
|
2018-04-20 20:58:49 +08:00
|
|
|
|
2021-01-21 22:47:56 +08:00
|
|
|
MG_ASSERT(server.Start(), "Couldn't start the Bolt server!");
|
2022-01-04 19:13:48 +08:00
|
|
|
websocket_server.Start();
|
|
|
|
|
2018-01-10 20:56:12 +08:00
|
|
|
server.AwaitShutdown();
|
2022-01-04 19:13:48 +08:00
|
|
|
websocket_server.AwaitShutdown();
|
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::query::procedure::gModuleRegistry.UnloadAllModules();
|
2017-12-19 19:40:30 +08:00
|
|
|
|
2020-02-24 21:06:21 +08:00
|
|
|
Py_END_ALLOW_THREADS;
|
|
|
|
// Shutdown Python
|
|
|
|
Py_Finalize();
|
|
|
|
PyMem_RawFree(program_name);
|
2021-02-23 03:51:46 +08:00
|
|
|
|
2022-02-22 20:33:45 +08:00
|
|
|
memgraph::utils::total_memory_tracker.LogPeakMemoryUsage();
|
2020-02-24 21:06:21 +08:00
|
|
|
return 0;
|
2019-12-09 18:31:27 +08:00
|
|
|
}
|