1
0
Fork 0
mirror of https://github.com/dragonflydb/dragonfly.git synced 2024-12-14 11:58:02 +00:00

chore(acl): Implicit categories (#3411)

Most of our CO:: categories became meaningless with the introduction of acl. For example, CO::FAST literally doesn't mean anything, it's never read or used.

* add implicit categories

---------

Signed-off-by: Vladislav Oleshko <vlad@dragonflydb.io>
This commit is contained in:
Vladislav 2024-11-08 13:40:56 +03:00 committed by GitHub
parent c9537bb52e
commit b860b712c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 95 additions and 68 deletions

View file

@ -37,11 +37,48 @@ using absl::StrAppend;
using absl::StrCat; using absl::StrCat;
using absl::StrSplit; using absl::StrSplit;
CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, namespace {
int8_t last_key, uint32_t acl_categories)
: facade::CommandId(name, mask, arity, first_key, last_key, acl_categories) { uint32_t ImplicitCategories(uint32_t mask) {
if (mask & CO::ADMIN) if (mask & CO::ADMIN)
opt_mask_ |= CO::NOSCRIPT; mask |= CO::NOSCRIPT;
return mask;
}
uint32_t ImplicitAclCategories(uint32_t mask) {
mask = ImplicitCategories(mask);
uint32_t out = 0;
if (mask & CO::WRITE)
out |= acl::WRITE;
if ((mask & CO::READONLY) && ((mask & CO::NOSCRIPT) == 0))
out |= acl::READ;
if (mask & CO::ADMIN)
out |= acl::ADMIN | acl::DANGEROUS;
// todo pubsub
if (mask & CO::FAST)
out |= acl::FAST;
if (mask & CO::BLOCKING)
out |= acl::BLOCKING;
if ((out & acl::FAST) == 0)
out |= acl::SLOW;
return out;
}
} // namespace
CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key,
int8_t last_key, std::optional<uint32_t> acl_categories)
: facade::CommandId(name, ImplicitCategories(mask), arity, first_key, last_key,
acl_categories.value_or(ImplicitAclCategories(mask))) {
implicit_acl_ = !acl_categories.has_value();
} }
bool CommandId::IsTransactional() const { bool CommandId::IsTransactional() const {
@ -152,6 +189,9 @@ CommandRegistry& CommandRegistry::operator<<(CommandId cmd) {
} }
cmd.SetFamily(family_of_commands_.size() - 1); cmd.SetFamily(family_of_commands_.size() - 1);
if (acl_category_)
cmd.SetAclCategory(*acl_category_);
if (!is_sub_command || absl::StartsWith(cmd.name(), "ACL")) { if (!is_sub_command || absl::StartsWith(cmd.name(), "ACL")) {
cmd.SetBitIndex(1ULL << bit_index_); cmd.SetBitIndex(1ULL << bit_index_);
family_of_commands_.back().push_back(std::string(k)); family_of_commands_.back().push_back(std::string(k));
@ -165,9 +205,10 @@ CommandRegistry& CommandRegistry::operator<<(CommandId cmd) {
return *this; return *this;
} }
void CommandRegistry::StartFamily() { void CommandRegistry::StartFamily(std::optional<uint32_t> acl_category) {
family_of_commands_.push_back({}); family_of_commands_.emplace_back();
bit_index_ = 0; bit_index_ = 0;
acl_category_ = acl_category;
} }
std::string_view CommandRegistry::RenamedOrOriginal(std::string_view orig) const { std::string_view CommandRegistry::RenamedOrOriginal(std::string_view orig) const {

View file

@ -77,7 +77,7 @@ class CommandId : public facade::CommandId {
// NOTICE: name must be a literal string, otherwise metrics break! (see cmd_stats_map in // NOTICE: name must be a literal string, otherwise metrics break! (see cmd_stats_map in
// server_state.h) // server_state.h)
CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, int8_t last_key, CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, int8_t last_key,
uint32_t acl_categories); std::optional<uint32_t> acl_categories = std::nullopt);
CommandId(CommandId&&) = default; CommandId(CommandId&&) = default;
@ -146,7 +146,13 @@ class CommandId : public facade::CommandId {
return command_stats_[thread_index]; return command_stats_[thread_index];
} }
void SetAclCategory(uint32_t mask) {
if (implicit_acl_)
acl_categories_ |= mask;
}
private: private:
bool implicit_acl_;
std::unique_ptr<CmdCallStats[]> command_stats_; std::unique_ptr<CmdCallStats[]> command_stats_;
Handler handler_; Handler handler_;
ArgValidator validator_; ArgValidator validator_;
@ -194,7 +200,7 @@ class CommandRegistry {
} }
} }
void StartFamily(); void StartFamily(std::optional<uint32_t> acl_category = std::nullopt);
std::string_view RenamedOrOriginal(std::string_view orig) const; std::string_view RenamedOrOriginal(std::string_view orig) const;
@ -212,6 +218,7 @@ class CommandRegistry {
FamiliesVec family_of_commands_; FamiliesVec family_of_commands_;
size_t bit_index_; size_t bit_index_;
std::optional<uint32_t> acl_category_; // category of family currently being built
}; };
} // namespace dfly } // namespace dfly

View file

@ -2487,8 +2487,8 @@ void Service::Command(CmdArgList args, Transaction* tx, SinkReplyBuilder* builde
}); });
auto* rb = static_cast<RedisReplyBuilder*>(builder); auto* rb = static_cast<RedisReplyBuilder*>(builder);
auto serialize_command = [&rb](string_view name, const CommandId& cid) { auto serialize_command = [&rb, this](string_view name, const CommandId& cid) {
rb->StartArray(6); rb->StartArray(7);
rb->SendSimpleString(cid.name()); rb->SendSimpleString(cid.name());
rb->SendLong(cid.arity()); rb->SendLong(cid.arity());
rb->StartArray(CommandId::OptCount(cid.opt_mask())); rb->StartArray(CommandId::OptCount(cid.opt_mask()));
@ -2504,6 +2504,17 @@ void Service::Command(CmdArgList args, Transaction* tx, SinkReplyBuilder* builde
rb->SendLong(cid.first_key_pos()); rb->SendLong(cid.first_key_pos());
rb->SendLong(cid.last_key_pos()); rb->SendLong(cid.last_key_pos());
rb->SendLong(cid.opt_mask() & CO::INTERLEAVED_KEYS ? 2 : 1); rb->SendLong(cid.opt_mask() & CO::INTERLEAVED_KEYS ? 2 : 1);
{
const auto& table = acl_family_.GetRevTable();
vector<string> cats;
for (uint32_t i = 0; i < 32; i++) {
if (cid.acl_categories() & (1 << i)) {
cats.emplace_back("@" + table[i]);
}
}
rb->SendSimpleStrArr(cats);
}
}; };
// If no arguments are specified, reply with all commands // If no arguments are specified, reply with all commands

View file

@ -1530,68 +1530,36 @@ void StringFamily::ClThrottle(CmdArgList args, Transaction* tx, SinkReplyBuilder
#define HFUNC(x) SetHandler(&StringFamily::x) #define HFUNC(x) SetHandler(&StringFamily::x)
namespace acl {
constexpr uint32_t kSet = WRITE | STRING | SLOW;
constexpr uint32_t kSetEx = WRITE | STRING | SLOW;
constexpr uint32_t kPSetEx = WRITE | STRING | SLOW;
constexpr uint32_t kSetNx = WRITE | STRING | FAST;
constexpr uint32_t kAppend = WRITE | STRING | FAST;
constexpr uint32_t kPrepend = WRITE | STRING | FAST;
constexpr uint32_t kIncr = WRITE | STRING | FAST;
constexpr uint32_t kDecr = WRITE | STRING | FAST;
constexpr uint32_t kIncrBy = WRITE | STRING | FAST;
constexpr uint32_t kIncrByFloat = WRITE | STRING | FAST;
constexpr uint32_t kDecrBy = WRITE | STRING | FAST;
constexpr uint32_t kGet = READ | STRING | FAST;
constexpr uint32_t kGetDel = WRITE | STRING | FAST;
constexpr uint32_t kGetEx = WRITE | STRING | FAST;
constexpr uint32_t kGetSet = WRITE | STRING | FAST;
constexpr uint32_t kMGet = READ | STRING | FAST;
constexpr uint32_t kMSet = WRITE | STRING | SLOW;
constexpr uint32_t kMSetNx = WRITE | STRING | SLOW;
constexpr uint32_t kStrLen = READ | STRING | FAST;
constexpr uint32_t kGetRange = READ | STRING | SLOW;
constexpr uint32_t kSubStr = READ | STRING | SLOW;
constexpr uint32_t kSetRange = WRITE | STRING | SLOW;
// ClThrottle is a module in redis. Therefore we introduce a new extension
// to the category. We should consider other defaults as well
constexpr uint32_t kClThrottle = THROTTLE;
} // namespace acl
void StringFamily::Register(CommandRegistry* registry) { void StringFamily::Register(CommandRegistry* registry) {
constexpr uint32_t kMSetMask = constexpr uint32_t kMSetMask =
CO::WRITE | CO::DENYOOM | CO::INTERLEAVED_KEYS | CO::NO_AUTOJOURNAL; CO::WRITE | CO::DENYOOM | CO::INTERLEAVED_KEYS | CO::NO_AUTOJOURNAL;
registry->StartFamily(); registry->StartFamily(acl::STRING);
*registry *registry << CI{"SET", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, 1}.HFUNC(Set)
<< CI{"SET", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, 1, acl::kSet}.HFUNC(Set) << CI{"SETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1}.HFUNC(SetEx)
<< CI{"SETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, acl::kSetEx}.HFUNC( << CI{"PSETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1}.HFUNC(PSetEx)
SetEx) << CI{"SETNX", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1}.HFUNC(SetNx)
<< CI{"PSETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, acl::kPSetEx}.HFUNC( << CI{"APPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1}.HFUNC(Append)
PSetEx) << CI{"PREPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1}.HFUNC(Prepend)
<< CI{"SETNX", CO::WRITE | CO::DENYOOM, 3, 1, 1, acl::kSetNx}.HFUNC(SetNx) << CI{"INCR", CO::WRITE | CO::FAST, 2, 1, 1}.HFUNC(Incr)
<< CI{"APPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, acl::kAppend}.HFUNC(Append) << CI{"DECR", CO::WRITE | CO::FAST, 2, 1, 1}.HFUNC(Decr)
<< CI{"PREPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, acl::kPrepend}.HFUNC(Prepend) << CI{"INCRBY", CO::WRITE | CO::FAST, 3, 1, 1}.HFUNC(IncrBy)
<< CI{"INCR", CO::WRITE | CO::FAST, 2, 1, 1, acl::kIncr}.HFUNC(Incr) << CI{"INCRBYFLOAT", CO::WRITE | CO::FAST, 3, 1, 1}.HFUNC(IncrByFloat)
<< CI{"DECR", CO::WRITE | CO::FAST, 2, 1, 1, acl::kDecr}.HFUNC(Decr) << CI{"DECRBY", CO::WRITE | CO::FAST, 3, 1, 1}.HFUNC(DecrBy)
<< CI{"INCRBY", CO::WRITE | CO::FAST, 3, 1, 1, acl::kIncrBy}.HFUNC(IncrBy) << CI{"GET", CO::READONLY | CO::FAST, 2, 1, 1}.HFUNC(Get)
<< CI{"INCRBYFLOAT", CO::WRITE | CO::FAST, 3, 1, 1, acl::kIncrByFloat}.HFUNC(IncrByFloat) << CI{"GETDEL", CO::WRITE | CO::FAST, 2, 1, 1}.HFUNC(GetDel)
<< CI{"DECRBY", CO::WRITE | CO::FAST, 3, 1, 1, acl::kDecrBy}.HFUNC(DecrBy) << CI{"GETEX", CO::WRITE | CO::DENYOOM | CO::FAST | CO::NO_AUTOJOURNAL, -2, 1, 1}.HFUNC(
<< CI{"GET", CO::READONLY | CO::FAST, 2, 1, 1, acl::kGet}.HFUNC(Get) GetEx)
<< CI{"GETDEL", CO::WRITE | CO::FAST, 2, 1, 1, acl::kGetDel}.HFUNC(GetDel) << CI{"GETSET", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1}.HFUNC(GetSet)
<< CI{"GETEX", CO::WRITE | CO::DENYOOM | CO::FAST | CO::NO_AUTOJOURNAL, -2, 1, 1, acl::kGetEx} << CI{"MGET", CO::READONLY | CO::FAST | CO::IDEMPOTENT, -2, 1, -1}.HFUNC(MGet)
.HFUNC(GetEx) << CI{"MSET", kMSetMask, -3, 1, -1}.HFUNC(MSet)
<< CI{"GETSET", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, acl::kGetSet}.HFUNC(GetSet) << CI{"MSETNX", kMSetMask, -3, 1, -1}.HFUNC(MSetNx)
<< CI{"MGET", CO::READONLY | CO::FAST | CO::IDEMPOTENT, -2, 1, -1, acl::kMGet}.HFUNC(MGet) << CI{"STRLEN", CO::READONLY | CO::FAST, 2, 1, 1}.HFUNC(StrLen)
<< CI{"MSET", kMSetMask, -3, 1, -1, acl::kMSet}.HFUNC(MSet) << CI{"GETRANGE", CO::READONLY, 4, 1, 1}.HFUNC(GetRange)
<< CI{"MSETNX", kMSetMask, -3, 1, -1, acl::kMSetNx}.HFUNC(MSetNx) << CI{"SUBSTR", CO::READONLY, 4, 1, 1}.HFUNC(GetRange) // Alias for GetRange
<< CI{"STRLEN", CO::READONLY | CO::FAST, 2, 1, 1, acl::kStrLen}.HFUNC(StrLen) << CI{"SETRANGE", CO::WRITE | CO::DENYOOM, 4, 1, 1}.HFUNC(SetRange)
<< CI{"GETRANGE", CO::READONLY | CO::FAST, 4, 1, 1, acl::kGetRange}.HFUNC(GetRange) << CI{"CL.THROTTLE", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, acl::THROTTLE}.HFUNC(
<< CI{"SUBSTR", CO::READONLY | CO::FAST, 4, 1, 1, acl::kSubStr}.HFUNC( ClThrottle);
GetRange) // Alias for GetRange
<< CI{"SETRANGE", CO::WRITE | CO::FAST | CO::DENYOOM, 4, 1, 1, acl::kSetRange}.HFUNC(SetRange)
<< CI{"CL.THROTTLE", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, acl::kClThrottle}.HFUNC(
ClThrottle);
} }
} // namespace dfly } // namespace dfly