Add privilege check in triggers and streams (#200)
This commit is contained in:
committed by
GitHub
parent
09c58501f1
commit
09cfca35f8
@@ -3,6 +3,7 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
#include "glue/communication.hpp"
|
||||
#include "query/constants.hpp"
|
||||
@@ -463,8 +464,13 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> StringPointerToOptional(const std::string *str) {
|
||||
return str == nullptr ? std::nullopt : std::make_optional(*str);
|
||||
}
|
||||
|
||||
Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶meters,
|
||||
InterpreterContext *interpreter_context, DbAccessor *db_accessor) {
|
||||
InterpreterContext *interpreter_context, DbAccessor *db_accessor,
|
||||
const std::string *username) {
|
||||
Frame frame(0);
|
||||
SymbolTable symbol_table;
|
||||
EvaluationContext evaluation_context;
|
||||
@@ -484,19 +490,21 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
std::string consumer_group{stream_query->consumer_group_.empty() ? kDefaultConsumerGroup
|
||||
: stream_query->consumer_group_};
|
||||
|
||||
callback.fn = [interpreter_context, stream_name = stream_query->stream_name_,
|
||||
topic_names = stream_query->topic_names_, consumer_group = std::move(consumer_group),
|
||||
batch_interval =
|
||||
GetOptionalValue<std::chrono::milliseconds>(stream_query->batch_interval_, evaluator),
|
||||
batch_size = GetOptionalValue<int64_t>(stream_query->batch_size_, evaluator),
|
||||
transformation_name = stream_query->transform_name_]() mutable {
|
||||
interpreter_context->streams.Create(stream_name, query::StreamInfo{.topics = std::move(topic_names),
|
||||
.consumer_group = std::move(consumer_group),
|
||||
.batch_interval = batch_interval,
|
||||
.batch_size = batch_size,
|
||||
.transformation_name = transformation_name});
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
callback.fn =
|
||||
[interpreter_context, stream_name = stream_query->stream_name_, topic_names = stream_query->topic_names_,
|
||||
consumer_group = std::move(consumer_group),
|
||||
batch_interval = GetOptionalValue<std::chrono::milliseconds>(stream_query->batch_interval_, evaluator),
|
||||
batch_size = GetOptionalValue<int64_t>(stream_query->batch_size_, evaluator),
|
||||
transformation_name = stream_query->transform_name_, owner = StringPointerToOptional(username)]() mutable {
|
||||
interpreter_context->streams.Create(stream_name,
|
||||
query::StreamInfo{.topics = std::move(topic_names),
|
||||
.consumer_group = std::move(consumer_group),
|
||||
.batch_interval = batch_interval,
|
||||
.batch_size = batch_size,
|
||||
.transformation_name = std::move(transformation_name),
|
||||
.owner = std::move(owner)});
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::START_STREAM: {
|
||||
@@ -535,8 +543,8 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::SHOW_STREAMS: {
|
||||
callback.header = {"name", "topics", "consumer_group", "batch_interval", "batch_size", "transformation_name",
|
||||
"is running"};
|
||||
callback.header = {"name", "topics", "consumer_group", "batch_interval", "batch_size", "transformation_name",
|
||||
"owner", "is running"};
|
||||
callback.fn = [interpreter_context]() {
|
||||
auto streams_status = interpreter_context->streams.GetStreamInfo();
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
@@ -565,6 +573,11 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
typed_status.emplace_back();
|
||||
}
|
||||
typed_status.emplace_back(stream_info.transformation_name);
|
||||
if (stream_info.owner.has_value()) {
|
||||
typed_status.emplace_back(*stream_info.owner);
|
||||
} else {
|
||||
typed_status.emplace_back();
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto &status : streams_status) {
|
||||
@@ -1231,16 +1244,17 @@ TriggerEventType ToTriggerEventType(const TriggerQuery::EventType event_type) {
|
||||
|
||||
Callback CreateTrigger(TriggerQuery *trigger_query,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba) {
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba, std::optional<std::string> owner) {
|
||||
return {
|
||||
{},
|
||||
[trigger_name = std::move(trigger_query->trigger_name_), trigger_statement = std::move(trigger_query->statement_),
|
||||
event_type = trigger_query->event_type_, before_commit = trigger_query->before_commit_, interpreter_context, dba,
|
||||
user_parameters]() -> std::vector<std::vector<TypedValue>> {
|
||||
user_parameters, owner = std::move(owner)]() mutable -> std::vector<std::vector<TypedValue>> {
|
||||
interpreter_context->trigger_store.AddTrigger(
|
||||
trigger_name, trigger_statement, user_parameters, ToTriggerEventType(event_type),
|
||||
std::move(trigger_name), trigger_statement, user_parameters, ToTriggerEventType(event_type),
|
||||
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT, &interpreter_context->ast_cache,
|
||||
dba, &interpreter_context->antlr_lock, interpreter_context->config.query);
|
||||
dba, &interpreter_context->antlr_lock, interpreter_context->config.query, std::move(owner),
|
||||
interpreter_context->auth_checker);
|
||||
return {};
|
||||
}};
|
||||
}
|
||||
@@ -1255,7 +1269,7 @@ Callback DropTrigger(TriggerQuery *trigger_query, InterpreterContext *interprete
|
||||
}
|
||||
|
||||
Callback ShowTriggers(InterpreterContext *interpreter_context) {
|
||||
return {{"trigger name", "statement", "event type", "phase"}, [interpreter_context] {
|
||||
return {{"trigger name", "statement", "event type", "phase", "owner"}, [interpreter_context] {
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
auto trigger_infos = interpreter_context->trigger_store.GetTriggerInfo();
|
||||
results.reserve(trigger_infos.size());
|
||||
@@ -1267,6 +1281,9 @@ Callback ShowTriggers(InterpreterContext *interpreter_context) {
|
||||
typed_trigger_info.emplace_back(TriggerEventTypeToString(trigger_info.event_type));
|
||||
typed_trigger_info.emplace_back(trigger_info.phase == TriggerPhase::BEFORE_COMMIT ? "BEFORE COMMIT"
|
||||
: "AFTER COMMIT");
|
||||
typed_trigger_info.emplace_back(trigger_info.owner.has_value() ? TypedValue{*trigger_info.owner}
|
||||
: TypedValue{});
|
||||
|
||||
results.push_back(std::move(typed_trigger_info));
|
||||
}
|
||||
|
||||
@@ -1276,7 +1293,8 @@ Callback ShowTriggers(InterpreterContext *interpreter_context) {
|
||||
|
||||
PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters) {
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
const std::string *username) {
|
||||
if (in_explicit_transaction) {
|
||||
throw TriggerModificationInMulticommandTxException();
|
||||
}
|
||||
@@ -1284,11 +1302,12 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, const bool in_explic
|
||||
auto *trigger_query = utils::Downcast<TriggerQuery>(parsed_query.query);
|
||||
MG_ASSERT(trigger_query);
|
||||
|
||||
auto callback = [trigger_query, interpreter_context, dba, &user_parameters] {
|
||||
auto callback = [trigger_query, interpreter_context, dba, &user_parameters,
|
||||
owner = StringPointerToOptional(username)]() mutable {
|
||||
switch (trigger_query->action_) {
|
||||
case TriggerQuery::Action::CREATE_TRIGGER:
|
||||
EventCounter::IncrementCounter(EventCounter::TriggersCreated);
|
||||
return CreateTrigger(trigger_query, user_parameters, interpreter_context, dba);
|
||||
return CreateTrigger(trigger_query, user_parameters, interpreter_context, dba, std::move(owner));
|
||||
case TriggerQuery::Action::DROP_TRIGGER:
|
||||
return DropTrigger(trigger_query, interpreter_context);
|
||||
case TriggerQuery::Action::SHOW_TRIGGERS:
|
||||
@@ -1315,14 +1334,15 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, const bool in_explic
|
||||
|
||||
PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters) {
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
const std::string *username) {
|
||||
if (in_explicit_transaction) {
|
||||
throw StreamQueryInMulticommandTxException();
|
||||
}
|
||||
|
||||
auto *stream_query = utils::Downcast<StreamQuery>(parsed_query.query);
|
||||
MG_ASSERT(stream_query);
|
||||
auto callback = HandleStreamQuery(stream_query, parsed_query.parameters, interpreter_context, dba);
|
||||
auto callback = HandleStreamQuery(stream_query, parsed_query.parameters, interpreter_context, dba, username);
|
||||
|
||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||
@@ -1651,7 +1671,8 @@ void Interpreter::RollbackTransaction() {
|
||||
}
|
||||
|
||||
Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
const std::map<std::string, storage::PropertyValue> ¶ms) {
|
||||
const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||
const std::string *username) {
|
||||
if (!in_explicit_transaction_) {
|
||||
query_executions_.clear();
|
||||
}
|
||||
@@ -1748,10 +1769,10 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
prepared_query = PrepareFreeMemoryQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_);
|
||||
} else if (utils::Downcast<TriggerQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareTriggerQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
|
||||
&*execution_db_accessor_, params);
|
||||
&*execution_db_accessor_, params, username);
|
||||
} else if (utils::Downcast<StreamQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareStreamQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
|
||||
&*execution_db_accessor_, params);
|
||||
&*execution_db_accessor_, params, username);
|
||||
} else if (utils::Downcast<IsolationLevelQuery>(parsed_query.query)) {
|
||||
prepared_query =
|
||||
PrepareIsolationLevelQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_, this);
|
||||
@@ -1809,7 +1830,7 @@ void RunTriggersIndividually(const utils::SkipList<Trigger> &triggers, Interpret
|
||||
trigger_context.AdaptForAccessor(&db_accessor);
|
||||
try {
|
||||
trigger.Execute(&db_accessor, &execution_memory, interpreter_context->config.execution_timeout_sec,
|
||||
&interpreter_context->is_shutting_down, trigger_context);
|
||||
&interpreter_context->is_shutting_down, trigger_context, interpreter_context->auth_checker);
|
||||
} catch (const utils::BasicException &exception) {
|
||||
spdlog::warn("Trigger '{}' failed with exception:\n{}", trigger.Name(), exception.what());
|
||||
db_accessor.Abort();
|
||||
@@ -1864,7 +1885,7 @@ void Interpreter::Commit() {
|
||||
AdvanceCommand();
|
||||
try {
|
||||
trigger.Execute(&*execution_db_accessor_, &execution_memory, interpreter_context_->config.execution_timeout_sec,
|
||||
&interpreter_context_->is_shutting_down, *trigger_context);
|
||||
&interpreter_context_->is_shutting_down, *trigger_context, interpreter_context_->auth_checker);
|
||||
} catch (const utils::BasicException &e) {
|
||||
throw utils::BasicException(
|
||||
fmt::format("Trigger '{}' caused the transaction to fail.\nException: {}", trigger.Name(), e.what()));
|
||||
|
||||
Reference in New Issue
Block a user