dragonfly/src/server/acl/validator.cc
2024-07-19 14:23:46 +03:00

91 lines
2.6 KiB
C++

// Copyright 2023, DragonflyDB authors. All rights reserved.
// See LICENSE for licensing terms.
//
#include "server/acl/validator.h"
#include "base/logging.h"
#include "facade/dragonfly_connection.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h"
#include "server/server_state.h"
#include "server/transaction.h"
// we need this because of stringmatchlen
extern "C" {
#include "redis/util.h"
}
namespace dfly::acl {
[[nodiscard]] bool IsUserAllowedToInvokeCommand(const ConnectionContext& cntx, const CommandId& id,
CmdArgList tail_args) {
if (cntx.skip_acl_validation) {
return true;
}
const auto [is_authed, reason] =
IsUserAllowedToInvokeCommandGeneric(cntx.acl_commands, cntx.keys, tail_args, id);
if (!is_authed) {
auto& log = ServerState::tlocal()->acl_log;
log.Add(cntx, std::string(id.name()), reason);
}
return is_authed;
}
// GCC yields a wrong warning about uninitialized optional use
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
[[nodiscard]] std::pair<bool, AclLog::Reason> IsUserAllowedToInvokeCommandGeneric(
const std::vector<uint64_t>& acl_commands, const AclKeys& keys, CmdArgList tail_args,
const CommandId& id) {
const size_t index = id.GetFamily();
const uint64_t command_mask = id.GetBitIndex();
DCHECK_LT(index, acl_commands.size());
const bool command = (acl_commands[index] & command_mask) != 0;
if (!command) {
return {false, AclLog::Reason::COMMAND};
}
auto match = [](const auto& pattern, const auto& target) {
return stringmatchlen(pattern.data(), pattern.size(), target.data(), target.size(), 0);
};
const bool is_read_command = id.IsReadOnly();
const bool is_write_command = id.IsWriteOnly();
auto iterate_globs = [&](auto target) {
for (auto& [elem, op] : keys.key_globs) {
if (match(elem, target)) {
if (is_read_command && (op == KeyOp::READ || op == KeyOp::READ_WRITE)) {
return true;
}
if (is_write_command && (op == KeyOp::WRITE || op == KeyOp::READ_WRITE)) {
return true;
}
}
}
return false;
};
bool keys_allowed = true;
if (!keys.all_keys && id.first_key_pos() != 0 && (is_read_command || is_write_command)) {
auto keys_index = DetermineKeys(&id, tail_args);
DCHECK(keys_index);
for (std::string_view key : keys_index->Range(tail_args))
keys_allowed &= iterate_globs(key);
}
return {keys_allowed, AclLog::Reason::KEY};
}
#pragma GCC diagnostic pop
} // namespace dfly::acl